locked
C# Usercontrol dosen't work on VB.NET solution is possible? RRS feed

  • Question

  • Hi to all the ML.

    Recently I've found this very interesting control:

    http://www.codeproject.com/Articles/626132/WPF-MultiRangeSlider-Control

    I've tried to use it in a C# solution and it works fine. Then I've made a VB.NET solution for using it in a application I'm using for work and it dosen't work. It'possibile?



    • Edited by Jotric1978 Wednesday, January 15, 2014 2:43 PM
    Wednesday, January 15, 2014 2:42 PM

Answers

  • Hi,

    there is no initialization problem (as far my code is concerned).

    The PropertyChanged event has no function in the constructor. At that time there are no subscribers. The C# delegates variant results just in a superfluous dummy call - a reason I don't like that construct just to spare a null check.

    If you want to save some coding add a method like:

        ' Attribute requires .NET 4.5
        Protected Sub NotifyPropertyChanged(<System.Runtime.CompilerServices.CallerMemberNameAttribute> Optional propertyName As String = Nothing)
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
        End Sub
    

    Now you can replace the RaiseEvent - e. g. for the RangeItemViewModel From property:

    Public Property From As Integer
        Get
            Return _from
        End Get
        Set(value As Integer)
            If _from <> value Then
                _from = value
                NotifyPropertyChanged()
                'RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("From"))
            End If
        End Set
    End Property
    

    But that doesn't change the behavior in any way.

    As I don't know what's going wrong on your side, I've moved the code into stand-alone project and you can download it from SkyDrive:

    http://sdrv.ms/1acO5aw

    As a little plus I added the second slider example, that I left out yesterday as it requires two additional classes.

    Regards, Elmar

    • Marked as answer by Jotric1978 Friday, January 17, 2014 4:18 PM
    Friday, January 17, 2014 2:54 PM

All replies

  • Hello,

    Is your project a WPF or Windows form project ? I ask because you did not indicate which one and the code you are looking at is WPF.


    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem.

    Wednesday, January 15, 2014 3:11 PM
  • Sorry... both of solutions are WPF
    Wednesday, January 15, 2014 3:23 PM
  • is this usercontrol in a seperate project within your solution or are you referencing a DLL
    Wednesday, January 15, 2014 3:26 PM
  • Yes. If you want I can send you the project...
    Wednesday, January 15, 2014 3:35 PM
  • Yes. If you want I can send you the project...

    You answered "yes" to two different questions.  =P

    The simple answer should be to compile the code from that link into its own assembly and then reference that compiled assembly from your VB project.  If you get an error at that point, please post back and include the exception message text.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Wednesday, January 15, 2014 3:51 PM
  • You answered "yes" to two different questions.  =P

    Sorry...

    I've already done this steps and the solutions compiled are error free...

    When I try to initialize the control in the VB.NET solution (in debug mode) I can see that the compiler stops and exits. The same istruction in C# works fine. But no errors appear

    Wednesday, January 15, 2014 3:59 PM
  • Show then the code where it stops and exits and the likewise C# code.

    By the way, that crazy chain button is to insert links. In some browsers like IE your link is difficult to copy so that means less who look at it.


    Success
    Cor

    Wednesday, January 15, 2014 7:08 PM
  • The C# code is this: 

    private readonly ObservableContentCollection<RangeItemViewModel> m_rangeItems;
    
             public  ObservableContentCollection<RangeItemViewModel> RangeItems
            {
                get { return m_rangeItems; }
            }
    
    
    #region Constructors
    
            public ApplicationViewModel()
            {
                m_rangeItems = new ObservableContentCollection<RangeItemViewModel>
                                   {
                                       new RangeItemViewModel {From = 0, To = 13, Name = "BoundRange0"},
                                       new RangeItemViewModel {From = 13, To = 17, Name = "BoundRange1"},
                                   };
                m_insertRangeCmd = new DelegateCommand(x => InsertRange((int)(double)x));
            }
    #endregion

    The same code writed in VB.NET is:

     Private ReadOnly m_rangeItems As ObservableContentCollection(Of RangeItemViewModel)
        Public ReadOnly Property RangeItems() As ObservableContentCollection(Of RangeItemViewModel)
            Get
                Return m_rangeItems
            End Get
        End Property
    
    
    #Region "Constructors"
    
        Public Sub New()
            m_rangeItems = New ObservableContentCollection(Of RangeItemViewModel) From { _
                New RangeItemViewModel With {.From = 0, .To = 13, .Name = "BoundRange0"}, _
                New RangeItemViewModel With {.From = 13, .To = 17, .Name = "BoundRange1"}}
    
            m_insertRangeCmd = New DelegateCommand(Sub(x) InsertRange(CInt(Math.Truncate(CDbl(x)))))
        End Sub
    #End Region

    The compiler stops when I try to insert items  in the constructor and then exits. The instruction m_insertRangeCmd is not executed .


    • Edited by Jotric1978 Thursday, January 16, 2014 11:13 AM
    Thursday, January 16, 2014 7:11 AM
  • It is not the same, the casting goes in another way and in my idea there is no equivalent, this are things C# accept more. (In other circumstances VB accept more, that are the slight differences). 

    Try the Fix function.

    http://msdn.microsoft.com/en-us/library/xh29swte(v=vs.90).aspx

    Otherwise try the Tangible converter

    http://www.tangiblesoftwaresolutions.com/Product_Details/Instant_CSharp.html

    Which does not help you, I see now, because you have used that probably.


    Success
    Cor


    • Edited by Cor Ligthert Thursday, January 16, 2014 11:22 AM
    Thursday, January 16, 2014 11:16 AM
  • sorry but maybe I didn't understand.Where should I use fix?

    The compiler stops work in the assignment of m_rageItems...

    Yes, I usually use the converter... but in this case I'm unable to understand the differences?



    • Edited by Jotric1978 Thursday, January 16, 2014 11:54 AM
    Thursday, January 16, 2014 11:37 AM
  • Hi,

    If you want to use the C# assemblies you must reference InWit.WPF.MultiRangeSlider.dll and InWit.Core.dll in your Visual Basic Project. The assemblies use .NET 4.5.

    The converted classes from the GUI Project:

    Imports System
    Imports System.Linq
    Imports System.Collections.ObjectModel
    Imports System.ComponentModel
    Imports System.Windows.Input
    Imports InWit.Core.Collections
    
    Public Class RangeViewModel
        Implements INotifyPropertyChanged
    
        Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    
        Private ReadOnly m_rangeItems As ObservableContentCollection(Of RangeItemViewModel)
    
        Private m_selectedRange As RangeItemViewModel
    
        Private ReadOnly m_unboundRange1 As RangeItemViewModel
        Private ReadOnly m_unboundRange2 As RangeItemViewModel
        Private ReadOnly m_unboundRange3 As RangeItemViewModel
        Private ReadOnly m_unboundRange4 As RangeItemViewModel
    
        Private ReadOnly m_insertRangeCmd As ICommand   ' was Command (but ICommand is more common)
    
    
        Public Sub New()
            m_rangeItems = New ObservableContentCollection(Of RangeItemViewModel)() From
            {
                New RangeItemViewModel() With {.From = 0, .[To] = 13, .Name = "BoundRange0"},
                New RangeItemViewModel() With {.From = 13, .[To] = 17, .Name = "BoundRange1"}
            }
    
            m_unboundRange1 = New RangeItemViewModel() With {.From = 200, .[To] = 500, .Name = "UnboundRange1"}
            m_unboundRange2 = New RangeItemViewModel() With {.From = 500, .[To] = 700, .Name = "UnboundRange1"}
            m_unboundRange3 = New RangeItemViewModel() With {.From = 700, .[To] = 1100, .Name = "UnboundRange1"}
            m_unboundRange4 = New RangeItemViewModel() With {.From = 1100, .[To] = 1500, .Name = "UnboundRange1"}
    
            ' changed from DelegateCommand to RelayCommand as I had it already 
            m_insertRangeCmd = New RelayCommand(Sub(value) InsertRange(CInt(CDbl(value))))
        End Sub
    
        Private Sub InsertRange(level As Integer)
            If level > m_rangeItems.Last().[To] Then
                InsertRightRange(level)
            ElseIf level < m_rangeItems.First().From Then
                InsertLeftRange(level)
            Else
                Dim previousRange = m_rangeItems.First(Function(x) x.[To] >= level)
    
                Dim newRange = New RangeItemViewModel() With {
                    .From = level,
                    .[To] = previousRange.[To],
                    .Name = String.Format("BoundRange{0}", m_rangeItems.Count)}
    
                m_rangeItems.Insert(m_rangeItems.IndexOf(previousRange) + 1, newRange)
    
                previousRange.[To] = level
            End If
    
        End Sub
    
        Private Sub InsertRightRange(level As Integer)
            Dim rightRange = New RangeItemViewModel() With {
                 .From = m_rangeItems.Last().[To],
                 .[To] = level,
                 .Name = String.Format("BoundRange{0}", m_rangeItems.Count)}
    
            m_rangeItems.Add(rightRange)
        End Sub
    
        Private Sub InsertLeftRange(level As Integer)
            Dim leftRange = New RangeItemViewModel() With {
                .From = level,
                .[To] = m_rangeItems.First().From,
                .Name = String.Format("BoundRange{0}", m_rangeItems.Count)}
    
            m_rangeItems.Insert(0, leftRange)
        End Sub
    
        Public ReadOnly Property RangeItems() As ObservableCollection(Of RangeItemViewModel)
            Get
                Return m_rangeItems
            End Get
        End Property
    
        Public Property SelectedRange() As RangeItemViewModel
            Get
                Return m_selectedRange
            End Get
            Set(value As RangeItemViewModel)
                m_selectedRange = value
                RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("SelectedRange"))
            End Set
        End Property
    
        Public ReadOnly Property InsertRangeCmd() As ICommand
            Get
                Return m_insertRangeCmd
            End Get
        End Property
    
        Public ReadOnly Property UnboundRange1() As RangeItemViewModel
            Get
                Return m_unboundRange1
            End Get
        End Property
    
        Public ReadOnly Property UnboundRange2() As RangeItemViewModel
            Get
                Return m_unboundRange2
            End Get
        End Property
    
        Public ReadOnly Property UnboundRange3() As RangeItemViewModel
            Get
                Return m_unboundRange3
            End Get
        End Property
    
        Public ReadOnly Property UnboundRange4() As RangeItemViewModel
            Get
                Return m_unboundRange4
            End Get
        End Property
    End Class
    
    Public Class RangeItemViewModel
        Implements INotifyPropertyChanged
    
        Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    
        Private _from As Integer
        Private _to As Integer
        Private _name As String
    
        Public Property From As Integer
            Get
                Return _from
            End Get
            Set(value As Integer)
                If _from <> value Then
                    _from = value
                    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("From"))
                End If
            End Set
        End Property
    
        Public Property [To] As Integer
            Get
                Return _to
            End Get
            Set(value As Integer)
                If _to <> value Then
                    _to = value
                    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("To"))
                End If
            End Set
        End Property
        Public Property Name As String
            Get
                Return _name
            End Get
            Set(value As String)
                If _name <> value Then
                    _name = value
                    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Name"))
                End If
            End Set
        End Property
    End Class
    

    Note: I've used a RelayCommand instead of DelegateCommand, as it was already in my WPF Test project. If you are using a DelegateCommand change it in the constructor.

    For a quick test, I've used only the UnBoundMultiRangeSliderControl XAML from the project, but shortened the name to UnboundSliderControl:

    <UserControl x:Class="UnboundSliderControl"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                 xmlns:InWit="clr-namespace:InWit.WPF.MultiRangeSlider;assembly=InWit.WPF.MultiRangeSlider"
                 xmlns:ViewModel="clr-namespace:WpfVB2012"
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Label Grid.Row="0" Content="Unbound MultiRange slider" FontWeight="Bold"/>
            <TextBlock Grid.Row="1" Text="*You can not add new or remove slider thumb (new range)" Padding="5,0,0,0" TextWrapping="Wrap"/>
            <InWit:WitMultiRangeSlider Grid.Row="3" Margin="0,3,0,3" Minimum="0.0" Maximum="2200.0">
                <InWit:WitMultiRangeSlider.Items>
                    <InWit:WitMultiRangeSliderItem LeftValue="{Binding UnboundRange1.From, Mode=TwoWay}" RightValue="{Binding UnboundRange1.To, Mode=TwoWay}" />
                    <InWit:WitMultiRangeSliderItem LeftValue="{Binding UnboundRange2.From, Mode=TwoWay}" RightValue="{Binding UnboundRange2.To, Mode=TwoWay}"/>
                    <InWit:WitMultiRangeSliderItem LeftValue="{Binding UnboundRange3.From, Mode=TwoWay}" RightValue="{Binding UnboundRange3.To, Mode=TwoWay}"/>
                    <InWit:WitMultiRangeSliderItem LeftValue="{Binding UnboundRange4.From, Mode=TwoWay}" RightValue="{Binding UnboundRange4.To, Mode=TwoWay}"/>
                </InWit:WitMultiRangeSlider.Items>
            </InWit:WitMultiRangeSlider>
            <ListBox Grid.Row="4">
                <ListBox.Resources>
                    <DataTemplate DataType="{x:Type ViewModel:RangeItemViewModel}">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="40"/>
                                <ColumnDefinition Width="40"/>
                            </Grid.ColumnDefinitions>
    
                            <Label Grid.Column="0" Content="{Binding Name}"/>
                            <Label Grid.Column="1" Content="{Binding From}"/>
                            <Label Grid.Column="2" Content="{Binding To}"/>
                        </Grid>
    
                    </DataTemplate>
                </ListBox.Resources>
                <ListBox.Items>
                    <ListBoxItem Content="{Binding UnboundRange1}"/>
                    <ListBoxItem Content="{Binding UnboundRange2}"/>
                    <ListBoxItem Content="{Binding UnboundRange3}"/>
                    <ListBoxItem Content="{Binding UnboundRange4}"/>
                </ListBox.Items>
            </ListBox>
        </Grid>
    </UserControl>
    
    and embedded it in a Window:
    <Window x:Class="RangeSliderWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfVB2012" 
        Title="RangeSliderWindow" Height="300" Width="300">
        <Grid>
            <local:UnboundSliderControl Grid.Column="0" />
        </Grid>
    </Window>
    

    The windows code behind consists of:

    Public Class RangeSliderWindow
        Public Sub New()
            InitializeComponent()
            Me.DataContext = New RangeViewModel()
        End Sub
    End Class
    

    Regards, Elmar

    • Proposed as answer by Cor Ligthert Friday, January 17, 2014 3:57 PM
    Thursday, January 16, 2014 3:28 PM
  • Thanks for you complete answer.

    This seem works better but not at all.

    If you compare the original progect with this, you can see some differences of the behavior of the usercontrol.

    In particular the initialization of the control :

    m_unboundRange1 = new RangeItemViewModel { From = 200, To = 500, Name = "UnboundRange1" };

    Raise the event PropertyChangedEventHandler. In the VB.NET solution this dosen't happens. This means that the binding dosen't work for the firsts thumbs...

    Any suggestions?

    P.S. Where I can see somthing about GUI Project?


    • Edited by Jotric1978 Friday, January 17, 2014 10:05 AM
    Friday, January 17, 2014 9:26 AM
  • Hi,

    the GUI Project is part of the Codeproject Download as MultiRangeSliderTestApp.GUI.

    The Root Namespace of my test project was WpfVB2012 and can be found in some clr-namespace. The RangeViewModel is named ApplicationViewModel class in the demo project.

    All four sliders are working for me.

    RaiseEvent for PropertyChanged is also working for me - otherwise the numbers wouldn't change at all. I removed the FirePropertyChanged extension method as I hadn't the time to test if it would work correctly - because of the delegate {} in the initial code.

    One conversion error, that I've noticed later, happened in

    Public Class RangeViewModel
    	' ...
    	' was ObservableCollection 
        Public ReadOnly Property RangeItems() As ObservableContentCollection(Of RangeItemViewModel)
            Get
                Return m_rangeItems
            End Get
        End Property
    End Class

    but that shouldn't have effect here - as it also worked with a ObservableCollection I've used at the beginning.

    Regards, Elmar

    • Proposed as answer by Cor Ligthert Friday, January 17, 2014 3:57 PM
    Friday, January 17, 2014 10:49 AM
  • I can mouve all of thumbs, but there is an initialization problem.

    In the RangeViewModel code, the initialization of the control is:

            m_unboundRange1 = New RangeItemViewModel() With {.From = 200, .[To] = 500, .Name = "UnboundRange1"}
            m_unboundRange2 = New RangeItemViewModel() With {.From = 500, .[To] = 700, .Name = "UnboundRange1"}
            m_unboundRange3 = New RangeItemViewModel() With {.From = 700, .[To] = 1100, .Name = "UnboundRange1"}
            m_unboundRange4 = New RangeItemViewModel() With {.From = 1100, .[To] = 1500, .Name = "UnboundRange1"}

    But when i press F5 key, the result is this:

    As you can see...the text in the listbox is correct, but it haven't a realy corrispondence with the control. Infact if I want to set by the mouse the same values the result is...

    The only difference I can see is that in the C# solution the assignment istructions raise the event but not in the VB.net solution and I don't know way.

    I think I'm ok with the references because I have no errors in compiling.


    • Edited by Jotric1978 Friday, January 17, 2014 11:29 AM
    Friday, January 17, 2014 11:28 AM
  • Hi,

    there is no initialization problem (as far my code is concerned).

    The PropertyChanged event has no function in the constructor. At that time there are no subscribers. The C# delegates variant results just in a superfluous dummy call - a reason I don't like that construct just to spare a null check.

    If you want to save some coding add a method like:

        ' Attribute requires .NET 4.5
        Protected Sub NotifyPropertyChanged(<System.Runtime.CompilerServices.CallerMemberNameAttribute> Optional propertyName As String = Nothing)
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
        End Sub
    

    Now you can replace the RaiseEvent - e. g. for the RangeItemViewModel From property:

    Public Property From As Integer
        Get
            Return _from
        End Get
        Set(value As Integer)
            If _from <> value Then
                _from = value
                NotifyPropertyChanged()
                'RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("From"))
            End If
        End Set
    End Property
    

    But that doesn't change the behavior in any way.

    As I don't know what's going wrong on your side, I've moved the code into stand-alone project and you can download it from SkyDrive:

    http://sdrv.ms/1acO5aw

    As a little plus I added the second slider example, that I left out yesterday as it requires two additional classes.

    Regards, Elmar

    • Marked as answer by Jotric1978 Friday, January 17, 2014 4:18 PM
    Friday, January 17, 2014 2:54 PM
  • Thank's for your solution.

    It's works perfectly...now I try to study the differences of mine....

    • Marked as answer by Jotric1978 Friday, January 17, 2014 4:18 PM
    • Unmarked as answer by Jotric1978 Friday, January 17, 2014 4:18 PM
    Friday, January 17, 2014 3:14 PM
  • Thank's for your solution.

    It's works perfectly...now I try to study the differences of mine....

    Then mark all correct replies (I thought from Elmar alone) as answer for the benefit of others searching for a solution if they have also this problem.

    Success
    Cor

    Friday, January 17, 2014 3:51 PM
  • Maybe I'm too stupid or too tired...but I've compared all parts of your solution with mine and there are the same...

    I'm realy unable to find the differences...

    I've upload my solutiono here:

    https://skydrive.live.com/redir?resid=4D4C1AFBB7F20EE4%21128

    If you can view the differences...I'll pay you a beer :-)

    Friday, January 17, 2014 4:18 PM
  • Hi,

    Thanks for the solution as I was curious, what is the reason. And as always the simple things create the problems ;)

    Here: You've moved the initialization code from the constructor to the windows loaded event - open the MainWindow's code behind:

    Class MainWindow 
        Public Sub New()
            InitializeComponent()
            Me.DataContext = New RangeViewModel()
        End Sub
        ' Remove it - just to show the difference
        'Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
        '    Dim MainWindowViewModel As New RangeViewModel
        '    Me.DataContext = MainWindowViewModel
        '    InitializeComponent()
        'End Sub
    End Class
    

    Now the slider should work as expected.

    Regards, Elmar

    Friday, January 17, 2014 4:45 PM