避免在MVVM模型中使用ObservableCollection

时间:2017-06-30 09:15:47

标签: c# wpf mvvm

我有一个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的功能。也许用某种转换器或铸件或类似的东西。

3 个答案:

答案 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;您的ModelViewModel之间的关系,因此您有以下几种选择:

选项1: 如果没有继承,则可以在模型中使用具有相同名称但属性不同的属性。在这两个类之间进行映射时要小心。

选项2:如果属性确实需要具有相同类型,请使用IList<>IEnumberable<>

IList<object> list1 = new ObservableCollection<object>();
IList<object> list2 = new List<object>();

C#中的所有集合都实现IList,因此所有集合都可以存储在类型为IList的变量或属性中。如果您不需要任何特殊列表功能,例如AddClearContains等,您可以使用IEnumerable<>作为属性类型。

选项3: 如果ModelViewModel之间存在继承,则可以使用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从模型继承,请使用选项二。