Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Data binding to a UserControl in WPF

I have a UserControl that I want to participate in data binding. I've set up the dependency properties in the user control, but can't get it work.

The uc displays the correct text when I call it with static text (e.g BlueText="ABC") . When i try to bind it to a local public property, it is always blank.

<src:BlueTextBox BlueText="Feeling blue" />            <!--OK-->
<src:BlueTextBox BlueText="{Binding Path=MyString}" /> <!--UserControl always BLANK!-->
<TextBox Text="{Binding Path=MyString}" Width="100"/>  <!--Simple TextBox Binds OK-->

I've boiled the code down to the following simplified example. Here is the XAML of the UserControl:

    <UserControl x:Class="Binding2.BlueTextBox" ...
    <Grid>
        <TextBox x:Name="myTextBox" Text="{Binding BlueText}" Foreground="Blue" Width="100" Height="26" />
    </Grid>

Here is the code behind of the UserControl:

public partial class BlueTextBox : UserControl
{
    public BlueTextBox()
    {
        InitializeComponent(); 
        DataContext = this; // shouldn't do this - see solution
    }

    public static readonly DependencyProperty BlueTextProperty =
        DependencyProperty.Register("BlueText", typeof(string), typeof(BlueTextBox));

    public string BlueText
    {
        get { return GetValue(BlueTextProperty).ToString(); }
        set { SetValue( BlueTextProperty, value.ToString() ); }
    }

This seems like it should be really easy, but I can't make it work. Thanks for your help!

More info: When i was trying the fix suggested by Eugene, I noticed some peculiar behavior. I added a PropertyChangedCallback to the metadata; this allows me to watch the value of BlueText getting set. When setting the string to a static value (="feeling blue") the PropertyChanged event fires. The data binding case does not fire PropertyChanged. I think this means the data-bound value is not getting sent to the UserControl. (I think the constructor does not get called in the static case)

Solution: The problems were correctly identified by Arcturus and jpsstavares. First, I was overwriting the data binding when is set DataContext=this in the constructor of the control. This prevented the data bound value from getting set. I also had to name the control x:Name=root, and specify the Binding ElementName=root int the XAML. To get the TwoWay binding, I needed to set Mode=TwoWay in the caller. Here is the correct code:

<src:BlueTextBox BlueText="{Binding Path=MyString, Mode=TwoWay}}" /> <!--OK-->

Now the XAML in the UserControl:

    <UserControl x:Class="Binding2.BlueTextBox" x:Name="root"...
    <Grid>
        <TextBox x:Name="myTextBox" Text="{Binding ElementName=root, Path=BlueText}" Foreground="Blue" Width="100" Height="26" />
    </Grid>

Finally I removed the DataContext=this in the constructor of the UserControl.

    public BlueTextBox()
    {
        InitializeComponent(); 
        //DataContext = this; -- don't do this
    }

Thanks everyone for the tremendous help!

like image 313
RaoulRubin Avatar asked Jul 16 '10 14:07

RaoulRubin


2 Answers

You set the DataContext in the Control to itself, thus overwriting the DataContext when using this Control in other controls. Taking your binding as example in your situation:

<src:BlueTextBox BlueText="{Binding Path=MyString}" /> 

Once loaded and all the Datacontext is set, it will look for the path MyString in your BlueTextBox thing control due to you setting the DataContext to it. I guess this is not how you intended this to work ;).

Solution:

Change the text binding either one of the 2 bindings:

{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type src:BlueTextBox}}, Path=BlueText}

or

Name your control Root (or something like that)

<UserControl x:Name="Root"

{Binding ElementName=Root, Path=BlueText}

And remove the

DataContext = this;

from the constructor of your UserControl and it should work like a charm..

like image 93
Arcturus Avatar answered Nov 05 '22 09:11

Arcturus


I think in this case you need to set the ElementName property in the binding. Something like this:

<UserControl x:Class="Binding2.BlueTextBox" x:Name="blueTextBox"...
<Grid>
    <TextBox x:Name="myTextBox" Text="{Binding ElementName=blueTextBox, Path=BlueText}" Foreground="Blue" Width="100" Height="26" />
</Grid>
like image 39
jpsstavares Avatar answered Nov 05 '22 10:11

jpsstavares