Controladores WNDProc personalizados en WPF • Pigeon Oleksii

Controladores WNDProc personalizados en WPF • Pigeon Oleksii

WNDPROC es una función de devolución de llamada que se encarga de los mensajes del sistema enviados desde el sistema operativo. A diferencia de WinForms, en WPF, no está directamente expuesto a usted, ya que está oculto debajo de la capa de abstracción del marco.

Sin embargo, hay momentos en que necesita procesar estos mensajes manualmente, por ejemplo, cuando se trata de la API de Windows. Veamos algunas formas en que podemos hacerlo.

Forma no mvvm

Podemos usar estos métodos de ayuda para ayudar a usar una de las ventanas, y luego agregarle un gancho:

var window = Application.Current.MainWindow;
var source = HwndSource.FromHwnd(new WindowInteropHelper(window).Handle);
source.AddHook(WndProc);

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    // Handle messages...

    return IntPtr.Zero;
}

En el ejemplo anterior, utilizamos la ventana principal de la aplicación como host, ya que generalmente permanece abierto mientras la aplicación se esté ejecutando. Puede especificar una ventana diferente a través de un parámetro en el FromVisual(...) método, pero luego asegúrate de llamar source.RemoveHook(...) y source.Dispose() Después de que hayas terminado.

El enfoque anterior sufre de no ser amigable con MVVM, el WndProc(...) El método, que probablemente se definirá en la capa del modelo, en realidad se acopla a una ventana. Como resultado, puede introducir una dependencia circular entre la vista y el modelo, lo que a menudo puede conducir a consecuencias indeseables.

Forma de MVVM

Como alternativa, podemos desacoplar el procesamiento de mensajes desde la capa de vista creando una ventana especializada de “esponja” invisible.

Convenientemente, System.Windows.Forms.NativeWindow Se adapta exactamente a este propósito: es una clase de ventana de bajo nivel que no hace nada más que escuchar los mensajes del sistema. Podemos usarlo agregando una referencia al System.Windows.Forms asamblea.

Así es como definí mi ventana de esponja:

public sealed class SpongeWindow : NativeWindow
{
    public event EventHandler<Message> WndProcCalled;

    public SpongeWindow()
    {
        CreateHandle(new CreateParams());
    }

    protected override void WndProc(ref Message m)
    {
        WndProcCalled?.Invoke(this, m);
        base.WndProc(ref m); // don't forget this line
    }
}

Asegúrate de no olvidar llamar base.WndProc(ref m)de lo contrario, la ventana no se inicializará correctamente.

Ahora, suponiendo que tengamos algún tipo de WndProcServicepodemos usar nuestra ventana de esponja así:

public class WndProcService : IDisposable
{
    private readonly SpongeWindow _sponge;

    public WndProcService()
    {
        _sponge = new SpongeWindow();
        _sponge.WndProcCalled += (s, e) => ProcessMessage(e);

        RegisterMessages();
    }

    private void RegisterMessages()
    {
        // Some Windows API calls here to register
        // window messages with sponge's handle.
    }

    private void ProcessMessage(Message message)
    {
        // Here we process incoming messages
    }

    public void Dispose()
    {
        _sponge.Dispose();
    }
}

Al manejar el WndProcCalled Evento, puede escuchar mensajes entrantes. Por lo general, desea llamar a algún método de API de Windows que suscribe una ventana a mensajes WNDPROC adicionales utilizando su identificación, por ejemplo, RegisterPowerSettingNotification(...) o RegisterHotKey(...).

Por ejemplo, si estuviéramos interesados ​​en registrar una tecla de acceso rápido global y escuchar sus eventos, podríamos hacerlo de esa manera: Solana Token Creator

public class GlobalHotkeyService : IDisposable
{
    (DllImport("user32.dll", EntryPoint = "RegisterHotKey", SetLastError = true))
    private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);

    private readonly SpongeWindow _sponge;

    public GlobalHotkeyService()
    {
        _sponge = new SpongeWindow();
        _sponge.WndProcCalled += (s, e) => ProcessMessage(e);

        RegisterMessages();
    }

    private void RegisterMessages()
    {
        // Register F1 as a global hotkey
        var id = 1;
        RegisterHotKey(_sponge.Handle, id, 0, 0x70);
    }

    private void ProcessMessage(Message message)
    {
        // Only interested in hotkey messages, so skip others
        if (message.Msg != 0x0312)
            return;

        // Get hotkey id
        var id = message.WParam.ToInt32();

        // Do something else...
    }

    public void Dispose()
    {
        _sponge.Dispose();
    }
}

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *