【问题标题】:Better way to raise property changed MVVMLight提高财产的更好方法改变了MVVMLight
【发布时间】:2017-09-15 01:44:11
【问题描述】:

使用 MVVM Light 创建了一个项目。 ViewModel 有很多类似这样的属性是很常见的

class TestModel
{
    public string DisplayValue { get; set; }
}

class TestViewModel : ViewModelBase
{
    public string DisplayValue
    {
         private TestModel model = new TestModel();

         get
         {
              return model.DisplayValue;
         }
         set
         {
              if (model.DisplayValue != value)
              {
                   model.DisplayValue = value;
                   RaisePropertyChanged();
              }
         }
    }
}

有时该属性不在模型中,而是由本地私有字段支持。这种方法效果很好,但是有大量的样板代码。如何减少代码重复?

有没有比我提出的解决方案更好的解决方案,或者我错过了 MVVM Light 内置的东西?

【问题讨论】:

    标签: c# wpf mvvm


    【解决方案1】:

    我的样板 MVVM Light 属性版本如下所示:

    private string _p = "";
    
    public string P
    {
      get { return _p; }
      set { Set(ref _p, value); }
    }
    

    这几乎是最薄的,通过使用底层ObservableObject 类提供的Set 函数成为可能(你也可以使用ViewModelBase)。

    编辑

    在 C# 7 及更高版本中,您可以进一步压缩它:

    private string _p = "";
    public string P
    {
      get => _p;
      set => Set(ref _p, value);
    }
    

    【讨论】:

    • 您可以省略 nameof(Prop),其中一个重载使用 CallerMemberName,因此 set { ref _Prop, value); } 给出相同的结果
    • 做 Set(ref _Prop, value);包括 if (_Prop != value) ?
    • @gts13:我认为是的。
    • @gts13,是的。
    • @dotNET ,是的。如果您导航到 ViewModelBase.cs 中的 Set 访问器,您可以找到以下代码: if (EqualityComparer.Default.Equals(field, newValue)) { return false; }
    【解决方案2】:

    MVVM light 提供如下设置方法,你可以直接使用它。

    class TestViewModel : ViewModelBase
    {
       private TestModel model = new TestModel();
    
       public string DisplayValue
      {
         get{return model.DisplayValue;}
         set{ Set(()=>DisplayValue, ref model.DisplayValue, value); }
      }   
    }
    

    【讨论】:

    • 有没有办法传入一个只有在属性改变时才会执行的函数?
    • 这个 set 方法也会给你的属性赋值并引发属性更改事件
    • 我明白了,但是如果值发生这样的变化,我想运行一个方法if (_highDwellDiameter != value) { _highDwellDiameter = value; highDwellRadius = value / 2; RaisePropertyChanged(); }
    • 这也很好,但是你使用的是 MVVM 灯,这就是为什么我被告知使用 set 方法,然后使用 highDwellRadius=value/2
    • 其实这行不通,因为model.DisplayValue是一个属性,而不是一个字段。你不能在属性上使用ref
    【解决方案3】:

    在我的应用程序中,模型是一个纯数据存储,主要用于读取和写入 SQL Server。该模型包含 ViewModel 中保存到服务器的所有属性的支持字段。所以在模型中只有字段并且只能通过 ViewModel 访问它们是有意义的

    class TestModel
    {
        public string displayValue;
        public override bool Equals(object obj)
        {
            if (obj.GetType() != typeof(TestModel))
                    return false;
    
            var testObj = obj as TestModel;
            return testObj?.GetHashCode() == testValue?.GetHashCode();
        }
    
        public override int GetHashCode()
        {
            return displayValue.GetHashCode();
        }
    }
    

    GetHashCode 将是在向类中添加其他字段时唯一需要更新的内容。由于它是一个字段,因此可以通过引用一个通用函数来传递它并在所有属性中使用它。

    class TestViewModel:ViewModelBase
    {
        private TestModel model = new TestModel();
        public string DisplayValue
        {
            get { return model.displayValue; }
            set { SetIfChanged(ref model.displayValue, value, RunCalculations); }
        }
    
    
        public bool SetIfChanged<T>(ref T field, T value, Action MoreWork, [CallerMemberName] string propertyName = null)
        {
            if (!Equals(field, value))
            {
                field = value;
                MoreWork.Invoke();
                RaisePropertyChanged(propertyName);
                return true;
            }
    
            return false;
        }
    
        private void RunCalculations()
        {
           // Do some work before RaisePropertyChanged()
        }
    }
    

    这接受所有类型,根据需要覆盖 EqualTo 以使相等性正常工作。它还可以根据需要运行其他计算。

    如果可以像 MVVM Light Set 方法一样将其从 ViewModel 中删除,那就太好了,但我一直无法弄清楚。

    【讨论】:

    • 我推荐object.Equals(field, value),以防你有null字段。同样在我的项目中,我有类似的东西,但它在提升属性更改之前执行Action(有时这可能非常有用)并且该函数返回一个布尔值,指示该值是否实际更改,因此我可以使用简单的触发附加代码if 声明。
    • @Adam, !field.Equals(value) 如果field 为空,将抛出 NullReferenceException!也许你最好写if (EqualityComparer&lt;T&gt;.Default.Equals(field, value))
    猜你喜欢
    • 2018-04-11
    • 2016-08-05
    • 1970-01-01
    • 1970-01-01
    • 2013-04-27
    • 2022-01-15
    • 1970-01-01
    • 2013-10-03
    相关资源
    最近更新 更多