Question

There is an issue that bothers me when using usercontrols and databinding. I would like to know why this does not work, and how I can make it work.

I have created a minimal example (I did not bother to create viewmodels):

First, there is a usercontrol that contains a textbox and a button.

<UserControl x:Class="DatabindingProblem.UserControl1"
         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="23" d:DesignWidth="300">
<StackPanel Orientation="Horizontal">
    <TextBox Text="{Binding Path}" Width="250"></TextBox>
    <Button Click="Button_Click" Width="50">click</Button>
</StackPanel>
</UserControl>

Code behind (one dependencyproperty "Path"):

namespace DatabindingProblem
{
public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
    }

    public string Path
    {
        get { return (string)GetValue(PathProperty); }
        set { SetValue(PathProperty, value); }
    }

    public static readonly DependencyProperty PathProperty =
        DependencyProperty.Register("Path", typeof(string), typeof(UserControl1), new PropertyMetadata(""));

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Path = "test";
    }
}
}

The window:

<Window x:Class="DatabindingProblem.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DatabindingProblem"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <local:UserControl1 Height="23" Path="{Binding FilePath}" />
</Grid>
</Window>

windows code-behind (dependency property FilePath):

namespace DatabindingProblem
{
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    public string FilePath
    {
        get { return (string)GetValue(FilePathProperty); }
        set { SetValue(FilePathProperty, value); }
    }

    public static readonly DependencyProperty FilePathProperty =
        DependencyProperty.Register("FilePath", typeof(string), typeof(MainWindow), new PropertyMetadata(""));
}
}

I would like to know why 'test' is not visibile in the textbox, and does not get propagated to 'FilePath' in the window after clicking the button.

Could someone be so kind as to explain this to me?

Thanks

Was it helpful?

Solution

By default binding engine will search for property in its DataContext, but since you haven't provided any DataContext to your UserControl and Window, binding is failing silently which you can see in output window of Visual Studio.

You can use RelativeSource markup extension to resolve the binding.

UserControl:

<TextBox Text="{Binding Path,RelativeSource={RelativeSource Mode=FindAncestor, 
                                                   AncestorType=UserControl}}"
         Width="250"/>

Window

<local:SampleUserControl Path="{Binding FilePath, Mode=TwoWay,
     RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/>

Also you have to set Mode to TwoWay so that any change gets propagated from source to target and vice versa. Default value for custom DP is OneWay.

Othwerwise, you can specify it in your custom DP Path to bind TwoWay by default by setting FrameworkPropertyMetadataOptions.BindsTwoWayByDefault on Path DP. This way you don't have to explicitly set binding mode on your XAML bindings.

public static readonly DependencyProperty PathProperty =
        DependencyProperty.Register("Path", typeof(string), typeof(UserControl1),
                          new FrameworkPropertyMetadata(string.Empty, 
                         FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

OTHER TIPS

The UserControl1 does not have a DataContext set so the binding is failing.

As you are using DPs in code behind, setting the DataContext to RelativeSource Self will fix this.

DataContext="{Binding Mode=OneWay, RelativeSource={RelativeSource Self}}"
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top