Search code examples
c#wpfdata-bindinguser-controlsdependency-properties

Binding DP of Control2 to DP of Control1 in WPF


How can I ensure that the Value dependency property of my two UserControls is consistently updated in my MainWindow when I make changes to the property in one of the controls?

Control1:

<UserControl x:Class="ControlBining.Control1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <TextBox Text="{Binding Value, RelativeSource={RelativeSource AncestorType=UserControl}}" Width="100"/>
    </Grid>
</UserControl>

  public static readonly DependencyProperty ValueProperty =
     DependencyProperty.Register(
        "Value", typeof(string),
        typeof(Control1)
     );

  public string Value
  {
     get => (string)GetValue(ValueProperty);
     set => SetValue(ValueProperty, value);
  }

Control2:

<UserControl x:Class="ControlBining.Control2"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:ControlBining"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <TextBlock Text="{Binding Value, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
    </Grid>
</UserControl>

  public static readonly DependencyProperty ValueProperty =
     DependencyProperty.Register(
        "Value", typeof(string),
        typeof(Control2)
     );

  public string Value
  {
     get => (string)GetValue(ValueProperty);
     set => SetValue(ValueProperty, value);
  }

MainWindow:

<Window x:Class="ControlBining.MainWindow"
        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:local="clr-namespace:ControlBining"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel>
        <local:Control1 Width="100" x:Name="Control1" Value="10"/>
        <local:Control2 Width="100" Value="{Binding ElementName=Control1, Path=Value}"/>
    </StackPanel>
</Window>

Solution

  • The UserControl should not set its own DataContext, because doing so breaks any DataContext based Bindings of its dependency propertises. Instead, use a RelativeSource Binding in its XAML:

    <UserControl ...>
        <Grid>
            <TextBox Text="{Binding Value,
                            RelativeSource={RelativeSource AncestorType=UserControl}}"/>
        </Grid>
    </UserControl>
    

    If you want to update the Value property while you are typing into the TextBox, also set this:

    <TextBox Text="{Binding Value,
                    RelativeSource={RelativeSource AncestorType=UserControl},
                    UpdateSourceTrigger=PropertyChanged}"/>
    

    When you also want to type into Control2, make the Value Binding TwoWay:

    <local:Control2 Value="{Binding ElementName=Control1, Path=Value, Mode=TwoWay}"/>
    

    or make the Value property bind TwoWay by default:

    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register(
            nameof(Value), typeof(string), typeof(Control2),
            new FrameworkPropertyMetadata(
                null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));