我的问题是:如何管理视图模型的继承链?
我的情况:
我有一个标准的ViewModelBase,它只实现了INotifyPropertyChanged接口。
此外,我有一个具有GUID的BusinessObjectViewModel,一个具有人员核心数据的PersonBaseViewModel,一个包含客户相关内容的CustomerViewModel和一个带有员工相关内容的EmployeeViewModel。
所有视图模型当然都封装了一个模型对象(客户、员工、PersonBase(。
- BusinessObjectViewModel 继承自 ViewModelBase
- PersonBaseViewModel 继承自 BusinessObjectViewModel
- CustomerViewModel 继承自 PersonBaseViewModel
- EmployeeViewModel 继承自 PersonBaseViewModel
模型通过构造函数进入视图模型。
如果我使用构造函数链(每个视图模型都调用基构造函数(,则每个视图模型都有它的模型来从模型中返回封装的值。
但是我必须在每个视图模型中都有一个模型属性。在 CustomerViewModel 的情况下,我会在 CustomerViewModel 中有一个引用,一个在 PersonBaseViewModel 中引用,一个在 BusinessObjectViewModel 中引用同一个对象。这对我来说听起来很愚蠢。
或者我必须在上视图模型中转换每个属性访问权限。
附言这只是我的模型层次结构的一小部分。
提前谢谢。
如果 BusinessObject 和 Person 类(及其虚拟机对应项(是抽象的,那么您可以像这样访问正确的模型:
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 类都通过实现抽象属性为其基本 VM 模型属性提供一个值,并隐藏基类 Model 属性,因此每个 VM 都使用适当类型的 Model 属性(因此不需要强制转换(。
这种方法有其优点和缺点:
好处:
- 不涉及选角。
缺点:
- 仅当基类(BusinessObjectViewModel 和 PersonViewModel(是抽象的时才有效,因为必须存在由派生类实现的抽象属性,并为这些基类提供模型实例。 不应在基类构造函数
- 中访问模型属性,因为构造函数链接从基类转到派生最多的类。派生最多的类构造函数将设置 Model,因此基类构造函数被调用到早期查看它。可以通过构造函数将 Model 作为参数传递来避免这种情况。
- 派生类不需要看到业务对象和人员属性。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 上下文中并不明显。
最简单的答案是使用泛型,这可能很简单
public abstract class ViewModelBase<TModel> TModel : class{
public TModel Model { get; protected set; }
}
.net 键入系统无需强制转换即可知道您的 TModel 是个人、客户或其他任何内容。
如果您需要更多或想发布一些需要帮助的代码,请告诉我。是的,一开始让你的超类型层次结构恰到好处可能会很棘手。
呵呵,
贝瑞尔
如果您只想在 ViewModel 中公开模型属性,则无需在 ViewModel 中重新声明模型属性即可公开它们。 我通常将基础模型对象作为属性公开在我的视图模型中。例如,在您的 EmployeeView 模型中,您将拥有:
private Employee _MyEmployee;
public Employee MyEmployee {
get
{
return _MyEmployee;
}
set
{
_MyEmployee = value;
NotifyPropertyChanged(x=>x.MyEmployee);
}
然后,您的视图可以通过视图模型中公开的 MyEmployee 属性绑定到您的员工属性。据我了解,想要在 VM 中重新声明或包装模型属性的唯一情况是需要进行一些数据操作才能呈现给视图。