Compilar um aplicativo WPF

Os aplicativos do Windows Presentation Foundation (WPF) podem ser criados como executáveis (.exe), bibliotecas (.dll) do .NET Framework ou uma combinação de ambos os tipos de assemblies. Este tópico apresenta como criar aplicativos WPF e descreve as principais etapas do processo de compilação.

Compilando um aplicativo WPF

Um aplicativo WPF pode ser compilado das seguintes maneiras:

Pipeline de build do WPF

Quando um projeto WPF é criado, a combinação de destinos específicos da linguagem e WPF são invocados. O processo de execução desses destinos denomina-se pipeline de build, e as principais etapas estão ilustradas na figura a seguir.

WPF build process

Inicializações pré-build

Antes de criar, o MSBuild determina o local de ferramentas e bibliotecas importantes, incluindo o seguinte:

  • O .NET Framework.

  • Os diretórios do SDK do Windows.

  • O local dos assemblies de referência do WPF.

  • A propriedade para os caminhos de pesquisa de assembly.

O primeiro local onde o MSBuild procura assemblies é o diretório de assembly de referência (%ProgramFiles%\Reference Assemblies\Microsoft\Framework\v3.0\). Durante essa etapa, o processo de build também inicializa as várias propriedades e grupos de itens e realiza qualquer trabalho necessário de limpeza.

Resolvendo referências

O processo de build localiza e associa os assemblies necessários para compilar o projeto de aplicativo. Essa lógica está contida na tarefa ResolveAssemblyReference. Todos os assemblies declarados como Reference no arquivo de projeto são fornecidos para a tarefa, junto com as informações sobre os caminhos de pesquisa e metadados sobre os assemblies já instalados no sistema. A tarefa procura assemblies e usa os metadados do assembly instalado para filtrar os assemblies WPF principais que não precisam aparecer nos manifestos de saída. Isso é feito para evitar informações redundantes nos manifestos do ClickOnce. Por exemplo, como o PresentationFramework.dll pode ser considerado representativo de um aplicativo criado no e para o WPF, e como todos os assemblies do WPF existem no mesmo local em cada computador que tem o .NET Framework instalado, não há necessidade de incluir todas as informações sobre todos os assemblies de referência do .NET Framework nos manifestos.

Compilação de marcação — passo 1

Nesta etapa, os arquivos XAML são analisados e compilados para que o tempo de execução não gaste tempo analisando XML e validando valores de propriedade. O arquivo XAML compilado é pré-tokenizado para que, em tempo de execução, carregá-lo seja muito mais rápido do que carregar um arquivo XAML.

Durante esta etapa, as seguintes atividades ocorrem para cada arquivo XAML que é um item de Page compilação:

  1. O arquivo XAML é analisado pelo compilador de marcação.

  2. Uma representação compilada é criada para esse XAML e copiada para a pasta obj\Release.

  3. Uma representação CodeDOM de uma nova classe parcial é criada e copiada para a pasta obj\Release.

Além disso, um arquivo de código específico do idioma é gerado para cada arquivo XAML. Por exemplo, para uma página Page1.xaml em um projeto Visual Basic, um Page1.g.vb é gerado; para uma página Page1.xaml em um projeto C#, um Page1.g.cs é gerado. O ".g" no nome de arquivo indica que o arquivo é um código gerado que tem uma declaração de classe parcial para o elemento de nível superior do arquivo de marcação (como Page ou Window). A classe é declarada com o partial modificador em C# (Extends no Visual Basic) para indicar que há outra declaração para a classe em outro lugar, geralmente no arquivo code-behind Page1.xaml.cs.

A classe parcial se estende da classe base apropriada (como Page para uma página) e implementa a System.Windows.Markup.IComponentConnector interface. A IComponentConnector interface tem métodos para inicializar um componente e conectar nomes e eventos em elementos em seu conteúdo. Consequentemente, o arquivo de código gerado tem uma implementação de método semelhante à seguinte:

public void InitializeComponent() {
    if (_contentLoaded) {
        return;
    }
    _contentLoaded = true;
    System.Uri resourceLocater =
        new System.Uri(
            "window1.xaml",
            System.UriKind.RelativeOrAbsolute);
    System.Windows.Application.LoadComponent(this, resourceLocater);
}
Public Sub InitializeComponent() _

    If _contentLoaded Then
        Return
    End If

    _contentLoaded = True
    Dim resourceLocater As System.Uri = _
        New System.Uri("mainwindow.xaml", System.UriKind.Relative)

    System.Windows.Application.LoadComponent(Me, resourceLocater)

End Sub

Por padrão, a compilação de marcação é executada da mesma AppDomain forma que o mecanismo MSBuild. Isso proporciona ganhos significativos de desempenho. Esse comportamento pode ser alternado com a propriedade AlwaysCompileMarkupFilesInSeparateDomain. Isso tem a vantagem de descarregar todos os assemblies de referência descarregando o AppDomainarquivo separado .

Compilação de marcação — passo 2

Nem todas as páginas XAML são compiladas durante a passagem 1 da compilação de marcação. Os arquivos XAML que têm referências de tipo definidas localmente (referências a tipos definidos no código em outro lugar no mesmo projeto) estão isentos de compilação no momento. Isso ocorre porque esses tipos definidos localmente existem somente na fonte e ainda não foram compilados. Para determinar isso, o analisador usa heurísticas que envolvem a procura de itens como x:Name no arquivo de marcação. Quando uma instância dessas é encontrada, a compilação desse arquivo de marcação é adiada até que os arquivos de código tenham sido compilados, e depois disso, o segundo passo da compilação da marcação processa esses arquivos.

