Partager via


Compiler une application WPF

Les applications Windows Presentation Foundation (WPF) peuvent être générées en tant qu’exécutables .NET Framework (.exe), bibliothèques (.dll) ou une combinaison des deux types d’assemblys. Cette rubrique explique comment générer des applications WPF et décrit les étapes clés du processus de génération.

Création d’une application WPF

Une application WPF peut être compilée de la manière suivante :

Pipeline de build WPF

Lorsqu’un projet WPF est compilé, la combinaison de cibles spécifiques au langage et au WPF est invoquée. Le processus d’exécution de ces cibles est appelé pipeline de build, et les principales étapes sont illustrées par la figure suivante.

processus de construction WPF

Initialisations avant build

Avant de créer, MSBuild détermine l’emplacement des outils et bibliothèques importants, y compris les éléments suivants :

  • The .NET Framework.

  • Répertoires du Kit de développement logiciel (SDK) Windows.

  • Emplacement des assemblies de référence WPF.

  • Propriété pour les chemins de recherche des assemblys.

Le premier emplacement où MSBuild recherche des assemblys est le répertoire d’assemblys de référence (%ProgramFiles%\Reference Assemblys\Microsoft\Framework\v3.0\). Au cours de cette étape, le processus de génération initialise également les différentes propriétés et groupes d’éléments et effectue tout travail de nettoyage requis.

Résolution des références

Le processus de génération localise et lie les assemblys requis pour générer le projet d’application. Cette logique est contenue dans la tâche ResolveAssemblyReference. Tous les assemblys déclarés comme Reference dans le fichier projet sont fournis à la tâche, ainsi que des informations sur les chemins de recherche et les métadonnées sur les assemblys déjà installés sur le système. La tâche recherche des assemblys et utilise les métadonnées de l’assembly installé pour filtrer ces assemblys WPF principaux qui n’ont pas besoin d’apparaître dans les manifestes de sortie. Cela permet d’éviter les informations redondantes dans les manifestes ClickOnce. Par exemple, étant donné que PresentationFramework.dll peut être considéré comme représentatif d’une application basée sur et pour WPF, et puisque tous les assemblys WPF existent à l’emplacement même sur chaque ordinateur sur lequel le .NET Framework est installé, il n’est pas nécessaire d’inclure toutes les informations sur tous les assemblys de référence .NET Framework dans les manifestes.

Compilation du balisage—Étape 1

Dans cette étape, les fichiers XAML sont analysés et compilés afin que le runtime ne passe pas de temps à analyser xml et à valider les valeurs de propriété. Le fichier XAML compilé est pré-tokenisé pour que, au moment de l’exécution, le chargement soit beaucoup plus rapide que le chargement d’un fichier XAML.

Au cours de cette étape, les activités suivantes se produisent pour chaque fichier XAML qui est un élément de build Page :

  1. Le fichier XAML est analysé par le compilateur de balisage.

  2. Une représentation compilée est créée pour ce code XAML et copiée dans le dossier obj\Release.

  3. Une représentation CodeDOM d’une nouvelle classe partielle est créée et copiée dans le dossier obj\Release.

En outre, un fichier de code spécifique au langage est généré pour chaque fichier XAML. Par exemple, pour une page Page1.xaml dans un projet Visual Basic, une Page1.g.vb est générée ; pour une page Page1.xaml dans un projet C#, un Page1.g.cs est généré. Le « .g » dans le nom de fichier indique que le fichier est du code généré qui contient une déclaration de classe partielle pour l’élément de niveau supérieur du fichier de balisage (par exemple, Page ou Window). La classe est déclarée avec le modificateur partial en C# (Extends en Visual Basic) pour indiquer qu’il existe une autre déclaration pour la classe ailleurs, généralement dans le fichier code-behind Page1.xaml.cs.

La classe partielle s’étend de la classe de base appropriée (par exemple, Page pour une page) et implémente l’interface System.Windows.Markup.IComponentConnector. L’interface IComponentConnector a des méthodes pour initialiser un composant et connecter des noms et des événements sur des éléments dans son contenu. Par conséquent, le fichier de code généré a une implémentation de méthode comme suit :

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

Par défaut, la compilation de balisage s’exécute dans la même AppDomain que le moteur MSBuild. Cela offre des gains de performances significatifs. Ce comportement peut être basculé avec la propriété AlwaysCompileMarkupFilesInSeparateDomain. Cela présente l’avantage de décharger tous les assemblys de référence en déchargeant le AppDomain distinct.

Compilation de balisage—Deuxième passage

Toutes les pages XAML ne sont pas compilées au cours du passage 1 de la compilation de balisage. Les fichiers XAML qui ont des références de type définies localement (références aux types définis dans le code ailleurs dans le même projet) sont exemptés de la compilation pour l’instant. Cela est dû au fait que ces types définis localement existent uniquement dans la source et n’ont pas encore été compilés. Pour déterminer cela, l’analyseur utilise des heuristiques qui impliquent la recherche d’éléments tels que x:Name dans le fichier de balisage. Lorsqu’une telle instance est trouvée, la compilation du fichier de balisage est reportée jusqu’à ce que les fichiers de code aient été compilés, après quoi, le deuxième passe de compilation de balisage traite ces fichiers.

Classification des fichiers

