2012-07-13 57 views
1

我想了解更多關於被動擴展的知識,但是我發現很難找到真實世界的例子,所以我可以訓練自己。當鼠標不在兩個元素上時,Wpf反應性擴展反應

我幾天前發現自己寫了一些ToggleButton鼠標輸入,離開選中的未檢查事件,現在我想知道是否可以使用被動擴展來簡化它。

這裏是我們的目標:

給定一個ToggleButton,懸停時,它不是檢查時,彈出一個應該顯示,如果鼠標不在按鈕或彈出

如果彈出應該關閉我按下切換按鈕(選中)彈出窗口應該保持打開狀態,直到該按鈕未被選中(忽略鼠標輸入離開事件),之後鼠標懸停行爲應該再次啓動。

如果Popup在外部關閉,切換按鈕應該被自動取消選中。 (我知道這可能使用一些綁定和數據觸發器來實現,但我想鍛鍊我的反應擴展邏輯)

現在,我有以下幾點:

private void ToggleButton_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e) 
    { 
     if (!ToggleButton.IsChecked ?? false) 
      Popup.IsOpen = true; 
    } 

    private void ToggleButton_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e) 
    { 
     if (!Popup.Child.IsMouseOver && !(TaskManagerTab.IsChecked ?? false)) 
     { 
      Popup.IsOpen = false; 
      return; 
     } 

     popup.Child.MouseLeave += Popup_MouseLeave; 
    } 

    void Popup_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e) 
    { 
     Popup.Child.MouseLeave -= Popup_MouseLeave; 

     if (!ToggleButton.IsMouseOver && !(ToggleButton.IsChecked ?? false)) 
     { 
      Popup.IsOpen = false; 
      return; 
     } 
    } 

    private void ToggleButton_CheckedChanged(object sender, System.Windows.RoutedEventArgs e) 
    { 
     Popup.IsOpen = ToggleButton.IsChecked ?? false; 

     if (Popup.IsOpen) 
      Popup.Closed += Popup_Closed; 
    } 

    void Popup_Closed(object sender, System.EventArgs e) 
    { 
     Popup.Closed -= Popup_Closed; 

     ToggleButton.IsChecked = false; 
    } 
} 

我會一個粗略的版本,但我真的不知道該如何開始。

謝謝!

更新:我真的來了

了這一點,但我不知道性能,和我似乎無法重複,如果撥動按鈕被選中。

var mouseEnterSaveBtn = Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>(h => SaveBtn.MouseEnter += h, 
                     h => SaveBtn.MouseEnter -= h); 

     var mouseLeaveList = Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>(h => popup.MouseLeave += h, 
                     h => popup.MouseLeave -= h); 

     var toggleBtnChecked = 
      Observable.FromEventPattern<RoutedEventHandler, RoutedEventArgs>(h => SaveBtn.Checked += h, 
                      h => SaveBtn.Checked -= h); 

     var allCloseEvents = mouseLeaveList.Merge(mouseLeaveList); 

     mouseEnterSaveBtn.TakeUntil(toggleBtnChecked).Subscribe(pattern => 
                    { 
                     popup.Visibility = Visibility.Visible; 

                     allCloseEvents.TakeUntil(toggleBtnChecked). 
                      Subscribe(eventPattern => 
                          { 
                           if (!popup.IsMouseOver && !popup.IsMouseOver) 
                            popup.Visibility = Visibility.Collapsed; 
                          }); 

                    }); 

回答

5

這很接近我會做的。然而,我建議的一件事是,只要你覺得你應該在觀察者中訂閱觀察者(注意你的嵌套訂閱呼叫),你可以將這些觀察者和然後訂閱。

var mouseIn = Observable.FromEventPattern<MouseEventHandler, MouseEventArgs> 
    (
     h => SaveBtn.MouseEnter += h, 
     h => SaveBtn.MouseEnter -= h 
    ); 

var mouseOut = Observable.FromEventPattern<MouseEventHandler, MouseEventArgs> 
    (
     h => popup.MouseLeave += h, 
     h => popup.MouseLeave -= h 
    ); 

var isMouseOver = mouseIn 
    .Select((o,e) => true) 
    .Merge(mouseOut 
     .Select((o,e) => false)); 

var toggleBtnChecked = Observable.FromEventPattern<RoutedEventHandler, RoutedEventArgs> 
    (
     h => SaveBtn.Checked += h, 
     h => SaveBtn.Checked -= h 
    ) 
    .Select((o,e) => (o as ToggleButton).Checked); 

var shouldShow = isMouseOver 
    .DistinctUntilChanged() 
    .CombineLatest(toggleBtnChecked, (mouse, pressed) => pressed || mouse); 

shouldShow.Subscribe 
(
    shouldBeVisible => 
    { 
     popup.Visibility = shouldBeVisible ? Visibility.Visible : Visibility.Collapsed; 
    } 
); 
+2

安德森通過使用CombineLatest獲得它的關鍵。請記住,您可以使用ViewModel的+綁定完全做到這一點,並且不會對控件有任何依賴性。只是FYI。 – 2012-07-25 12:05:54