Interoperação Win32 e WPF

Este tópico fornece uma visão geral de como interoperar o código do Windows Presentation Foundation (WPF) e do Win32. O WPF fornece um ambiente rico 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 entre o Win32 e o WPF

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

  • Hospede o 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.

  • Hospede uma janela Win32 no conteúdo do WPF. Com essa técnica, você pode usar um controle Win32 personalizado existente no contexto de outro conteúdo WPF e passar dados através dos limites.

Essas duas técnicas são apresentadas conceitualmente neste tópico. Para obter uma ilustração mais orientada a código da hospedagem do WPF no Win32, consulte Demonstra Passo a passo: Hospedando conteúdo WPF no Win32. Para obter uma ilustração mais orientada a código da hospedagem do Win32 no WPF, consulte Demonstra Passo a passo: Hospedando um controle Win32 no WPF.

Projetos de interoperação do WPF

APIs WPF são código gerenciado, mas a maioria dos programas Win32 existentes são escritos em C++ não gerenciado. Não é possível chamar APIs WPF de um programa não gerenciado verdadeiro. No entanto, usando a /clr opção com o compilador do Microsoft Visual C++, você pode criar um programa misto gerenciado-não gerenciado onde você pode perfeitamente misturar 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 C# para o conteúdo WPF e faça referência a uma DLL C++ que contém o conteúdo Win32.

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

  • Não use XAML e escreva todo o seu WPF em código, criando a árvore de elementos a partir do Application.

Use a abordagem que funcionar 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 de sublinhado duplo (__gc) 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 tempo de execução.

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, você não pode misturar renderização WPF com renderização DirectX ou renderização GDI / GDI+. Isso tem várias implicações. Basicamente, para combinar esses modelos de renderização, você deve criar uma solução de interoperabilidade e usar segmentos de interoperação designados para cada modelo de renderização que optar 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 WPF, o WPF cria um HWND de nível superior e usa um HwndSource para colocar o Window e seu conteúdo WPF Windowdentro do HWND. O restante do conteúdo do WPF no aplicativo compartilha esse HWND singular. Uma exceção são menus, caixas de combinação suspensas e outros pop-ups. Esses elementos criam sua própria janela de nível superior, e é por isso que um menu WPF pode potencialmente ultrapassar a borda da janela HWND que o 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 a HWND é a transparência dentro e entre cada HWND. Isso também é discutido no tópico Visão geral das regiões de tecnologia.

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

A chave para hospedar um WPF em uma janela 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 seu conteúdo WPF (o elemento raiz de conteúdo) como uma classe gerenciada. Normalmente, a classe herda de uma das classes que podem conter vários elementos filho e/ou usados como um elemento raiz, como DockPanel ou Page. Nas etapas subsequentes, essa classe é chamada de classe de conteúdo WPF e as instâncias da classe são chamadas de objetos de conteú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 para chamar código gerenciado alterando as configurações do projeto para incluir o sinalizador do compilador (o /clr escopo completo do que pode ser necessário para dar suporte /clr à compilação não está descrito neste tópico).

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

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

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

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

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

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

    4. A HwndSource propriedade object Handle contém o identificador de janela (HWND). Para obter um HWND que você possa usar na parte não gerenciada de seu aplicativo, transmita Handle.ToPointer() para um HWND.

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

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

  8. Comunique-se com o objeto de conteú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 de classe de conteúdo WPF para a Etapa Um em XAML usando a classe parcial padrão da classe de conteúdo, se você produzir um assembly separado e, em seguida, fazer referência a ele. Embora você normalmente inclua um objeto como parte da compilação do XAML em um Application assembly, você não acaba usando isso Application como parte da interoperação, apenas usa uma ou mais das classes raiz para arquivos XAML referidos pelo aplicativo e faz referência a suas classes parciais. O restante do procedimento é basicamente semelhante ao descrito acima.

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

Hospedando uma janela do Microsoft Win32 no WPF

A chave para hospedar uma janela Win32 dentro de outro conteúdo WPF é a HwndHost classe. Essa classe encapsula a janela em um elemento WPF que pode ser adicionado a uma árvore de elementos WPF. HwndHost também oferece suporte a APIs que permitem que você execute tarefas como processar mensagens 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). Encontre um ponto apropriado e permitido na árvore de elementos onde a implementação pode ser adicionada HwndHost como um elemento filho. No restante dessas etapas, esse elemento será referenciado como elemento de reserva.

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

  3. Nessa classe de host, substitua o HwndHost método BuildWindowCore. Retorne o HWND da janela hospedada. Talvez você queira encapsular o(s) controle(s) real(is) como uma janela filho da janela retornada; encapsular os controles em uma janela do host fornece uma maneira simples para o conteúdo do WPF receber notificações dos controles. Essa técnica ajuda a corrigir alguns problemas do Win32 em relação à manipulação 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 as referências ao conteúdo hospedado, principalmente se você tiver criado referências a objetos não gerenciados.

  5. No seu arquivo code-behind, crie uma instância da classe que hospeda controles e torne-a filha 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. Processe mensagens de janela selecionadas, como notificações de controle. Há duas abordagens. Ambos fornecem acesso idêntico ao fluxo de mensagens, portanto sua escolha é principalmente 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.

    • Faça com que o elemento WPF de hospedagem processe as mensagens manipulando o MessageHook evento. Esse evento é gerado para cada mensagem que é enviada ao procedimento de janela principal da janela hospedada.

    • Não é possível processar mensagens de janelas que estão fora do processo usando WndProco .

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

Seguir essas cria um aplicativo que funciona com a entrada do mouse. Você pode adicionar suporte a guias 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 do Win32 no WPF.

Hwnds dentro do WPF

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

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. Isso inclui propriedades como , que altera o layout dos elementos dentro desse elemento como FrameworkElement.FlowDirectionum pai. No entanto, a maioria dessas propriedades não são mapeadas para possíveis equivalentes Win32, mesmo se tais equivalentes podem existir. Muitas dessas propriedades e seus significados são tão específicas da tecnologia de processamento para mapeamentos que não são práticas. Portanto, definir propriedades como FlowDirection on HwndHost não tem efeito.

  • HwndHost não pode ser girado, dimensionado, distorcido ou afetado de outra forma por uma Transformação.

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

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

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

Diferenças notáveis no comportamento de entrada

  • Em geral, enquanto os dispositivos de entrada têm escopo dentro da região Win32 hospedada, os eventos de entrada vão diretamente para o HwndHost Win32.

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

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

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

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

Uso da tecla TAB, mnemônicos e aceleradores

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

  • Tabulação entre componentes Win32 e WPF

  • Mnemônicos e aceleradores que funcionam quando o foco está em um componente do Win32 e quando está em um componente do WPF.

As HwndHost classes e HwndSource fornecem implementações do , mas podem não manipular todas as mensagens de IKeyboardInputSinkentrada desejadas para cenários mais avançados. Substitua os métodos apropriados para obter o comportamento de teclado desejado.

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

Confira também