Compartilhar via


Interoperação WPF e Win32

Este tópico fornece uma visão geral de como interoperar o WPF (Windows Presentation Foundation) e o código Win32. O WPF fornece um ambiente avançado para a criação de aplicativos. No entanto, quando você tem um investimento substancial no código Win32, pode ser mais eficaz reutilizar parte desse código.

Noções básicas de interoperação do WPF e win32

Há duas técnicas básicas para interoperação entre o código WPF e o Win32.

  • Hospedar conteúdo do WPF em uma janela do Win32. Com essa técnica, você pode usar os recursos gráficos avançados do WPF dentro da estrutura de uma janela e aplicativo Win32 padrão.

  • Hospedar uma janela do Win32 em conteúdo WPF. Com essa técnica, você pode usar um controle Win32 personalizado existente no contexto de outros conteúdos do WPF e atravessar os limites para passar dados.

Cada uma dessas técnicas é introduzida conceitualmente neste tópico. Para obter uma ilustração mais orientada a código da hospedagem do WPF no Win32, consulte Passo a passo: hospedagem de conteúdo do WPF no Win32. Para obter uma ilustração mais orientada a código da hospedagem do Win32 no WPF, consulte Passo a passo: hospedando um controle Win32 no WPF.

Projetos de Interoperação do WPF

As APIs do WPF são managed code, mas a maioria dos programas Win32 atualmente existentes são escritos em C++ não-gerenciado. Você não pode chamar APIs do WPF de um programa verdadeiramente não gerenciado. No entanto, usando a opção /clr com o compilador do Microsoft Visual C++, você pode criar um programa não gerenciado misto, no qual você pode misturar perfeitamente chamadas de API gerenciadas e não gerenciadas.

Uma complicação no nível do projeto é que você não pode compilar arquivos XAML (Extensible Application Markup Language) em um projeto C++. Há várias técnicas de divisão de projeto para compensar isso.

  • Crie uma DLL C# que contenha todas as suas páginas XAML como um assembly compilado e, em seguida, faça com que o executável C++ inclua essa DLL como referência.

  • Crie um executável em C# para o conteúdo do WPF e faça com que ele faça referência a uma DLL C++ que contenha o conteúdo do Win32.

  • Use Load para carregar qualquer XAML em tempo de execução, em vez de compilar seu XAML.

  • Não use XAML de forma alguma e escreva todo o WPF no código, construindo a árvore de elementos a partir de Application.

Use qualquer abordagem que funcione melhor para você.

Observação

Se você não tiver usado C++/CLI antes, poderá notar algumas palavras-chave "novas", como gcnew e nullptr nos exemplos de código de interoperação. Essas palavras-chave substituem a sintaxe__gc de sublinhado duplo () mais antiga e fornecem uma sintaxe mais natural para código gerenciado em C++. Para saber mais sobre os recursos gerenciados do C++/CLI, consulte Extensões de Componente para Plataformas de Runtime.

Como o WPF usa Hwnds

Para aproveitar ao máximo a "interoperabilidade HWND" do WPF, você precisa entender como o WPF usa HWNDs. Para qualquer HWND, não é possível misturar a renderização WPF com a renderização DirectX ou GDI/GDI+. Isso tem várias implicações. Principalmente, para misturar esses modelos de renderização, você deve criar uma solução de interoperação e usar segmentos designados de interoperação para cada modelo de renderização que você escolher usar. Além disso, o comportamento de renderização cria uma restrição de "espaço aéreo" para o que sua solução de interoperação pode realizar. O conceito de "espaço aéreo" é explicado com mais detalhes no tópico Visão geral das Regiões de Tecnologia.

Todos os elementos do WPF na tela são, em última análise, apoiados por um HWND. Quando você cria um Window WPF, o WPF cria um HWND de nível superior e, então, usa um HwndSource para inserir o Window e seu conteúdo WPF dentro do HWND. O restante do conteúdo do WPF no aplicativo compartilha um único identificador HWND. Uma exceção são menus, listas suspensas de caixa de combinação e outros pop-ups. Esses elementos criam sua própria janela de nível superior, e é por isso que um menu WPF pode potencialmente passar pela borda da janela HWND que a contém. Quando você usa HwndHost para colocar um HWND dentro do WPF, o WPF informa ao Win32 como posicionar o novo HWND filho em relação ao WPF Window HWND.

Um conceito relacionado ao HWND é a transparência dentro e entre cada HWND. Isso também é discutido no tópico Visão geral das regiões de tecnologia.

Hospedar conteúdo do WPF em uma janela do Microsoft Win32

