【问题标题】:Setting WPF Window DataContext to RelativeSource Self将 WPF 窗口 DataContext 设置为 RelativeSource Self
【发布时间】:2017-03-04 18:33:11
【问题描述】:

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

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

为什么XAML场景下exampleButton的Command绑定为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
    }
}

【问题讨论】:

    标签: c# wpf xaml


    【解决方案1】:

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

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

    如果您想在此之后将属性设置为新值,则该类必须实现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));
        }
    }
    

    【讨论】:

    • 所以,这是因为:在 XAML 场景中,Window.DataContext _is not_ null 所以 Button.Command _is_ 绑定到 null 值ExampleCommand(因为 ExampleCommand 尚未构建),但在 Code Behind 场景中,Window.DataContext _is_ null 等 Button.Command _is not_ 绑定到 Window .DataContext 被分配,然后 ExampleCommand 被分配,然后它没有实现 INotifyPropertyChanged?
    • 谢谢。老实说,这很令人困惑。
    【解决方案2】:

    InitializeComponent之前设置ExampleCommandDataContext

     DataContext = this;
     ExampleCommand = new DelegateCommand(x => { throw new ApplicationException(); });
     InitializeComponent();
    

    还要注意,如果在初始化组件之前设置 datacontext,使用DataContext="{Binding RelativeSource={RelativeSource Self}}"DataContext = this; 没有区别。

    【讨论】:

    • 谢谢@Ron,我对我的问题的确切答案仍然有点不清楚,但你的回答强调了调用 InitializeComponent() 的重要性。
    • 很高兴能帮上忙。当然,我认为在您的情况下不需要INotifyPropertyChanged,因为您将设置一次命令。请阅读this post 了解更多信息。
    猜你喜欢
    • 2012-09-07
    • 1970-01-01
    • 1970-01-01
    • 2013-09-13
    • 2018-04-15
    • 2013-11-07
    • 2014-11-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多