2009-11-06 102 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 
    } 
}