Classificação de Arquivos

O processo de build coloca arquivos de saída em diferentes grupos de recursos com base em qual assembly de aplicativo eles serão colocados. Em um aplicativo típico não localizado, todos os arquivos de dados marcados como Resource são colocados no assembly principal (executável ou biblioteca). Quando UICulture é definido no projeto, todos os arquivos XAML compilados e os recursos especificamente marcados como específicos do idioma são colocados no assembly de recursos satélite. Além disso, todos os recursos com neutralidade de idioma são colocados no assembly principal. Essa determinação é feita nessa etapa do processo de build.

As ações de build ApplicationDefinition, Page, e Resource no arquivo de projeto podem ser aumentadas com os metadados Localizable (os valores aceitáveis são true e false), que determinam se o arquivo é específico a um idioma ou com neutralidade de idioma.

Compilação principal

A etapa de compilação principal envolve a compilação de arquivos de código. Isso é orquestrado por lógica nos arquivos de destinos específicos a um idioma Microsoft.CSharp.targets e Microsoft.VisualBasic.targets. Se as heurísticas determinarem que uma única passagem do compilador de marcação é suficiente, então o assembly principal será gerado. No entanto, se um ou mais arquivos XAML no projeto tiverem referências a tipos definidos localmente, um arquivo de .dll temporário será gerado para que os assemblies finais do aplicativo possam ser criados após a conclusão da segunda passagem da compilação de marcação.

Geração de manifesto

No final do processo de compilação, depois que todos os assemblies de aplicativo e arquivos de conteúdo estiverem prontos, os manifestos ClickOnce para o aplicativo serão gerados.

O arquivo de manifesto de implantação descreve o modelo de implantação: a versão atual, o comportamento de atualização e a identidade do editor junto com a assinatura digital. Esse manifesto deve ser criado pelos administradores que lidam com a implantação. A extensão do arquivo é .xbap (para aplicativos de navegador XAML (XBAPs)) e .application para aplicativos instalados. A primeira é determinada pela propriedade do projeto HostInBrowser e, como resultado, o manifesto identifica o aplicativo como hospedado pelo navegador.

O manifesto do aplicativo (um arquivo de manifesto .exe) descreve os assemblies do aplicativo e as bibliotecas dependentes e permissões de lista exigidas pelo aplicativo. Esse arquivo deve ser criado pelo desenvolvedor do aplicativo. Para iniciar um aplicativo ClickOnce, um usuário abre o arquivo de manifesto de implantação do aplicativo.

Esses arquivos de manifesto são sempre criados para XBAPs. Para aplicativos instalados, eles não são criados, a menos que a propriedade GenerateManifests seja especificada no arquivo de projeto com o valor true.

Os XBAPs obtêm duas permissões adicionais além das permissões atribuídas a aplicativos típicos da zona da Internet: WebBrowserPermission e MediaPermission. O sistema de compilação do WPF declara essas permissões no manifesto do aplicativo.

Suporte ao build incremental

O sistema de compilação WPF fornece suporte para compilações incrementais. Ele é bastante inteligente para detectar as alterações feitas na marcação ou no código, e ele compila somente os artefatos afetados pela alteração. O mecanismo de build incremental usa os seguintes arquivos:

  • Um arquivo $(AssemblyName)_MarkupCompiler.Cache para manter o estado atual do compilador.

  • Um arquivo $(AssemblyName)_MarkupCompiler.lref para armazenar em cache os arquivos XAML com referências a tipos definidos localmente.

Abaixo está um conjunto de regras que rege o build incremental:

  • O arquivo é a menor unidade na qual o sistema de build detecta alterações. Portanto, para um arquivo de código, o sistema de build não pode determinar se um tipo foi alterado ou se algum código foi adicionado. O mesmo ocorre para arquivos de projeto.

  • O mecanismo de compilação incremental deve estar ciente de que uma página XAML define uma classe ou usa outras classes.

  • Se entradas Reference forem alteradas, recompilar todas as páginas.

  • Se um arquivo de código for alterado, recompilar todas as páginas com referências de tipo definidas localmente.

  • Se um arquivo XAML for alterado:

    • Se XAML for declarado como Page no projeto: se o XAML não tiver referências de tipo definidas localmente, recompile esse XAML mais todas as páginas XAML com referências locais; se o XAML tiver referências locais, recompile todas as páginas XAML com referências locais.

    • Se XAML for declarado como ApplicationDefinition no projeto: recompile todas as páginas XAML (motivo: cada XAML tem referência a um Application tipo que pode ter sido alterado).

  • Se o arquivo de projeto declarar um arquivo de código como definição de aplicativo em vez de um arquivo XAML:

    • Verifique se o valor ApplicationClassName no arquivo de projeto foi alterado (existe um novo tipo de aplicativo?). Em caso afirmativo, recompilar o aplicativo inteiro.

    • Caso contrário, recompile todas as páginas XAML com referências locais.

  • Se um arquivo de projeto for alterado: aplique todas as regras anteriores e veja o que precisa ser recompilado. As alterações nas seguintes propriedades disparam uma recompilação completa: AssemblyName, IntermediateOutputPath, RootNamespace e HostInBrowser.

Os seguintes cenários de recompilação são possíveis:

  • Todo o aplicativo é recompilado.

  • Somente os arquivos XAML que têm referências de tipo definidas localmente são recompilados.

  • Nada é recompilado (se nada no projeto tiver sido alterado).

Confira também