简体   繁体   中英

Setting WPF Window DataContext to RelativeSource Self

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 (ie 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
    }
}

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.

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));
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM