2010-08-14 11 views
5

Tengo dos preguntas.C# Método de anulación en tiempo de ejecución

1) Encontré una pequeña joya de código para how to make a control scroll smoothly.

Genial. Pero anula el método WndProc, así que para usarlo, tuve que arrancar el FlowLayoutPanel que había dejado caer en el formulario en el momento del diseño, la subclase FlowLayoutPanel, y finalmente crear una instancia de mi nueva clase y crear todas las propiedades de forma manual y cambiar todas las referencias al control ser esto. Controles ["ControlName"]. (O supongo que podría hacer una variable de nivel de clase que es esencialmente lo que originalmente era el control, aunque ¿cómo te permiten usar intellisense cuando no está declarado en ninguna parte?)

Así que ahora solo me pregunto si de hecho, había una manera de ejecutarlo para hacerlo.

¿Puedo hacer algo simple como este, donde MainPanel es el nombre del control:

MainPanel = (SmoothScrollingFlowLayoutPanel)MainPanel 

No puede ser tan fácil, ¿verdad? Aun así, es molesto porque todavía tengo que tener la subclase (lo que puede ser una buena decisión de diseño, pero me gustaría tener la libertad de hacerlo de manera única). Así sería posible poner el código en la matriz de la FlowLayoutPanel algo como esto:

private Delegate void WndProcHandler(ref Message m); 
private WndProcHandler w; 

public void SomeCode() { 
    w = MainPanel.WndProc; // get reference to existing wndproc method 
    MainPanel.WndProc = WndProcSmoothScroll; //replace with new method 
} 

private void WndProcSmoothScroll(ref Message m) { // make smooth scrolling work 
    if (
     (m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL) 
     && (((int)m.WParam & 0xFFFF) == 5) 
    ) { 
     m.WParam = (IntPtr)(((int)m.WParam & ~0xFFFF) | 4); 
    } 
    if (w != null) { w(); } 
    base.WndProc(ref m); 
    } 

Comprendo que esto es probablemente bastante ingenua. Estoy tratando el método WndProc como si fuera un evento, y no lo es.

2) Así que mi segunda pregunta es, si WndProc era un evento en lugar de un método, ¿cómo iba a hacer lo mismo que la tienda una copia de la lista original de los manipuladores para un evento, instalar mi propio evento controlador para ejecutar primero, luego llamar a todos los controladores de eventos originales?

BITS SABROSOS

En caso de que alguien está interesado me di cuenta de una optimización posible en el código desplazamiento suave:

//m.WParam = (IntPtr)(((int)m.WParam & ~0xFFFF) | 4); 
m.WParam = (IntPtr)((int)m.WParam^1); 

Dado que queremos convertir los últimos 16 bits de 5 a 4, que puede simplemente voltear el último bit (XOR) en lugar de Y luego O.

Respuesta

8

Si entiendo su pregunta correctamente, todo lo que quiero hacer es anular WndProc en tiempo de ejecución. Si es así, todo lo que necesitas es un poco de magia Win32.

Cada control tiene un "identificador" que lo identifica para que el sistema operativo pueda enviarle mensajes. Este identificador está expuesto a través de la propiedad Handle en cada control. El sistema Win32 subyacente en realidad le permite escuchar cualquier control WndProc mientras tenga su control. Esto significa que no tiene que heredar de un control Winforms para modificar su comportamiento de Win32. System.Windows.Forms.NativeWindow en .NET envuelve esta funcionalidad subyacente.

Aquí hay un ejemplo de cómo se puede lograr esto:

class SmoothScrollIntercept : System.Windows.Forms.NativeWindow 
{ 
    public SmoothScrollIntercept(IntPtr hWnd) 
    { 
     // assign the handle and listen to this control's WndProc 
     this.AssignHandle(hWnd); 
    } 

    protected override void WndProc(ref Message m) 
    { 
     // listen to WndProc here, do things 

     if ((m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL) 
      && (((int)m.WParam & 0xFFFF) == 5)) 
     { 
      m.WParam = (IntPtr)(((int)m.WParam & ~0xFFFF) | 4); 
     } 

     base.WndProc(ref m); 
    } 
} 

Luego, en el código detrás, coloque el punto de intersección con el control:

SmoothScrollIntercept intercept = new SmoothScrollIntercept(myControl.Handle); 

// myControl is now using smooth scrolling, without inheriting from the control 
+0

Gracias Zach, pero ya he logrado que mis ventanas se desplacen sin problemas. Solo odiaba cómo tenía que quitarle el control al diseñador y hacer todo de forma dinámica. – ErikE

+0

@Emtucifor: No hay problema, solo quería hacerle saber que no tenía que heredar de 'FlowLayoutPanel' para anular su' WndProc'. –

+0

Oh, ahora lo entiendo. Gracias. Confieso que no leí tan cuidadosamente como debería haberlo hecho. ¡Esto es útil! De hecho, estás 100% en lo cierto, es la respuesta que estaba buscando. (golpea la frente) – ErikE

2

No, lo que está pidiendo es imposible. Tendrás que crear una subclase como lo hiciste antes.

Incluso si fuera un evento, no podrías hacer lo que estás buscando. La interfaz pública para un evento solo expone add y remove; no hay forma de obtener o asignar los delegados reales adjuntos al evento.

Sin embargo, mirando el problema desde una perspectiva diferente, es posible que pueda hacer uso de la interfaz IMessageFilter con el fin de lograr el resultado final que está buscando.

Editar

Después de mirar el código de nuevo, IMessageFilter no va a funcionar, ya que no puede modificar el mensaje dentro de PreFilterMessage; solo puedes examinarlo o suprimirlo.Su mejor opción en este punto es anular WndProc en el padre Form y tratar de realizar sus manipulaciones allí. No parece que haya una solución genérica para su problema.

+0

Gracias. Sé un poco de javascript donde puedes hacer esto con métodos, así que solo tenía curiosidad. Ahora se me ocurre, ¿puedo heredar un FlowLayoutPanel Y un UserControl para que mi nuevo SmoothScrollingFlowLayoutPanel se convierta en un elemento que pueda colocar en el formulario en el momento del diseño? – ErikE

+0

@Emtucifor: No, ninguno de los lenguajes .NET permite la herencia múltiple. Sin embargo, puede heredar de 'FlowLayoutPanel' y poner su control en la caja de herramientas. Si el control está en su proyecto, debería aparecer allí automáticamente. Si está en otro proyecto, deberá hacer clic con el botón derecho en la caja de herramientas, hacer clic en "Elegir elementos ...", luego buscar ese .dll y agregar el control de esa manera. –

+0

Eso fue útil. Sabía que los UserControls subclase se mostrarían en la caja de herramientas, pero de alguna manera pasaron por alto que los controles regulares subclase aparecerían allí también. Gracias. – ErikE

Cuestiones relacionadas