【问题标题】:WPF, set DataContext to property of same classWPF,将 DataContext 设置为同一类的属性
【发布时间】: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>

【问题讨论】:

    标签: c# wpf data-binding blend


    【解决方案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;
     }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-08-15
      • 2021-07-20
      • 1970-01-01
      • 2011-11-06
      • 2023-03-17
      • 2011-03-31
      • 1970-01-01
      相关资源
      最近更新 更多