如何在不使用字符串名称的情况下引发 PropertyChanged 事件 [英] How to raise PropertyChanged event without using string name

查看:20
本文介绍了如何在不使用字符串名称的情况下引发 PropertyChanged 事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果能够在不明确指定已更改属性的名称的情况下引发 'PropertyChanged' 事件会很好.我想做这样的事情:

It would be good to have ability to raise 'PropertyChanged' event without explicit specifying the name of changed property. I would like to do something like this:

    public string MyString
    {
        get { return _myString; }
        set
        {
            ChangePropertyAndNotify<string>(val=>_myString=val, value);
        }
    }

    private void ChangePropertyAndNotify<T>(Action<T> setter, T value)
    {
        setter(value);
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(setter.Method.Name));
        }
    }

在这种情况下,接收到的名称是 lambda 方法的名称:<set_MyString>b__0".

In this case received name is a name of lambda-method: "<set_MyString>b__0".

  1. 我可以确定,修剪<set_"和>b__0"将始终提供正确的属性名称?
  2. 是否还有其他人需要通知有关财产变更的信息(来自财产本人)?

谢谢.

推荐答案

添加了 C# 6 答案

在 C# 6(以及 Visual Studio 2015 附带的任何版本的 VB)中,我们有 nameof 运算符,这使事情变得比以往更容易.在下面的原始答案中,我使用 C# 5 功能(调用者信息属性)来处理自我更改"通知的常见情况.nameof 运算符可以在所有情况下使用,并且在相关属性更改"通知场景中特别有用.

In C# 6 (and whatever version of VB comes with Visual Studio 2015) we have the nameof operator which makes things easier than ever. In my original answer below, I use a C# 5 feature (caller info attributes) to handle the common case of "self-changed" notifications. The nameof operator can be used in all cases, and is especially useful in the "related-property-changed" notification scenario.

为了简单起见,我想我会保留调用者信息属性方法来处理常见的自我更改通知.更少的输入意味着更少的拼写错误和复制/粘贴引起的错误......这里的编译器确保您选择有效的类型/成员/变量,但并不能确保您选择正确的类型.然后使用新的 nameof 运算符进行相关属性更改通知很简单.下面的例子演示了调用者信息属性的一个关键行为......如果参数是由调用者指定的,则该属性对参数没有影响(即,只有当参数被忽略时,才为参数值提供调用者信息)来电者).

For simplicity, I think I'll keep the caller info attribute approach for common self-changed notifications. Less typing means less chances for typos and copy/paste induced bugs... the compiler here ensures that you pick a valid type/member/variable, but it doesn't ensure you pick the correct one. It is simple to then use the new nameof operator for related-property change notifications. The example below demonstrates a key behavior of caller info attributes... the attribute has no effect on a parameter if the parameter is specified by the caller (that is, the caller info is provided for the parameter value only when the parameter is omitted by the caller).

还值得注意的是,nameof 运算符也可以被 PropertyChanged 事件处理程序使用.现在,您可以使用 nameof 运算符将事件中的 PropertyName 值(它是一个 string)与特定属性进行比较,从而消除更多魔术字符串.

It is also worth observing that the nameof operator can be used by PropertyChanged event handlers as well. Now you can compare the PropertyName value in the event (which is a string) to a particular property using the nameof operator, eliminating more magic strings.

nameof 的参考信息:https:///msdn.microsoft.com/en-us/library/dn986596.aspx

示例:

public class Program
{
    void Main()
    {
        var dm = new DataModel();
        dm.PropertyChanged += propertyChangedHandler;
    }

    void propertyChangedHandler(object sender, PropertyChangedEventArgs args)
    {
        if (args.PropertyName == nameof(DataModel.NumberSquared))
        {
            //do something spectacular
        }
    }
}


public class DataModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            var e = new PropertyChangedEventArgs(propertyName);
            handler(this, e);
        }
    }
}

public class DataModel : DataModelBase
{
    //a simple property
    string _something;
    public string Something 
    { 
        get { return _something; } 
        set { _something = value; OnPropertyChanged(); } 
    }

    //a property with another related property
    int _number;
    public int Number
    {
        get { return _number; }

        set 
        { 
            _number = value; 
            OnPropertyChanged(); 
            OnPropertyChanged(nameof(this.NumberSquared)); 
         }
    }

    //a related property
    public int NumberSquared { get { return Number * Number; } }
}

原始 C# 5 答案

从 C# 5 开始,最好使用调用者信息属性,这是在编译时解决的,不需要反射.

Since C# 5, best to use caller info attributes, this is resolved at compile time, no reflection necessary.

我在基类中实现了这一点,派生类只是从它们的属性设置器中调用 OnPropertyChanged 方法.如果某些属性隐式地更改了另一个值,我也可以在属性设置器中使用该方法的显式"版本,这样就不再安全"了,但这是我接受的一种罕见情况.

I implement this in a base class, derived classes just call the OnPropertyChanged method from within their property setters. If some property implicitly changes another value, I can use the "Explicit" version of the method in the property setter as well, which then is no longer "safe" but is a rare situation that I just accept.

或者,您可以使用此方法进行自我更改通知,并使用@Jehof 给出的答案进行相关属性更改通知......更改通知.

Alternatively you could use this method for self change notifications, and use the answer given by @Jehof for related property change notifications ... this would have the advantage of no magic strings, with the fastest execution for the common case of self change notifications.

这个最新的建议在下面实施(我想我会开始使用它!)

This latest suggestion is implemented below (I think I'll start using it!)

public class DataModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        OnPropertyChangedExplicit(propertyName);
    }

    protected void OnPropertyChanged<TProperty>(Expression<Func<TProperty>> projection)
    {
        var memberExpression = (MemberExpression)projection.Body;
        OnPropertyChangedExplicit(memberExpression.Member.Name);
    }

    void OnPropertyChangedExplicit(string propertyName)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            var e = new PropertyChangedEventArgs(propertyName);
            handler(this, e);
        }
    }
}

public class DataModel : DataModelBase
{
    //a simple property
    string _something;
    public string Something 
    { 
        get { return _something; } 
        set { _something = value; OnPropertyChanged(); } 
    }

    //a property with another related property
    int _number;
    public int Number
    {
        get { return _number; }

        set 
        { 
            _number = value; 
            OnPropertyChanged(); 
            OnPropertyChanged(() => NumberSquared); 
         }
    }

    //a related property
    public int NumberSquared { get { return Number * Number; } }
}

这篇关于如何在不使用字符串名称的情况下引发 PropertyChanged 事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