2009-11-06 98 views
10

我在我的應用程序中綁定了一個命令按鈕。此按鈕位於另一個對鼠標點擊作出反應的控件內。當按鈕被啓用時,我得到了我期望的行爲 - 點擊按鈕並觸發命令,點擊按鈕外部但在容器控件內部,而是觸發。禁用WPF按鈕,但仍然吞下點擊事件

不幸的是,當按鈕被禁用時(通過命令的CanExecute方法),按鈕上的點擊冒泡到容器控件。我不想要這個,我想要點擊被吞噬 - 既不觸發命令也不冒泡。

我試圖創建一個新的類從按鈕,但沒有下列方法繼承甚至似乎獲得上被禁用按鈕稱爲解決此問題:

  • OnPreviewMouseDown
  • OnPreviewMouseUp
  • OnPreviewMouseLeftButtonDown
  • OnPreviewMouseLeftButtonUp
  • OnMouseDown
  • OnMouseUp
  • OnMouseLeftButtonDown在
  • OnMouseLeftButtonUp
  • 的OnClick

由WPF完全忽略禁用的控制路由事件系統?如果是這樣的話,無論如何,我可以得到我期待的行爲?

回答

9

RCGoforth的回答讓我90%的方式出現,但解決的辦法不是把一個長方形背後按鈕,因爲冒泡事件發生在兄弟姐妹不在的地方。最後,我周圍的按鈕,一個ContentControl中,這吞噬的情況下,才能夠進一步上升(因爲矩形的不能生孩子):

<ContentControl MouseDown="ContentControl_MouseDown"> 
    <Button Content="Click Test" 
      Padding="2" 
      Command="{Binding TestCommand}"/> 
</ContentControl> 

在後面的代碼:

private void ContentControl_MouseDown(object sender, MouseButtonEventArgs e) 
{ 
    e.Handled = true; 
} 

或者完全在XAML中完成此操作(並增加代碼的黑客級別...)

<Button> 
    <Button.Template> 
     <ControlTemplate> 
      <Button Content="Click Test" 
        Command="{Binding TestCommand}"/> 
     </ControlTemplate> 
    </Button.Template> 
</Button> 
2

它不是很乾淨,但是你可以在你的按鈕後面放一個透明的矩形來吞噬點擊事件。

0

你想要做的是使用the overload that takes the handledEventsToo parameter註冊路由事件處理程序,併爲該參數指定值爲true。這樣,無論按鈕是否實際處理事件,外部處理程序都將接收事件。這將是這個樣子:

this.AddHandler(Mouse.MouseUpEvent, this.MyMouseUpHandler, true); 

然後在你的處理器,你可以隨時檢查被點擊什麼,它是否處理等通過MouseButtonEventArgs你是右手。例如,要檢查是否另一個控制實際處理該事件已經可以這樣做:

if(!args.Handled) 
{ 
    // handle it here instead 
} 
+0

謝謝,但我只是在尋找到這一點,我想你可能誤解了我的問題。我不希望外部控件總是*接收事件,我希望它只在點擊位於按鈕外部時才接收事件,即使該按鈕被禁用。 – 2009-11-06 16:01:59

+0

你說得對,我誤解了你的意圖。我會修改。 – 2009-11-06 18:08:24

0

更清潔,更可重用的解決方案將作爲附加屬性實現此功能。

使用服務/動作模式:

namespace Control.Services 
{ 

    public class UIElementService 
    { 
    public static readonly DependencyProperty HandleMouseEventsProperty = DependencyProperty.RegisterAttached("HandleMouseEvents", 
     typeof(bool), typeof(UIElementService), new FrameworkPropertyMetadata(false, UIElementService.HandleMouseEventsPropertyChanged)); 

    static void HandleMouseEventsPropertyChanged(object sender, DependencyPropertyChangedEventArgs e) 
    { 
     FrameworkElement element = sender as FrameworkElement; 
     if (element == null) 
     return; 

     new HandleMouseEventsAction(element); 
    } 

    public static bool GetHandleMouseEvents(FrameworkElement target) 
    { 
     return (bool)target.GetValue(HandleMouseEventsProperty); 
    } 

    public static void SetHandleMouseEvents(FrameworkElement target, bool value) 
    { 
     target.SetValue(HandleMouseEventsProperty, value); 
    } 

    class HandleMouseEventsAction 
    { 
     UIElement m_Target; 
     MouseButtonEventHandler m_Handler; 

