首页 文章

wpf这些视图模型类之间的区别是什么?

提问于
浏览
2

我很想知道这两个类选项之间的差异和/或好处 . 我已经在堆栈溢出中看到了几个视图模型的例子,并且经常可以看到下面的两个类示例 . 我不确定区别是什么 . 希望有人可以解释原因吗?何时使用另一个?...谢谢 .

Class Option #1

private ObservableCollection<Family> families;

public ObservableCollection<Family> Families
{
    get { return families ?? (families = new ObservableCollection<Families>()); }
}

Class Option #2

private ObservableCollection<Family> families; 

public ObservableCollection<Family> Families
{
    get { return families; }
    set
    {
        families = value;
        NotifyPropertyChanged("Families");
    }
}

should they be combined?

public ObservableCollection<Family> Families
{
    get { return families ?? (families = new ObservableCollection<Family>()); }
    set
    {
        families = value;
        NotifyPropertyChanged("Families");
    }
}

2 回答

  • 1

    我不确定我是否完全理解这个问题,对我而言,两种选择之间的差异是不言而喻的 . 但是从面值来看问题:

    选项#1有两个重要特征:

    • 它是只读的 . 这并不常见,但当然可以在某些情况下使用 .

    • 很懒 . 即在访问之前,它不会初始化底层集合 . 恕我直言,这在WPF视图模型场景中的 Value 有限,因为通常一个属性将绑定到其他东西,因此一旦对象被实例化,其他一些对象将读取属性并强制初始化底层集合无论如何 .

    选项#2是 INotifyPropertyChanged 的典型实现 . 这适用于WPF 's property binding system, but doesn' t充分利用系统 .

    我认为差异非常明显:因为第一个选项是只读的,所以它不需要属性更改通知,因此当然不实现 . 另一方面,第二个选项需要外部初始化属性;它不仅不像第一个一样懒,它甚至根本不初始化属性,保留默认值为 null .

    如果你想要一个具有两者特性的视图模型(延迟初始化和可写性/ INotifyPropertyChanged 实现),那么是的......你实际上可以结合两个例子中的技术 .

    就个人而言,我更喜欢尽可能将我的视图模型类实现为 DependencyObject 子类(几乎所有时间) . 这需要更多的输入,因为声明 DependencyProperty 对象的冗长,但它与WPF绑定系统完全集成,可以更高效,并且只在使用 INotifyPropertyChanged (例如值强制)时启用不可用的绑定功能 .

    DependencyObject 属性看起来与您发布的两个示例中的任何一个都不相同 . 相反,它可能看起来像这样:

    class ViewModel : DependencyObject
    {
        public static readonly DependencyProperty FamiliesProperty =
            DependencyProperty.Register("Families", typeof(ObservableCollection<Families>),
            typeof(ViewModel), new PropertyMetadata(new ObservableCollection<Families>()));
    
        public ObservableCollection<Family> Families
        {
            get { return (ObservableCollection<Family>)GetValue(FamiliesProperty); }
            set { SetValue(FamiliesProperty, value); }
        }
    }
    

    那么,什么时候应该实现 INotifyPropertyChanged ,何时应该继承 DependencyObject . 坦率地说,我发现这可以归结为个人偏好,就像其他任何东西一样 . 实际的权衡取舍是不寻常的 . 但需要权衡利弊 .

    其中一个最彻底的讨论可以在这里找到:
    INotifyPropertyChanged vs. DependencyProperty in ViewModel
    为了充分理解,请务必阅读整个讨论,即所有答案,而不仅仅是已接受的答案 .

    简要总结一些亮点:

    • 使用 DependencyObjectDependencyProperty 的优点包括:

    一个 . 能够使用该属性作为绑定目标 . WPF可以使用 INotifyPropertyChanged 对象属性作为绑定源,但绑定的目标属性必须是依赖项属性 . 对于简单的场景,这不是问题,但有时您可能希望链接属性绑定和/或使用源绑定在XAML中声明视图模型(即使只是初始化它),并且只有依赖项属性允许这个 .

    湾更高效 . WPF绑定系统针对依赖项属性方案进行了优化,使用依赖项属性时,与使用 INotifyPropertyChanged 时相比,属性值的更新将更快 . 大多数时候,与绑定相比,这在API的其他领域赢得了很多开销 . 但在某些情况下,这很重要 .

    C . 访问依赖项属性系统的其他功能,例如值强制 .

    • 使用 INotifyPropertyChanged 的优点包括:

    一个 . 不需要继承具体类型 . 在实践中,大多数人最终使用实现接口的专用基类,因此最终结束了清洗 . 此外,如果视图模型类继承任何其他类型,它几乎总是将继承适当的基类(无论是 DependencyObject 还是实现 INotifyPropertyChanged 的帮助器基类)的另一个视图模型类 . 但这是理论上的可能性 .

    湾您可以更轻松地序列化对象,因为 DependencyObject 本身不可序列化 .

    C . 能够覆盖 Equals()GetHashCode() . 虽然在实践中,这可能是您在使用其相等特征(例如,将对象存储在哈希集或字典中)之后的值 . 如果您确实想要进行相等比较,您也可以为视图模型类实现 IEqualityComparer<T> ,无论它是否为 DependencyObject .

    d . 它的属性可以更新任何线程( DependencyObject 要求在拥有它的线程上访问该对象) . 同样,在实践中,这并不是一个问题,因为视图模型的本质是绑定到也具有线程亲和性的其他对象 . 即如果你在一些非UI线程上改变你的视图模型,它将导致调用一个需要UI线程的对象并且无论如何都会失败 . 相反, DependencyObject 本身仍然可以在非UI线程中使用...你只需要't do anything with it that would affect a dependency property, or create in it other objects that require the UI thread (e.g. other dependency objects). On occasion, this limitation may matter but in most cases it won' t .

    换句话说,所有这些都是两种技术之间真正的实际差异,但实际上,这两种技术的假设优势都不是真正重要的 .

  • 3

    第一个选择:

    private ObservableCollection<TabItem> families;
    
    public ObservableCollection<TabItem> Families
    {
        get { return families ?? (families = new ObservableCollection<Families>()); }
    }
    

    当您需要懒惰地实例化readonly属性的支持字段时使用 . 由于此处没有setter,因此您永远不能为该属性赋值,您只能读取该值 .

    这种方法可让您在需要时在最后一刻创建支持字段 . 如果这是数据绑定的,则由于视图将在创建期间查询属性值而浪费惰性实例化,这意味着后备字段将始终立即被实例化 .

    第二种选择

    private ObservableCollection<Family> families; 
    
    public ObservableCollection<Family> Families
    {
        get { return families; }
        set
        {
            families = value;
            NotifyPropertyChanged("Families");
        }
    }
    

    允许读取和写入属性 . 由于属性可以为其分配新值,因此必须调用 NotifyPropertyChanged ,以便可以告知View已更改该值 . 一旦视图知道值已更改,它将通过从属性中获取新值来刷新自身 .

    在数据绑定属性的情况下,将两者结合起来实际上只是一个浪费的IMO . 在分配DataContext并实例化后备字段时,视图将数据绑定到属性 .

    对于不是数据绑定的属性,懒惰实例化的行为取决于您 . 创建集合需要大量开销吗?在您提供的示例中,没有 . 但是,如果你懒得从其他数据源创建它,那可能有点贵,那么是的 .

    这完全取决于您尝试使用该属性解决的问题 .

相关问题