【问题标题】:ViewModel that automagically has same properties than generic modelViewModel 自动具有与通用模型相同的属性
【发布时间】:2019-03-27 13:55:53
【问题描述】:

我正在尝试在下面的代码中为用例创建代码,其中通用 ViewModel 类“捕获”其模型中的所有属性,并呈现具有相同名称和类型的属性,并且还触发 @987654321 @ 数据绑定事件。

有办法吗?我正在使用 .NET 4.6。

public class Rectangle
{
    public double Width {get; set;}
    public double Height {get; set;}
}

public class RectangleViewModel : MagicViewModel<Rectangle>
{
    public RectangleViewModel(Rectangle model) 
        : base(model){ }
}

public class MagicViewModel<TModel> : INotifyPropertyChanged
{
    protected readonly TModel _model;

    public MagicViewModel(TModel model)
    {
        _model = model;
    }

    // inpc implementation

    // what else?
}

public class Program
{
    public static void Main(string[] args)
    {
        var vm = new RectangleViewModel(new Rectangle());
        var calls = 0;
        vm.PropertyChanged += (sender, args) => calls++;
        vm.Height = 10;  // magic happened here
        Debug.Assert(calls > 0);
    }
}

【问题讨论】:

    标签: c# mvvm data-binding inotifypropertychanged .net-4.6


    【解决方案1】:

    您可以使用Fody 自动注入代码,在编译 时为模型类的所有属性引发PropertyChanged 事件。

    然后你可以直接绑定到Rectangle而不修改它并显式实现INotifyPropertyChanged接口。

    否则,恐怕您将不得不在每个视图模型中一个接一个地定义每个属性,或者想办法在构建之前自动生成视图模型类。

    【讨论】:

      【解决方案2】:

      您正在寻找类似的东西吗?

      using System.Collections.Generic;
      using System.ComponentModel;
      using System.Diagnostics;
      using System.Runtime.CompilerServices;
      
      public class Rectangle : INotifyPropertyChanged
      {
          private double height;
      
          public double Width { get; set; }
          public double Height { get => height; set => SetField(ref height, value); }
      
          #region INotifyPropertyChanged Implementation
          public event PropertyChangedEventHandler PropertyChanged;
          protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
          {
              PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
          }
      
          protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
          {
              if (EqualityComparer<T>.Default.Equals(field, value))
                  return false;
              field = value;
              OnPropertyChanged(propertyName);
              return true;
          }
          #endregion
      }
      
      public class RectangleViewModel : MagicViewModel<Rectangle>
      {
          public RectangleViewModel(Rectangle model)
              : base(model)
          {
              this.model = model;
              model.PropertyChanged += (s, e) => OnPropertyChanged(e.PropertyName);
          }
      
          private Rectangle model;
      
          public Rectangle Model { get => model; set => SetField(ref model, value); }
      }
      
      public class MagicViewModel<TModel> : INotifyPropertyChanged
      {
          protected readonly TModel _model;
      
          public MagicViewModel(TModel model)
          {
              _model = model;
          }
      
          #region INotifyPropertyChanged Implementation
          public event PropertyChangedEventHandler PropertyChanged;
          public void OnPropertyChanged([CallerMemberName] string propertyName = null)
          {
              PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
          }
      
          public bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
          {
              if (EqualityComparer<T>.Default.Equals(field, value))
                  return false;
              field = value;
              OnPropertyChanged(propertyName);
              return true;
          }
          #endregion
      }
      
      public class Program
      {
          public static void Main(string[] args)
          {
              var vm = new RectangleViewModel(new Rectangle());
              var calls = 0;
              vm.PropertyChanged += (sender, propChangedArgs) => calls++;
              vm.Model.Height = 10;  // magic happened here
              Debug.Assert(calls > 0);
          }
      }
      

      【讨论】:

      • 不完全是。我需要模型是任何类,甚至是具有公共 get/set 属性的结构。您的建议要求模型(重新)实现您在 MagicViewModel 实现中使用的大量基础架构,从而消除作为主要目标的任何重复删除优势。例如,肯定不需要实现 INPC 的模型。
      • 除非您使用 PostSharp 或 Fody 之类的东西(例如提到的 mm8),否则您确实必须在每个级别实现 INPC。否则,没有什么可以引发 PropertyChanged 事件。您可以从具有实际 INPC 实现的基类继承,但您的设置器仍需要调用 Set(ref yourField, value)。
      • 目前我所做的是在 ViewModel 中实现 INPC,而 Model 类只有自动属性,具有简单的 get/set 方法。问题是我必须在 ViewModel 中为模型中的每个属性编写一个属性和一个 INPC get/set 对,并且它们的类型最终是强耦合的。我将仔细研究 Fody,我发现在这个用例中经常提到它。谢谢!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-25
      • 1970-01-01
      • 2016-09-22
      • 2015-04-09
      相关资源
      最近更新 更多