Share via


Visão geral dos suplementos do WPF

O .NET Framework inclui um modelo de suplemento que os desenvolvedores podem usar para criar aplicativos que oferecem suporte à extensibilidade de suplemento. Esse modelo permite a criação de suplementos que integram e estendem a funcionalidade do aplicativo. Em alguns cenários, os aplicativos também precisam exibir interfaces de usuário fornecidas por suplementos. Este tópico mostra como o WPF aumenta o modelo de suplemento do .NET Framework para habilitar esses cenários, a arquitetura por trás dele, seus benefícios e suas limitações.

Pré-requisitos

É necessária familiaridade com o modelo de suplemento do .NET Framework. Para obter mais informações, consulte Suplementos e extensibilidade.

Visão geral dos suplementos

Para evitar as complexidades da recompilação do aplicativo e de sua reimplantação para incorporação de uma nova funcionalidade, os aplicativos implementam mecanismos de extensibilidade que permitem aos desenvolvedores (fornecedores principais e terceiros) criar outros aplicativos que se integrem a eles. A maneira mais comum para dar suporte a esse tipo de extensibilidade é através do uso de suplementos (também conhecidos como "complementos" e "plug-ins"). Exemplos de aplicativos reais que expõem extensibilidade com suplementos incluem:

  • Complementos do Internet Explorer.

  • Plug-ins do Windows Media Player.

  • Suplementos do Visual Studio.

Por exemplo, o modelo de suplemento do Windows Media Player permite que desenvolvedores de terceiros implementem "plug-ins" que estendam o Windows Media Player de várias maneiras, incluindo a criação de decodificadores e codificadores para formatos de mídia que não têm suporte nativo pelo Windows Media Player (por exemplo, DVD, MP3), efeitos de áudio e capas. Cada modelo de suplemento é desenvolvido para expor a funcionalidade que é exclusiva de um aplicativo, embora haja várias entidades e comportamentos que são comuns a todos os modelos de suplemento.

As três principais entidades das soluções típicas de extensibilidade de suplementos são contratos, suplementos e aplicativos host. Contratos definem como os suplementos se integram a aplicativos host de duas maneiras:

  • Suplementos se integram à funcionalidade que é implementada por aplicativos host.

  • Aplicativos host expõem a funcionalidade que os suplementos se integrem a ela.

Para que suplementos possam ser usados, é necessário que eles sejam localizados e carregados em tempo de execução pelos aplicativos host. Consequentemente, os aplicativos que dão suporte a suplementos têm as seguintes responsabilidades adicionais:

  • Descoberta: encontrar suplementos que aderem a contratos com suporte por aplicativos host.

  • Ativação: carregar, executar e estabelecer comunicação com suplementos.

  • Isolamento: usar domínios do aplicativo ou processos para estabelecer limites de isolamento que protegem aplicativos de possíveis problemas de execução e segurança com suplementos.

  • Comunicação: permitir, por meio da chamada de métodos e transmissão de dados, que suplementos e aplicativos host se comuniquem uns com os outros através de limites de isolamento.

  • Gerenciamento de tempo de vida: Carregando e descarregando domínios do aplicativo e processos de uma maneira limpa e previsível (consulte Domínios do aplicativo).

  • Controle de versão: garantir que os aplicativos host e suplementos possam se comunicar quando novas versões de qualquer um deles forem criadas.

Por fim, desenvolver um modelo de suplemento robusto é uma tarefa não trivial. Por esse motivo, o .NET Framework fornece uma infraestrutura para a criação de modelos de suplemento.

Observação

Para obter informações mais detalhadas sobre suplementos, consulte Suplementos e extensibilidade.

Visão geral de modelo de suplemento do .NET Framework

O modelo de suplemento do System.AddIn .NET Framework, encontrado no namespace, contém um conjunto de tipos projetados para simplificar o desenvolvimento da extensibilidade do suplemento. A unidade fundamental do modelo de suplemento do .NET Framework é o contrato, que define como um aplicativo host e um suplemento se comunicam entre si. Um contrato é exposto a um aplicativo host utilizando uma exibição do contrato específica do aplicativo host. Da mesma forma, uma exibição do contrato específica do suplemento é exposta ao suplemento. Um adaptador é usado para permitir que um aplicativo host e um suplemento se comuniquem entre suas respectivas exibições do contrato. Contratos, exibições e adaptadores são referidos como segmentos, enquanto um conjunto de segmentos relacionados constitui um pipeline. Os pipelines são a base sobre a qual o modelo de suplemento do .NET Framework oferece suporte à descoberta, ativação, isolamento de segurança, isolamento de execução (usando domínios e processos de aplicativo), comunicação, gerenciamento de tempo de vida e controle de versão.

