这必须是一个可怜的谷歌在我的一部分的情况下,我知道我看到了这个之前的解决方案,但我将如何去实现一个扩展方法,能够将INotifyPropertyChanged.PropertyChanged
事件转换成IObservable<Tuple<TProperty,TProperty>>
,其中元组的值代表的oldValue和newValue的属性?
所以我想知道什么是最好的方式来接受这样的东西: (credit for below to)
public static IObservable<TProperty> ObservePropertyChanged<TNotifier, TProperty>(this TNotifier notifier,
Expression<Func<TNotifier, TProperty>> propertyAccessor,
bool startWithCurrent = false)
where TNotifier : INotifyPropertyChanged {
// Parse the expression to find the correct property name.
MemberExpression member = (MemberExpression)propertyAccessor.Body;
string name = member.Member.Name;
// Compile the expression so we can run it to read the property value.
var reader = propertyAccessor.Compile();
var propertyChanged = Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
handler => (sender, args) => handler(sender, args),
x => notifier.PropertyChanged += x,
x => notifier.PropertyChanged -= x);
// Filter the events to the correct property name, then select the value of the property from the notifier.
var newValues = from p in propertyChanged
where p.EventArgs.PropertyName == name
select reader(notifier);
// If the caller wants the current value as well as future ones, use Defer() so that the current value is read when the subscription
// is added, rather than right now. Otherwise just return the above observable.
return startWithCurrent ? Observable.Defer(() => Observable.Return(reader(notifier)).Concat(newValues)) : newValues;
}
并将其转换为适合此签名:
public static IObservable<Tuple<TProperty,TProperty>> ObservePropertyChanged<TNotifier, TProperty>(this TNotifier notifier,
Expression<Func<TNotifier, TProperty>> propertyAccessor,
bool startWithCurrent = false)
where TNotifier : INotifyPropertyChanged {
// Parse the expression to find the correct property name.
MemberExpression member = (MemberExpression)propertyAccessor.Body;
string name = member.Member.Name;
// Compile the expression so we can run it to read the property value.
var reader = propertyAccessor.Compile();
var propertyChanged = Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
handler => (sender, args) => handler(sender, args),
x => notifier.PropertyChanged += x,
x => notifier.PropertyChanged -= x);
// Filter the events to the correct property name, then select the value of the property from the notifier.
var newValues = from p in propertyChanged
where p.EventArgs.PropertyName == name
select reader(notifier);
throw new NotImplementedException();
}
编辑:在尝试了许多不同的操作符之后,我发现了一些似乎有效的东西。这样做正确吗?我错过什么了吗?
public static IObservable<Tuple<TProperty,TProperty>> ObserveValueChanged<TNotifier, TProperty>(this TNotifier notifier,
Expression<Func<TNotifier, TProperty>> propertyAccessor,
bool startWithCurrent = false)
where TNotifier : INotifyPropertyChanged {
var observable = ObservePropertyChanged(notifier, propertyAccessor, startWithCurrent);
return observable.Scan(new Tuple<TProperty, TProperty>(default(TProperty), default(TProperty)),
(acc, p) => new Tuple<TProperty, TProperty>(acc.Item2, p));
}
编辑:我结合了Gideon的解决方案,最终得到以下内容:
public static IObservable<Tuple<TProperty, TProperty>> ObserveValueChanged2<TNotifier, TProperty>(this TNotifier notifier,
Expression<Func<TNotifier, TProperty>> propertyAccessor,
bool startWithCurrent = false)
where TNotifier : INotifyPropertyChanged {
// Compile the expression so we can run it to read the property value.
var reader = propertyAccessor.Compile();
var newValues = ObservePropertyChanged(notifier, propertyAccessor, false);
if (startWithCurrent) {
var capturedNewValues = newValues; //To prevent warning about modified closure
newValues = Observable.Defer(() => Observable.Return(reader(notifier))
.Concat(capturedNewValues));
}
return Observable.Create<Tuple<TProperty, TProperty>>(obs => {
Tuple<TProperty, TProperty> oldNew = null;
return newValues.Subscribe(v => {
if (oldNew == null) {
oldNew = Tuple.Create(default(TProperty), v);
} else {
oldNew = Tuple.Create(oldNew.Item2, v);
obs.OnNext(oldNew);
}
},
obs.OnError,
obs.OnCompleted);
});
}
注:我最终偶然发现了我目前的解决方案,但我不想违反任何礼仪,我应该添加一个答案或关闭问题(我宁愿不删除,因为这可能证明以后有用)?我还是不确定这是不是最好的方法
如果你想坚持现有的操作符,Zip
和Skip
可能是最接近你需要的。我自己可能会这样写(拾取你抛出notimplented的地方):
if (startWithCurrent)
{
newValues = Observable.Defer(() => Observable.Return(reader(notifier))
.Concat(newValues));
}
return Observable.Create<Tuple<TProperty, TProperty>>(obs =>
{
Tuple<TProperty, TProperty> oldNew = null;
return newValues.Subscribe(v =>
{
if (oldNew == null)
{
oldNew = Tuple.Create(default(TProperty), v);
}
else
{
oldNew = Tuple.Create(oldNew.Item2, v);
obs.OnNext(oldNew);
}
},
obs.OnError,
obs.OnCompleted);
});