Мышь родительского контроля Ввод/выключение событий с помощью детского контроля
У меня есть приложение С#.NET 2.0 WinForms. Мое приложение имеет элемент управления, который представляет собой контейнер для двух дочерних элементов управления: ярлык и какой-то элемент управления редактированием. Вы можете думать об этом так, где внешний блок является родительским элементом управления:
+---------------------------------+
| [Label Control] [Edit Control] |
+---------------------------------+
Я пытаюсь что-то сделать, когда мышь входит или выходит из родительского элемента управления, но мне все равно, перемещается ли мышь в один из его дочерних элементов. Я хочу, чтобы один флаг представлял "мышь где-то внутри родителя или детей" и "мышь перемещалась за пределы родительского контроля".
Я попытался обработать MouseEnter и MouseLeave на родительском и обоих дочерних элементах управления, но это означает, что действие начинается и заканчивается несколько раз, когда мышь перемещается по элементу управления. Другими словами, я получаю следующее:
Parent.OnMouseEnter (start doing something)
Parent.OnMouseLeave (stop)
Child.OnMouseEnter (start doing something)
Child.OnMouseLeave (stop)
Parent.OnMouseEnter (start doing something)
Parent.OnMouseLeave (stop)
Промежуточные события OnMouseLeave вызывают некоторые нежелательные эффекты, так как все, что я делаю, запускается, а затем останавливается. Я хочу этого избежать.
Я не хочу захватывать мышь, поскольку родитель получает указатель мыши, потому что дочерние элементы управления нуждаются в событиях мыши, и я хочу, чтобы меню и другие сочетания клавиш работали.
Есть ли способ сделать это внутри платформы .NET? Или мне нужно использовать крючок для мыши Windows?
Ответы
Ответ 1
После дополнительных исследований я обнаружил метод Application.AddMessageFilter. Используя это, я создал .NET-версию мышиного крюка:
class MouseMessageFilter : IMessageFilter, IDisposable
{
public MouseMessageFilter()
{
}
public void Dispose()
{
StopFiltering();
}
#region IMessageFilter Members
public bool PreFilterMessage(ref Message m)
{
// Call the appropriate event
return false;
}
#endregion
#region Events
public class CancelMouseEventArgs : MouseEventArgs
{...}
public delegate void CancelMouseEventHandler(object source, CancelMouseEventArgs e);
public event CancelMouseEventHandler MouseMove;
public event CancelMouseEventHandler MouseDown;
public event CancelMouseEventHandler MouseUp;
public void StartFiltering()
{
StopFiltering();
Application.AddMessageFilter(this);
}
public void StopFiltering()
{
Application.RemoveMessageFilter(this);
}
}
Затем я могу обработать событие MouseMove в элементе управления контейнера, проверить, находится ли мышь внутри моего родительского элемента управления и начать работу. (Я также должен был отслеживать последний moused над родительским контролем, чтобы я мог остановить ранее начатый родитель.)
---- Редактировать ----
В моем классе формы я создаю и подключаю фильтр:
public class MyForm : Form
{
MouseMessageFilter msgFilter;
public MyForm()
{...
msgFilter = new MouseMessageFilter();
msgFilter.MouseDown += new MouseMessageFilter.CancelMouseEventHandler(msgFilter_MouseDown);
msgFilter.MouseMove += new MouseMessageFilter.CancelMouseEventHandler(msgFilter_MouseMove);
}
private void msgFilter_MouseMove(object source, MouseMessageFilter.CancelMouseEventArgs e)
{
if (CheckSomething(e.Control)
e.Cancel = true;
}
}
Ответ 2
Я чувствую, что нашел гораздо лучшее решение, чем принятое в настоящее время решение.
Проблема с другими предлагаемыми решениями заключается в том, что они либо довольно сложны (непосредственно обрабатывают сообщения нижнего уровня).
Или они выходят из строя из-за угла: полагаясь на положение мыши на MouseLeave, вы можете пропустить выход мыши, если мышь идет прямо изнутри дочернего элемента управления вне контейнера.
Хотя это решение не совсем изящно, оно простое и работает:
Добавьте прозрачный элемент управления, который занимает все пространство контейнера, для которого вы хотите получать события MouseEnter и MouseLeave для.
Я нашел хороший прозрачный контроль в ответе Amed здесь: Создание прозрачного контроля
Что я тогда разделил до этого:
public class TranspCtrl : Control
{
public TranspCtrl()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.Opaque, true);
this.BackColor = Color.Transparent;
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle = cp.ExStyle | 0x20;
return cp;
}
}
}
Пример использования:
public class ChangeBackgroundOnMouseEnterAndLeave
{
public Panel Container;
public Label FirstLabel;
public Label SecondLabel;
public ChangeBackgroundOnMouseEnterAndLeave()
{
Container = new Panel();
Container.Size = new Size(200, 60);
FirstLabel = new Label();
FirstLabel.Text = "First Label";
FirstLabel.Top = 5;
SecondLabel = new Label();
SecondLabel.Text = "Second Lable";
SecondLabel.Top = 30;
FirstLabel.Parent = Container;
SecondLabel.Parent = Container;
Container.BackColor = Color.Teal;
var transparentControl = new TranspCtrl();
transparentControl.Size = Container.Size;
transparentControl.MouseEnter += MouseEntered;
transparentControl.MouseLeave += MouseLeft;
transparentControl.Parent = Container;
transparentControl.BringToFront();
}
void MouseLeft(object sender, EventArgs e)
{
Container.BackColor = Color.Teal;
}
void MouseEntered(object sender, EventArgs e)
{
Container.BackColor = Color.Pink;
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var test = new ChangeBackgroundOnMouseEnterAndLeave();
test.Container.Top = 20;
test.Container.Left = 20;
test.Container.Parent = this;
}
}
Наслаждайтесь соответствующими событиями MouseLeave и MouseEnter!
Ответ 3
Вы можете узнать, находится ли мышь в пределах вашего элемента управления (при условии, что этот код находится в вашем контейнере, а если нет, замените this
ссылкой на элемент управления контейнером):
private void MyControl_MouseLeave(object sender, EventArgs e)
{
if (this.ClientRectangle.Contains(this.PointToClient(Cursor.Position)))
{
// the mouse is inside the control bounds
}
else
{
// the mouse is outside the control bounds
}
}
Ответ 4
Я не думаю, что вам нужно подключить насос сообщений, чтобы решить эту проблему. Некоторые помехи в вашем пользовательском интерфейсе должны делать трюк. Я думаю, что вы создаете переменную-член, например Control_someParent, в вашем контрольном классе, которая будет ссылаться на родительский элемент управления, когда вызывается один из ваших обработчиков OnMouseEnter. Затем в OnMouseLeave проверьте значение флага _someParent и, если он тот же, что и текущий отправитель, на самом деле не остановит вашу обработку, просто вернитесь. Только когда родитель отличается, вы останавливаетесь и reset _someParent равны нулю.