我有一个Model类,它有一个Collection和一个View类的ViewModel包装类。包装器类实现INotifyPropertyChanged
,并为模型类中的每个属性提供包装器属性。我已经通过这种方式实现它,使模型类尽可能独立于任何WPF名称空间,因为这些类也用于另一个项目(Windows服务)。实现(简化)如下所示:
模型
public class FbiDirectory
{
private string type;
private ObservableCollection<PluginValue> pluginValues = new ObservableCollection<PluginValue>();
public string Type
{
get
{
return this.type;
}
set
{
this.type = value;
}
}
public ObservableCollection<PluginValue> PluginValues
{
get
{
return this.pluginValues;
}
set
{
this.pluginValues = value;
}
}
}
ViewModel包装
public class FbiDirectoryViewModel : INotifyPropertyChanged
{
private FbiDirectory fbiDirectory = new FbiDirectory();
public string Type
{
get
{
return this.fbiDirectory.Type;
}
set
{
this.fbiDirectory.Type = value;
this.OnPropertyChanged("Type");
this.OnPropertyChanged("Title");
}
}
public ObservableCollection<PluginValue> PluginValues
{
get
{
return this.fbiDirectory.PluginValues;
}
set
{
this.fbiDirectory.PluginValues = value;
this.OnPropertyChanged("PluginValues");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
我的问题是,是否有办法在PluginValues
类型的模型中生成List
集合,并且在ViewModel中仍然具有ObservableCollection
的功能。也许用某种转换器或铸件或类似的东西。
答案 0 :(得分:4)
您始终可以将模型类保持为常规List<>
,并使视图模型上的匹配属性为ObservableCollection<>
。我经常这样做,没有任何问题。在View Model的构造函数中(以Model作为参数),我只是从Model上属性的内容中实例化View Model的ObservableCollection<>
。如果您实际上也包含了 属性Type
,那么这种方法效果特别好。
当Model属性发生变化时,你不会在View Model属性上获得自动更新,所以你必须自己保持同步,但我仍然认为View Model的部分工作是,特别是当你包装一个你无法控制的模型时。
public void MyViewModel(MyModel<MyModelPropertyType> model)
{
MyListProperty = new ObservableCollection<MyWrappedModelPropertyType>(model.ModelListProperty.Select(i => new MyWrappedModelPropertyType(i));
}
如果您不需要包装属性类型,则可以省略LINQ。在实例化时间和内存开销方面,您最终会从复制列表中获得一些开销。如果你需要包装的属性类型,你最终会这样做。
答案 1 :(得分:1)
我的问题是,是否有一种方法可以在List of type中创建PluginValues集合,并且在ViewModel中仍然具有ObservableCollection的功能。
没有。 ObservableCollection<T>
实现INotifyCollectionChanged
界面而List<T>
不实现。
因此,如果您打算在运行时向PluginValues
集合动态添加项,则应该是ObservableCollection<T>
或实现INotifyCollectionChanged
接口的任何其他类型的集合类型。
但是,正如评论中所指出的,ObservableCollection<T>
并不是真正的WPF特定类,因为它是在System.Collections.ObjectModel
中的System.dll
命名空间中定义的。因此,您可以在模型中使用ObservableCollection<T>
,或者实现自己的自定义集合,以提高通知。后者可能看起来有点必要。
答案 2 :(得分:-1)
你没有任何继承或其他强迫&#34;您的Model
和ViewModel
之间的关系,因此您有以下几种选择:
选项1: 如果没有继承,则可以在模型中使用具有相同名称但属性不同的属性。在这两个类之间进行映射时要小心。
选项2:如果属性确实需要具有相同类型,请使用IList<>
或IEnumberable<>
。
IList<object> list1 = new ObservableCollection<object>();
IList<object> list2 = new List<object>();
C#中的所有集合都实现IList
,因此所有集合都可以存储在类型为IList
的变量或属性中。如果您不需要任何特殊列表功能,例如Add
,Clear
,Contains
等,您可以使用IEnumerable<>
作为属性类型。
选项3:
如果Model
和ViewModel
之间存在继承,则可以使用new
关键字覆盖属性。
public class Model
{
public List<object> Plugins { get; set; }
}
public class ViewModel : Model
{
public new ObservableCollection<object> Plugins { get; set; }
}
上面的列表是有效代码,会更改Plugin
的类型。请注意,使用new
键盘会产生一些奇怪的效果。
假设您使用WPF(猜测MVVM和C#),选项3将无效。 WPF使用反射进行数据绑定。如果用new覆盖一个属性,WPF将找到两个实现而不知道使用哪一个,它将抛出异常。
<强>结论:强> 我个人更喜欢选项1.没有理由为什么模型,DTO和ViewModel应该从同一个基础继承,并且他们没有理由不能拥有相同名称但不同类型的属性。
如果您希望ViewModel从模型继承,请使用选项二。