NotifyPropertyChanged事件,其中事件args包含旧值

Ala*_*ain 20 c# wpf events .net-3.5

是否存在类似于INotifyPropertyChanged的接口,其中事件args包含要更改的属性的旧值,或者我是否必须扩展该接口以创建一个?

例如:

    public String ProcessDescription
    {
        get { return _ProcessDescription; }
        set
        {
            if( value != ProcessDescription )
            {
                String oldValue = _ProcessDescription;
                _ProcessDescription = value;
                InvokePropertyChanged("ProcessDescription", oldvalue);
            }
        }
    }

    InvokePropertyChanged(String PropertyName, OldValue)
    {
         this.PropertyChanged( new ExtendedPropertyChangedEventArgs(PropertyName, OldValue) );
    }
Run Code Online (Sandbox Code Playgroud)

我也会讨论类似PropertyChanging的事件,它提供这些信息,无论它是否支持e.Cancel.

Ala*_*ain 37

如答案所示,我必须实施自己的解决方案.为了别人的利益,我在这里介绍了它:

扩展PropertyChanged事件

此活动专门设计为向后兼容旧的propertyChanged事件.它可以与调用者使用简单的PropertyChangedEventArgs互换使用.当然,在这种情况下,事件处理程序有责任检查传递的PropertyChangedEventArgs是否可以向下转换为PropertyChangedExtendedEventArgs,如果他们想要使用它.如果他们感兴趣的是PropertyName属性,则不需要向下转换.

public class PropertyChangedExtendedEventArgs<T> : PropertyChangedEventArgs
{
    public virtual T OldValue { get; private set; }
    public virtual T NewValue { get; private set; }

    public PropertyChangedExtendedEventArgs(string propertyName, T oldValue, T newValue)
        : base(propertyName)
    {
        OldValue = oldValue;
        NewValue = newValue;
    }
}
Run Code Online (Sandbox Code Playgroud)

扩展PropertyChanged接口

如果程序员想要创建一个强制通知属性包含旧值和新值的事件,则只需要实现以下接口:

// Summary: Notifies clients that a property value is changing, but includes extended event infomation
/* The following NotifyPropertyChanged Interface is employed when you wish to enforce the inclusion of old and
 * new values. (Users must provide PropertyChangedExtendedEventArgs, PropertyChangedEventArgs are disallowed.) */
public interface INotifyPropertyChangedExtended<T>
{
    event PropertyChangedExtendedEventHandler<T> PropertyChanged;
}

public delegate void PropertyChangedExtendedEventHandler<T>(object sender, PropertyChangedExtendedEventArgs<T> e);
Run Code Online (Sandbox Code Playgroud)

例1

用户现在可以指定一个更高级的NotifyPropertyChanged方法,允许属性设置器传递其旧值:

public String testString
{
    get { return testString; }
    set
    {
        String temp = testString;
        testValue2 = value;
        NotifyPropertyChanged("TestString", temp, value);
    }
}
Run Code Online (Sandbox Code Playgroud)

您的新NotifyPropertyChanged方法如下所示:

protected void NotifyPropertyChanged<T>(string propertyName, T oldvalue, T newvalue)
{
    OnPropertyChanged(this, new PropertyChangedExtendedEventArgs<T>(propertyName, oldvalue, newvalue));
}
Run Code Online (Sandbox Code Playgroud)

OnPropertyChanged以往一样:

public virtual void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null)
        handler(sender, e);
}
Run Code Online (Sandbox Code Playgroud)

例2

或者,如果您更喜欢使用lambda表达式并完全取消硬编码的属性名称字符串,则可以使用以下命令:

public String TestString
{
    get { return testString; }
    private set { SetNotifyingProperty(() => TestString, ref testString, value); }
}
Run Code Online (Sandbox Code Playgroud)

以下魔术支持以下内容:

protected void SetNotifyingProperty<T>(Expression<Func<T>> expression, ref T field, T value)
{
    if (field == null || !field.Equals(value))
    {
        T oldValue = field;
        field = value;
        OnPropertyChanged(this, new PropertyChangedExtendedEventArgs<T>(GetPropertyName(expression), oldValue, value));
    }
}
protected string GetPropertyName<T>(Expression<Func<T>> expression)
{
    MemberExpression memberExpression = (MemberExpression)expression.Body;
    return memberExpression.Member.Name;
}
Run Code Online (Sandbox Code Playgroud)

性能

如果需要考虑性能,请参阅此问题:在没有魔术字符串的情况下实现NotifyPropertyChanged.

总之,开销很小.添加旧值并切换到扩展事件大约减少了15%,仍然允许每秒大约一百万个属性通知,并且切换到lambda表达式是5倍减速,允许每个大约十万个属性通知第二.这些数字远远不能在任何UI驱动的应用程序中形成瓶颈.

  • 双向绑定对我有用.可能你错过了一些东西......但请记住,你不需要实现INotifyPropertyChangedExtended <T>接口.在你的班级中你仍然可以实现INotifyPropertyChanged,所以你的类定义不会改变......我发现那个部分与INotifyPropertyChangedExtended <T>有点令人困惑.要点是,不要实现该接口,只需使用PropertyChangedExtendedEventArgs即可. (4认同)
  • _“我发现 INotifyPropertyChangedExtended&lt;T&gt; 的部分有点令人困惑”_ - 我同意@lightxx 的评论。如果您可以保证 _all_ 属性具有相同的类型“T”,那么拥有通用接口才有意义,这似乎没什么用。此外,使用您的通用 `NotifyPropertyChanged&lt;T&gt;()` 方法,甚至不清楚您发布的代码应该如何编译,因为您甚至没有实现通用接口,更不用说在该接口中引发事件。这个答案可能有一些有用的想法,但它是半生不熟的,让新手感到困惑。 (2认同)