I have added a DependencyProperty to my View, binding to the DependencyProperty works, but only if I do not also set the DataContext.
GenericView.xaml
<UserControl x:Class="GenericProject.View.GenericView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Button Command="{Binding VMFactory.CreateViewModelCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
<TextBox IsEnabled="False" Text="{Binding SomeProperty, Mode=OneWay}" />
</StackPanel>
</UserControl>
GenericView.xaml.cs
public partial class GenericView : UserControl
{
// The DependencyProperty for VMFactory.
public static readonly DependencyProperty VMFactoryProperty = DependencyProperty.Register("VMFactory", typeof(VMFactoryViewModel<GenericViewModel>), typeof(GenericView));
public VMFactoryViewModel<GenericViewModel> VMFactory
{
get { return (VMFactoryViewModel<GenericViewModel>)GetValue(VMFactoryProperty); }
set { SetValue(VMFactoryProperty, value); }
}
public GenericView()
{
InitializeComponent();
}
}
Here I am creating two views to illustrate the issue at hand. The VMFactory binding in the first view will fail because I have DataContext set. The second view will succeed, what is the cause of this behavior?
MainPage.xaml
<vw:GenericView DataContext="{Binding Generic}" VMFactory="{Binding GenericFactory}" />
<vw:GenericView VMFactory="{Binding GenericFactory}" />
This is a fairly common Binding "gotcha"...
In order to access VMFactory
, you need to bind your UserControl
to itself using...
DataContext="{Binding RelativeSource={RelativeSource Self}}"
You would not then bind DataContext
on a GenericView
item to anything elsewhere.
However, if you are intending to bind other values to VMFactory
external to the UserControl
(i.e. <vw:GenericView VMFactory={Binding ...}"/>
), you should use RelativeSource
with mode FindAncestor
for type UserControl
.
<!-- Shortened to show pertinent Binding -->
<ctrl:CommandTextBox Command="{Binding VMFactory.CreateViewModelCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}"/>
As @toadflakz said, this is a very common issue in WPF and one that took me a while to get my head around when I was learning WPF. Luckily, the solution is simple. Let's say that we have a UserControl
that has an object set as its DataContext
and another set as the value of a DependencyProperty
that is declared within the UserControl
... your situation.
From within the UserControl
XAML, you can data bind to a property of the object set as the DataContext
as normal:
<TextBlock Text="{Binding PropertyFromDataContextObject}" />
If you want to data bind to an object from the object set as the value of the DependencyProperty
, you can simply use a RelativeSource Binding
:
<TextBlock Text="{Binding PropertyFromDependencyPropertyObject, RelativeSource={
RelativeSource AncestorType={x:Type YourPrefix:YourUserControl}}}" />
Note that both of these Binding
s can be used together in the same UserControl
as long as both of the DataContext
and DependencyProperty
properties have been set.
I've got a working solution, it seems as though properties of a control are bound relative to the DataContext of the control?
I was certainly aware items within the control would be bound relative to the DataContext, but I apparently have never used a control in this way before and did not understand that properties of the control would also inherit the scope of the set DataContext. Essentially everything within my View was correct, but the binding to my DependencyProperty was failing.
GenericView.xaml
<!-- Everything in here was correct. -->
<UserControl x:Class="GenericProject.View.GenericView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Button Command="{Binding VMFactory.CreateViewModelCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
<TextBox IsEnabled="False" Text="{Binding SomeProperty, Mode=OneWay}" />
</StackPanel>
</UserControl>
MainPage.xaml
<!-- This is where I messed up earlier, VMFactory requires a relative binding. -->
<vw:GenericView DataContext="{Binding Generic}"
VMFactory="{Binding DataContext.GenericFactory, RelativeSource={RelativeSource AncestorType={x:Type Page}}}" />