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 中,为控件的OnLoaded
和OnUnloaded
连接事件。
在事件处理程序的代码隐藏中,订阅/取消订阅 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