A soma desse suporte permite aos desenvolvedores compilar suplementos que se integram com a funcionalidade de um aplicativo host. No entanto, alguns cenários exigem que os aplicativos host exibam interfaces de usuário fornecidas por suplementos. Como cada tecnologia de apresentação no .NET Framework tem seu próprio modelo para implementar interfaces de usuário, o modelo de suplemento do .NET Framework não oferece suporte a nenhuma tecnologia de apresentação específica. Em vez disso, o WPF estende o modelo de suplemento do .NET Framework com suporte de interface do usuário para suplementos.

Suplementos do WPF

O WPF, em conjunto com o modelo de suplemento do .NET Framework, permite que você aborde uma ampla variedade de cenários que exigem que os aplicativos host exibam interfaces de usuário de suplementos. Em particular, esses cenários são abordados pelo WPF com os dois modelos de programação a seguir:

  1. O suplemento retorna uma interface do usuário. Um suplemento retorna uma interface do usuário para o aplicativo host por meio de uma chamada de método, conforme definido pelo contrato. Esse cenário é utilizado nos seguintes casos:

    • A aparência de uma interface do usuário retornada por um suplemento depende de dados ou condições que existem apenas em tempo de execução, como relatórios gerados dinamicamente.

    • A interface do usuário para serviços fornecidos por um suplemento é diferente da interface do usuário dos aplicativos host que podem usar o suplemento.

    • O suplemento executa principalmente um serviço para o aplicativo host e relata o status para o aplicativo host com uma interface do usuário.

  2. O suplemento é uma interface do usuário. Um suplemento é uma interface do usuário, conforme definido pelo contrato. Esse cenário é utilizado nos seguintes casos:

    • Um suplemento não fornece nenhum serviço além de ser exibido, por exemplo, um anúncio.

    • A interface do usuário para serviços fornecidos por um suplemento é comum a todos os aplicativos host que podem usar esse suplemento, como uma calculadora ou um seletor de cores.

Esses cenários exigem que os objetos da interface do usuário possam ser passados entre domínios de aplicativo host e de aplicativo-suplemento. Como o modelo de suplemento do .NET Framework depende da comunicação remota para se comunicar entre domínios de aplicativo, os objetos que são passados entre eles devem ser remotamente.

Um objeto remoto é uma instância de uma classe que satisfaz uma ou mais das condições a seguir:

Observação

Para obter mais informações sobre a criação de objetos do .NET Framework, consulte Tornando objetos remotas.

Os tipos de interface do usuário do WPF não são remotamente. Para resolver o problema, o WPF estende o modelo de suplemento do .NET Framework para permitir que a interface do usuário do WPF criada por suplementos seja exibida a partir de aplicativos host. Esse suporte é fornecido pelo WPF por dois tipos: a INativeHandleContract interface e dois métodos estáticos implementados pela FrameworkElementAdapters classe: ContractToViewAdapter e ViewToContractAdapter. Em um nível elevado, esses tipos e métodos são usados da seguinte maneira:

  1. O WPF requer que as interfaces de usuário fornecidas pelos suplementos sejam classes derivadas direta ou indiretamente do , como formas, controles, controles de usuário, painéis de FrameworkElementlayout e páginas.

  2. Sempre que o contrato declara que uma interface do usuário será passada entre o suplemento e o aplicativo host, ela deve ser declarada como uma (não uma ); INativeHandleContract é uma FrameworkElementINativeHandleContract representação remotamente da interface do usuário do suplemento que pode ser passada através dos limites de isolamento.

  3. Antes de ser passado do domínio de aplicativo do suplemento, um é empacotado como um FrameworkElementINativeHandleContract chamando ViewToContractAdapter.

  4. Depois de ser passado para o domínio do aplicativo do aplicativo host, o deve ser reempacotado INativeHandleContract como um FrameworkElement chamando ContractToViewAdapter.

