Nasenbaer
Nasenbaer

Reputation: 4900

How to setup datacontext and custom usercontrol binding

I do not understand why binding works for a Textbox but does not work for usercontrol. On image below you see how it should work. The service can bind to the yellow usercontrol and this usercontrol contains a property of my own class. In my case this property is called Email. Problem is, that this Email is never bind to the yellow usercontrol. If I replace the usercontrol by simple "TextBox" control, it works proper.

Please can you advice me how to get Binding work?

User control description of binding

Codebehind of Silvelright Main Page

#Region "UserProfile"

    ''' <summary>
    ''' UserProfile Dependency Property
    ''' </summary>
    Public Shared ReadOnly UserProfileProperty As DependencyProperty = _
        DependencyProperty.Register("UserProfile", GetType(ServiceReference1.UserProfile), GetType(MainPage), _
            New Windows.PropertyMetadata(Nothing, _
                AddressOf OnUserProfileChanged))

    ''' <summary>
    ''' Gets or sets the UserProfile property.  This dependency property 
    ''' indicates ....
    ''' </summary>
    Public Property UserProfile() As ServiceReference1.UserProfile
        Get
            Return CType(GetValue(UserProfileProperty), ServiceReference1.UserProfile)
        End Get
        Set(ByVal value As ServiceReference1.UserProfile)
            SetValue(UserProfileProperty, value)
        End Set
    End Property

    ''' <summary>
    ''' Handles changes to the UserProfile property.
    ''' </summary>
    Private Overloads Shared Sub OnUserProfileChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
        Dim target As MainPage = CType(d, MainPage)
        Dim oldUserProfile As ServiceReference1.UserProfile = CType(e.OldValue, ServiceReference1.UserProfile)
        Dim newUserProfile As ServiceReference1.UserProfile = target.UserProfile
        target.OnUserProfileChanged(oldUserProfile, newUserProfile)
    End Sub

    ''' <summary>
    ''' Provides derived classes an opportunity to handle changes to the UserProfile property.
    ''' </summary>
    Protected Overridable Overloads Sub OnUserProfileChanged(ByVal oldUserProfile As ServiceReference1.UserProfile, ByVal newUserProfile As ServiceReference1.UserProfile)
        Me.DataContext = newUserProfile

    End Sub

#End Region

when tracking the property, the "newUserProfile" item was set successfully in codebehind.

XAML

<UserControl x:Class="CH_App.ucUserEditor"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                          xmlns:my="clr-namespace:CH_App"

    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <TextBox Text="{Binding Path=Email}"/>
        <my:ucDbRow Title="Email" Value="{Binding Path=Email, Mode=TwoWay}" />
    </Grid>
</UserControl>

The Texbox with binding of email works like it should and shows the email address. The usercontrol does not show the email address. The user control shows the Title correct.

UserControl

<UserControl x:Class="CH_App.ucDbRow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:my="clr-namespace:CH_App"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
            DataContext="{Binding RelativeSource={RelativeSource Self}}"
    d:DesignHeight="300" d:DesignWidth="400">

    <StackPanel>
        <TextBlock x:Name="txtTitle" Text="{Binding Path=Title}" />
        <TextBox x:Name="txtValue" Text="{Binding Path=Value, Mode=TwoWay}"/>
    </StackPanel>
</UserControl>

Codebehind of usercontrol

#Region "Title"

    ''' <summary>
    ''' Title Dependency Property
    ''' </summary>
    Public Shared ReadOnly TitleProperty As DependencyProperty = _
        DependencyProperty.Register("Title", GetType(String), GetType(ucDbRow), _
            New Windows.PropertyMetadata(""))

    ''' <summary>
    ''' Gets or sets the Title property.  This dependency property 
    ''' indicates ....
    ''' </summary>
    Public Property Title() As String
        Get
            Return CType(GetValue(TitleProperty), String)
        End Get
        Set(ByVal value As String)
            SetValue(TitleProperty, value)
        End Set
    End Property

#End Region



#Region "Value"

    ''' <summary>
    ''' Value Dependency Property
    ''' </summary>
    Public Shared ReadOnly ValueProperty As DependencyProperty = _
        DependencyProperty.Register("Value", GetType(String), GetType(ucDbRow), _
            New Windows.PropertyMetadata(""))

    ''' <summary>
    ''' Gets or sets the Value property.  This dependency property 
    ''' indicates ....
    ''' </summary>
    Public Property Value() As String
        Get
            Return CType(GetValue(ValueProperty), Object)
        End Get
        Set(ByVal value As String)
            SetValue(ValueProperty, value)
        End Set
    End Property

#End Region

Upvotes: 1

Views: 4150

Answers (1)

Silvermind
Silvermind

Reputation: 5944

Edit (insert):

You're using Silverlight 4, and i was either testing in Silverlight 5 or WPF from which both probably, but
WPF certainly supports RelativeSourceBinding that way, however you had it almost right.
The output window in Visual Studio gives you the following error if I'm correct.

System.Windows.Data Error: BindingExpression path error:
'Email' property not found on 'VisualBasicSilverlightApplication1.ucDbRow'
'VisualBasicSilverlightApplication1.ucDbRow' (HashCode=72766).
BindingExpression: Path='DataContext.Email'
DataItem='VisualBasicSilverlightApplication1.ucDbRow' (HashCode=72766);
target element is 'VisualBasicSilverlightApplication1.ucDbRow' (Name='');
target property is 'Value' (type 'System.String')..

The DataContext will fall through the usercontrol, except at the toplevel/instance of the usercontrol.
So instead of doing this:

<my:ucDbRow Title="Email" Value="{Binding Path=Email, Mode=TwoWay}" />

The only thing you will have to change in your code is point to the element where you set the DataContext on, which is most of the time the LayoutRoot:
(in ucUserEditor)

<my:ucDbRow Title="Email" Value="{Binding ElementName=LayoutRoot, 
            Path=DataContext.Email, Mode=TwoWay}" />

Previous answer
You are overriding the binded datacontext with this DataContext="{Binding RelativeSource={RelativeSource Self}}" in CH_App.ucDbRow. Remove it and the Value works because you've got your underlaying DataContext back, however Title is not working anymore.

There is a solution:

Change ucDbRow to this:

<UserControl x:Class="CH_App.ucDbRow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
    <Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding}">
    <StackPanel>
      <TextBlock x:Name="txtTitle" Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=Title}" Height="23"/>
      <TextBox x:Name="txtValue" Text="{Binding Path=Value, Mode=TwoWay}"/>
    </StackPanel>
  </Grid>
</UserControl>

Note: Check your ouput window if Bindings don't work, if it failed it's in the output window.

In addition:

I would suggest not to use usercontrol's as control's. In my opinion usercontrols should more be used to represent individual context, not half a context on one and half a context on another. Start looking at usercontrols more as pages and use custom control's and datatemplate's for the detailed work. Also building a better layout is becominging very easy using a Grid which is (again in my opinion) one of the best features in WPF and Silverlight, can't be beaten by ordered usercontrols.

Upvotes: 2

Related Questions