我是Reactive Extensions的新手,這就是我想要做的Popup Toaster通知。當鼠標移過烤麪包機時,不透明度恢復到100%。否則,它會逐漸淡出。正確處置RX訂閱
該代碼有效,但我並不完全確信我沒有泄漏資源,特別是在mouseOut訂閱中。另外,我不確定這是否是實現此功能的最佳方式。
任何批評,提示將不勝感激。
private void rxPop()
{
Rectangle toaster = (Rectangle)this.FindName("toaster1");
Thickness newToasterPosition = new Thickness(
toaster.Margin.Left, toaster.Margin.Top,
toaster.Margin.Right, toaster.Margin.Bottom + toaster.Height);
/* Animations */
Storyboard slideInAnimation = slide(toaster,
newToasterPosition,
TimeSpan.FromMilliseconds(450));
Storyboard fadeInAnimation = animateOpacity(toaster, 1.0, TimeSpan.FromMilliseconds(150));
Storyboard fadeOutAnimation = animateOpacity(toaster, 0.0, TimeSpan.FromSeconds(3));
/* Events */
var mouseEnter = Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>
(h => toaster.MouseEnter += h,
h => toaster.MouseEnter -= h);
var mouseOut = Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>
(h => toaster.MouseLeave += h,
h => toaster.MouseLeave -= h);
var slideInCompleted = Observable.FromEventPattern<EventHandler, EventArgs>(
h => slideInAnimation.Completed += h,
h => slideInAnimation.Completed -= h);
var fadeOutCompleted = Observable.FromEventPattern<EventHandler, EventArgs>(
h => fadeOutAnimation.Completed += h,
h => fadeOutAnimation.Completed -= h);
// slideIn then fadeOut
slideInCompleted.Subscribe(e => fadeOutAnimation.Begin());
var mouseEnterSubscription = mouseEnter
.ObserveOnDispatcher()
.Do(a =>
{
fadeOutAnimation.Pause();
fadeInAnimation.Begin();
slideInAnimation.Pause();
mouseOut.Do(
b =>
{
fadeOutAnimation.Begin();
fadeInAnimation.Stop();
slideInAnimation.Resume();
}).Subscribe();
})
.Subscribe();
fadeOutCompleted.Subscribe((e) => mouseEnterSubscription.Dispose());
slideInAnimation.Begin();
}
理想情況下,我本來希望表達以下方式的事件:
slideIn then fadeOut
unless mouseEnter
then fadeIn , slideIn.Pause
until mouseLeave
then fadeOut.Begin and slideIn.Resume
是什麼在RX做到這一點最接近的方式?
更新#1 * 更新#2 *(清理訂閱())
這裏是一個有點cleanear嘗試。
protected CompositeDisposable _disposables = new CompositeDisposable();
private void rxPop()
{
IDisposable mouseEnterSubscription = null;
/* Business logic: slideIn then fadeOut then remove from visual tree */
_disposables.Add(
slideInAnimation
.BeginUntilDone()
.Select(slideInCompletedEvent =>
fadeOutAnimation.BeginUntilDone())
.Switch()
.Subscribe(fadeOutAnimationCompletedEvent =>
{
mouseEnterSubscription.Dispose();
// remove from visual tree
(toaster.Parent as Panel).Children.Remove(toaster);
}));
/* Business logic: mouseEnter/mouseLeave should pause/restart animations */
mouseEnterSubscription = mouseEnter
.ObserveOnDispatcher()
.Do(mouseEnterEventArgs =>
{
fadeOutAnimation.Pause();
fadeInAnimation.Begin();
slideInAnimation.Pause();
})
.Select(e => mouseOut)
.Switch()
.Do(mouseLeaveEventArgs =>
{
fadeOutAnimation.Begin();
fadeInAnimation.Stop();
slideInAnimation.Resume();
})
.Subscribe();
}
public static class RxExtensions
{
public static IObservable<EventPattern<EventArgs>> BeginUntilDone(this Storyboard sb)
{
var tmp = Observable.FromEventPattern(
h => sb.Completed += h,
h => sb.Completed -= h);
sb.Begin();
return tmp;
}
}
我的問題是:
是ObserveOnDispatcher()做正確?
Switch()爲我配置了以前的IObservable嗎?
我很難轉化到上述LINQ查詢語法
/* Business Logic */ var showToast = // Start by sliding in from slideInComplete in slideIn.BeginObservable() where slideInComplete // Then in parallel, fadeOut as well as wait for mouseEnter from fadeOutComplete in fadeOut.BeginObservable() from enter in mouseEnter // ... I'm lost here. // ... how do I express // ..... mouseEnter should pause fadeOut? select new Unit();
你還是留下未予處置'IDisposable's - 那'Subscribe'調用創建一個訂閱,*會*超出範圍並被收集(可能會破壞您的動畫),但可能會泄露其他資源。至於暫停/取消,這實際上是這種linq樣式語法的一個好處......在這裏,我已經在我的完整測試平臺中添加了,試試看。 – JerKimball 2013-02-27 02:01:29