Como INativeHandleContract, ContractToViewAdaptere ViewToContractAdapter são usados depende do cenário específico. As seções a seguir fornecem detalhes para cada modelo de programação.

O suplemento retorna uma interface do usuário

Para que um suplemento retorne uma interface do usuário para um aplicativo host, é necessário o seguinte:

  1. O aplicativo host, o suplemento e o pipeline devem ser criados, conforme descrito pela documentação de Suplementos e Extensibilidade do .NET Framework.

  2. O contrato deve ser implementado IContract e, para retornar uma interface do usuário, o contrato deve declarar um método com um valor de retorno do tipo INativeHandleContract.

  3. A interface do usuário que é passada entre o suplemento e o aplicativo host deve derivar direta ou indiretamente do FrameworkElement.

  4. A interface do usuário retornada pelo suplemento deve ser convertida de um para um FrameworkElementINativeHandleContract antes de cruzar o limite de isolamento.

  5. A interface do usuário retornada deve ser convertida de um para um INativeHandleContractFrameworkElement depois de cruzar o limite de isolamento.

  6. O aplicativo host exibe o FrameworkElementarquivo .

Para obter um exemplo que demonstra como implementar um suplemento que retorna uma interface do usuário, consulte Criar um suplemento que retorna uma interface do usuário.

O suplemento é uma interface do usuário

Quando um suplemento é uma interface do usuário, os seguintes itens são necessários:

  1. O aplicativo host, o suplemento e o pipeline devem ser criados, conforme descrito pela documentação de Suplementos e Extensibilidade do .NET Framework.

  2. A interface de contrato para o suplemento deve implementar INativeHandleContract.

  3. O suplemento que é passado para o aplicativo host deve derivar direta ou indiretamente do FrameworkElement.

  4. O suplemento deve ser convertido de um para um FrameworkElementINativeHandleContract antes de cruzar o limite de isolamento.

  5. O suplemento deve ser convertido de um para um INativeHandleContractFrameworkElement depois de cruzar o limite de isolamento.

  6. O aplicativo host exibe o FrameworkElementarquivo .

Para obter um exemplo que demonstra como implementar um suplemento que é uma interface do usuário, consulte Criar um suplemento que é uma interface do usuário.

Retornando múltiplas interfaces do usuário de um suplemento

Os suplementos geralmente fornecem várias interfaces de usuário para exibição de aplicativos host. Por exemplo, considere um suplemento que é uma interface do usuário que também fornece informações de status para o aplicativo host, também como uma interface do usuário. Um suplemento como esse pode ser implementado pelo uso de uma combinação de técnicas de ambos os modelos O suplemento retorna uma interface do usuário e O suplemento é uma interface do usuário.

Suplementos e aplicativos de navegação XAML

Nos exemplos até agora, o aplicativo host tem sido um aplicativo autônomo instalado. Mas os aplicativos de navegador XAML (XBAPs) também podem hospedar suplementos, embora com os seguintes requisitos adicionais de compilação e implementação:

  • O manifesto do aplicativo XBAP deve ser configurado especialmente para fazer download do pipeline (pastas e assemblies) e do assembly de suplemento para o cache do aplicativo ClickOnce na máquina cliente, na mesma pasta que o XBAP.

  • O código XBAP para descobrir e carregar suplementos deve usar o cache do aplicativo ClickOnce para o XBAP como o pipeline e o local do suplemento.

  • O XBAP deve carregar o suplemento em um contexto de segurança especial se o suplemento fizer referência a arquivos soltos localizados no site de origem; quando hospedados por XBAPs, os suplementos só podem fazer referência a arquivos soltos localizados no site de origem do aplicativo host.

Essas tarefas são descritas detalhadamente nas subseções a seguir.

Configurar o pipeline e o suplemento para a implantação do ClickOnce

Os XBAPs são baixados e executados a partir de uma pasta segura no cache de implantação do ClickOnce. Para que um XBAP hospede um suplemento, o pipeline e o assembly do suplemento também devem ser baixados para a pasta segura. Para fazer isso, você precisa configurar o manifesto do aplicativo para incluir ambos o assembly do suplemento e do pipeline para baixar. Isso é feito mais facilmente no Visual Studio, embora o pipeline e o assembly de suplemento precisem estar na pasta raiz do projeto XBAP do host para que o Visual Studio detecte os assemblies de pipeline.

