如何避免手动实现INotifyPropertyChanged

时间:2010-06-28 06:37:27

标签: c# winforms data-binding collections

有没有办法避免这种情况。我有很多绑定到DataGridViews的类,它们只是带有默认getter和setter的简单属性集合。所以这些类非常简单。现在我需要为它们实现INotifyPropertyChanged接口,这将增加很多代码量。 是否有任何类可以继承以避免编写所有这些无聊的代码?我认为我可以从某个类继承我的类,并用一些属性装饰属性,它会发挥魔力。这可能吗?

我很清楚面向方面编程,但我宁愿以面向对象的方式进行。

8 个答案:

答案 0 :(得分: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;
    }
}

答案 1 :(得分:4)

创建容器基类,例如:

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; } }
  }
}

注意:非常简化的代码

答案 2 :(得分:1)

如果没有AOP,我认为没有一种简单的方法可以将这种方法改进现有的课程。无论你如何做,你至少都需要改变你的所有属性。

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

答案 3 :(得分: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); }
}

高级示例调用OnProperty在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();
    }
}

答案 4 :(得分:0)

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

答案 5 :(得分:0)

使用代码gen(比如,T4)是另一种方式。请查看讨论:Automatic INotifyPropertyChanged Implementation through T4 code generation?

我使用这种方法,效果很好。

答案 6 :(得分:0)

我刚刚找到ActiveSharp - Automatic INotifyPropertyChanged,我还没有使用它,但它看起来不错。

引用它的网站......


  

发送房产更改通知   没有指定属性名称   字符串。

相反,写下这样的属性:

public int Foo
{
    get { return _foo; }
    set { SetValue(ref _foo, value); }  // <-- no property name here
}

请注意,不需要将属性的名称包含在字符串中。 ActiveSharp可靠而正确地为自己确定了这一点。它的工作原理是您的属性实现通过ref传递了支持字段(_foo)。 (ActiveSharp使用“by ref”调用来标识传递了哪个支持字段,并从字段中识别属性)。

答案 7 :(得分:0)

leepie 的改进。

  • 这会处理 values 字典中没有这样的元素的情况(导致 KeyNotFoundException)
  • avoid passing the name 的属性(使用 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 那里也有基准。