A chave para hospedar um WPF em uma janela do Win32 é a HwndSource classe. Essa classe encapsula o conteúdo do WPF em uma janela do Win32, para que o conteúdo do WPF possa ser incorporado à interface do usuário como uma janela filho. A abordagem a seguir combina o Win32 e o WPF em um único aplicativo.

  1. Implemente o conteúdo do WPF (o elemento raiz do conteúdo) como uma classe gerenciada. Normalmente, a classe herda de uma das classes que podem conter vários elementos filho e/ou usadas como um elemento raiz, como DockPanel ou Page. Nas etapas subsequentes, essa classe é conhecida como a classe de conteúdo do WPF e as instâncias da classe são conhecidas como objetos de conteúdo do WPF.

  2. Implemente um aplicativo do Windows com C++/CLI. Se você estiver começando com um aplicativo C++ não gerenciado existente, geralmente poderá habilitá-lo a chamar código gerenciado alterando as configurações do projeto para incluir o /clr sinalizador do compilador (o escopo completo do que pode ser necessário para dar suporte /clr à compilação não é descrito neste tópico).

  3. Defina o modelo de threading como STA (Single Threaded Apartment). O WPF usa esse modelo de threading.

  4. Trate a notificação WM_CREATE no seu procedimento de janela.

  5. Dentro do manipulador (ou uma função que o manipulador chama), faça o seguinte:

    1. Crie um novo objeto HwndSource com a janela pai HWND como parâmetro parent.

    2. Crie uma instância da classe de conteúdo do WPF.

    3. Atribua uma referência ao objeto de conteúdo do WPF à propriedade do HwndSource objeto RootVisual .

    4. A HwndSource propriedade do objeto Handle contém o identificador de janela (HWND). Para obter um HWND que você pode usar na parte não gerenciada do aplicativo, converta Handle.ToPointer() em um HWND.

  6. Implemente uma classe gerenciada que contenha um campo estático que contém uma referência ao objeto de conteúdo do WPF. Essa classe permite que você obtenha uma referência ao objeto de conteúdo do WPF a partir do código Win32, mas, mais importante, impede que seu HwndSource seja coletado inadvertidamente pelo garbage collector.

  7. Receba notificações do objeto de conteúdo do WPF anexando um manipulador a um ou mais eventos de objeto de conteúdo do WPF.

  8. Comunique-se com o objeto de conteúdo do WPF usando a referência armazenada no campo estático para definir propriedades, métodos de chamada etc.

Observação

** Você pode fazer parte ou toda a definição da classe de conteúdo do WPF para a Etapa Um em XAML usando a classe parcial padrão da classe de conteúdo, ao produzir um assembly separado e referenciá-lo depois. Embora você normalmente inclua um objeto Application como parte da compilação do XAML em um assembly, você não o utiliza como parte da interoperação; em vez disso, usa uma ou mais das classes raiz para os arquivos XAML referenciados pelo aplicativo e referencia suas classes parciais. O restante do procedimento é essencialmente semelhante ao descrito acima.

Cada uma dessas etapas é ilustrada por meio do código no tópico Passo a passo: Hospedagem de conteúdo do WPF no Win32.

Hospedar uma janela do Microsoft Win32 no WPF

A chave para hospedar uma janela do Win32 dentro de outro conteúdo do WPF é a classe HwndHost. Essa classe encapsula a janela em um elemento WPF que pode ser adicionado a uma árvore de elementos do WPF. HwndHost também dá suporte a APIs que permitem realizar tarefas como mensagens de processo para a janela hospedada. O procedimento básico é:

  1. Crie uma árvore de elementos para um aplicativo WPF (pode ser por meio de código ou marcação). Localize um ponto apropriado e permitido na árvore de elementos em que a HwndHost implementação possa ser adicionada como um elemento filho. No restante dessas etapas, esse elemento é chamado de elemento de reserva.

  2. Derivar de HwndHost para criar um objeto que detém o conteúdo do Win32.

  3. Nessa classe host, substitua o método HwndHostBuildWindowCore. Retorne o HWND da janela hospedada. Talvez você queira acomodar os controles reais como uma janela filha da janela retornada; acomodar os controles em uma janela de host fornece uma maneira simples para o conteúdo WPF receber notificações dos controles. Essa técnica ajuda a corrigir alguns problemas do Win32 relacionados ao tratamento de mensagens no limite de controle hospedado.

  4. Substitua os HwndHost métodos DestroyWindowCore e WndProc. A intenção aqui é processar a limpeza e remover referências ao conteúdo hospedado, especialmente se você criou referências a objetos não gerenciados.

  5. No arquivo code-behind, crie uma instância da classe de hospedagem de controle e torne-a um filho do elemento de reserva. Normalmente, você usaria um manipulador de eventos, como Loaded, ou usaria o construtor de classe parcial. Mas você também pode adicionar o conteúdo de interoperação por meio de um comportamento de runtime.

  6. Processar mensagens de janela selecionadas, como notificações de controle. Há duas abordagens. Ambos fornecem acesso idêntico ao fluxo de mensagens, portanto, sua escolha é em grande parte uma questão de conveniência de programação.

    • Implemente o processamento de mensagens para todas as mensagens (não apenas mensagens de desligamento) na substituição do HwndHost método WndProc.

    • Fazer com que o elemento WPF de hospedagem processe as mensagens manipulando o MessageHook evento. Esse evento é gerado sempre que uma mensagem é enviada à janela principal do procedimento da janela hospedada.

    • Você não pode processar mensagens de janelas que estão fora do processo usando WndProc.

  7. Comunique-se com a janela hospedada usando a invocação de plataforma para chamar a função não gerenciada SendMessage .