Consequentemente, a primeira etapa é criar o pipeline e o assembly de suplemento para a raiz do projeto XBAP, definindo a saída de compilação de cada assembly de pipeline e projetos de montagem de suplemento. A tabela a seguir mostra os caminhos de saída de compilação para projetos de assembly de pipeline e projeto de assembly de suplemento que estão na mesma solução e pasta raiz que o projeto XBAP host.

Tabela 1: caminhos de saída de build para os assemblies do pipeline hospedados por um XBAP

Projeto de assembly do pipeline Caminho de saída da compilação
Contrato ..\HostXBAP\Contracts\
Exibição do suplemento ..\HostXBAP\AddInViews\
Adaptador no lado do suplemento ..\HostXBAP\AddInSideAdapters\
Adaptador no lado do host ..\HostXBAP\HostSideAdapters\
Suplemento ..\HostXBAP\AddIns\WPFAddIn1

A próxima etapa é especificar os assemblies de pipeline e o assembly de suplemento como os arquivos de conteúdo XBAPs no Visual Studio fazendo o seguinte:

  1. Incluindo o assembly do pipeline e do suplemento no projeto clicando com o botão direito do mouse em cada pasta de pipeline no Gerenciador de Soluções e escolhendo Incluir no Projeto.

  2. Definindo a Ação de Build de cada assembly do pipeline e assembly do suplemento para Conteúdo da janela Propriedades.

A etapa final é configurar o manifesto do aplicativo para incluir os arquivos do assembly do pipeline e o arquivo do assembly do suplemento para download. Os arquivos devem estar localizados em pastas na raiz da pasta no cache ClickOnce que o aplicativo XBAP ocupa. A configuração pode ser obtida no Visual Studio fazendo o seguinte:

  1. Clique com o botão direito do mouse no projeto XBAP, clique em Propriedades, clique em Publicar e, em seguida, clique no botão Arquivos de aplicativo.

  2. Na caixa de diálogo Arquivos de Aplicativo, defina o Status da Publicação de cada DLL de suplemento e de pipeline a Incluir (Auto)e defina o Grupo de Download para cada DLL de pipeline e de suplemento para (Obrigatório).

Usando o pipeline e o suplemento da base de aplicativo

Quando o pipeline e o suplemento são configurados para a implantação do ClickOnce, eles são baixados para a mesma pasta de cache do ClickOnce que o XBAP. Para usar o pipeline e o suplemento do XBAP, o código XBAP deve obtê-los da base do aplicativo. Os vários tipos e membros do modelo de suplemento do .NET Framework para usar pipelines e suplementos fornecem suporte especial para esse cenário. Em primeiro lugar, o caminho é identificado pelo valor de ApplicationBase enumeração. Você pode usar esse valor com sobrecargas de membros de suplemento pertinentes para usar pipelines que incluem o seguinte:

Acessar o site de origem do host

Para garantir que um suplemento possa referenciar arquivos do site de origem, o suplemento deve ser carregado com isolamento de segurança que é equivalente ao do aplicativo host. Esse nível de segurança é identificado pelo AddInSecurityLevel.Host valor de enumeração e passado para o Activate método quando um suplemento é ativado.

Arquitetura de suplemento do WPF

No nível mais alto, como vimos, o WPF permite que os suplementos do .NET Framework implementem interfaces de usuário (que derivam direta ou indiretamente de FrameworkElement) usando INativeHandleContract, ViewToContractAdapter e ContractToViewAdapter. O resultado é que o aplicativo host é retornado um FrameworkElement que é exibido da interface do usuário no aplicativo host.

Para cenários de suplemento de interface do usuário simples, isso é o máximo de detalhes que um desenvolvedor precisa. Para cenários mais complexos, particularmente aqueles que tentam utilizar serviços WPF adicionais, como layout, recursos e associação de dados, é necessário um conhecimento mais detalhado de como o WPF estende o modelo de suplemento do .NET Framework com suporte à interface do usuário para entender seus benefícios e limitações.

