我正在为我们的应用程序实现观察者模式 - 目前正在使用RX Framework。
我目前有一个看起来像这样的例子:
Observable.FromEventPattern<PropertyChangedEventArgs>(Instance.Address, "PropertyChanged")
.Where(e => e.EventArgs.PropertyName == "City")
.ObserveOn(Scheduler.ThreadPool)
.Subscribe(search => OnNewSearch(search.EventArgs));
(我有一个类似的“PropertyChanging”)
EventArgs不会给我太多。我想要的是EventArgs的扩展,它使我能够查看先前和新值,以及在“更改”侦听器中标记事件的能力,以便更改实际上不会持续存在。如何才能做到这一点?感谢。
答案 0 :(得分:24)
我认为这取决于您如何实现INotifyPropertyChanging和INotifyPropertyChanged接口。
遗憾的是,PropertyChangingEventArgs和PropertyChangedEventArgs类不提供属性的前后值或取消更改的能力,但是您可以派生自己的事件args类来提供该功能。
首先,定义以下事件args类。请注意,这些派生自PropertyChangingEventArgs类和PropertyChangedEventArgs类。这允许我们将这些对象作为参数传递给PropertyChangingEventHandler和PropertyChangedEventHandler委托。
class PropertyChangingCancelEventArgs : PropertyChangingEventArgs
{
public bool Cancel { get; set; }
public PropertyChangingCancelEventArgs(string propertyName)
: base(propertyName)
{
}
}
class PropertyChangingCancelEventArgs<T> : PropertyChangingCancelEventArgs
{
public T OriginalValue { get; private set; }
public T NewValue { get; private set; }
public PropertyChangingCancelEventArgs(string propertyName, T originalValue, T newValue)
: base(propertyName)
{
this.OriginalValue = originalValue;
this.NewValue = newValue;
}
}
class PropertyChangedEventArgs<T> : PropertyChangedEventArgs
{
public T PreviousValue { get; private set; }
public T CurrentValue { get; private set; }
public PropertyChangedEventArgs(string propertyName, T previousValue, T currentValue)
: base(propertyName)
{
this.PreviousValue = previousValue;
this.CurrentValue = currentValue;
}
}
接下来,您需要在INotifyPropertyChanging和INotifyPropertyChanged接口的实现中使用这些类。实现的示例如下:
class Example : INotifyPropertyChanging, INotifyPropertyChanged
{
public event PropertyChangingEventHandler PropertyChanging;
public event PropertyChangedEventHandler PropertyChanged;
protected bool OnPropertyChanging<T>(string propertyName, T originalValue, T newValue)
{
var handler = this.PropertyChanging;
if (handler != null)
{
var args = new PropertyChangingCancelEventArgs<T>(propertyName, originalValue, newValue);
handler(this, args);
return !args.Cancel;
}
return true;
}
protected void OnPropertyChanged<T>(string propertyName, T previousValue, T currentValue)
{
var handler = this.PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs<T>(propertyName, previousValue, currentValue));
}
int _ExampleValue;
public int ExampleValue
{
get { return _ExampleValue; }
set
{
if (_ExampleValue != value)
{
if (this.OnPropertyChanging("ExampleValue", _ExampleValue, value))
{
var previousValue = _ExampleValue;
_ExampleValue = value;
this.OnPropertyChanged("ExampleValue", previousValue, value);
}
}
}
}
}
注意,PropertyChanging和PropertyChanged事件的事件处理程序仍然需要将原始的PropertyChangingEventArgs类和PropertyChangedEventArgs类作为参数,而不是更具体的版本。但是,您可以将事件args对象强制转换为更具体的类型,以便访问新属性。
以下是这些事件的事件处理程序示例:
class Program
{
static void Main(string[] args)
{
var exampleObject = new Example();
exampleObject.PropertyChanging += new PropertyChangingEventHandler(exampleObject_PropertyChanging);
exampleObject.PropertyChanged += new PropertyChangedEventHandler(exampleObject_PropertyChanged);
exampleObject.ExampleValue = 123;
exampleObject.ExampleValue = 100;
}
static void exampleObject_PropertyChanging(object sender, PropertyChangingEventArgs e)
{
if (e.PropertyName == "ExampleValue")
{
int originalValue = ((PropertyChangingCancelEventArgs<int>)e).OriginalValue;
int newValue = ((PropertyChangingCancelEventArgs<int>)e).NewValue;
// do not allow the property to be changed if the new value is less than the original value
if(newValue < originalValue)
((PropertyChangingCancelEventArgs)e).Cancel = true;
}
}
static void exampleObject_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "ExampleValue")
{
int previousValue = ((PropertyChangedEventArgs<int>)e).PreviousValue;
int currentValue = ((PropertyChangedEventArgs<int>)e).CurrentValue;
}
}
}
答案 1 :(得分:2)
接受的响应非常糟糕,只需使用Buffer()即可。
Observable.FromEventPattern<PropertyChangedEventArgs>(Instance.Address, "PropertyChanged")
.Where(e => e.EventArgs.PropertyName == "City")
.Buffer(2,1) //Take 2 events at a time, every 1 event
.ObserveOn(Scheduler.ThreadPool)
.Subscribe(search => ...); //search[0] is old value, search[1] is new value
答案 2 :(得分:0)
对于任何想要最好的RX和能够取消的人来说,这两种想法都是混合的
ViewModel基类的东西
public abstract class INPCBase : INotifyPropertyChanged, INotifyPropertyChanging
{
public event PropertyChangingEventHandler PropertyChanging;
public event PropertyChangedEventHandler PropertyChanged;
protected bool OnPropertyChanging<T>(string propertyName, T originalValue, T newValue)
{
var handler = this.PropertyChanging;
if (handler != null)
{
var args = new PropertyChangingCancelEventArgs<T>(propertyName, originalValue, newValue);
handler(this, args);
return !args.Cancel;
}
return true;
}
protected void OnPropertyChanged<T>(string propertyName, T previousValue, T currentValue)
{
var handler = this.PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs<T>(propertyName, previousValue, currentValue));
}
}
public class PropertyChangingCancelEventArgs : PropertyChangingEventArgs
{
public bool Cancel { get; set; }
public PropertyChangingCancelEventArgs(string propertyName)
: base(propertyName)
{
}
}
public class PropertyChangingCancelEventArgs<T> : PropertyChangingCancelEventArgs
{
public T OriginalValue { get; private set; }
public T NewValue { get; private set; }
public PropertyChangingCancelEventArgs(string propertyName, T originalValue, T newValue)
: base(propertyName)
{
this.OriginalValue = originalValue;
this.NewValue = newValue;
}
}
public class PropertyChangedEventArgs<T> : PropertyChangedEventArgs
{
public T PreviousValue { get; private set; }
public T CurrentValue { get; private set; }
public PropertyChangedEventArgs(string propertyName, T previousValue, T currentValue)
: base(propertyName)
{
this.PreviousValue = previousValue;
this.CurrentValue = currentValue;
}
}
然后我有这几个扩展。
从表达式树获取属性名称
public static class ExpressionExtensions
{
public static string GetPropertyName<TProperty>(this Expression<Func<TProperty>> expression)
{
var memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
{
var unaryExpression = expression.Body as UnaryExpression;
if (unaryExpression != null)
{
if (unaryExpression.NodeType == ExpressionType.ArrayLength)
return "Length";
memberExpression = unaryExpression.Operand as MemberExpression;
if (memberExpression == null)
{
var methodCallExpression = unaryExpression.Operand as MethodCallExpression;
if (methodCallExpression == null)
throw new NotImplementedException();
var arg = (ConstantExpression)methodCallExpression.Arguments[2];
return ((MethodInfo)arg.Value).Name;
}
}
else
throw new NotImplementedException();
}
var propertyName = memberExpression.Member.Name;
return propertyName;
}
public static string GetPropertyName<T, TProperty>(this Expression<Func<T, TProperty>> expression)
{
var memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
{
var unaryExpression = expression.Body as UnaryExpression;
if (unaryExpression != null)
{
if (unaryExpression.NodeType == ExpressionType.ArrayLength)
return "Length";
memberExpression = unaryExpression.Operand as MemberExpression;
if (memberExpression == null)
{
var methodCallExpression = unaryExpression.Operand as MethodCallExpression;
if (methodCallExpression == null)
throw new NotImplementedException();
var arg = (ConstantExpression)methodCallExpression.Arguments[2];
return ((MethodInfo)arg.Value).Name;
}
}
else
throw new NotImplementedException();
}
var propertyName = memberExpression.Member.Name;
return propertyName;
}
public static String PropertyToString<R>(this Expression<Func<R>> action)
{
MemberExpression ex = (MemberExpression)action.Body;
return ex.Member.Name;
}
public static void CheckIsNotNull<R>(this Expression<Func<R>> action, string message)
{
MemberExpression ex = (MemberExpression)action.Body;
string memberName = ex.Member.Name;
if (action.Compile()() == null)
{
throw new ArgumentNullException(memberName, message);
}
}
}
然后是Rx部分
public static class ObservableExtensions
{
public static IObservable<ItemPropertyChangingEvent<TItem, TProperty>> ObserveSpecificPropertyChanging<TItem, TProperty>(
this TItem target, Expression<Func<TItem, TProperty>> propertyName) where TItem : INotifyPropertyChanging
{
var property = propertyName.GetPropertyName();
return ObserveSpecificPropertyChanging(target, property)
.Select(i => new ItemPropertyChangingEvent<TItem, TProperty>()
{
OriginalEventArgs = (PropertyChangingCancelEventArgs<TProperty>)i.OriginalEventArgs,
Property = i.Property,
Sender = i.Sender
});
}
public static IObservable<ItemPropertyChangingEvent<TItem>> ObserveSpecificPropertyChanging<TItem>(
this TItem target, string propertyName = null) where TItem : INotifyPropertyChanging
{
return Observable.Create<ItemPropertyChangingEvent<TItem>>(obs =>
{
Dictionary<string, PropertyInfo> properties = new Dictionary<string, PropertyInfo>();
PropertyChangingEventHandler handler = null;
handler = (s, a) =>
{
if (propertyName == null || propertyName == a.PropertyName)
{
PropertyInfo prop;
if (!properties.TryGetValue(a.PropertyName, out prop))
{
prop = target.GetType().GetProperty(a.PropertyName);
properties.Add(a.PropertyName, prop);
}
var change = new ItemPropertyChangingEvent<TItem>()
{
Sender = target,
Property = prop,
OriginalEventArgs = a,
};
obs.OnNext(change);
}
};
target.PropertyChanging += handler;
return () =>
{
target.PropertyChanging -= handler;
};
});
}
public class ItemPropertyChangingEvent<TSender>
{
public TSender Sender { get; set; }
public PropertyInfo Property { get; set; }
public PropertyChangingEventArgs OriginalEventArgs { get; set; }
public override string ToString()
{
return string.Format("Sender: {0}, Property: {1}", Sender, Property);
}
}
public class ItemPropertyChangingEvent<TSender, TProperty>
{
public TSender Sender { get; set; }
public PropertyInfo Property { get; set; }
public PropertyChangingCancelEventArgs<TProperty> OriginalEventArgs { get; set; }
}
}
然后示例用法将是这样的
public class MainWindowViewModel : INPCBase
{
private string field1;
private string field2;
public MainWindowViewModel()
{
field1 = "Hello";
field2 = "World";
this.ObserveSpecificPropertyChanging(x => x.Field2)
.Subscribe(x =>
{
if (x.OriginalEventArgs.NewValue == "DOG")
{
x.OriginalEventArgs.Cancel = true;
}
});
}
public string Field1
{
get
{
return field1;
}
set
{
if (field1 != value)
{
if (this.OnPropertyChanging("Field1", field1, value))
{
var previousValue = field1;
field1 = value;
this.OnPropertyChanged("Field1", previousValue, value);
}
}
}
}
public string Field2
{
get
{
return field2;
}
set
{
if (field2 != value)
{
if (this.OnPropertyChanging("Field2", field2, value))
{
var previousValue = field2;
field2 = value;
this.OnPropertyChanged("Field2", previousValue, value);
}
}
}
}
}
运作一种享受