Compartilhando Loops de Mensagem entre Win32 e WPF
Este tópico descreve como implementar um loop de mensagem para interoperação com Windows Presentation Foundation (WPF), seja utilizando uma exposição de loop de mensagem já existente em Dispatcher ou criando um loop de mensagem separado no lado Win32 de seu código de interoperação.
ComponentDispatcher e o Loop de Mensagem
Um cenário comum para interoperação e suporte a evento de teclado é implementar IKeyboardInputSink, ou fazer subclassing de classes que já implementam IKeyboardInputSink, como HwndSource, ou HwndHost. Entretanto, o suporte ao coletor de teclado não trata todas as possíveis necessidades de loop de mensagem que você pode ter quando enviando e recebendo mensagens entre os limites de interoperação. Para ajudar a formalizar a arquitetura de loop de mensagem, Windows Presentation Foundation (WPF) fornece a classe ComponentDispatcher, que define um protocolo simples a ser seguido para um loop de mensagem.
ComponentDispatcher é uma classe estática que expõe diversos membros. O escopo de cada método é implicitamente amarrado ao segmento chamador. Um loop de mensagem precisa chamar algumas das APIs em instantes críticos (como definido na seção seguinte).
ComponentDispatcher fornece eventos que outros componentes (como o coletor de teclado) podem aguardar. A classe Dispatcher chama todos os métodos do ComponentDispatcher em uma sequência apropriada. Se você estiver implementando seu próprio loop de mensagem, seu código será responsável por chamar os métodos de ComponentDispatcher de maneira similar.
Chamar métodos de ComponentDispatcher em um segmento irá chamar somente tratadores de evento registrados naquele segmento.
Escrevendo Loops de Mensagem
A seguir uma lista de verificação dos membros de ComponentDispatcher que irá usar caso escreva seu próprio loop de mensagem:
PushModal: seu loop de mensagem deve chamá-lo para indicar que o segmento é modal.
PopModal: seu loop de mensagem deve chamá-lo para indicar que o segmento mudou para não modal.
RaiseIdle: o loop de mensagem deve chamar isso para indicar que ComponentDispatcher deverá aumentar o ThreadIdle evento. ComponentDispatcher não irá disparar ThreadIdle Se IsThreadModal é true, mas loops de mensagem podem optar por telefonar RaiseIdle mesmo se ComponentDispatcher não pode responder a ele no estado de janela restrito.
RaiseThreadMessage: seu loop de mensagem deve chamá-lo para indicar que uma nova mensagem está disponível. O valor de retorno indica caso um ouvinte de evento ComponentDispatcher tratou a mensagem. Se RaiseThreadMessage retornar true (tratado), o despachante não deve fazer nada com a mensagem. Se o valor de retorno for false, espera-se que o despachante chame a função Win32 TranslateMessage, e depois DispatchMessage.
Usando ComponentDispatcher e Tratamento de Mensagem Existente
A seguir uma lista de verificação dos membros de ComponentDispatcher que irá usar caso se baseie no loop de mensagem WPF inerente.
IsThreadModal: Retorna se o aplicativo estiver modal (por exemplo, um loop de mensagem modal foi pressionado). ComponentDispatcher pode controlar esse estado porque a classe mantém uma contagem de PushModal e PopModal chamadas a partir do loop de mensagem.
Eventos ThreadFilterMessage e ThreadPreprocessMessage seguem as regras padrões para invocações delegadas. Delegados são invocados em uma ordem não especificada, e todos os delegados são invocados mesmo que o primeiro marque a mensagem como tratada.
ThreadIdle: indica um time apropriado e eficiente de ocioso processamento (há nenhum Outros mensagens para o segmento pendentes). ThreadIdle não será gerado se o thread está restrito.
ThreadFilterMessage: disparado para todas as mensagens que o bombeador processa.
ThreadPreprocessMessage: disparado para todas as mensagens não tratadas durante ThreadFilterMessage.
Uma mensagem é considerada tratada se depois do evento ThreadFilterMessage ou do evento ThreadPreprocessMessage, o parâmetro handled passado por referência nos dados do evento for true. Tratadores de evento devem ignorar uma mensagem se handled for true, porque isto significa que um tratador diferente a tratou primeiro. Tratadores de eventos para ambos eventos podem modificar a mensagem. O dispatcher deve Despache mensagem modificada e não a mensagem original inalterada. ThreadPreprocessMessage é enviada para todos os ouvintes, mas a intenção de arquitetura é que somente a janela de nível superior que contém o HWND na qual as mensagens direcionadas devem chamar o código em resposta à mensagem.
Como HwndSource Trata Eventos ComponentDispatcher
Se o HwndSource for uma janela alto nível (sem HWND pai), ele irá registrar com ComponentDispatcher. Se ThreadPreprocessMessage for disparada, e se a mensagem for direcionada ao HwndSource ou janelas filho, HwndSource chama sua sequência coletora de teclado IKeyboardInputSink.TranslateAccelerator, TranslateChar, OnMnemonic.
Se o HwndSource não for uma janela alto nível (sem HWND pai), não haverá tratamento. Espera-se que somente a janela alto nível faça o tratamento, e que exista uma janela alto nível com suporte à coletor de teclado como parte de qualquer cenário de interoperação.
Se WndProc em um HwndSource for chamado sem o método coletor de teclado apropriado ser chamado primeiro, seu aplicativo receberá eventos de teclado superiores como KeyDown. Entretanto, nenhum método coletor de teclado será chamado, o que foge aos recursos desejáveis de modelos de entrada de teclado como suporte à teclas de acesso. Isto pode acontecer porque o loop de mensagem não notificou adequadamente o segmento no ComponentDispatcher, ou porque o HWND pai não invocou as respostas de coleta de teclado apropriadas.
Uma mensagem que vai para o coletor de teclado pode não ser enviada ao HWND se você adicionou ganchos para aquela mensagem usando o método AddHook. A mensagem pode ser sido tratada diretamente no nível do bombeador de mensagens e não submetido à função DispatchMessage.
Consulte também
Conceitos
Visão geral sobre interoperabilidade entre WPF e Win32