Partilhar via


Interoperação WPF e Win32

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

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 do Win32 no conteúdo do WPF. Com essa técnica, você pode usar um controle Win32 personalizado existente no contexto de outro conteúdo do WPF e passar dados através dos limites.

Cada uma dessas técnicas é conceitualmente introduzida neste tópico. Para obter uma ilustração mais orientada a código da hospedagem WPF no Win32, consulte 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 Passo a passo: Hospedando um controle Win32 no WPF.

Projetos de Interoperação WPF

As APIs do 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 do WPF de um verdadeiro programa não gerenciado. No entanto, usando a opção /clr com o compilador do Microsoft Visual C++, você pode criar um programa misto gerenciado-não gerenciado onde 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++. Existem várias técnicas de divisão de projetos 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 seu executável C++ inclua essa DLL como referência.

  • Crie um executável C# para o conteúdo do WPF e faça com que ele faça referência a uma DLL C++ que contém 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 e escreva todo o seu WPF em 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 antiga sintaxe de sublinhado duplo (__gc) e fornecem uma sintaxe mais natural para código gerenciado em C++. Para saber mais sobre os recursos gerenciados em C++/CLI, consulte Component Extensions for Runtime Platforms.

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+. Isto tem uma série de 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 mais pormenorizadamente no tópico Technology Regions Overview.

Todos os elementos do WPF na tela são apoiados por um HWND. Ao criar um WPF Window, o WPF cria um HWND de topo e utiliza um HwndSource para colocar o Window e o seu conteúdo WPF dentro do HWND. O restante do conteúdo do WPF no aplicativo compartilha esse HWND singular. Uma exceção são os 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 passar da 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 filho HWND 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.

Hospedando conteúdo WPF em uma janela do Microsoft Win32

A chave para hospedar um WPF em uma janela do Win32 é a classe HwndSource. Esta classe envolve o conteúdo do WPF numa janela Win32, para que o conteúdo do WPF possa ser integrado à sua IU como uma janela filha. A abordagem a seguir combina o Win32 e WPF em um único aplicativo.

  1. Implemente seu conteú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 usados como um elemento raiz, como DockPanel ou Page. Em etapas subsequentes, essa classe é referida como a classe de conteúdo WPF, e instâncias da classe são referidas como 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 pode habilitá-lo para chamar código gerenciado alterando as configurações do projeto para incluir o sinalizador de compilador /clr (o escopo completo do que pode ser necessário para dar suporte a /clr compilação não é descrito neste tópico).

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

  4. Manipule a notificação do WM_CREATE no procedimento da janela.

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

    1. Crie um novo objeto HwndSource usando a janela pai HWND como o seu 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 RootVisual do objeto HwndSource.

    4. A propriedade Handle objeto HwndSource contém o identificador de janela (HWND). Para obter um HWND que possa ser usado na parte não gerida da sua aplicação, converta Handle.ToPointer() para um HWND.

  6. Implemente uma classe gerenciada que contenha um campo estático que contenha uma referência ao seu objeto de conteúdo WPF. Esta classe permite obter uma referência ao objeto de conteúdo WPF a partir do teu código Win32, mas, mais importante, impede que os teus HwndSource sejam coletados inadvertidamente pelo garbage collector.

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

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

Observação

Você pode fazer parte ou a totalidade da 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, se você produzir um conjunto separado e, em seguida, referenciá-lo. Embora você normalmente inclua um objeto Application como parte da compilação do XAML em um assembly, você não acaba usando esse Application como parte da interoperação, você apenas usa uma ou mais das classes raiz para arquivos XAML referidos pelo aplicativo e faz referência a suas classes parciais. O resto do procedimento é essencialmente semelhante ao acima descrito.

Cada uma dessas etapas é ilustrada por meio de código no tópico Passo a passo: Hospedando conteú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 classe HwndHost. Essa classe encapsula a janela em um elemento WPF que pode ser adicionado a uma árvore de elementos WPF. HwndHost também suporta APIs que permitem realizar tarefas como processar mensagens para a janela hospedada. O procedimento básico é:

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

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

  3. Nessa classe de anfitrião, substitua o método HwndHostBuildWindowCore. Retorne o HWND da janela hospedada. Você pode querer envolver o(s) controle(s) real(is) como uma subjanela da janela retornada; envolver os controles em uma janela de 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 relacionados ao tratamento de mensagens no limite de controle hospedado.

  4. Substitua os métodos HwndHostDestroyWindowCore 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. Em seu 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 também pode adicionar o conteúdo de interoperabilidade através de um comportamento em tempo de execução.

  6. Processar mensagens de janela selecionadas, como notificações de controle. Existem 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) em sua substituição do método HwndHostWndProc.

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

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

  7. Comunique-se com a janela hospedada usando platform invoke para chamar a função SendMessage não gerenciada.

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

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

Hwnds dentro do WPF

Você pode pensar em HwndHost como um controle especial. (Tecnicamente, HwndHost é uma classe derivada FrameworkElement, não uma classe derivada Control, 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 controle, que deve renderizar e processar a entrada. HwndHost geralmente se comporta como qualquer outro WPF FrameworkElement, embora existam algumas diferenças importantes em torno da saída (desenho e gráficos) e entrada (mouse e teclado) com base nas 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 FrameworkElement.FlowDirection, que altera o layout dos elementos dentro desse elemento na qualidade de elemento pai. No entanto, a maioria dessas propriedades não são mapeadas para possíveis equivalentes do Win32, mesmo que tais 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 por uma transformação.

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

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

  • HwndHost não respeita a área de recorte do seu objeto pai UIElement. Isso é potencialmente um problema se tentares colocar uma classe HwndHost dentro de uma 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 HwndHost, os eventos de entrada são encaminhados diretamente para o Win32.

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

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

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

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

Tabbing, Mnemônicos e Aceleradores

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

  • Tabulação entre componentes Win32 e WPF

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

As classes HwndHost e HwndSource fornecem implementações de IKeyboardInputSink, mas podem não lidar com todas as mensagens de entrada 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 pelo Win32 para tabulação, se houver.

Ver também