Skip to content

WPF Dependency Property – Two Way Data Binding

You create a new User Control, add a Dependency Property and hope you can do two way data binding on the property in XAML. Only to find out it either does not bind at all or can only get the value from view model on load. Changes to the control doesn’t get passed back to the view model. I did struggle for a while, and found out there are things that the Microsoft documentation showed incorrectly and things you need to know. So I decided to write down this example so you guys dont have to to go through the pain I did. Here is how:

Step 1: XAML of your user control

Look at the following example of a really simple User Control, I have a child text box that I want to bind Text changes from the calling control/view model. The bound dependency property is called: TextOne. So I can do something like <control TextOne={Binding Text}/>. There are two ways you can define binding of the child control towards the dependency property

<UserControl x:Class=.... 
             x:Name="bindingText">
    <Grid>
        <TextBox Text="{Binding TextOne, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
    </Grid>
</UserControl>

or

<UserControl x:Class=.... 
             x:Name="bindingText">
    <Grid>
        <TextBox Text="{Binding Path=TextOne, ElementName=bindingText}"/>
    </Grid>
</UserControl>

Both declarations will work for our purpose. The first one defines bound property and declares that it comes from the parent/ancestor user control’s data context. While the seond one pretty much does the same thing by saying I am going to use element “bindingText”‘s data context property TextOne.

Step 2: Code behind

In the code behind of your User Control, define a custom Dependency Property like this:

 public partial class BindingText : UserControl
    {
        public static readonly DependencyProperty TextProperty =
           DependencyProperty.Register(nameof(TextOne), typeof(string), typeof(BindingText), new FrameworkPropertyMetadata(
            string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
        public string TextOne
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }
        public BindingText()
        {
            InitializeComponent();
        }
    }

Now one thing to note here, if you leave out “new FrameworkPropertyMetadata(
string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)”. The binding will only work as one way. i.e. When values changed on your view model, the UI will still get notified, which works for things like TextBlock. But if you do this for text boxes, whatever you type into the text box will only trigger change events on dependency property, and will not notify the view model. adding the “BindsTwoWayByDefault” option will make sure two way binding is established.

Gotcha: VERY IMPORTANT! DO NOT do this!

Some documentation or example online tells you to do the following in contructor that essentially breaks the whole thing for, and I saw a lot of people struggle:

        public BindingText()
        {
            DataContext = this;
            InitializeComponent();
        }

The line DataContext = this appears in some examples and demos, but this line will actually break the data binding. DO NOT DO THIS in your code

So if you follow this simple code, you should be able to create a user control and do the sweet two way data binding by the following code in any of your window or controls:

<ns:BindingText TextOne="{Binding SomeText}"/>

Hope this help someone 🙂

2 thoughts on “WPF Dependency Property – Two Way Data Binding”

  1. Paul thank you for publishing this article. I’ve spent the past hour trying to figure out what I did wrong. Your warning NOT to use “DataContext=this” finally rescued me from insanity. I had placed mine AFTER the “InitializeComponent()”.

    I must admit I don’t understand WHY this is such a bad thing to do. It seems logical.

    Anyhow, thanks a million! You undoubtedly saved me countless more hours of frustration.

Leave a Reply

Your email address will not be published. Required fields are marked *