This has bothered me for a while and I'm tired of working around the issue. In WPF, what is the "order of operations" when it comes to:
All this taking into consideration nested controls and templates (when templates are applied).
I've had a number of problematic scenarios, but here's just one example:
Custom user control
<UserControl x:Class="UserControls.TestUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<StackPanel>
<Label Content="{Binding Label1}" />
<Label Content="{Binding Label2}" />
</StackPanel>
</UserControl>
User control code-behind
using System;
using System.Windows;
using System.Windows.Controls;
namespace UserControls
{
public partial class TestUserControl : UserControl
{
public static readonly DependencyProperty Label1Property = DependencyProperty.Register("Label1", typeof(String), typeof(TestUserControl), new FrameworkPropertyMetadata(OnLabel1PropertyChanged));
public String Label1
{
get { return (String)GetValue(Label1Property); }
set { SetValue(Label1Property, value); }
}
public static readonly DependencyProperty Label2Property = DependencyProperty.Register("Label2", typeof(String), typeof(TestUserControl), new FrameworkPropertyMetadata(OnLabel2PropertyChanged));
public String Label2
{
get { return (String)GetValue(Label2Property); }
set { SetValue(Label2Property, value); }
}
public TestUserControl()
{
DataContext = this;
InitializeComponent();
}
private static void OnLabel1PropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
//used for breakpoint
}
private static void OnLabel2PropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
//used for breakpoint
}
}
}
Window to use the user control
<Window x:Class="Windows.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:UC="clr-namespace:UserControls"
>
<StackPanel>
<Label Content="Non user control label" />
<UC:TestUserControl x:Name="uc" Label1="User control label 1" Label2="{Binding Label2FromWindow}" />
</StackPanel>
</Window>
And the code-behind for the window
using System;
using System.Windows;
namespace Windows
{
public partial class TestWindow : Window
{
public String Label2FromWindow
{
get { return "User control label 2"; }
}
public TestWindow()
{
DataContext = this;
InitializeComponent();
}
}
}
So in this scenario, why doesn't "Label2" in the user control ever get the value from "Label2FromWindow" from the window? I feel like it's a timing issue, where the user control evaluates all of its expressions first, and then the window evaluates its expressions later, and the user control is never "notified" of the window's evaluated values.
The example is hopefully helpful to illustrate one problem, but my real question is:
What is the order of operations with regards to DataContext, Hard-Coded Values on properties, Binding Expressions, Templates, and Nested Controls?
EDIT:
H.B. helped me come to this realization. When the window's DataContext is set to itself, the user control will "inherit" the DataContext. This lets the Binding work on the user control's property, but then within the user control Binding to its local properties won't work. When DataContext is set directly on the user control, the window's Binding to the user control's property no longer works, but the user control can then Bind to its own local properties. Below is the updated code sample that works.
User control:
<UserControl x:Class="UserControls.TestUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="uc">
<StackPanel>
<Label Content="{Binding ElementName=uc, Path=Label1}" />
<Label Content="{Binding ElementName=uc, Path=Label2}" />
</StackPanel>
</UserControl>
User control code-behind:
using System;
using System.Windows;
using System.Windows.Controls;
namespace UserControls
{
public partial class TestUserControl : UserControl
{
public static readonly DependencyProperty Label1Property = DependencyProperty.Register("Label1", typeof(String), typeof(TestUserControl));
public String Label1
{
get { return (String)GetValue(Label1Property); }
set { SetValue(Label1Property, value); }
}
public static readonly DependencyProperty Label2Property = DependencyProperty.Register("Label2", typeof(String), typeof(TestUserControl));
public String Label2
{
get { return (String)GetValue(Label2Property); }
set { SetValue(Label2Property, value); }
}
public TestUserControl()
{
InitializeComponent();
}
}
}
Test window:
<Window x:Class="Windows.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:UC="clr-namespace:UserControls"
>
<StackPanel>
<Label Content="Non user control label" />
<UC:TestUserControl Label1="User control label 1" Label2="{Binding Label2FromWindow}" />
</StackPanel>
</Window>
Test window code-behind:
using System;
using System.Windows;
namespace Windows
{
public partial class TestWindow : Window
{
public String Label2FromWindow
{
get { return "User control label 2"; }
}
public TestWindow()
{
DataContext = this;
InitializeComponent();
}
}
}
It's not really about order I think, but rather precedence (maybe I am splitting hairs here). You explicitly set the DataContext
of the UserControl
-- this means that it will not be inherited, thus your binding looks for the property Label2FromWindow
inside the UserControl. Obviously, it does not find it.
Just never set the DataContext
of UserControl
instances and you should not run into such problems. (Name your UserControl
and use ElementName
for internal bindings)
For a full precedence list see MSDN.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With