首页 > 解决方案 > WPF:使用接口作为 DataContext 而不是实现 INotifyPropertyChanged 的​​对象

问题描述

我重构了我的 WPF 应用程序,以便 DataContexts 不再是某个实现的类的实例,INotifyPropertyChanged而是我的一些自定义接口。实现每个自定义接口的对象也需要实现INotifyPropertyChanged。但我不能再在构建时强制执行此要求。我不确定这是否是个好主意。

例如,考虑以下简单的类层次结构

// Base view model class has stock implementation of INotifyPropertyChanged.

public BaseViewModel : INotifyPropertyChanged
{
   // Assume INotifyPropertyChanged implementation with this utility to raise the event

   void RaisePropertyChanged(string property) { // ...blah blah blah ... }
}

// Interface for nameable object

public interface INameable
{
    string Name { get; set; }
}

// View model for nameable object

public class NameableViewModel : BaseViewModel, INameable
{
    private string _name;
    public string Name
    {
        get => _name;
        set
        {
            if (value == _name) return;
            _name = value
            RaisePropertyChanged();
    }
}

// Main view model exposes named object by interface, NOT by view-model

public class MainViewModel : BaseViewModel
{
    public INameable NameableObject { get; set; } 
}

这是一个视图绑定,INameable而不是NameableViewModel

<UserControl x:Class="MyView" 
             (... blah blah blah... namespace declarations...)
             d:DataContext="{d:DesignInstance MainViewModel}">

    <TextBox DataContext={Binding NameableObject.Name, Mode=TwoWay}"/>
</UserControl>

这在 WPF 中运行良好,因为它查询任何数据上下文INotifyPropertyChanged。它接收到 PropertyChanged 通知,即使它得到的只是INameable. 但是如果我忘记正确设置我的类,WPF 将永远不会收到属性更改通知。我最终可能会遇到需要一段时间才能注意到或跟踪的错误(此处的测试资源有限)。

此外,在我的代码隐藏中,我有时会通过 INotifyPropertyChanged使用 Josh Smith 出色的PropertyObserver.

public class PropertyObserver<TPropertySource> 
    : IWeakEventListener
    where TPropertySource : INotifyPropertyChanged
{
   ... blah blah blah..
}

确实需要在构建时使用 INotifyPropertyChanged,这在我使用 BaseViewModel 实例时非常有用。但是现在我只有一个接口,我必须自己查询它并依靠运行时错误来让我知道我搞砸了。

这引出了我的问题。一个通用的,一个非常具体的:

  1. 这种设计合理吗?它是在 WPF 中工作的人在实践中经常做的事情吗?或者,确定我用作数据上下文的任何类型是否已经声明为实现 INotifyPropertyChanged 总是更好?(我意识到我可能在这个问题上思考得太像 C++ 程序员了)
  2. 假设在我的代码中,我转换了一个对象,但它不支持我需要的接口,我想抛出一个异常。我应该抛出什么特定的 .NET 异常?什么最合适? NotSupportedException? 还有什么?

标签: c#wpfinotifypropertychanged

解决方案


我不完全确定您所指的“设计”或“实践”是什么。我没有看到一般模式,只有接口的特定用途,我不清楚它的用途。

根据我的经验,最好让视图模型尽可能简单。在某些情况下,它们甚至不需要实现INotifyPropertyChanged(想想只读视图模型)。通常,我使用提供ViewModelBase. 几乎我制作的每个视图模型都派生自ViewModelBase,并且视图模型只包含 setter、getter 和命令。

对于您的第二个问题,我认为具体的例外情况取决于使用界面的意图。但也许一个普遍的例外可能是InvalidOperationException.


推荐阅读