46

I am creating a UserControl for a series of controls shared by several windows. One of the controls is a Label which shows the flow of some other process in terms of "protocol numbers".

I am trying to offer DataBinding with this Label so the Window automatically reflects the state of the process as the protocol number variable changes.

This is the User control XAML:

<UserControl Name="MainOptionsPanel"
    x:Class="ExperienceMainControls.MainControls"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    >
<Label Height="Auto" Name="numberLabel">Protocol:</Label>
<Label Content="{Binding Path=ProtocolNumber}" Name="protocolNumberLabel"/>
(...)
</UserControl>

And this is the Code-Behind:

public partial class MainControls 
{
    public MainControls()
    {
        InitializeComponent();
    }

    public int ProtocolNumber
    {
        get { return (int)GetValue(ProtocolNumberProperty); }
        set { SetValue(ProtocolNumberProperty, value); }
    }

    public static DependencyProperty ProtocolNumberProperty = 
       DependencyProperty.Register("ProtocolNumber", typeof(int), typeof(MainControls));
}

This seems to be working because if on the constructor I set ProtocolNumber to an arbitrary value, it is reflected in the user control.

However, when using this usercontrol on the final window, the data binding breaks.

XAML:

<Window x:Class="UserControlTesting.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:expControl="clr-namespace:ExperienceMainControls;assembly=ExperienceMainControls"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    >
    <StackPanel>
        <expControl:MainControls ProtocolNumber="{Binding Path=Number, Mode=TwoWay}" />
    </StackPanel>

</Window>

Code-Behind for window:

public partial class Window1 : Window
{
    public Window1()
    {
        Number= 15;
        InitializeComponent();
    }

    public int Number { get; set; }
}

This sets the Protocol Number to zero, ignoring the value set to Number.

I've read example

1
  • 1
    in your output window you will see a binding error, something like object MainOptionsPanel has no Property Number - and thats true. simply change your usercontrol xaml to that in my answer. Commented Jun 28, 2012 at 6:15

3 Answers 3

54

if you look at your output window you should see the binding exception.

The problem you have is the following: within your usercontrol you will bind the label to the DP ProtocolNumber of your usercontrol and not the DataContext, so you have to add for example the element name to the binding.

<UserControl Name="MainOptionsPanel"
    x:Class="ExperienceMainControls.MainControls"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="uc"
    >
<Label Height="Auto" Name="numberLabel">Protocol:</Label>
<Label Content="{Binding Path=ProtocolNumber, ElementName=uc}" Name="protocolNumberLabel"/>
(...)
</UserControl>

EDIT: to clear some things up, your usercontrol also works if you change the binding in your MainWindow. but you have to bind to the DataContext of the MainWindow with RelativeSource.

    <expControl:MainControls ProtocolNumber="{Binding Path=Number, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
Sign up to request clarification or add additional context in comments.

Comments

9

What you have in effect:

<expControl:MainControls DataContext="{Binding RelativeSource={RelativeSource Self}}"
                         ProtocolNumber="{Binding Path=Number, Mode=TwoWay}"/>

=> Do not set the DataContext in UserControl declarations, use RelativeSource or ElementName bindings instead.

8 Comments

Why not set the DataContext? I know there are ways it propagates to child controls (I think) but we haven't ran into any troubles so far in our application.
@mizipzor: It's bad practice, setting the DataContext like that is invisible "from the outside" and impractical as inheritance of the DataContext is usually what you want and expect.
Actually, inheritance of the DataContext is (thus far) not what I want. CustomControls is another story. But for UserControls I'll argue its good practice.
So what you're saying is to remove the DataContext line from Usercontrol definition? Because there I also defined it as RelativeSource.
@kelmer: Yes. It't the same object, anything you do in the definition applies to your instances, which in the case of setting a DataContext, as you just have shown, is a bad idea as it overwrites the inherited DataContext.
|
4

If you're not specifying the RelativeSource of the binding, try set the DataContext in the constructor:

    public Window1()
    {
        Number= 15;
        DataContext = this;
        InitializeComponent();
    }

1 Comment

To be honest this approach is bad because you can change data context from the outside.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.