如何避免手动实现INotifyPropertyChanged

8
有没有什么方法可以避免这种情况。我有很多类绑定到DataGridViews,它们只是一些具有默认getter和setter的简单属性集合。因此,这些类非常简单。现在我需要为它们实现INotifyPropertyChanged接口,这将大大增加代码量。有没有任何类可以继承,以避免编写所有这些无聊的代码?我想象中我可以从某个类继承我的类,并使用一些属性进行修饰,它就会自动完成。这可行吗?
我很清楚面向方面编程,但我更愿意用面向对象的方式来做。

1
请参见https://dev59.com/bHM_5IYBdhLWcg3wlEPO。 - Ian Ringrose
http://code.google.com/p/notifypropertyweaver/ 可能会有用。 - Ian Ringrose
8个回答

9

这要看情况,你可以使用PostSharp编写这样的属性,并由织入器进行重写; 然而,我会倾向于手动执行 - 可能使用一种通用方法来处理数据更新,即。

private string name;
public string Name {
    get { return name; }
    set { Notify.SetField(ref name, value, PropertyChanged, this, "Name"); }
}

使用:

public static class Notify {
    public static bool SetField<T>(ref T field, T value,
         PropertyChangedEventHandler handler, object sender, string propertyName)
    {
        if(!EqualityComparer<T>.Default.Equals(field,value)) {
            field = value;
            if(handler!=null) {
                handler(sender, new PropertyChangedEventArgs(propertyName));
            }
            return true;
        }
        return false;
    }
}

5
创建一个容器基类,例如:
abstract class Container : INotifyPropertyChanged
{
  Dictionary<string, object> values;

  protected object this[string name]
  {
    get {return values[name]; }
    set 
    { 
      values[name] = value;
      PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
  }
}

class Foo : Container
{
  public int Bar 
  {
    {get {return (int) this["Bar"]; }}
    {set { this["Bar"] = value; } }
  }
}

注意:代码非常简化

1

这里有一个类似于Marc的解决方案,已经扩展以允许多个属性onpropertychanges和多个RaiseCanExecuteChanged

最简单的使用示例

string _firstName;
public string FirstName
{
    get { return _firstName; }
    set { OnPropertyChanged(ref _firstName, value, "FirstName"); }
}

高级示例,使用多个属性更新和多个命令

string _firstName;
public string FirstName
{
    get { return _firstName; }
    set { OnPropertyChanged(ref _firstName, value, "FirstName", "FullName", Command1, Command2); }
}

高级示例调用 OnPropertychanged 在 firstname 和 fullname 上,并且还调用 command1 和 command2 的 RaiseCanExecuteChanged。

基本 ViewModel 代码

protected void OnPropertyChanged<T>(ref T field, T value, params object[] updateThese)
{
    if (!EqualityComparer<T>.Default.Equals(field, value))
    {
        field = value;
        OnPropertyChanged(updateThese);
    }
}

protected void OnPropertyChanged(params object[] updateThese)
{
    if (PropertyChanged != null)
    {
        foreach (string property in updateThese.Where(property => property is string))
            PropertyChanged(this, new PropertyChangedEventArgs(property));

        foreach (DelegateCommand<object> command in updateThese.Where(property => property is DelegateCommand<object>))
            command.RaiseCanExecuteChanged();
    }
}

1

没有AOP,我认为没有简单的方法将其应用到现有类中。无论如何,您至少都需要更改所有属性。

我使用一个继承INotifyPropertyChanged的基类,并具有OnPropertyChanged(string propertyName)方法来触发事件。然后,我使用Visual Studio Code片段创建属性,自动在属性设置器中调用OnPropertyChanged。


0

我刚刚发现了ActiveSharp - Automatic INotifyPropertyChanged,虽然我还没有使用它,但它看起来很不错。

引用自它的网站...


发送属性更改通知,而无需将属性名称指定为字符串。相反,像这样编写属性:
public int Foo
{
    get { return _foo; }
    set { SetValue(ref _foo, value); }  // <-- no property name here
}

请注意,无需将属性名称作为字符串包含在内。ActiveSharp可靠而正确地自行解决此问题。它的工作原理是基于您的属性实现通过ref传递了支持字段(_foo)。 (ActiveSharp使用这个“按引用”调用来识别传递的是哪个后备字段,并从该字段确定属性)。

0

对leepie进行改进。

  • 处理values字典中没有这样的元素的情况(导致KeyNotFoundException
  • 避免传递属性的名称(使用CallerMemberName属性)
  • 使用通用方法而不是索引器(["name"])

用法:

public class MyViewModel : PropertyChangedHelper
{
    public int Bar
    {
        get => GetProperty<int>();
        set => SetProperty<int>(value);
    }
}

代码:

public class PropertyChangedHelper : INotifyPropertyChanged
{
    private readonly Dictionary<string, object> _values = new Dictionary<string, object>();

    public event PropertyChangedEventHandler PropertyChanged;

    public T GetProperty<T>([CallerMemberName] string propertyName = "")
    {
        EnsureElement<T>(propertyName);
        return (T) _values[propertyName];
    }

    public void SetProperty<T>(object value, [CallerMemberName] string propertyName = "")
    {
        EnsureElement<T>(propertyName);
        if (_values[propertyName] == value)
        {
            return;
        }
        _values[propertyName] = value;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private void EnsureElement<T>(string propertyName)
    {
        if (!_values.ContainsKey(propertyName))
        {
            _values.Add(propertyName, default(T));
        }
    }
}

来自: devto 里面还有基准测试。


0

如果您愿意使用AOP,可以尝试使用PostSharp。搜索PostSharp INotifyPropertyChanged,您会发现很多解释它的文章,例如thisthis


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接