2012-07-13 54 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