     internal HandleMouseEventsAction(FrameworkElement source) 
     { 
     m_Source = source; 
     m_Handler = new MouseButtonEventHandler(PreviewMouseLeftButtonUp); 

     m_Source.Loaded += OnSource_Loaded; 
     m_Source.Unloaded += OnSource_Unloaded; 
     } 

     void OnSource_Loaded(object sender, RoutedEventArgs e) 
     { 
     m_Source.AddHandler(Mouse.PreviewMouseUpEvent, m_Handler, true); 
     } 

     void OnSource_Unloaded(object sender, RoutedEventArgs e) 
     { 
     m_Source.RemoveHandler(Mouse.PreviewMouseUpEvent, m_Handler); 
     } 

     void PreviewMouseLeftUIElementUp(object sender, MouseUIElementEventArgs e) 
     { 
     e.Handled = true; 
     } 

    } 

    } 

} 

即可使用,導入命名空間。

<Button sv:UIElementService.HandleMouseEvents="True" /> 

<ContentControl sv:UIElementService.HandleMouseEvents="True"> 
    <Button Content="Click Test" Padding="2" Command="{Binding TestCommand}"/> 
</ContentControl> 

我沒有測試過這一點(目前沒時間)。我相信即使禁用了該動作仍然會獲得鼠標事件。

HTH,

丹尼斯

+0

這不會編譯,如果你只是重構足夠的編譯它不起作用。 – 2017-01-26 18:07:25

+0

@KellyElton,它在答案中說:「我沒有測試過(現在沒有時間)」。你得到了什麼錯誤?也許我可以幫忙。 – Dennis 2017-01-30 00:10:33

+0

是的,我知道你沒有測試它。這些錯誤是編譯時錯誤......一旦你修復了這些錯誤並啓動並運行,更正後的代碼並不能解決問題。 – 2017-01-30 21:18:37

0

我創建了一個行爲(即實際工作),其插入Button與其父之間的ContentPresenter。當Button被禁用時,ContentPresenter吞下鼠標點擊。該行爲使用幾種基於this answer的代碼的擴展方法。使用它很簡單:

<Button ...> 
    <i:Interaction.Behaviors> 
     <behaviors:SwallowMouseClicksWhenDisabled /> 
    </i:Interaction.Behaviors> 
</Button> 

這裏是源:

// the behavior (could also be an attached behavior) 
public class SwallowMouseClicksWhenDisabled : Behavior<FrameworkElement> 
{ 
    protected override void OnAttached() 
    { 
     var oldParent = AssociatedObject.Parent; 

     oldParent.RemoveChild(AssociatedObject); 

     var newParent = new ContentPresenter { Content = AssociatedObject }; 

     oldParent.AddChild(newParent); 

     newParent.PreviewMouseDown += OnPreviewMouseEvent; 

     newParent.PreviewMouseUp += OnPreviewMouseEvent; 
    } 

    private void OnPreviewMouseEvent(object sender, MouseButtonEventArgs e) 
    { 
     e.Handled = AssociatedObject.IsEnabled == false; 
    } 
} 

// the extension methods 
public static class DependencyObjectExtensions 
{ 
    public static void AddChild(this DependencyObject parent, UIElement child) 
    { 
     var panel = parent as Panel; 
     if (panel != null) 
     { 
      panel.Children.Add(child); 
      return; 
     } 

     var decorator = parent as Decorator; 
     if (decorator != null) 
     { 
      decorator.Child = child; 
      return; 
     } 

     var contentPresenter = parent as ContentPresenter; 
     if (contentPresenter != null) 
     { 
      contentPresenter.Content = child; 
      return; 
     } 

     var contentControl = parent as ContentControl; 
     if (contentControl != null) 
     { 
      contentControl.Content = child; 
      return; 
     } 

     // maybe more 
    } 

    public static void RemoveChild(this DependencyObject parent, UIElement child) 
    { 
     var panel = parent as Panel; 
     if (panel != null) 
     { 
      panel.Children.Remove(child); 
      return; 
     } 

     var decorator = parent as Decorator; 
     if (decorator != null) 
     { 
      if (Equals(decorator.Child, child)) 
      { 
       decorator.Child = null; 
      } 
      return; 
     } 

     var contentPresenter = parent as ContentPresenter; 
     if (contentPresenter != null) 
     { 
      if (Equals(contentPresenter.Content, child)) 
      { 
       contentPresenter.Content = null; 
      } 
      return; 
     } 

     var contentControl = parent as ContentControl; 
     if (contentControl != null) 
     { 
      if (Equals(contentControl.Content, child)) 
      { 
       contentControl.Content = null; 
      } 
      return; 
     } 

     // maybe more 
    } 
}