我可以扩展Button来添加RightClick事件吗?这样就可以保持图形的副作用

本文关键字:就可以 图形 副作用 Button 扩展 添加 RightClick 事件 我可以 | 更新日期: 2023-09-27 18:29:30

我正在尝试扩展Button以添加RightClick事件。

我的客户想要一个按钮来做不同的事情,这取决于你是左键点击还是右键点击。我本以为会有一个简单的右键点击事件,但事实并非如此。

我更希望Button的视觉行为与之前存在的Click事件相同,但事实证明这很难。按钮有许多图形行为,当您单击和拖动按钮时会发生这些行为。

  • 当您向下单击时,按钮会降低。如果您拖动降低的按钮,它会升高(例如取消降低)。您拖动的任何其他按钮都将忽略它
  • 当您向后拖动原始按钮时,它将再次降低
  • 如果您拖动并释放,则原始按钮的状态应重置(即,您不能再次单击并拖动)

如果左键点击的视觉效果与右键点击的不匹配,这些小的图形怪癖看起来会很粗糙。

当前我被困在这个问题上:如果右键单击并按住按钮,然后拖动按钮,我如何检测用户是否取消单击?我需要知道这一点,这样我才能知道在重新进入时不要重新按下按钮。

一个更广泛的问题:我是否走上了正确的轨道?我找不到以前做过这种事的人。我的代码在下面。

public class RightClickButton : Button
{
    public event RoutedEventHandler RightClick;
    public RightClickButton()
    {
        this.MouseRightButtonDown += new System.Windows.Input.MouseButtonEventHandler(RightClickButton_MouseRightButtonDown);
        this.MouseRightButtonUp += new System.Windows.Input.MouseButtonEventHandler(RightClickButton_MouseRightButtonUp);
        this.MouseEnter += new System.Windows.Input.MouseEventHandler(RightClickButton_MouseEnter);
        this.MouseLeave += new System.Windows.Input.MouseEventHandler(RightClickButton_MouseLeave);
    }
    void RightClickButton_MouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        this.IsPressed = true;
    }
    void RightClickButton_MouseRightButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        this.IsPressed = false;
        if (RightClick != null)
            RightClick.Invoke(this, e);
    }
    void RightClickButton_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
    {
        if (this.IsPressed)
            this.IsPressed = false;
    }
    void RightClickButton_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
    {
        if (this.IsFocused && Mouse.RightButton == MouseButtonState.Pressed)
            this.IsPressed = true;
    }
}

我可以扩展Button来添加RightClick事件吗?这样就可以保持图形的副作用

感谢我对另一个问题的回答,这就是我的想法。

XP和Win7的行为似乎有一些不同。我需要在Win7上进一步测试。

public class RightClickButton : Button
{
    public event RoutedEventHandler RightClick;
    private bool _clicked = false;
    public RightClickButton()
    {
        this.MouseRightButtonDown += new System.Windows.Input.MouseButtonEventHandler(RightClickButton_MouseRightButtonDown);
        this.MouseRightButtonUp += new System.Windows.Input.MouseButtonEventHandler(RightClickButton_MouseRightButtonUp);
    }
    // Subclasses can't invoke this event directly, so supply this method
    protected void TriggerRightClickEvent(System.Windows.Input.MouseButtonEventArgs e)
    {
        if (RightClick != null)
            RightClick(this, e);
    }
    void RightClickButton_MouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        this.IsPressed = true;
        CaptureMouse();
        _clicked = true;
    }
    void RightClickButton_MouseRightButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        ReleaseMouseCapture();
        if(this.IsMouseOver && _clicked)
        {
            if (RightClick != null)
                RightClick.Invoke(this, e);
        }
        _clicked = false;
        this.IsPressed = false;
    }
    protected override void OnMouseMove(MouseEventArgs e)
    {
        base.OnMouseMove(e);
        if (this.IsMouseCaptured)
        {
            bool isInside = false;
            VisualTreeHelper.HitTest(
                this,
                d =>
                {
                    if (d == this)
                    {
                        isInside = true;
                    }
                    return HitTestFilterBehavior.Stop;
                },
                ht => HitTestResultBehavior.Stop,
                new PointHitTestParameters(e.GetPosition(this)));
            if (isInside)
                this.IsPressed = true;
            else
                this.IsPressed = false;
        }
    }
}

如果查看ButtonBase的源代码,您会发现很难轻松复制OnMouseLeftButtonDownOnMouseLeftButtonUp覆盖中发生的内容。

一个似乎工作得很好的简单破解方法是从Button派生如下:

