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.

Génération d’une application WPF

Une application WPF peut être compilée des façons suivantes :

Pipeline de génération WPF

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

WPF build process

Initialisations avant génération

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 assemblys 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’assembly de référence (%ProgramFiles%\Reference Assemblys\Microsoft\Framework\v3.0\). Pendant cette étape, le processus de génération initialise également les diverses propriétés et groupes d’éléments, et exécute tout travail de nettoyage nécessaire.

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 vise à éviter des 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 au même emplacement 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 Page élément de build :

  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, un fichier Page1.g.vb est généré ; pour une page Page1.xaml dans un projet C#, une page Page1.g.cs est générée. Le « .g » dans le nom de fichier indique que le fichier est du code généré qui a une déclaration de classe partielle pour l’élément de niveau supérieur du fichier de balisage (tel que Page ou Window). La classe est déclarée avec le partial modificateur 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 de son contenu. Par conséquent, le fichier de code généré a une implémentation de méthode similaire à la suivante :

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 de la même façon AppDomain que le moteur MSBuild. Cela assure 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 les éléments distincts AppDomain.

Compilation du balisage — Étape 2

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. En effet, ces types définis localement existent uniquement dans la source et n’ont pas encore été compilés. Pour déterminer ceci, l’analyseur utilise des heuristiques qui impliquent la recherche d’éléments tels que x:Name dans le fichier de balisage. Quand une telle instance est trouvée, la compilation de ce fichier de balisage est remise à plus tard jusqu’à ce que les fichiers de code aient été compilés, après quoi la deuxième étape de la compilation du balisage traite ces fichiers.

Classification des fichiers

Le processus de génération organise 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 type, tous les fichiers de données marqués comme Resource sont placés dans l’assembly principal (fichier exécutable ou bibliothèque). Lorsqu’elle UICulture est définie dans le projet, tous les fichiers XAML compilés et ces ressources spécifiquement marqués comme spécifiques au langage sont placés dans l’assembly de ressources satellite. En outre, toutes les ressources indépendantes du langage sont placées dans l’assembly principal. Cette détermination est faite lors de cette étape du processus de génération.

Les actions de build ApplicationDefinition, Page et 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 indépendant de celui-ci.

Compilation principale

L’étape de compilation principale implique la compilation des fichiers de code. Cela répond à une logique dans les fichiers cibles Microsoft.CSharp.targets et Microsoft.VisualBasic.targets spécifiques au langage. Si les heuristiques ont déterminé qu’un seul passage du compilateur de balisage est suffisant, l’assembly principal est alors généré. Toutefois, si un ou plusieurs fichiers XAML du projet ont des références à des types définis localement, un fichier .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é d’éditeur avec la signature numérique. Ce manifeste est prévu pour ê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. La première extension est déterminée par la propriété du projet HostInBrowser et, en conséquence, le manifeste identifie l’application comme étant hébergée par le navigateur.

Le manifeste de l’application (un fichier .exe.manifest) décrit les assemblys d’application et les bibliothèques dépendantes, et répertorie les autorisations requises par l’application. Ce fichier est prévu pour ê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 adresses XBAPs. Pour les applications installées, ils ne sont pas créés à moins que la propriété GenerateManifests ne soit spécifiée dans le fichier projet avec la valeur true.

Les XBAPs obtiennent deux autorisations supplémentaires sur et au-dessus de ces autorisations 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 de la build incrémentielle

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

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

  • 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 qui régissent la build incrémentielle :

  • Le fichier est la plus petite unité au niveau de laquelle le système de génération détecte une modification. Par conséquent, pour un fichier de code, le système de génération ne peut pas déterminer si un type a été modifié ou si du code a été ajouté. Il en va 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 des entrées Reference sont modifiées, vous devez recompiler toutes les pages.

  • Si un fichier de code est modifié, recompilez toutes les pages avec des références à des types définis 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 XAML a une référence à un Application type 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 toute 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 déterminez ce qui doit être recompilé. Les modifications apportées aux propriétés suivantes entraînent une recompilation complète : AssemblyName, IntermediateOutputPath, RootNamespace et HostInBrowser.

Les scénarios de recompilation suivants sont possibles :

  • Toute l’application 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