Как определить, нажаты ли и левая, и правая кнопки?

Я хотел бы иметь три действия мыши над элементом управления: влево, вправо и ОБА.

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

ОБНОВЛЕНИЕ. После просмотра предложенных ответов мне нужно уточнить, что я пытался выполнить действие при щелчке мыши в событии MouseDown (на самом деле метод OnMouseDown элемента управления). Поскольку кажется, что .NET всегда будет вызывать два события MouseDown при нажатии левой и правой кнопок мыши (по одному для каждой кнопки), я предполагаю, что единственный способ сделать это - либо сделать какое-нибудь сообщение окна низкого уровня management или реализовать какое-то отложенное выполнение действия после MouseDown. В конце концов, пользоваться средней кнопкой мыши намного проще.

Теперь, если действие происходит в MouseUp, то предложения Гэри или его нет сработают.

Будем признательны за любые дальнейшие идеи по этой проблеме. Спасибо!


person Greg McGuffey    schedule 25.07.2009    source источник
comment
Чувак. Не ходи туда. Стандарты пользовательского интерфейса существуют не зря. Пострадает ваше удобство использования. Звучит хуже, чем тройной щелчок   -  person JohnFx    schedule 26.07.2009
comment
@JohnFx Любой достойный клон Minesweeper требует для записи обоих щелчков. Это один из примеров, когда это полезно.   -  person mbomb007    schedule 03.06.2016


Ответы (6)


Всегда есть подход «сделай сам»:

Просто запомните состояние нажатия и отпускания кнопки. В OnMouseDown вы просто запоминаете нажатую кнопку, а в OnMouseUp просто проверяете, какие кнопки были запомнены, а также очищаете состояние кнопки.

Вам нужна логика, чтобы не выполнять несколько действий при отпускании кнопок. Что-то вроде

MouseButtons buttonPressed;
..

void OnMouseDown(MouseEventArgs e) 
{
   buttonPressed |= e.Button;
}


void OnMouseUp(MouseEventArgs e) 
{
  if(!doneAction) {
    if((buttonPressed & MouseButtons.Right == MouseButtons.Right 
       && buttonPressed & MouseButtons.Left == MouseButtons.Left)
       || buttonPressed & MouseButtons.Middle== MouseButtons.Middle) {
       DoMiddleAction();
       doneAction = true;
    } else if(check Right button , etc.) {
       .... 
    }
  }

  buttonpressed &= ~e.Button;
  if(buttonpressed == None)
      doneAction = false;

}
person nos    schedule 25.07.2009
comment
В настоящее время я пытаюсь обработать действие MouseDown, поэтому это будет сложно (см. Комментарий к ответу Гэри Уиллоуби). Однако я собираюсь вернуться к коду, чтобы увидеть, смогу ли я сделать это с помощью мыши вверх, где это будет иметь смысл. Спасибо! - person Greg McGuffey; 26.07.2009
comment
Я считаю это решение лучшим. Он использует существующие MouseEventArgs и e.Button, чтобы делать то, что необходимо. Но ваш код можно было бы упростить. Вам не нужен doneAction, потому что ЭТО OnMouseUp является обработчиком для всех соответствующих событий mouseUp. Просто используйте Select Case buttonPressed с оператором case для каждого обрабатываемого типа: Case MouseButtons.Left, Case MouseButtons.Middle, MouseButtons.Left Or MouseButtons.Right и т. Д. - person mbomb007; 03.06.2016
comment
Мое предложение, приведенное выше, также работает для OnMouseDown, используя аналогичный оператор Select Case. - person mbomb007; 03.06.2016

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

using System.Windows.Forms;

namespace WindowsFormsApplication1
{

    public static class MouseButtonStatus
    {
        static bool RightButton;
        static bool LeftButton;

        public static bool RightButtonPressed
        {
            get
            {
                return RightButton;
            }
            set
            {
                RightButton = value;
            }
        }

