這一定是我的一個糟糕的谷歌搜索案例,因爲我知道我以前在wbe上看到了這個解決方案,但是我怎麼去實現一個擴展方法,它能夠將INotifyPropertyChanged.PropertyChanged
事件轉換爲IObservable<Tuple<TProperty,TProperty>>
,其中元組的值代表屬性的oldValue和newValue?Rx INotifyPropertyChanged to IObservable <Tuple <TProperty,TProperty >>
所以我想知道什麼是採取這樣的事情最好的辦法:(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));
}
編輯:我納入基甸的解決方案具有以下落得:
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);
});
}
附: 我最終偶然發現了我目前的解決方案,但我不想違反任何針對SO的禮儀,我應該添加一個答案還是關閉該問題(我不想刪除,因爲這可能稍後會有用)?我仍然不確定這是做這個的最佳方式。
INotifyPropertyChanged不允許你訪問舊值。我不認爲這個事件適合你正在嘗試做的事 – cadrell0
我記得有一個關於使用ReplaySubject來捕獲值的討論。所以我認爲我可以以某種方式捕捉初始值,然後始終顯示舊值和當前/新值的交錯窗口。那有意義嗎?我只是不知道如何在Rx中做到這一點。 – Damian
我會創建一個自定義事件,讓生活變得輕鬆。 – cadrell0