If I set the Window's DataContext to this
in the constructor as well as to {Binding RelativeSource={RelativeSource Self}}
in XAML then I can see that the DataContext refers to the correct object instance (i.e. the MainWindow) by placing a break point in the code-behind's Loaded event. However, the Window's child element exampleButton's Command binding is null. The assertion fails.
When I remove the XAML DataContext setting (and rely on the constructor setting only) then exampleButton's Command uses the DataContext correctly.
Why is exampleButton's Command bound to null in the XAML scenario?
MainWindow.xaml
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Example"
SizeToContent="WidthAndHeight"
x:Name="mainWindow"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Loaded="mainWindow_Loaded">
<Button x:Name="exampleButton" Command="{Binding Path=ExampleCommand}" Content="Click"/>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public ICommand ExampleCommand { get; }
public MainWindow()
{
InitializeComponent();
DataContext = this;
ExampleCommand = new DelegateCommand(x => { throw new ApplicationException(); });
}
private void mainWindow_Loaded(object sender, RoutedEventArgs e)
{
Debug.Assert(mainWindow == this);
Debug.Assert(mainWindow.DataContext == this);
Debug.Assert(exampleButton.DataContext == this);
Debug.Assert(exampleButton.Command == ExampleCommand); //<-- FAIL
}
}
Why is exampleButton's Command bound to null in the XAML scenario?
Because the ExampleCommand
property actually has a value of null by the time the InitializeComponent()
method returns and the DataContext
property is set.
If you want to set a property to a new value after this, the class must implement the INotifyPropertyChanged interface for the target property to get refreshed automatically:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
ExampleCommand = new RelayCommand<object>(x => { throw new ApplicationException(); });
}
private ICommand _exampleCommand;
public ICommand ExampleCommand
{
get { return _exampleCommand; }
set { _exampleCommand = value; NotifyPropertyChanged(); }
}
private void mainWindow_Loaded(object sender, RoutedEventArgs e)
{
Debug.Assert(exampleButton.DataContext == this);
Debug.Assert(exampleButton.Command == ExampleCommand); //<-- FAIL
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Set ExampleCommand
and DataContext
before InitializeComponent
:
DataContext = this;
ExampleCommand = new DelegateCommand(x => { throw new ApplicationException(); });
InitializeComponent();
Also note that there is no difference between using DataContext="{Binding RelativeSource={RelativeSource Self}}"
or DataContext = this;
, if you set datacontext before initializecomponent.
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