Могу ли я расширить Button, чтобы добавить событие RightClick таким образом, чтобы графические побочные эффекты также сохранялись? - PullRequest
5 голосов
/ 31 марта 2012

Я пытаюсь расширить Button, чтобы добавить событие RightClick.

Мой клиент хочет, чтобы кнопка делала разные вещи в зависимости от того, щелкнули ли вы левой или правой кнопкой мыши. Я ожидал, что будет легкое событие для щелчка правой кнопкой мыши, но оказывается, что нет.

Я бы предпочел, чтобы визуальное поведение Баттона было идентичным ранее существовавшему событию 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;
    }
}

Ответы [ 6 ]

3 голосов
/ 02 апреля 2012

Благодаря ответу на мой другой вопрос , это то, что я придумал.

Кажется, могут быть некоторые различия между поведением 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;
        }
    }
}
1 голос
/ 13 февраля 2016

Просто добавляю свой 2с. Это основано на ответе @Grant со следующими изменениями:

  1. При этом правильно используется флаг _rightButtonDown, а не свойство CaptureMouse для определения обработки, поскольку CaptureMouse может быть задано в другом месте (т. Е. Это соответствует правильной инкапсуляции.)

  2. Имеется поддержка свойства ClickMode, допускающая значение Press.

  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 элемента управления.

1 голос
/ 01 апреля 2012

Если вы посмотрите на исходный код ButtonBase, вы увидите, что было бы трудно легко скопировать то, что происходит в переопределениях OnMouseLeftButtonDown и OnMouseLeftButtonUp.

Простой взлом, которыйКажется, работает довольно хорошо, это получить из 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);
    }
}

Затем используйте <MyButton> вместо <Button> в вашем Xaml.

Я не могу гарантироватьэто будет работать правильно во всех сценариях, но, похоже, работает должным образом, так как при нажатии правой или левой кнопки визуальное поведение кнопки одинаково.

0 голосов
/ 31 марта 2012

Вы можете использовать событие 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;
    }
0 голосов
/ 31 марта 2012

Конечно, 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 - щелчки мышью

0 голосов
/ 31 марта 2012

Конечно, вы можете сделать это, почему бы и нет?

Просто реализуйте события для события Mouse-Up и проверьте, была ли нажата правая кнопка мыши - если true, то вызовите делегата / событие, которое вы добавили ранее, например OnRightMouseClick.

Посмотрите на странице MSDN Маршрутизированные события - и да, вы на правильном пути.

Привет,

...