Fundamentalmente, o WPF não passa uma interface do usuário de um suplemento para um aplicativo host; em vez disso, o WPF passa o identificador de janela Win32 para a interface do usuário usando a interoperabilidade do WPF. Como tal, quando uma interface do usuário de um suplemento é passada para um aplicativo host, ocorre o seguinte:

  • No lado do suplemento, o WPF adquire um identificador de janela para a interface do usuário que será exibido pelo aplicativo host. O identificador de janela é encapsulado por uma classe WPF interna que deriva de HwndSource e implementa INativeHandleContract. Uma instância dessa classe é retornada e é empacotada do ViewToContractAdapter domínio de aplicativo do suplemento para o domínio de aplicativo do aplicativo host.

  • No lado do aplicativo host, o WPF reempacota o como uma classe WPF interna que deriva HwndSource e HwndHost consome INativeHandleContract. Uma instância dessa classe é retornada pelo ContractToViewAdapter aplicativo host.

HwndHost existe para exibir interfaces de usuário, identificadas por identificadores de janela, a partir de interfaces de usuário WPF. Para obter mais informações, consulte Interoperação Win32 e WPF.

Em resumo, , e existem para permitir que o identificador de janela para uma interface do usuário do WPF seja passado de um suplemento para um aplicativo host, INativeHandleContractViewToContractAdapteronde ele é encapsulado por um HwndHost e ContractToViewAdapter exibido a interface do usuário do aplicativo host.

Observação

Como o aplicativo host obtém um , o aplicativo host não pode converter o objeto retornado pelo para o tipo que ele é implementado como pelo ContractToViewAdapter suplemento (por exemplo, um ).HwndHostUserControl

Por sua natureza, HwndHost tem certas limitações que afetam como os aplicativos host podem usá-los. No entanto, o WPF se estende HwndHost com vários recursos para cenários de suplemento. Essas limitações e benefícios são descritos abaixo.

Benefícios de suplementos do WPF

Como as interfaces de usuário do suplemento WPF são exibidas de aplicativos host usando uma classe interna derivada do , essas interfaces de usuário são restritas pelos recursos de serviços de interface do usuário do WPF, como layout, renderização, associação de HwndHostHwndHost dados, estilos, modelos e recursos. No entanto, o WPF aumenta sua subclasse interna HwndHost com recursos adicionais que incluem o seguinte:

  • Tabulação entre a interface do usuário de um aplicativo host e a interface do usuário de um suplemento. Observe que o modelo de programação "suplemento é uma interface do usuário" requer que o adaptador do lado do suplemento seja substituído QueryContract para habilitar a tabulação, independentemente de o suplemento ser totalmente confiável ou parcialmente confiável.

  • Honrando os requisitos de acessibilidade para interfaces de usuário de suplemento que são exibidas a partir de interfaces de usuário de aplicativo host.

  • Permitindo que aplicativos WPF sejam executados com segurança em vários cenários de domínio de aplicativo.

  • Impedir o acesso ilegal a identificadores de janela de interface do usuário de suplemento quando suplementos são executados com isolamento de segurança (ou seja, uma caixa de proteção de segurança de confiança parcial). A chamada ViewToContractAdapter garante essa segurança:

    • Para o modelo de programação "add-in returns a UI", a única maneira de passar o identificador de janela para uma interface do usuário de suplemento através do limite de isolamento é chamar ViewToContractAdapter.

    • Para o modelo de programação "add-in is a UI", é necessária a substituição do adaptador do lado do suplemento e a chamada (conforme mostrado nos exemplos anteriores), assim como chamar a ViewToContractAdapter implementação do QueryContract adaptador do lado do suplemento a QueryContract partir do adaptador do lado do host.

  • Fornecer proteção contra múltiplas execuções de domínio do aplicativo. Devido a limitações com domínios do aplicativo, as exceções sem tratamento que forem lançadas em domínios do aplicativo do suplemento fazem com que todo o aplicativo falhe, mesmo com a existência do limite de isolamento. No entanto, o WPF e o modelo de suplemento do .NET Framework fornecem uma maneira simples de contornar esse problema e melhorar a estabilidade do aplicativo. Um suplemento WPF que exibe uma interface do usuário cria um para o thread no qual o domínio do aplicativo é executado, se o aplicativo host for um Dispatcher aplicativo WPF. Você pode detectar todas as exceções não tratadas que ocorrem no domínio do aplicativo manipulando o UnhandledException evento do suplemento DispatcherWPF . Você pode obter o DispatcherCurrentDispatcher da propriedade.

