c# - raisepropertychanged - inotifypropertychanged用法



自動INotifyPropertyChanged (9)

有沒有辦法自動獲得類中屬性更改的通知,而無需在每個setter中編寫OnPropertyChanged? (如果有更改,我有數百個我想知道的屬性)。

安東建議使用動態代理 。 我實際上使用了“Castle”庫來獲得類似的東西,雖然它確實減少了我必須編寫的代碼量,但它增加了大約30秒到我的程序啟動時間(ymmv) - 因為它是一個運行時方案。

我想知道是否有編譯時解決方案,可能使用編譯時屬性...

Slashene和TcKs給出了生成重複代碼的建議 - 遺憾的是,並非所有屬性都是m_Value = value的簡單情況 - 很多都在setter中有自定義代碼,所以來自片段和xml的cookie-cutter代碼實際上不可行我的項目也是。


Answer #1

nameof運算符是在2015年7月使用.NET 4.6和VS2015在C#6.0中實現的。以下內容對C#<6.0仍然有效

我們使用下面的代碼(來自http://www.ingebrigtsen.info/post/2008/12/11/INotifyPropertyChanged-revisited.aspx )。 很棒:)

public static class NotificationExtensions
{
    #region Delegates

    /// <summary>
    /// A property changed handler without the property name.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="sender">The object that raised the event.</param>
    public delegate void PropertyChangedHandler<TSender>(TSender sender);

    #endregion

    /// <summary>
    /// Notifies listeners about a change.
    /// </summary>
    /// <param name="EventHandler">The event to raise.</param>
    /// <param name="Property">The property that changed.</param>
    public static void Notify(this PropertyChangedEventHandler EventHandler, Expression<Func<object>> Property)
    {
        // Check for null
        if (EventHandler == null)
            return;

        // Get property name
        var lambda = Property as LambdaExpression;
        MemberExpression memberExpression;
        if (lambda.Body is UnaryExpression)
        {
            var unaryExpression = lambda.Body as UnaryExpression;
            memberExpression = unaryExpression.Operand as MemberExpression;
        }
        else
        {
            memberExpression = lambda.Body as MemberExpression;
        }

        ConstantExpression constantExpression;
        if (memberExpression.Expression is UnaryExpression)
        {
            var unaryExpression = memberExpression.Expression as UnaryExpression;
            constantExpression = unaryExpression.Operand as ConstantExpression;
        }
        else
        {
            constantExpression = memberExpression.Expression as ConstantExpression;
        }

        var propertyInfo = memberExpression.Member as PropertyInfo;

        // Invoke event
        foreach (Delegate del in EventHandler.GetInvocationList())
        {
            del.DynamicInvoke(new[]
            {
                constantExpression.Value, new PropertyChangedEventArgs(propertyInfo.Name)
            });
        }
    }


    /// <summary>
    /// Subscribe to changes in an object implementing INotifiyPropertyChanged.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="ObjectThatNotifies">The object you are interested in.</param>
    /// <param name="Property">The property you are interested in.</param>
    /// <param name="Handler">The delegate that will handle the event.</param>
    public static void SubscribeToChange<T>(this T ObjectThatNotifies, Expression<Func<object>> Property, PropertyChangedHandler<T> Handler) where T : INotifyPropertyChanged
    {
        // Add a new PropertyChangedEventHandler
        ObjectThatNotifies.PropertyChanged += (s, e) =>
            {
                // Get name of Property
                var lambda = Property as LambdaExpression;
                MemberExpression memberExpression;
                if (lambda.Body is UnaryExpression)
                {
                    var unaryExpression = lambda.Body as UnaryExpression;
                    memberExpression = unaryExpression.Operand as MemberExpression;
                }
                else
                {
                    memberExpression = lambda.Body as MemberExpression;
                }
                var propertyInfo = memberExpression.Member as PropertyInfo;

                // Notify handler if PropertyName is the one we were interested in
                if (e.PropertyName.Equals(propertyInfo.Name))
                {
                    Handler(ObjectThatNotifies);
                }
            };
    }
}

例如這樣使用:

public class Employee : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private string _firstName;
    public string FirstName
    {
        get { return this._firstName; }
        set
        {
            this._firstName = value;
            this.PropertyChanged.Notify(()=>this.FirstName);
        }
    }
}

private void firstName_PropertyChanged(Employee sender)
{
    Console.WriteLine(sender.FirstName);
}

employee = new Employee();
employee.SubscribeToChange(() => employee.FirstName, firstName_PropertyChanged);

示例中可能存在一些語法錯誤。 沒試過。 但你應該至少有這個概念:)

編輯:我現在看到你可能想要更少的工作,但是是......上面的東西至少使它變得容易多了。 並且使用字符串引用屬性可以防止所有可怕的問題。


Answer #2

Framework 4.5為我們提供了CallerMemberNameAttribute ,它使得屬性名稱不必作為字符串傳遞:

private string m_myProperty;
public string MyProperty
{
    get { return m_myProperty; }
    set
    {
        m_myProperty = value;
        OnPropertyChanged();
    }
}

