禁用WPF按钮,但仍然会吞下单击事件

时间:2009-11-06 15:30:56

标签: wpf

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

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

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

  • OnPreviewMouseDown
  • OnPreviewMouseUp
  • OnPreviewMouseLeftButtonDown
  • OnPreviewMouseLeftButtonUp
  • onmousedown事件
  • OnMouseUp
  • OnMouseLeftButtonDown在
  • OnMouseLeftButtonUp
  • 的OnClick

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

5 个答案:

答案 0 :(得分:10)

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>

答案 1 :(得分:2)

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

答案 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
}

答案 3 :(得分: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,

丹尼斯

答案 4 :(得分:0)

我创建了一个在ContentPresenter及其父级之间插入Button的行为(实际上有效)。 ContentPresenter禁用Button时,<Button ...> <i:Interaction.Behaviors> <behaviors:SwallowMouseClicksWhenDisabled /> </i:Interaction.Behaviors> </Button> 会吞下鼠标。该行为使用了一些基于this answer代码的扩展方法。使用它非常简单:

// 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
    }
}

以下是来源:

import pandas as pd

ordered_satisfaction = ['Very Unhappy', 'Unhappy', 'Neutral', 'Happy', 'Very Happy']

df = pd.DataFrame({'satisfaction': ['Mad', 'Happy', 'Unhappy', 'Neutral']})

df.satisfaction = df.satisfaction.astype('category', ordered=True, categories=ordered_satisfaction).cat.codes

print df