如何避免手动实现 INotifyPropertyChanged

发布于 2024-09-07 10:21:54 字数 239 浏览 6 评论 0原文

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

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

Is there some way to avoid this. I have a lot of classes that are bound to DataGridViews and they are just simple collection of properties with default getter and setter. So these classes are very simple. Now I need to implement INotifyPropertyChanged interface for them which will increase the amount of code a lot.
Is there any class that I can inherit from to avoid writing all this boring code? I image that I can inherit my classes from some class and decorate the properties with some attributes and it will do the magic. Is that possible?

I'm well aware of Aspect Oriented Programming, but I'd rather do it object oriented way.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

别念他 2024-09-14 10:21:55

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

引用它的网站...


发送属性更改通知
不指定属性名称作为
字符串。

相反,应像这样编写属性:

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

请注意,无需将属性名称包含为字符串。 ActiveSharp 能够可靠且正确地自行解决这一问题。它的工作原理是您的属性实现通过 ref 传递支持字段 (_foo)。 (ActiveSharp 使用“by ref”调用来识别传递了哪个支持字段,并从该字段中识别属性)。

I have just found ActiveSharp - Automatic INotifyPropertyChanged, I have yet to use it, but it looks good.

To quote from it's web site...


Send property change notifications
without specifying property name as a
string.

Instead, write properties like this:

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

Note that there is no need to include the name of the property as a string. ActiveSharp reliably and correctly figures that out for itself. It works based on the fact that your property implementation passes the backing field (_foo) by ref. (ActiveSharp uses that "by ref" call to identify which backing field was passed, and from the field it identifies the property).

一枫情书 2024-09-14 10:21:55

对 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
那里也有基准。

improvements on leepie.

  • this handles a case where there is no such element in the values dictionary (which results in a KeyNotFoundException)
  • avoid passing the name of the property ( using CallerMemberName attribute )
  • use generic methods rather than indexers (["name"]).

usage:

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

code:

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

from : devto
there is also benchmark in there.

恋竹姑娘 2024-09-14 10:21:54

这取决于;您可以使用 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;
    }
}

It depends; you could use PostSharp to write such an attribute that is re-written by the weaver; however, I would be tempted to just do it manually - perhaps using a common method for handling the data updates, i.e.

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

with:

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;
    }
}
看春风乍起 2024-09-14 10:21:54

创建容器基类,例如:

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

注意:非常简化的代码

Create a container base class, eg:

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

Note: very simplified code

离线来电— 2024-09-14 10:21:54

如果没有 AOP,我认为没有一种简单的方法可以将其改造为现有的类。不管你怎么做,你至少都必须改变你的所有属性。

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

Without AOP, I don't think there is an easy way to retrofit this to your existing classes. However you do it, you're at the very least going to have to change all your properties.

I use a base class inheriting INotifyPropertyChanged with an OnPropertyChanged(string propertyName) method to fire the event. I then use a Visual Studio Code snippet to create properties that automatically call OnPropertyChanged in the property setter.

记忆之渊 2024-09-14 10:21:54

这是与 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 更改,还为 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();
    }
}

Here is a similar solution to Marc, that has been extended to allow multiple property onpropertychanges and multiple RaiseCanExecuteChanged

simplest example usage

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

advanced example using multiple property updates and multiple commands

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

The advanced example calls OnProperty changed on firstname and fullname and also calls RaiseCanExecuteChanged for command1 and command2

base ViewModel code

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();
    }
}
静谧 2024-09-14 10:21:54

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

If you're amenable to AOP, you could try using PostSharp. Search for PostSharp INotifyPropertyChanged and you'll find lots of articles explaining it, such as this and this.

我为君王 2024-09-14 10:21:54

使用代码生成(例如,T4)是另一种方法。查看讨论:通过 T4 代码生成自动 INotifyPropertyChanged 实现?

我用这个方法,效果很好。

Using code gen (say, T4) is another way. Check the discussion at: Automatic INotifyPropertyChanged Implementation through T4 code generation?.

I use this method, and it works well.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文