Есть ли способ обнаружить щелчок мыши за пределами пользовательского элемента управления? - PullRequest
14 голосов
/ 18 апреля 2011

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

user control

Ответы [ 5 ]

13 голосов
/ 18 апреля 2011

Итак, я наконец понимаю, что вы хотите, чтобы он закрывался только тогда, когда пользователь нажимает за его пределами. В этом случае событие Leave должно работать просто отлично ... По какой-то причине у меня сложилось впечатление, что вы хотели, чтобы оно закрывалось всякий раз, когда они перемещали мышь за пределы вашего пользовательского раскрывающегося списка. Событие Leave возникает всякий раз, когда ваш элемент управления теряет фокус, и если пользователь нажимает на что-то другое, он, безусловно, теряет фокус, поскольку объект, на который он нажал, получает фокус.

В документации также говорится, что это событие распространяется по цепочке управления вверх и вниз по мере необходимости:

События Enter и Leave являются иерархическими и будут каскадно перемещаться вверх и вниз по родительской цепочке, пока не будет достигнут соответствующий контроль. Например, предположим, что у вас есть форма с двумя элементами управления GroupBox, а каждый элемент управления GroupBox имеет один элемент управления TextBox. Когда курсор переносится из одного TextBox в другой, событие Leave вызывается для TextBox и GroupBox, а событие Enter вызывается для других GroupBox и TextBox.

Переопределение метода UserControl OnLeave - лучший способ справиться с этим:

protected override void OnLeave(EventArgs e)
{
   // Call the base class
   base.OnLeave(e);

   // When this control loses the focus, close it
   this.Hide();
}

А затем для целей тестирования я создал форму, которая показывает выпадающий UserControl по команде:

public partial class Form1 : Form
{
   private UserControl1 customDropDown;

   public Form1()
   {
      InitializeComponent();

      // Create the user control
      customDropDown = new UserControl1();

      // Add it to the form's Controls collection
      Controls.Add(customDropDown);
      customDropDown.Hide();
   }

   private void button1_Click(object sender, EventArgs e)
   {         
      // Display the user control
      customDropDown.Show();
      customDropDown.BringToFront();   // display in front of other controls
      customDropDown.Select();         // make sure it gets the focus
   }
}

Все отлично работает с приведенным выше кодом, за исключением , с одной стороны: если пользователь нажимает на пустую область формы, UserControl не закрывается. Хм, а почему бы и нет? Ну, потому что сама форма не хочет фокусироваться. Только элементы управления могут получить фокус, и мы не нажали на элемент управления. И поскольку ничто другое не привлекло внимание, событие Leave никогда не вызывалось, а это означает, что UserControl не знал, что должен был закрыться.

Если вам нужно, чтобы UserControl закрывался сам, когда пользователь нажимает на пустую область в форме, для этого вам потребуется специальная обработка. Поскольку вы говорите, что вас беспокоят только клики , вы можете просто обработать событие Click для формы и установить фокус на другой элемент управления:

protected override void OnClick(EventArgs e)
{
   // Call the base class
   base.OnClick(e);

   // See if our custom drop-down is visible
   if (customDropDown.Visible)
   {
      // Set the focus to a different control on the form,
      // which will force the drop-down to close
      this.SelectNextControl(customDropDown, true, true, true, true);
   }
}

Да, эта последняя часть выглядит как хак. Лучшее решение, как уже упоминали другие, состоит в том, чтобы использовать функцию SetCapture , чтобы дать указание Windows захватывать мышь над окном вашего UserControl. Свойство Capture элемента управления предоставляет еще более простой способ сделать то же самое.

2 голосов
/ 03 июня 2014

Обработка события MouseDown формы или переопределение OnMouseDown формы Метод:

enter code here

А потом:

protected override void OnMouseDown(MouseEventArgs e)
{

    if (!theListBox.Bounds.Contains(e.Location)) 
    {
        theListBox.Visible = false;
    }
}

Старый метод Contains System.Drawing.Rectangle может использоваться для указания точка содержится внутри прямоугольника. Свойство Bounds элемента управления внешний Rectangle определяется границами элемента управления. Местонахождение Свойство MouseEventArgs является Точкой относительно Контроля, который получил событие MouseDown. Свойство Bounds элемента управления в форме относительно формы.

2 голосов
/ 18 апреля 2011

Технически, вам нужно p / invoke SetCapture () , чтобы получать события щелчка, которые происходят вне вашего контроля.

Но в вашем случае достаточно обработки Leave , как предполагает @Martin.

РЕДАКТИРОВАНИЕ: При поиске примера использования для SetCapture() я наткнулся на свойство Control.Capture , о котором я не знал. Использование этого свойства означает, что вам не придется ничего п / вызывать, что всегда хорошо в моей книге.

Итак, вам нужно установить Capture на true при отображении выпадающего списка, затем определить, находится ли указатель мыши внутри элемента управления в вашем обработчике события щелчка, и, если это не так, установить Capture на false и закройте раскрывающийся список.

0 голосов
/ 18 апреля 2011

Я сделал это сам, и вот как я это сделал.

Когда раскрывающийся список открыт, зарегистрируйте событие щелчка в родительской форме элемента управления:

this.Form.Click += new EventHandler(CloseDropDown);

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

[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();

и

var timer = new Timer();
timer.Interval = 100;
timer.Tick += (sender, args) =>
{
    IntPtr f = GetForegroundWindow();
    if (this.Form == null || f != this.Form.Handle)
    {
        CloseDropDown();
    }
};

Конечно, вы должны только позволить таймеру работатькогда выпадающий виденТакже, возможно, есть несколько других событий в родительской форме, которые вы хотите зарегистрировать при открытии раскрывающегося списка:

this.Form.LocationChanged += new EventHandler(CloseDropDown);
this.Form.SizeChanged += new EventHandler(CloseDropDown);

Просто не забудьте отменить регистрацию всех этих событий в методе CloseDropDown :)

РЕДАКТИРОВАТЬ:

Я забыл, вы также должны зарегистрировать событие Leave на вашем элементе управления, чтобы увидеть, если другой элемент управления активирован / нажата:

this.Leave += new EventHandler(CloseDropDown);

Я думаю, что я 'у нас есть это сейчас, это должно охватывать все основы.Дайте мне знать, если я что-то упустил.

0 голосов
/ 18 апреля 2011

Возможно, вы ищете событие выхода:

http://msdn.microsoft.com/en-us/library/system.windows.forms.control.leave.aspx

Выход происходит, когда фокус ввода покидает элемент управления.

...