        public static bool LeftButtonPressed
        {
            get
            {
                return LeftButton;
            }
            set
            {
                LeftButton = value;
            }
        }


    }


    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        public void HandleButtons(bool LeftPressed, bool RightPressed)
        {
            if(LeftPressed && RightPressed)
            {
                //BOTH ARE PRESSED
            }
            else if(LeftPressed)
            {
                //LEFT IS PRESSED
            }
            else if(RightPressed)
            {
                //RIGHT IS PRESSED
            }
            else
            {
                //NONE ARE PRESSED
            }
        }

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            if(e.Button == MouseButtons.Left)
            {
                MouseButtonStatus.LeftButtonPressed = true;
            }
            if(e.Button == MouseButtons.Right)
            {
                MouseButtonStatus.RightButtonPressed = true;
            }

            HandleButtons(MouseButtonStatus.LeftButtonPressed, MouseButtonStatus.RightButtonPressed);
        }

        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            if(e.Button == MouseButtons.Left)
            {
                MouseButtonStatus.LeftButtonPressed = false;
            }
            if(e.Button == MouseButtons.Right)
            {
                MouseButtonStatus.RightButtonPressed = false;
            }

            HandleButtons(MouseButtonStatus.LeftButtonPressed, MouseButtonStatus.RightButtonPressed);
        }



    }
}
person Gary Willoughby    schedule 25.07.2009
comment
Проблема с этим подходом заключается в том, что .NET отправит два нажатия кнопки мыши, одно для левого и одно для правого (в зависимости от того, что считается нажатым первым. Это сработало бы, если бы мне нужно было обрабатывать только оба, но если бы я нужно обрабатывать только левый, только правый или оба, тогда это становится действительно сложно, я бы подумал, так как мне пришлось бы установить какой-то таймер для отслеживания интервала между щелчками, а затем мне пришлось бы отложить действие брать, пока я не был уверен, что это не обе кнопки. - person Greg McGuffey; 26.07.2009
comment
Я отредактировал приведенный выше код, чтобы применить решение вашей проблемы. Вам просто нужно использовать метод для обработки щелчков и обновлять его каждый раз MouseUp и MouseDown. Проверить это. :) - person Gary Willoughby; 26.07.2009
comment
Вы никогда не сможете обойтись без того факта, что при нажатии кнопки запускается событие. Итак, если вы хотите обрабатывать нажатие обеих кнопок, вам придется сначала обработать событие щелчка правой кнопкой мыши, а затем событие щелчка левой кнопкой. Вероятно, поэтому ни одна программа не использует нажатие обеих кнопок для пользовательского интерфейса. - person Gary Willoughby; 26.07.2009
comment
Я подумал, что могу упомянуть, что я получил идею от MineSweeper. Нажатие на обе кнопки используется для отображения всех неотмеченных ячеек вокруг выбранной. Я тоже видел это в других играх. Таким образом, это возможно в Windows, но, возможно, не совсем так в .NET. - person Greg McGuffey; 27.07.2009

Не уверен, что есть собственный .Net способ сделать это, но если вас устраивает P / Invoke, вы можете использовать GetKeyState или GetAsyncKeyState вот так:

[DllImport("user32.dll")]
public static extern short GetKeyState(int nVirtKey);

if (GetKeyState((int)Keys.LButton) < 0 && GetKeyState((int)Keys.RButton) < 0)
{
    // Both buttons are pressed.
}
person RichieHindle    schedule 25.07.2009
comment
Спасибо. Я не уверен, что «счастлив» - правильное слово, но думаю, что смогу набраться смелости, чтобы использовать его в этой ситуации. : D - person Greg McGuffey; 26.07.2009
comment
Смотрите мой ответ, чтобы избежать p / invoke. - person Gary Willoughby; 26.07.2009
comment
После некоторого тестирования это оказалось не очень надежным. При этом ловится примерно 1 из 4. Не уверен, почему, и, возможно, я могу что-то сделать, чтобы это исправить, но я понятия не имею, что. - person Greg McGuffey; 26.07.2009
comment
Вы искали разницу между GetKeyState и GetAsyncKeyState? Вам придется использовать то, что подходит для того, что вы пытаетесь сделать. - person RichieHindle; 26.07.2009
comment
При этом нужно учитывать, что кнопки точно вместе не опускаются. Недостаточно поставить проверку в обработчике OnLButtonDown, нажата ли правая кнопка - нужно еще поставить проверку в обработчике OnRButtonDown, чтобы увидеть, нажата ли левая кнопка. - person RichieHindle; 26.07.2009

