将WPF窗口DataContext设置为RelativeSource Self

Jon*_*ono 5 c# wpf xaml

this如果我在构造函数中以及在XAML 中将Window 的 DataContext 设置为{Binding RelativeSource={RelativeSource Self}},那么我可以通过在代码隐藏的 Loaded 事件中放置一个断点来看到 DataContext 引用正确的对象实例(即 MainWindow)。但是,Window 的子元素 exampleButton 的 Command 绑定为 null。断言失败。

当我删除 XAML DataContext 设置(并且仅依赖于构造函数设置)时,exampleButton 的命令会正确使用 DataContext。

为什么 XAML 场景中 exampleButton 的 Command 绑定为 null?

主窗口.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>
Run Code Online (Sandbox Code Playgroud)

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
    }
}
Run Code Online (Sandbox Code Playgroud)

mm8*_*mm8 4

为什么 XAML 场景中 exampleButton 的 Command 绑定为 null?

因为在方法返回并设置属性ExampleCommand时,该属性实际上具有 null 值。InitializeComponent()DataContext

如果您想在此之后将属性设置为新值,则该类必须实现INotifyPropertyChanged接口,以便目标属性自动刷新:

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));
    }
}
Run Code Online (Sandbox Code Playgroud)