WPF,将 DataContext 设置为同一类的属性

Posted

技术标签:

【中文标题】WPF,将 DataContext 设置为同一类的属性【英文标题】:WPF, set DataContext to property of same class 【发布时间】:2016-05-06 13:40:24 【问题描述】:

我有一个使用INotifyPropertyChanged 的类Device,它已经过测试并且可以工作。

现在我有一个deviceMonitor,它是这个设备的 UI 表示。在代码中我引用了Device,我想将设备中的更改链接到UI中的更改(不需要两种方式,但单击deviceMonitor应该调用device的某个函数)

我正在将表达式 Blend 与 VS2015 一起使用,因此非常欢迎基于单击以使其工作的位置的指导。

这是device的模型

public class Device : INotifyPropertyChanged

    public string Name  ...  //uses NotifyPropertyChanged in the set
    // other properties and their relative private vars.

然后是GUI的xaml.cs,这里我引用了包含Device的dll:

public partial class DeviceControl : UserControl

   public Device myDevice = new Device();

   public DeviceControl()
    
        InitializeComponent();
        // here I tried setting the datacontest to the myDevice

        // also tried to set the dataContext in Blend and here grab a
        // reference to it and store it in myDevice. But nothing workerd
    

    public void ChangeDevName()
    
         this.myDevice.DeviceName = "Test";
         //UI Representation of deviceName never changed
    

然后是 XAML

<UserControl>
    <UserControl.DataContext>
        <recoveriX:RecoverixDevice  DeviceName="thisIsAName"/>
    </UserControl.DataContext>

    <Grid>
        <TextBlock x:Name="title"  Text="Binding DeviceName"/>
    </Grid>
</UserControl>

【问题讨论】:

【参考方案1】:

这可能有效:

在您的DeviceControl UserControl 中,为控件的OnLoadedOnUnloaded 连接事件。

在事件处理程序的代码隐藏中,订阅/取消订阅 UserControl 的 DataContext (this.DataContext) 的 PropertyChanged 事件;像这样:

private void OnLoaded(object sender, RoutedEventArgs e)

    if (this.DataContext is INotifyPropertyChanged)
    
        ((INotifyPropertyChanged)this.DataContext).PropertyChanged += OnDataContextPropertyChanged;
    


private void OnUnloaded(object sender, RoutedEventArgs e)

    if (this.DataContext is INotifyPropertyChanged)
    
        ((INotifyPropertyChanged)this.DataContext).PropertyChanged -= OnDataContextPropertyChanged;
    


private void OnDataContextPropertyChanged(object sender, PropertyChangedEventArgs e)

    // You could also just update every time something is changed.
    // As an example you could check for the "Name" property being changed.
    if (e.PropertyName == nameof(Device.Name))
    
        title.Text = this.DataContext.Name;
    

需要注意的是if (this.myDevice is INotifyPropertyChanged) 检查。

它确保Device 类继承自INotifyPropertyChanged

如果确实如此,它会将作为您的 DataContext (this.DataContext) 的 Device 转换为 (INotifyPropertyChanged),因此您可以从 INotifyPropertyChanged 接口订阅 PropertyChanged 事件。

然后,当DataContext 上的属性发生更改时,您的处理程序将被触发。显然你可以把你想做的事情放在OnMyDevicePropertyChanged的代码中,我刚才以“姓名”为例。

希望这会有所帮助!

编辑 此外;您还可以在 UserControl 的代码隐藏中存储 Device 类型的私有字段。有点像:

private Device _viewModel; // You could also use the interface (like 'IDevice'), too.

然后在您的 `OnLoaded' 事件中,将其存储在字段中:

if (this.DataContext is INotifyPropertyChanged)

    this.viewModel = this.DataContext;

    // Wire up your PropertyChanged handler as before.

在您的 OnUnloaded 事件中,如果 viewModel 不为空,只需取消订阅即可:

if (this.viewModel != null)

    this.viewModel.PropertyChanged -= OnDataContextPropertyChanged;

当您将 DataContext 存储为字段时,这也为您提供了更多的灵活性,因为您可以在其他方法中使用它(如果您在后面的代码中使用更多 - 您不应该。 ..; 但它可以节省 CPU 时间,一直将其转换为 INotifyPropertyChanged。

为了将来的参考,我会在您的项目中查看 Implementing MVVM Practices。

祝你好运!

【讨论】:

我完全同意 MVVM 的做法,我已经在我的待办事项列表中......已经在那里太久了:(顺便说一句,检查我的答案,因为它在没有你编写的所有代码的情况下工作,并且我不知道为什么xD 这是因为您在私有字段中实例化一个新的Device,然后在您的ctor 中设置DataContext...这与您的相同 Xaml 正在做的事情(因为您也在那里设置了 DataContext)。确保你做一个/或,两者。我不确定您的 SetDevice() 方法是如何被调用的 - 因此在加载/卸载时连接 PropertyChanged 更多 - 刚刚意识到您在 Xaml 中的 Binding 将使用来自 Device 的属性;从而调用PropertyChanged 并更新TextBlock。不再需要您的SetDevice() 方法了。 我使用 SetDevice 方法在运行时更改连接的设备,以便我可以重用控件。我想将 this.DataContext 和 _device 都保留为私有内容并公开明确的方法。感谢所有这些细节,这非常有趣 啊,我不知道你的SetDevice() 是从哪里来的。我用一些有用的提示编辑了你自己的答案。然而,这一切的长短;我必须强调 - 你是否可以真的首先不用麻烦,并使用 MVVM smug face。很高兴能帮上忙。【参考方案2】:

问题是覆盖私有设备,设置数据上下文修复了问题。 这是最后一课:

public partial class DeviceControl : UserControl

private Device _device = new Device();

public DeviceControl()

    InitializeComponnents();
    this.DataContext = _device;


public void SetDevice(Device d)

    //This fails:
    //_device = d;

    //This works
    this.DataContext = d;
 

【讨论】:

以上是关于WPF,将 DataContext 设置为同一类的属性的主要内容,如果未能解决你的问题,请参考以下文章

设置DataContext后WPF依赖属性两种方式绑定不起作用[重复]

在 WPF 中使用 DataContext 作为 CommandParameter

如何在MVVM中使用相同的ViewModel拥有多个视图?

WPF 绑定到父 DataContext

WPF中利用控件的DataContext属性为多个TextBox绑定数据

DataGrid中的WPF绑定到DataContext