private void OnPropertyChanged([CallerMemberName] string propertyName = "none passed")
{
    // ... do stuff here ...
}

類似於Svish的解決方案,只需用無聊的框架功能取代lambda awesomeness ;-)

如果您正在使用安裝了KB2468871 Framework 4.0, KB2468871可以通過nuget安裝Microsoft BCL兼容包 ,它還提供此屬性。


Answer #3

只需在自動屬性聲明上方使用此屬性即可

[NotifyParentProperty(true)]
public object YourProperty { get; set; }

Answer #4

實現類型安全的INotifyPropertyChanged請參見此處

然後製作自己的代碼段:

private $Type$ _$PropertyName$;
public $Type$ $PropertyName$
{
    get
    {
        return _$PropertyName$;
    }
    set
    {
        if(value != _$PropertyName$)
        {
            _$PropertyName$ = value;
            OnPropertyChanged(o => o.$PropertyName$);               
        }
    }
}

使用Code代碼段設計器 ,您已經完成了! 簡單,安全的方式來建立您的INotifyPropertyChanged。


Answer #5

您可以查看Castle或Spring.NET並實現攔截器功能嗎?


Answer #6

您可能希望從整體上考慮面向方面編程

框架=>你可以看看linfu


Answer #7

我剛剛找到ActiveSharp - 自動INotifyPropertyChanged ,我還沒有使用它,但它看起來不錯。

引用它的網站......

發送屬性更改通知,而不指定屬性名稱作為字符串。

相反,寫這樣的屬性:

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

請注意,不需要將屬性的名稱包含在字符串中。 ActiveSharp可靠而正確地為自己確定了這一點。 它的工作原理是您的屬性實現通過ref傳遞了支持字段(_foo)。 (ActiveSharp使用“by ref”調用來識別傳遞了哪個支持字段,並從字段中識別屬性)。


Answer #8

改進兒童課程中的活動:

感謝:this.NotifyPropertyChange(()=> PageIndex);

在NotificationExtensions類中添加:

    /// <summary>
    /// <para>Lève l'évènement de changement de valeur sur l'objet <paramref name="sender"/>
    /// pour la propriété utilisée dans la lambda <paramref name="property"/>.</para>
    /// </summary>
    /// <param name="sender">L'objet portant la propriété et l'évènement.</param>
    /// <param name="property">Une expression lambda utilisant la propriété subissant la modification.</param>
    public static void NotifyPropertyChange(this INotifyPropertyChanged sender, Expression<Func<Object>> property)
    {
        if (sender == null)
            return;

        // Récupère le nom de la propriété utilisée dans la lambda en argument
        LambdaExpression lambda = property as LambdaExpression;
        MemberExpression memberExpression;
        if (lambda.Body is UnaryExpression)
        {
            UnaryExpression unaryExpression = lambda.Body as UnaryExpression;
            memberExpression = unaryExpression.Operand as MemberExpression;
        }
        else
        {
            memberExpression = lambda.Body as MemberExpression;
        }
        ConstantExpression constantExpression = memberExpression.Expression as ConstantExpression;
        PropertyInfo propertyInfo = memberExpression.Member as PropertyInfo;


        // il faut remonter la hierarchie, car meme public, un event n est pas visible dans les enfants
        FieldInfo eventField;
        Type baseType = sender.GetType();
        do
        {
            eventField = baseType.GetField(INotifyPropertyChangedEventFieldName, BindingFlags.Instance | BindingFlags.NonPublic);
            baseType = baseType.BaseType;
        } while (eventField == null);

        // on a trouvé l'event, on peut invoquer tt les delegates liés
        MulticastDelegate eventDelegate = eventField.GetValue(sender) as MulticastDelegate;
        if (eventDelegate == null) return; // l'event n'est bindé à aucun delegate
        foreach (Delegate handler in eventDelegate.GetInvocationList())
        {
            handler.Method.Invoke(handler.Target, new Object[] { sender, new PropertyChangedEventArgs(propertyInfo.Name) });
        }
    }

Answer #9

為了更快地實現,您可以使用代碼段

來自http://aaron-hoffman.blogspot.it/2010/09/visual-studio-code-snippet-for-notify.html

遵循MV-VM模式的ViewModel項目類通常需要從屬性的setter中引發“PropertyChanged”事件(以協助INotifyPropertyChanged接口實現)。 這是一項繁瑣的任務,希望有一天能夠通過使用編譯器即服務來解決...

片段核心( 完全歸功於作者,不是我)是以下內容

  <Code Language= "csharp "> 
    <![CDATA[public $type$ $property$ 
{ 
    get { return _$property$; } 
    set 
    { 
        if (_$property$ != value) 
        { 
            _$property$ = value; 
            OnPropertyChanged($property$PropertyName); 
        } 
    } 
} 
private $type$ _$property$; 
public const string $property$PropertyName = "$property$";$end$]]> 
</Code> 




inotifypropertychanged