Reputation: 1577
I often have the following scenario:
I have a custom UserControl:
<UserControl x:Class="BindingBindingBindingTest.MyUserControl"
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"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<TextBox Text="{Binding MyUserControlValue}"></TextBox>
</Grid>
</UserControl>
with its code behind:
public partial class MyUserControl
{
public MyUserControl()
{
InitializeComponent();
}
public int MyUserControlValue
{
get { return (int)GetValue(MyUserControlValueProperty); }
set { SetValue(MyUserControlValueProperty, value); }
}
public static readonly DependencyProperty MyUserControlValueProperty =
DependencyProperty.Register("MyUserControlValue", typeof(int), typeof(MyUserControl), new PropertyMetadata(0));
}
This usercontrol I'm using in a other control or window:
<Window x:Class="BindingBindingBindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:BindingBindingBindingTest">
<Grid>
<controls:MyUserControl MyUserControlValue="{Binding MainWindowValue}" />
</Grid>
</Window>
In its code behind there is a property which should be passed to the usercontrol. This property is initialized in its constructor:
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
MainWindowValue = 34;
DataContext = this;
}
public int MainWindowValue { get; set; }
}
Running the application, the usercontrol binding works well.
But the binding defined in the MainWindow.xaml doesnt work. The error messing is following:
BindingExpression path error: 'MainWindowValue' property not found on 'object' ''MyUserControl' (Name='')'. BindingExpression:Path=MainWindowValue; DataItem='MyUserControl' (Name=''); target element is 'MyUserControl' (Name=''); target property is 'MyUserControlValue' (type 'Int32')
As described in the error message, the property MainWindowValue is not fount on MyUserControl.
And here is my question: Why the property is expected on MyUserControl? In my opinion, the binding is made to the MainWindowValue of the MainWindow because the DataContext is set to its own instance (DataContext = this).
Of course it's simple to fix this issue by defining the source of the binding. But I'm interesting about the reason of this behavior.
Any idea about this behavior?
Thanks in advance
Upvotes: 0
Views: 612
Reputation: 25623
The property is expected on MyUserControl
because MyUserControl.xaml
contains the following:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
This points the DataContext
of the MyUserControl
instance back to itself. This happens as part of the call to the MyUserControl()
constructor, which happens before the MyUserControlValue
setter is evaluated in MainWindow
:
<controls:MyUserControl MyUserControlValue="{Binding MainWindowValue}" />
By the time this setter is evaluated, the DataContext
of the MyUserControl
has been set to itself. When the binding engine looks for the MainWindowValue
property, it looks on the MyUserControl
, because that's the data context. The binding evaluates against the data context of the target object, not the data context of the "parent" object.
A simple workaround would be to give the window a name, and use ElementName
to make the binding use the window as its binding root:
<Window x:Class="BindingBindingBindingTest.MainWindow"
x:Name="Root"
...>
<Grid>
<controls:MyUserControl
MyUserControlValue="{Binding ElementName=Root, Path=MainWindowValue}" />
</Grid>
</Window>
Also, as a matter of style, I would recommend against initializing control properties in both the constructor and the Xaml. When possible, you should stick to initializing values in one place (preferably the Xaml). If you want to specify a default value, then specify it in the DependencyProperty
metadata when you Register()
the property. If the value you're specifying is specific to the control instance, then set the value where you declare the instance.
Upvotes: 3