public class MyButton : Button
{
    protected override void OnMouseRightButtonDown(MouseButtonEventArgs e)
    {
        base.OnMouseRightButtonDown(e); // this line probably optional/not required
        base.OnMouseLeftButtonDown(e);
    }
    protected override void OnMouseRightButtonUp(MouseButtonEventArgs e)
    {
        base.OnMouseRightButtonUp(e); // this line probably optional/not required
        base.OnMouseLeftButtonUp(e);
    }
}

然后在Xaml中使用<MyButton>代替<Button>

我不能保证这在所有情况下都能正确工作,但它似乎确实能按要求工作,因为右键或左键单击时按钮的视觉行为是相同的。

只是添加了我自己的2c。这是基于@Grant给出的答案,并进行了以下更改:

  1. 这适当地使用_rightButtonDown标志而不是CaptureMouse属性来确定处理,因为CaptureMouse可能已经在其他地方设置(即,这遵循适当的封装)

  2. 这支持允许值为PressClickMode属性。

  3. 这正确地使用了OnRightClick处理程序,该处理程序的名称现在不仅遵循标准约定,而且还处理引发RightClick事件的所有调用,而不是复制代码。它也被标记为virtual,以正确地支持子类功能,因为如果覆盖该函数,则可以处理/阻止所有RightClick事件。

  4. 我将其命名为EnhancedButton,以防我想添加其他功能。

  5. 我已经包含了一个例子,说明如何定义一种样式,使其正确地显示在工具栏中

注意:在控件上实现RightClick事件的正确方法是作为RoutedEvent,而不是标准CLR事件。然而,为了使这个示例保持简单,我将把它留给读者来实现。

这是EnhancedButton类的代码:

public class EnhancedButton : Button
{
    private bool _rightButtonDown = false;
    public EnhancedButton()
    {
        MouseRightButtonDown += RightClickButton_MouseRightButtonDown;
        MouseRightButtonUp   += RightClickButton_MouseRightButtonUp;
    }
    #region RightClick Event
        public event RoutedEventHandler RightClick;
        protected virtual void OnRightClick(MouseButtonEventArgs e)
        {
            RightClick?.Invoke(this, e);
        }
    #endregion RightClick Event
    #region Event Handlers
        private void RightClickButton_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            IsPressed = true;
            _rightButtonDown = true;
            CaptureMouse();
            if(ClickMode == ClickMode.Press)
                OnRightClick(e);
        }
        private void RightClickButton_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            if(!_rightButtonDown)
                return;
            ReleaseMouseCapture();
            IsPressed = false;
            if(IsMouseOver && ClickMode == ClickMode.Release)
                OnRightClick(e);
            _rightButtonDown = false;
        }
    #endregion Event Handlers
    #region Overrides
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if(!_rightButtonDown)
                return;
            bool isInside = false;
            VisualTreeHelper.HitTest(this, d =>
            {
                if (d == this)
                    isInside = true;
                return HitTestFilterBehavior.Stop;
            },
            ht => HitTestResultBehavior.Stop,
            new PointHitTestParameters(e.GetPosition(this)));
            IsPressed = isInside;
        }
    #endregion Overrides
}

以下是如何定义样式,使其看起来像所有其他工具栏按钮一样。将其添加到工具栏的资源中,或者更好的是,设置工具栏的样式以在样式中设置它,这样您就不必在任何地方手动添加它。

<Style TargetType="{x:Type myControls:EnhancedButton}" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" />

现在,它是标准Button控件的更兼容的插件替代品。

当然可以,为什么不呢?

只需为Mouse Up事件实现Events,并在那里检查鼠标右键是否已按下-如果为true,则调用您之前添加的代理/事件,例如OnRightMouseClick。

看看路由事件的MSDN页面,是的,你走在了正确的轨道上。

问候,

当然,wpf允许您将命令绑定到控件。首先连接窗口的命令绑定。

<Window.CommandBindings>
    <CommandBinding Command="{x:Static custom:Window1.RightClickCommand}" 
                    Executed="YourRightClickMethodInCSharpFile" />
</Window.CommandBindings>  

然后在按钮上注册右键绑定

<Button Content="Ima Button!">
    <Button.InputBindings>
        <MouseBinding Command="{x:Static custom:Window1.RightClickCommand}" 
                      MouseAction="RightClick" />
    </Button.InputBindings>
</Button>       

来源:WPF命令绑定-鼠标点击

您可以使用PreviewMouseDown事件。它会像这个

    private void buttonTest_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        if (e.RightButton == MouseButtonState.Pressed)
        {
            MessageBox.Show("Right");
        }
        else if (e.LeftButton == MouseButtonState.Pressed)
        {
            MessageBox.Show("Left");
        }
        e.Handled = true;
    }
相关文章: