gpt4 book ai didi

c# - NotifyPropertyChanged 事件,其中事件参数包含旧值

转载 作者:IT王子 更新时间:2023-10-29 04:37:23 25 4
gpt4 key购买 nike

是否有类似 INotifyPropertyChanged 的​​接口(interface),其中事件参数包含正在更改的属性的旧值,或者我是否必须扩展该接口(interface)来创建一个接口(interface)?

例如:

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

我也会满足于提供此信息的类似 PropertyChanging 的事件,无论它是否支持 e.Cancel。

最佳答案

如答案所示,我必须实现自己的解决方案。为了其他人的利益,我在这里展示了它:

扩展的 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;
}
}

示例 1

用户现在可以指定一个更高级的 NotifyPropertyChanged 方法,允许属性 setter 传入它们的旧值:

public String testString
{
get { return testString; }
set
{
String temp = testString;
testValue2 = value;
NotifyPropertyChanged("TestString", temp, value);
}
}

新的 NotifyPropertyChanged 方法如下所示:

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

OnPropertyChanged 和往常一样:

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

示例 2

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

public String TestString
{
get { return testString; }
private set { SetNotifyingProperty(() => TestString, ref testString, value); }
}

这是由以下魔术支持的:

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

性能

如果性能是一个问题,请看这个问题:Implementing NotifyPropertyChanged without magic strings .

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


(可选)扩展的 PropertyChanged 接口(interface)

注意:您不必这样做。您仍然可以只实现标准的 INotifyPropertyChanged 接口(interface)。

如果程序员想要创建一个需要通知属性包含旧值和新值的事件,他们需要定义和实现以下接口(interface):

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

现在任何 Hook PropertyChanged 事件的人都需要提供上面定义的扩展参数。请注意,根据您的用例,您的 UI 可能仍需要您实现基本的 INotifyPropertyChanged 接口(interface)和事件,这会与此冲突。例如,如果您要构建自己的依赖于此行为的 UI 元素,您就会这样做。


8 年后的常见问题解答 - 如何使用它?

以上示例展示了您将如何发送新的属性信息,但并未展示您将如何使用它们。晚了 8 年,但这里有一个事件实现的例子(感谢@Paddy 填补过去 6 年的不足):

myNotifyingClass.PropertyChanged += OnSomePropertyChanged;

private void OnSomePropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Without casting 'e' is a standard PropertyChanged event
Debug.WriteLine($"'{e.PropertyName}' has changed.");

// If you just care to check whether a certain properties changed, do so as usual
if (e.PropertyName == nameof(SomeClass.Description))
{
myNotifyingClass.MarkAsDirty(); // For example
}

// If the old/new value are if interest, you can cast in those situations
if (e.PropertyName == nameof(SomeClass.SortKey))
{
// For example, use it to order by some new property first, but by the last property second.
if(e is PropertyChangedExtendedEventArgs<string> sortKeyChanged)
myNotifyingClass.OrderBy(sortKeyChanged.NewValue, then_by: sortKeyChanged.OldValue);
else
throw new Exception("I must have forgotten to use the extended args!");
}

// To support more general operations, see the note below on creating interfaces
}

正如我们在上面的示例中所注意到的,如果不先进行强制转换,我们对这些通用参数无能为力。那是因为 8 年前,我可能甚至不知道什么是协方差。如果您希望它更有用,定义一些接口(interface)可能是有意义的,您可以使用这些接口(interface)进行类型检查并在不知道运行时类型的情况下提取属性值:

public interface IPropertyChangedExtendedEventArgs<out T> : IPropertyChangedEventArgs
{
public virtual T OldValue { get; }
public virtual T NewValue { get; }
}

public class PropertyChangedExtendedEventArgs<T> : IPropertyChangedExtendedEventArgs<T>
{
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;
}
}

这现在更好用了:

if (e is IPropertyChangedExtendedEventArgs<object> anyProperty)
Console.WriteLine($"'{anyProperty.PropertyName}' has changed, " +
$"from '{anyProperty.OldValue}' to '{anyProperty.NewValue}'.");

我希望一切都解决了!

关于c# - NotifyPropertyChanged 事件,其中事件参数包含旧值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7677854/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com