Seguir estas etapas cria um aplicativo que funciona com a entrada do mouse. Você pode adicionar suporte de tabulação para sua janela hospedada implementando a IKeyboardInputSink interface.

Cada uma dessas etapas é ilustrada por meio do código no tópico Passo a passo: hospedando um controle Win32 no WPF.

Hwnds dentro do WPF

Você pode pensar HwndHost como um controle especial. (Tecnicamente, HwndHost é uma FrameworkElement classe derivada, não uma Control classe derivada, mas pode ser considerada um controle para fins de interoperação.) HwndHost abstrai a natureza subjacente do Win32 do conteúdo hospedado, de modo que o restante do WPF considere o conteúdo hospedado como outro objeto semelhante a um controle, que deve renderizar e processar a entrada. HwndHost geralmente se comporta como qualquer outro WPF FrameworkElement, embora haja algumas diferenças importantes em relação à saída (desenho e elementos gráficos) e entrada (mouse e teclado) com base nas limitações do que os HWNDs subjacentes podem dar suporte.

Diferenças notáveis no comportamento de saída

  • FrameworkElement, que é a HwndHost classe base, tem algumas propriedades que implicam alterações na interface do usuário. Elas incluem propriedades como FrameworkElement.FlowDirection, que alteram o layout dos elementos dentro do elemento no papel de pai. No entanto, a maioria dessas propriedades não são mapeadas para possíveis equivalentes win32, mesmo que esses equivalentes possam existir. Muitas dessas propriedades e seus significados são muito específicos da tecnologia de renderização para que os mapeamentos sejam práticos. Portanto, definir propriedades como FlowDirection em HwndHost não tem efeito.

  • HwndHost não pode ser girada, dimensionada, distorcida ou afetada de outra forma por uma Transformação.

  • HwndHost não dá suporte à Opacity propriedade (mistura alfa). Se o conteúdo dentro das HwndHost operações de execução System.Drawing que incluem informações alfa, isso não é uma violação, mas o HwndHost como um todo só dá suporte a Opacidade = 1,0 (100%).

  • HwndHost aparecerá em cima de outros elementos do WPF na mesma janela de nível superior. No entanto, um menu gerado por ToolTip ou ContextMenu é uma janela independente de nível superior e, portanto, se comportará corretamente com HwndHost.

  • HwndHost não respeita a região de recorte do elemento pai UIElement. Isso é potencialmente um problema se você tentar colocar uma HwndHost classe dentro de uma região de rolagem ou Canvas.

Diferenças notáveis no comportamento de entrada

  • Em geral, enquanto os dispositivos de entrada estão delimitados na região hospedada do HwndHost Win32, os eventos de entrada vão diretamente para o Win32.

  • Enquanto o mouse estiver sobre o HwndHost, seu aplicativo não receberá eventos de mouse do WPF e o valor da propriedade WPF IsMouseOver será false.

  • Embora tenha o foco do HwndHost teclado, seu aplicativo não receberá eventos de teclado WPF e o valor da propriedade IsKeyboardFocusWithin WPF será false.

  • Quando o foco estiver dentro do HwndHost e mudar para outro controle dentro do HwndHost, seu aplicativo não receberá os eventos GotFocus ou LostFocus do WPF.

  • As propriedades e os eventos de caneta relacionados são análogos e não relatam informações enquanto a caneta acabou HwndHost.

Tabulação, Mnemônicos e Aceleradores

As IKeyboardInputSink interfaces e IKeyboardInputSite as interfaces permitem que você crie uma experiência de teclado perfeita para aplicativos WPF e Win32 mistos:

  • Navegando com a tecla Tab entre componentes Win32 e WPF

  • Mnemonics e aceleradores que funcionam quando o foco está dentro de um componente Win32 e quando ele está dentro de um componente WPF.

As classes HwndHost e HwndSource ambas oferecem implementações de IKeyboardInputSink, mas podem não lidar com todas as mensagens de entrada para cenários mais avançados que você deseja. Substitua os métodos apropriados para obter o comportamento do teclado desejado.

As interfaces fornecem suporte apenas para o que acontece na transição entre as regiões WPF e Win32. Na região Win32, o comportamento de tabulação é totalmente controlado pela lógica de tabulação implementada pelo Win32, se houver alguma.

Consulte também