Разве «середина» не то же самое, что «левый и правый вместе»? По крайней мере, это то, что я припоминаю откуда-то, но это было с давних времен, когда у меня были двухкнопочные мыши без кнопок с колесом прокрутки ...

person Kawa    schedule 25.07.2009
comment
Это флаг в Xorg для имитации щелчка средней кнопкой мыши на дрянных ноутбуках, таких как мой (на X11, когда вы выбираете текст, он автоматически копируется, а средний щелчок также действует как вставка, чего мне очень не хватает, когда я использую Windows). - person elcuco; 26.07.2009
comment
Забавно, потому что я помню это по винде. Ну что ж. - person Kawa; 26.07.2009

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

Я создал компонент MouseDownManager, который вызываю во время события MouseDown. Он отслеживает только что нажатую кнопку, определяет, какую кнопку мы ждем, чтобы произошло событие нажатия «обеих» кнопок, а затем запускает таймер для ожидания этой кнопки. Если в течение отведенного времени правильная кнопка нажата, MouseDownManager вызывает соответствующее событие нажатия кнопки «обе». В противном случае он вызывает соответствующее событие одной кнопки. В форме я обрабатываю событие MouseDown MouseDownManager, чтобы выполнить действие при переведенном щелчке мыши.

Теперь я могу просто перетащить этот компонент в форму / элемент управления и реагировать на "оба" щелчка.

Спасибо за помощь в выяснении этого.

/// <summary>
/// Manage mouse down event to enable using both buttons at once.
/// I.e. allowing the pressing of the right and left mouse
/// buttons together.
/// </summary>
public partial class MouseDownManager : Component
{

  /// <summary>
  /// Raised when a mouse down event is triggered by this
  /// component.
  /// </summary>
  public event EventHandler<MouseEventArgs> MouseDown;

  protected virtual void OnMouseDown( MouseEventArgs e )
  {
    if (this.MouseDown != null)
    {
      this.MouseDown( this, e );
    }
  }

  public MouseDownManager()
  { 
    //-- the timer was dropped on the designer, so it
    //   is initialized by InitializeComponent.
    InitializeComponent();
  }

  /// <summary>
  /// Defines the interval that the timer will wait for the other
  /// click, in milliseconds.
  /// </summary>
  public int BothClickInterval
  {
    get
    {
      return this.tmrBothInterval.Interval;
    }
    set
    {
      this.tmrBothInterval.Interval = value;
    }
  }

  private MouseButtons _virtualButton = MouseButtons.Middle;

  /// <summary>
  /// Defines the button that is sent when both buttons are
  /// pressed. This can be either a single button (like a middle
  /// button) or more than one button (like both the left and
  /// right buttons.
  /// </summary>
  public MouseButtons VirtualButton
  {
    get
    {
      return _virtualButton;
    }
    set
    {
      _virtualButton = value;
    }
  }

  private MouseEventArgs _originalArgs;

  /// <summary>
  /// Defines the original mouse event arguments that is associated
  /// with the original press.
  /// </summary>
  private MouseEventArgs OriginalArgs
  {
    get
    {
      return _originalArgs;
    }
    set
    {
      _originalArgs = value;
    }
  }

  private MouseButtons _waitButton = MouseButtons.None;

  /// <summary>
  /// Defines the button that we are waiting on, for there to be a
  /// both button click.
  /// </summary>
  private MouseButtons WaitButton
  {
    get
    {
      return _waitButton;
    }
    set
    {
      _waitButton = value;
    }
  }

