Manipuladores WNDProc personalizados em WPF • Oleksii Pigeon

Manipuladores WNDProc personalizados em WPF • Oleksii Pigeon

O WNDPROC é uma função de retorno de chamada que cuida das mensagens do sistema enviadas do sistema operacional. Ao contrário do WinForms, no WPF, ele não é diretamente exposto a você, pois está escondido sob a camada de abstração da estrutura.

Há momentos, no entanto, quando você precisa processar essas mensagens manualmente, por exemplo, ao lidar com a API do Windows. Vejamos algumas maneiras pelas quais podemos fazê -lo.

Maneira não mvvm

Podemos usar esses métodos auxiliares especiais para obter o identificador de uma das janelas e, em seguida, adicionar um 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;
}

No exemplo acima, usamos a janela principal do aplicativo como host, pois normalmente permanece aberto enquanto o aplicativo estiver em execução. Você pode especificar uma janela diferente através de um parâmetro no FromVisual(...) método, mas depois ligue source.RemoveHook(...) e source.Dispose() Depois que você terminar.

A abordagem acima sofre de não ser amigável para MVVM-o WndProc(...) O método, que provavelmente será definido na camada do modelo, é realmente acoplado a uma janela. Como resultado, ele pode introduzir uma dependência circular entre a visão e o modelo, o que geralmente pode levar a consequências indesejáveis.

MVVM Way

Como alternativa, podemos dissociar o processamento de mensagens da camada de visualização criando uma janela “esponja” invisível especializada.

Convenientemente, System.Windows.Forms.NativeWindow se encaixa exatamente nesse objetivo – é uma classe de janela de baixo nível que não faz nada além de ouvir as mensagens do sistema. Podemos usá -lo adicionando uma referência ao System.Windows.Forms conjunto.

Aqui está como eu defini minha janela 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
    }
}

Certifique -se de não esquecer de ligar base.WndProc(ref m)caso contrário, a janela não inicializará corretamente.

Agora, assumindo que temos algum tipo de WndProcServicepodemos usar nossa janela de esponja como assim:

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();
    }
}

Lidando com o WndProcCalled Evento, você pode ouvir mensagens recebidas. Normalmente, você gostaria de ligar para algum método da API do Windows que assina uma janela para mensagens WNDPROC adicionais usando seu identificador, por exemplo, RegisterPowerSettingNotification(...) ou RegisterHotKey(...).

Por exemplo, se estivéssemos interessados ​​em registrar uma tecla de atalho global e ouvir seus eventos, poderíamos fazê -lo de tal maneira: 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 *