Search code examples
.netwpfdata-binding

How do I bind to an object in my model to a user control in WPF?


I'm trying to populate labels in a user control. The code-behind my user control creates an object, and I'm trying to bind the label contents to properties in that object. The class creating the object implements INotifyPropertyChanges, and has several public properties. I created binding within the XAML, and Visual Studio has picked this up, with intellisense offering me the properties to bind to. However, when I run, the labels are all blank.
If I create a property directly in my code behind which just calls from the object (and appropriately change my datacontext), it works.

I'm not too experienced with WPF - I must have missed something but I just don't know what?!

When my XAML looks like this, neither label is populated. If I adjust the data context to the control rather than the instrument object, The ConStatus label appropriately populates (but does not update as the user control doesn't implement INotifyPropertyChange:

        <WrapPanel Grid.Row="1" DataContext="{Binding instrument}">
            <Label Name="PortName" Width="100" Content="{Binding ConnectionString}"  HorizontalAlignment="Right"/>
            <Label  Name="ConStatus" Width="200" Content="{Binding UniName}" HorizontalAlignment="Left"/>
        </WrapPanel>

My code-behind looks like this:

Public Class UnifaceControl
    Inherits UserControl

    Public instrument As UnifaceModel

    Sub New()
        ' This call is required by the designer.
        InitializeComponent()
        ' Add any initialization after the InitializeComponent() call.
        FindUniface("uniface")
    End Sub

    Public Sub FindUniface(Portname As String)
        instrument = New UnifaceModel(Portname)
        instrument.Connect()
    End Sub

    ReadOnly Property UniName
        Get
            Return instrument.Connected
        End Get
    End Property

End Class

And here's a sample of the UnifaceModel object:

Public Class UnifaceModel
    Implements INotifyPropertyChanged

    Shared Log As NLog.Logger = NLog.LogManager.GetCurrentClassLogger()

    Dim _ConnectionString As String
    Public Property ConnectionString As String
        Get
            Return _ConnectionString
        End Get
        Set(value As String)
            _ConnectionString = value
            OnPropertyChanged()
        End Set
    End Property

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Protected Sub OnPropertyChanged(<CallerMemberName> Optional propertyName As String = Nothing)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub

Solution

  • instrument must be a property for you to be able to bind to it:

    Public Property instrument As UnifaceModel
    

    But you also need to set the DataContext of the control or its parent window somewhere:

    Sub New()
        ' This call is required by the designer.
        InitializeComponent()
    
        ' Set the DataContext to itself
        DataContext = Me
    
        ' Add any initialization after the InitializeComponent() call.
        FindUniface("uniface")
    End Sub
    

    Since you currently bind to properties of both UserControl itself and instrument, you should not bind the DataContext to instrument:

    <WrapPanel Grid.Row="1">
        <Label Name="PortName" Width="100" Content="{Binding instrument.ConnectionString}"  HorizontalAlignment="Right"/>
        <Label  Name="ConStatus" Width="200" Content="{Binding UniName}" HorizontalAlignment="Left"/>
    </WrapPanel>