eventos - mouseup event c#
Control de padres Mouse Entrar/Salir de eventos con controles de niƱos (5)
Después de más investigación, descubrí el método Application.AddMessageFilter . Usando esto, creé una versión .NET de un gancho de ratón:
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);
}
}
Luego, puedo manejar el evento MouseMove en mi control de contenedor, verificar si el mouse está dentro de mi control principal e iniciar el trabajo. (También tuve que rastrear el último control sobre el control padre para poder detener al padre iniciado anteriormente).
---- Editar ----
En mi clase de formulario, creo y conecto el filtro:
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;
}
}
Tengo una aplicación WinForms C # .NET 2.0. Mi aplicación tiene un control que es un contenedor para dos controles secundarios: una etiqueta y algún tipo de control de edición. Puede pensarlo así, donde la caja exterior es el control principal:
+---------------------------------+ | [Label Control] [Edit Control] | +---------------------------------+
Estoy tratando de hacer algo cuando el mouse entra o sale del control de los padres, pero no me importa si el mouse se mueve hacia uno de sus hijos. Quiero que una sola bandera represente "el mouse está en algún lugar dentro del padre o los hijos" y "el mouse se ha movido fuera de los límites de control de los padres".
He intentado manejar MouseEnter y MouseLeave en los controles principal y secundario, pero esto significa que la acción comienza y finaliza varias veces a medida que el mouse se mueve sobre el control. En otras palabras, me sale esto:
Parent.OnMouseEnter (start doing something) Parent.OnMouseLeave (stop) Child.OnMouseEnter (start doing something) Child.OnMouseLeave (stop) Parent.OnMouseEnter (start doing something) Parent.OnMouseLeave (stop)
Los eventos intermedios de OnMouseLeave causan algunos efectos no deseados a medida que todo lo que estoy haciendo se inicia y luego se detiene. Quiero evitar eso.
No quiero capturar el mouse cuando el padre pasa el mouse, porque los controles secundarios necesitan sus eventos del mouse, y quiero que el menú y otras teclas de método abreviado funcionen.
¿Hay una manera de hacer esto dentro del marco .NET? ¿O necesito usar un gancho de mouse de Windows?
No creo que necesites enganchar la bomba de mensajes para resolver esto. Algunas marcas en tu interfaz de usuario deberían hacer el truco. Estoy pensando que creas una variable miembro, algo como Control _someParent, en tu clase de control que tomará la referencia del control principal cuando se llame a uno de tus manejadores OnMouseEnter. Luego, en OnMouseLeave, verifique el valor de la "bandera" _someParent y, si es el mismo que el del remitente actual, no detenga realmente el procesamiento, simplemente regrese. Solo cuando el padre es diferente, detiene y reinicia _someParent a nulo.
Puede averiguar si el mouse está dentro de los límites de su control de esta manera (asumiendo que este código reside en el control de su contenedor; si no, cámbielo por una referencia al control del contenedor):
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
}
}
Siento que encontré una solución mucho mejor que la mejor solución actualmente aceptada.
El problema con otras soluciones propuestas es que son bastante complejas (manejan directamente los mensajes de nivel inferior).
O fallan en los casos de esquina: confiar en la posición del mouse en MouseLeave puede hacer que pierda la salida del mouse si el mouse va directamente desde el interior de un control secundario al exterior del contenedor.
Si bien esta solución no es del todo elegante, es sencilla y funciona:
Agregue un control transparente que ocupe todo el espacio del contenedor para el que desea recibir los eventos MouseEnter y MouseLeave.
Encontré un buen control transparente en la respuesta de Amed aquí: hacer que un control sea transparente
Que luego me desvestí a esto:
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;
}
}
}
Ejemplo de uso:
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;
}
}
¡Disfruta de los eventos apropiados de MouseLeave y MouseEnter!
Tenía exactamente la misma necesidad. La respuesta de Paul Williams me dio la idea central, pero tuve dificultades para entender el código. Encontré otra toma here , y juntos, los dos ejemplos me ayudaron a desarrollar mi propia versión.
Para inicializar, pasa el control de interés de ContainerMessageFilter
constructor ContainerMessageFilter
. La clase recoge los manejadores de ventana del contenedor y todos los controles secundarios dentro de él.
Luego, durante la operación, la clase filtra el mensaje WM_MOUSEMOVE
, verificando el HWnd
los mensajes para determinar en qué control se está moviendo el mouse. De esta manera, determina cuándo el mouse se ha movido dentro o fuera del conjunto de controles dentro del contenedor que está mirando.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
public class ContainerMessageFilter : IMessageFilter {
private const int WM_MOUSEMOVE = 0x0200;
public event EventHandler MouseEnter;
public event EventHandler MouseLeave;
private bool insideContainer;
private readonly IEnumerable<IntPtr> handles;
public ContainerMessageFilter( Control container ) {
handles = CollectContainerHandles( container );
}
private static IEnumerable<IntPtr> CollectContainerHandles( Control container ) {
var handles = new List<IntPtr> { container.Handle };
RecurseControls( container.Controls, handles );
return handles;
}
private static void RecurseControls( IEnumerable controls, List<IntPtr> handles ) {
foreach ( Control control in controls ) {
handles.Add( control.Handle );
RecurseControls( control.Controls, handles );
}
}
public bool PreFilterMessage( ref Message m ) {
if ( m.Msg == WM_MOUSEMOVE ) {
if ( handles.Contains( m.HWnd ) ) {
// Mouse is inside container
if ( !insideContainer ) {
// was out, now in
insideContainer = true;
OnMouseEnter( EventArgs.Empty );
}
}
else {
// Mouse is outside container
if ( insideContainer ) {
// was in, now out
insideContainer = false;
OnMouseLeave( EventArgs.Empty );
}
}
}
return false;
}
protected virtual void OnMouseEnter( EventArgs e ) {
var handler = MouseEnter;
handler?.Invoke( this, e );
}
protected virtual void OnMouseLeave( EventArgs e ) {
var handler = MouseLeave;
handler?.Invoke( this, e );
}
}
En el siguiente ejemplo de uso, queremos monitorear la entrada y salida del mouse para un Panel
y los controles secundarios que contiene:
public partial class Form1 : Form {
private readonly ContainerMessageFilter containerMessageFilter;
public Form1() {
InitializeComponent();
containerMessageFilter = new ContainerMessageFilter( panel1 );
containerMessageFilter.MouseEnter += ContainerMessageFilter_MouseEnter;
containerMessageFilter.MouseLeave += ContainerMessageFilter_MouseLeave;
Application.AddMessageFilter( containerMessageFilter );
}
private static void ContainerMessageFilter_MouseLeave( object sender, EventArgs e ) {
Console.WriteLine( "Leave" );
}
private static void ContainerMessageFilter_MouseEnter( object sender, EventArgs e ) {
Console.WriteLine( "Enter" );
}
private void Form1_FormClosed( object sender, FormClosedEventArgs e ) {
Application.RemoveMessageFilter( containerMessageFilter );
}
}