1

我的问题是:如何管理视图模型的继承链?

我的情况:

我有一个仅实现 INotifyPropertyChanged 接口的标准ViewModelBase 。

此外,我有一个具有 Guid的BusinessObjectViewModel 、一个具有人员核心数据的PersonBaseViewModel 、一个具有客户相关内容的CustomerViewModel和一个具有员工相关内容的EmployeeViewModel

所有的视图模型都封装了一个模型对象(Customer、Employee、PersonBase)。

  • BusinessObjectViewModel 继承自 ViewModelBase
  • PersonBaseViewModel 继承自 BusinessObjectViewModel
  • CustomerViewModel 继承自 PersonBaseViewModel
  • EmployeeViewModel 继承自 PersonBaseViewModel

模型通过构造函数进入视图模型。

如果我使用构造函数链(每个视图模型都调用基本构造函数),每个视图模型都有它的模型来从模型返回封装的值。

但是我必须在每个视图模型中都有一个模型属性。在 CustomerViewModel 的情况下,我将在CustomerViewModel中有一个引用,在 PersonBaseViewModel 中有一个引用,在BusinessObjectViewModel中有一个引用,用于同一个对象。这对我来说听起来很愚蠢。

或者我必须在上层视图模型中转换每个属性访问。

ps 这只是我模型层次结构的一个小部分。

提前致谢。

4

3 回答 3

2

IMO 最简单的答案是使用泛型,这可能很简单

public abstract class ViewModelBase<TModel>  TModel : class{
    public TModel Model { get; protected set; }
}

.net 类型系统将知道您的 TModel 是 Person、Customer 或其他任何内容,无需强制转换。

让我知道您是否需要更多或是否想发布一些需要帮助的代码。是的,一开始要让你的超类型层次结构恰到好处可能会很棘手。

HTH,
绿柱石

于 2012-05-23T18:24:48.763 回答
1

如果 BusinessObject 和 Person 类(以及它们的 VM 对应物)是抽象的,那么您可以像这样访问正确的模型:

public abstract class BusinessObjectViewModel : ViewModelBase
{
    protected abstract BusinessObject BusinessObject { get; }

    protected BusinessObject Model { get { return this.BusinessObject; } }
}

public abstract class PersonViewModel : BusinessObjectViewModel
{
    protected abstract Person Person { get; }

    protected new Person Model { get { return this.Person; } }

    protected override sealed BusinessObject BusinessObject
    {
        get { return this.Model; }
    }
}

public class CustomerViewModel : PersonViewModel
{
    protected new Customer Model { get; set; }

    protected override sealed Person Person
    {
        get { return this.Model; }
    }
}

public class EmployeeViewModel : PersonViewModel
{
    protected new Employee Model { get; set; }

    protected override sealed Person Person
    {
        get { return this.Model; }
    }
}

这样,每个派生的 VM 类通过实现抽象属性并隐藏基类 Model 属性为其基本 VM Model 属性提供值,因此每个 VM 都使用适当类型的 Model 属性(因此不需要强制转换)。

这种方法有其优点和缺点:

好处:

  • 不涉及铸造。

缺点:

  • 仅当基类(BusinessObjectViewModel 和 PersonViewModel)是抽象类时才有效,因为必须存在由派生类实现的抽象属性,并为这些基类提供模型实例。
  • 不应在基类构造函数中访问模型属性,因为构造函数链接从基类到最派生的类。派生最多的类构造函数会设置模型,所以基类构造函数会被提前调用以查看它。这可以通过将 Model 作为参数传递给构造函数来避免。
  • 派生类不需要看到 BusinessObject 和 Person 属性。EditorBrowsableAttribute 可能在此处对 Intellisense 有所帮助,但仅当代码由不同 Visual Studio 解决方案中的另一个程序集使用时(这是 Visual Studio 特定的行为)。
  • 表现。当基类访问模型时,代码会经过一系列虚拟属性。但是由于实现的抽象属性被标记为密封,虚拟表查找不应该有太多的性能下降。
  • 不能很好地扩展。对于深层类层次结构,代码将包含许多不必要的成员。

另一种方法是:

public class BusinessObjectViewModel : ViewModelBase
{
    protected BusinessObject Model { get; private set; }

    public BusinessObjectViewModel(BusinessObject model)
    {
        this.Model = model;
    }
}

public class PersonViewModel : BusinessObjectViewModel
{
    protected new Person Model { get { return (Person)base.Model; } }

    public PersonViewModel(Person model)
        : base(model)
    {
    }
}

public class CustomerViewModel : PersonViewModel
{
    protected new Customer Model { get { return (Customer)base.Model; } }

    public CustomerViewModel(Customer model)
        : base(model)
    {
    }
}

public class EmployeeViewModel : PersonViewModel
{
    protected new Employee Model { get { return (Employee)base.Model; } }

    public EmployeeViewModel(Employee model)
        : base(model)
    {
    }
}

好处:

  • 基类不需要是抽象的。
  • 模型可以通过基类构造函数访问。
  • 没有不必要的附加属性。

缺点:

  • 铸件。

基于这个分析,我会选择第二个选项,因为修复它的唯一缺点,铸造性能,将是不必要的微优化,在 WPF 上下文中不会被注意到。

于 2012-05-23T20:28:12.473 回答
0

如果您只想在 ViewModel 中公开 Model 属性,则无需在 ViewModel 中重新声明 Model 属性即可公开它们。我通常将底层模型对象作为我的 ViewModel 中的属性公开。在您的情况下,例如在您的 EmployeeViewModel 中,您将拥有:

private Employee _MyEmployee;
public Employee MyEmployee {
get
{
return _MyEmployee;
}
set
{
_MyEmployee = value;
NotifyPropertyChanged(x=>x.MyEmployee);
}

然后,您的 View 可以通过 ViewModel 中公开的 MyEmployee 属性绑定到您的 Employee 属性。据我了解,您想要在 VM 中重新声明或包装模型属性的唯一情况是您需要进行一些数据操作以呈现给您的视图。

于 2012-05-23T16:42:20.413 回答