Le processus de génération place les fichiers de sortie dans différents groupes de ressources en fonction de l’assembly d’application dans lequel ils seront placés. Dans une application non localisée classique, tous les fichiers de données marqués comme Resource sont placés dans l’assembly principal (exécutable ou bibliothèque). Lorsque UICulture est défini dans le projet, tous les fichiers XAML compilés et ces ressources spécifiquement marquées comme spécifiques au langage sont placées dans l’assembly de ressources satellite. En outre, toutes les ressources indépendantes du langage sont placées dans l'assemblage principal. Dans cette étape du processus de construction, cette décision est prise.

Les actions de génération ApplicationDefinition, Pageet Resource dans le fichier projet peuvent être augmentées avec les métadonnées Localizable (les valeurs acceptables sont true et false), qui déterminent si le fichier est spécifique au langage ou neutre en langue.

Compilation principale

L’étape de compilation principale implique la compilation des fichiers de code. Cela est orchestré par logique dans les fichiers cibles spécifiques au langage Microsoft.CSharp.targets et Microsoft.VisualBasic.targets. Si les heuristiques ont déterminé qu’une seule passe du compilateur de balisage est suffisante, l’assembly principal est généré. Toutefois, si un ou plusieurs fichiers XAML du projet ont des références à des types définis localement, un fichier de .dll temporaire est généré afin que les assemblys d’application finaux puissent être créés une fois la deuxième passe de compilation de balisage terminée.

Génération de manifeste

À la fin du processus de génération, une fois tous les assemblys d’application et tous les fichiers de contenu prêts, les manifestes ClickOnce de l’application sont générés.

Le fichier manifeste de déploiement décrit le modèle de déploiement : la version actuelle, le comportement de mise à jour et l’identité de l’éditeur, ainsi que la signature numérique. Ce manifeste est destiné à être créé par les administrateurs qui gèrent le déploiement. L’extension de fichier est .xbap (pour les applications de navigateur XAML (XBAPs)) et .application pour les applications installées. L’ancien est dicté par la propriété de projet HostInBrowser et, par conséquent, le manifeste identifie l’application comme hébergée par le navigateur.

Le manifeste de l’application (un fichier .manifest .exe) décrit les assemblys d’application et les bibliothèques dépendantes et répertorie les autorisations requises par l’application. Ce fichier est destiné à être créé par le développeur d’applications. Pour lancer une application ClickOnce, un utilisateur ouvre le fichier manifeste de déploiement de l’application.

Ces fichiers manifestes sont toujours créés pour les XBAPs. Pour les applications installées, elles ne sont pas créées, sauf si la propriété GenerateManifests est spécifiée dans le fichier projet avec la valeur true.

Les XBAPs obtiennent deux autorisations supplémentaires sur et au-dessus de celles affectées aux applications de zone Internet classiques : WebBrowserPermission et MediaPermission. Le système de génération WPF déclare ces autorisations dans le manifeste de l’application.

Prise en charge du build incrémentiel

Le système de génération WPF prend en charge les builds incrémentielles. Il est assez intelligent de détecter les modifications apportées au balisage ou au code, et elle compile uniquement ces artefacts affectés par la modification. Le mécanisme de génération incrémentiel utilise les fichiers suivants :

  • Un fichier $(AssemblyName)_MarkupCompiler.Cache pour conserver l’état actuel du compilateur.

  • Un fichier $(AssemblyName)_MarkupCompiler.lref pour mettre en cache les fichiers XAML avec des références aux types définis localement.

Voici un ensemble de règles régissant la génération incrémentielle :

  • Le fichier est la plus petite unité à laquelle le système de build détecte les modifications. Par conséquent, pour un fichier de code, le système de build ne peut pas savoir si un type a été modifié ou si le code a été ajouté. Il en est de même pour les fichiers projet.

  • Le mécanisme de génération incrémentiel doit être conscient qu’une page XAML définit une classe ou utilise d’autres classes.

  • Si Reference entrées changent, recompilez toutes les pages.

  • Si un fichier de code change, recompilez toutes les pages avec des références de type définies localement.

  • Si un fichier XAML change :

    • Si XAML est déclaré comme Page dans le projet : si le code XAML n’a pas de références de type définies localement, recompilez ce code XAML plus toutes les pages XAML avec des références locales ; si le code XAML a des références locales, recompilez toutes les pages XAML avec des références locales.

    • Si XAML est déclaré comme ApplicationDefinition dans le projet : recompilez toutes les pages XAML (raison : chaque code XAML a une référence à un type Application qui a peut-être changé).

  • Si le fichier projet déclare un fichier de code comme définition d’application au lieu d’un fichier XAML :

    • Vérifiez si la valeur ApplicationClassName dans le fichier projet a changé (existe-t-il un nouveau type d’application ?). Dans ce cas, recompilez l’ensemble de l’application.

    • Sinon, recompilez toutes les pages XAML avec des références locales.

  • Si un fichier projet change : appliquez toutes les règles précédentes et voyez ce qui doit être recompilé. Les modifications apportées aux propriétés suivantes déclenchent une recompilation complète : AssemblyName, IntermediateOutputPath, RootNamespaceet HostInBrowser.

Les scénarios de recompilation suivants sont possibles :

  • L’application entière est recompilée.

  • Seuls les fichiers XAML qui ont des références de type définies localement sont recompilés.

  • Rien n’est recompilé (si rien dans le projet n’a changé).

Voir aussi