  /// <summary>
  /// Manage a mouse button being depressed.
  /// </summary>
  /// <remarks>
  /// This will instigate one of two actions.  If there is no
  /// current wait button, this will set the appropriate wait
  /// button (if the button pressed was the left button, then we
  /// are waiting on the right button) and start a timer. If the
  /// wait button is set, but this isn't that button, then the wait
  /// button is updated and the timer restarted.  Also, this will
  /// trigger the waiting event.  If it is the wait button, then
  /// the appropriate event is raised for a "both" button press.
  /// </remarks>
  public void ManageMouseDown( MouseEventArgs mouseArgs )
  {
    //-- Is the the button we are waiting for?
    if (mouseArgs.Button == this.WaitButton)
    {
      //-- Turn off timer.
      this.ClearTimer();

      //-- Create new mouse event args for our virtual event.
      MouseEventArgs bothArgs = new MouseEventArgs( this.VirtualButton
                                                  , mouseArgs.Clicks
                                                  , mouseArgs.X
                                                  , mouseArgs.Y
                                                  , mouseArgs.Delta );

      //-- Raise the mouse down event.
      this.OnMouseDown( bothArgs );
    }
    else
    {
      //-- Clear timer
      this.ClearTimer();

      //-- If we were waiting for a button, then
      //   fire the event for the original button.
      if (this.WaitButton != MouseButtons.None)
      {
        this.OnMouseDown( this.OriginalArgs );
      }

      //-- Cache the original mouse event args.
      MouseEventArgs newMouseArgs = new MouseEventArgs( mouseArgs.Button
                                                      , mouseArgs.Clicks
                                                      , mouseArgs.X
                                                      , mouseArgs.Y
                                                      , mouseArgs.Delta );
      this.OriginalArgs = newMouseArgs;

      //-- Reset to wait for the appropriate next button.
      switch (mouseArgs.Button)
      {
        case MouseButtons.Left:
          this.WaitButton = MouseButtons.Right;
          break;
        case MouseButtons.Right:
          this.WaitButton = MouseButtons.Left;
          break;
        default:
          this.WaitButton = MouseButtons.None;
          break;
      }

      //-- Start timer
      this.tmrBothInterval.Enabled = true;
    }
  }

  /// <summary>
  /// Raise the event for the button that was pressed initially
  /// and turn off the timer.
  /// </summary>
  private void tmrBothInterval_Tick( object sender, EventArgs e )
  {
    //-- Turn off the timer.
    this.tmrBothInterval.Enabled = false;

    //-- Based on the original button pressed, raise
    //   the appropriate mouse down event.
    this.OnMouseDown( this.OriginalArgs );

    //-- Clear timer.
    this.ClearTimer();
  }

  /// <summary>
  /// Clear the timer and associated variables so we aren't waiting
  /// for the second click.
  /// </summary>
  private void ClearTimer()
  {
    //-- Turn off the timer.
    this.tmrBothInterval.Enabled = false;

    //-- Clear the wait button.
    this.WaitButton = MouseButtons.None;

    //-- Clear the original args
    this.OriginalArgs = null;
  }
}

/// <summary>
/// Just the mouse code from the control needing the functionality.
/// </summary>
public class MyControl: Control
{
  /// <summary>
  /// Handle the mouse down event. This delegates the actual event
  /// to the MouseDownManager, so we can capture the situation
  /// where both buttons are clicked.
  /// </summary>
  protected override void OnMouseDown( MouseEventArgs e )
  {
    this.mdmMain.ManageMouseDown( e );
  }

  /// <summary>
  /// Process the mouse down event.
  /// </summary>
  private void mdmMain_MouseDown( object sender, MouseEventArgs e )
  {
    //-- Get the reported button state.
    MouseButtons mouseButton = e.Button;

    //-- Execute logic based on button pressed, which now can include
    //   both the left and right together if needed....
  }
}
person Greg McGuffey    schedule 27.04.2010

Легкий и простой ответ здесь

bool midL,midR;

private void form1_MouseUp(object sender, MouseEventArgs e)
{
    midL = false;
    midR = false;
}
    
private void form1_MouseDown(object sender, MouseEventArgs e)
{
    if (!midL)
        midL = e.Button == MouseButtons.Left;

    if (!MidR)
        midR = e.Button == MouseButtons.Right;

    if (e.Button == MouseButtons.Middle || (midL && midR))
    {
        MessageBox.show("Middle button is pressed")
    }
}
person Shahid Wani    schedule 28.02.2021