【问题标题】:Disable a WPF button, but still swallow the click events禁用 WPF 按钮,但仍吞下单击事件
【发布时间】:2009-11-06 15:30:56
【问题描述】:

我的应用程序中有一个绑定到命令的按钮。此按钮位于另一个控件内,该控件也对鼠标单击做出反应。启用按钮后,我得到了我期望的行为 - 单击按钮并触发命令,单击按钮外部但在容器控件内部,然后触发。

不幸的是,当按钮被禁用时(通过命令的 CanExecute 方法),对按钮的点击会冒泡到容器控件。我不希望这样,我希望点击被吞噬——既不触发命令也不冒泡。

我试图通过创建一个继承自 Button 的新类来克服这个问题,但以下方法似乎都没有在禁用的按钮上被 调用

  • OnPreviewMouseDown
  • OnPreviewMouseUp
  • OnPreviewMouseLeftButtonDown
  • OnPreviewMouseLeftButtonUp
  • OnMouseDown
  • 鼠标打开
  • OnMouseLeftButtonDown
  • OnMouseLeftButtonUp
  • 点击

WPF 路由事件系统是否完全忽略了禁用的控件?如果是这样的话,我是否可以获得我正在寻找的行为?

【问题讨论】:

    标签: wpf


    【解决方案1】:

    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 中执行此操作(并提高代码的 hack 级别...)

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

    【讨论】:

      【解决方案2】:

      它不是很干净,但您可以在按钮后面放置一个透明矩形来吞下点击事件。

      【讨论】:

        【解决方案3】:

        您要做的是使用the overload that takes the handledEventsToo parameter 注册一个路由事件处理程序,并为该参数指定值true。这样,无论按钮是否实际处理事件,您的外部处理程序都将接收事件。看起来像这样:

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

        然后在您的处理程序中,您始终可以通过MouseButtonEventArgs 来检查点击了什么,是否已处理等等。例如,要检查另一个控件是否已经实际处理了该事件,您可以这样做:

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

        【讨论】:

        • 谢谢,但我一直在研究这个问题,我想你可能误解了我的问题。我不希望外部控件总是接收事件,我希望它只在点击在按钮之外时接收事件,即使按钮被禁用。
        • 你说得对,我确实误解了你的意图。我会修改的。
        【解决方案4】:

        更简洁、更可重用的解决方案是将此功能实现为附加属性。

        使用服务/操作模式:

        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,

        丹尼斯

        【讨论】:

        • 这不会编译,如果你只是重构它足以编译它是行不通的。
        • @KellyElton,它在答案中说“我没有测试过这个(目前没有时间)。”你得到了什么错误?也许我可以帮忙。
        • 是的,我知道你没有测试它。这些错误是编译时错误......一旦你修复了这些并让它运行起来,更正的代码并不能解决问题。
        • @KellyElton,Mouse.PreviewMouseUpEvent 没有处理?
        • 我只是让你知道它在当前状态下无法编译,如果你修复了这些错误,它会编译并将 PreviewMouseUpEvent 标记为已处理,这并不能解决问题。
        【解决方案5】:

        我创建了一个在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
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2014-02-17
          • 2023-03-14
          • 2016-09-03
          • 2016-03-20
          • 1970-01-01
          • 1970-01-01
          • 2014-09-26
          • 1970-01-01
          • 2020-08-14
          相关资源
          最近更新 更多