Limitações de suplementos WPF

Além dos benefícios que o WPF adiciona aos comportamentos padrão fornecidos pelo HwndSource, HwndHoste pelos identificadores de janela, também há limitações para interfaces de usuário de suplemento que são exibidas a partir de aplicativos host:

  • As interfaces de usuário do suplemento exibidas de um aplicativo host não respeitam o comportamento de recorte do aplicativo host.

  • O conceito de espaço aéreo em cenários de interoperabilidade também se aplica a suplementos (consulte Visão geral das regiões de tecnologia).

  • Os serviços de interface do usuário de um aplicativo host, como herança de recursos, associação de dados e comandos, não estão automaticamente disponíveis para interfaces de usuário de suplemento. Para fornecer esses serviços para o suplemento, você precisa atualizar o pipeline.

  • Uma interface do usuário do suplemento não pode ser girada, dimensionada, distorcida ou afetada por uma transformação (consulte Visão geral de transformações).

  • O conteúdo dentro das interfaces de usuário do suplemento que é renderizado por operações de desenho do System.Drawing namespace pode incluir mistura alfa. No entanto, uma interface do usuário do suplemento e a interface do usuário do aplicativo host que a contém devem ser 100% opacas; em outras palavras, a Opacity propriedade em ambos deve ser definida como 1.

  • Se a AllowsTransparency propriedade de uma janela no aplicativo host que contém uma interface do usuário do suplemento estiver definida como true, o suplemento ficará invisível. Isso é verdadeiro mesmo se a interface do usuário do suplemento for 100% opaca (ou seja, a Opacity propriedade tiver um valor de 1).

  • Uma interface do usuário do suplemento deve aparecer sobre outros elementos do WPF na mesma janela de nível superior.

  • Nenhuma parte da interface do usuário de um suplemento pode ser renderizada usando um VisualBrusharquivo . Em vez disso, o suplemento pode tirar um instantâneo da interface do usuário gerada para criar um bitmap que pode ser passado para o aplicativo host usando métodos definidos pelo contrato.

  • Os arquivos de mídia não podem ser reproduzidos de uma MediaElement interface do usuário de um suplemento.

  • Os eventos de mouse gerados para a interface do usuário do suplemento não são recebidos nem gerados pelo aplicativo host, e a IsMouseOver propriedade da interface do usuário do aplicativo host tem um valor de false.

  • Quando o foco muda entre controles em uma interface do usuário do suplemento, os GotFocus eventos e LostFocus não são recebidos nem gerados pelo aplicativo host.

  • A parte de um aplicativo host que contém uma interface do usuário do suplemento aparece em branco quando impressa.

  • Todos os dispatchers (consulte Dispatcher) criados pela interface do usuário do suplemento devem ser desligados manualmente antes que o suplemento proprietário seja descarregado se o aplicativo host continuar a execução. O contrato pode implementar métodos que permitem que o aplicativo host sinalize o suplemento antes que o suplemento seja descarregado, permitindo assim que a interface do usuário do suplemento desligue seus dispatchers.

  • Se uma interface do usuário do suplemento for um ou contiver um InkCanvasInkCanvas, você não poderá descarregar o suplemento.

Otimização do Desempenho

Por padrão, quando vários domínios de aplicativo são usados, os vários assemblies do .NET Framework exigidos por cada aplicativo são todos carregados no domínio desse aplicativo. Como resultado, o tempo necessário para criar novos domínios de aplicativo e iniciar aplicativos neles pode afetar o desempenho. No entanto, o .NET Framework fornece uma maneira de reduzir as horas de início, instruindo os aplicativos a compartilhar assemblies entre domínios de aplicativo se eles já estiverem carregados. Você faz isso usando o LoaderOptimizationAttribute atributo , que deve ser aplicado ao método de ponto de entrada (Main). Nesse caso, você deve usar apenas código para implementar sua definição de aplicativo (consulte Visão geral de gerenciamento do aplicativo).

Confira também