C# 将WPF窗口DataContext设置为RelativeSource Self

C# 将WPF窗口DataContext设置为RelativeSource Self,c#,wpf,xaml,C#,Wpf,Xaml,如果我在构造函数中将窗口的DataContext设置为this,并在XAML中将其设置为{Binding RelativeSource={RelativeSource Self}},那么通过在代码隐藏的加载事件中放置断点,我可以看到DataContext引用了正确的对象实例(即主窗口)。但是,窗口的子元素exampleButton的命令绑定为null。断言失败了 当我删除XAML DataContext设置(并且仅依赖构造函数设置)时,exampleButton的命令将正确使用DataConte

如果我在构造函数中将窗口的DataContext设置为
this
,并在XAML中将其设置为
{Binding RelativeSource={RelativeSource Self}}
,那么通过在代码隐藏的加载事件中放置断点,我可以看到DataContext引用了正确的对象实例(即主窗口)。但是,窗口的子元素exampleButton的命令绑定为null。断言失败了

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

为什么在XAML场景中exampleButton的命令绑定为null

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
    }
}
公共部分类主窗口:窗口
{
公共ICommand示例命令{get;}
公共主窗口()
{
初始化组件();
DataContext=this;
ExampleCommand=newdelegateCommand(x=>{throw new ApplicationException();});
}
已加载私有void主窗口(对象发送器、路由目标)
{
Assert(mainWindow==this);
Assert(mainWindow.DataContext==this);
Assert(exampleButton.DataContext==this);

Debug.Assert(exampleButton.Command==ExampleCommand);///p>在
初始化组件之前设置
ExampleCommand
DataContext

 DataContext = this;
 ExampleCommand = new DelegateCommand(x => { throw new ApplicationException(); });
 InitializeComponent();
还请注意,如果在初始化组件之前设置DataContext=“{Binding RelativeSource={RelativeSource Self}}”
DataContext=this;
,则使用
DataContext=“{Binding RelativeSource={RelativeSource Self}}”之间没有区别

为什么在XAML场景中exampleButton的命令绑定为null

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

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

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));
    }
}
public分部类主窗口:窗口,INotifyPropertyChanged
{
公共主窗口()
{
初始化组件();
ExampleCommand=newrelayCommand(x=>{throw new ApplicationException();});
}
专用ICommand_示例命令;
公共ICommand example命令
{
获取{return\u exampleCommand;}
设置{u exampleCommand=value;NotifyPropertyChanged();}
}
已加载私有void主窗口(对象发送器、路由目标)
{
Assert(exampleButton.DataContext==this);

Debug.Assert(exampleButton.Command==ExampleCommand);//这是因为:在XAML场景中,Window.DataContext\u不是空的,所以Button.Command\u绑定到ExampleCommand的空值(因为ExampleCommand还没有被构造),但在代码隐藏场景中,Window.DataContext\u是空的,因此按钮。命令\u在Window.DataContext被分配之后才被绑定,然后ExampleCommand被分配,然后它就可以工作,而不实现INotifyPropertyChanged?谢谢。老实说,这很让人困惑。谢谢@Ron,我仍然不太清楚它的确切答案我的问题,但您的回答强调了调用InitializeComponent()的重要性。我很高兴能提供帮助。当然,我认为在您的情况下不需要使用
INotifyPropertyChanged
,因为您要设置一次命令。有关详细信息,请阅读。