Partager via


Chapitre 2 : Création d’une application « Longhorn »

 

Introduction
Chapitre 1 : Modèle d’application « Longhorn »

Chapitre 2 : Création d’une application « Longhorn »

Brent Rector
Wise Owl Consulting

Novembre 2003

Contenu

Moteur de build Microsoft .NET : MSBuild.exe
Génération de Hello World à l’aide de MSBuild
Terminologie MSBuild
Génération d’une application exécutable Longhorn
Création d’un assembly de bibliothèque Longhorn
Création d’un document Longhorn
Un fichier XAML en tant que déclaration de classe
Manifeste de l’application
Manifeste de déploiement
Exécution de l'application
Pourquoi créer un autre système de build ?
Résumé

Pour créer une application Longhorn, vous devez installer le Kit de développement logiciel (SDK) Longhorn. Vous pouvez également installer une version de Microsoft® Visual Studio® qui prend en charge Longhorn. Dans ce livre, je ne parle pas de l’utilisation de Visual Studio, car ses Assistants, ses fonctionnalités de génération de code de fantaisie et ses fonctionnalités de génération de projet masquent ce qui se passe réellement sous les couvertures. Je crois que vous devez comprendre ce qu’un outil fait pour vous avant de vous fier à l’outil.

Lorsque vous installez le SDK Longhorn, il crée un ensemble d’éléments de menu Démarrer que vous pouvez utiliser pour créer une session d’invite de commandes dans laquelle vous pouvez créer des applications Longhorn. Pour générer des versions de débogage de votre application sur un système Microsoft Windows® XP 32 bits, accédez aux éléments de menu suivants pour créer la session d’invite de commandes appropriée :

  • Démarrer
  • Programmes
  • Kit de développement logiciel (SDK) Microsoft Longhorn
  • Ouvrir la fenêtre Environnement de build
  • Environnement de génération Windows XP 32 bits
  • Définir l’environnement de build Windows XP 32 bits (Débogage)

Moteur de build Microsoft .NET : MSBuild.exe

MSBuild est l’outil principal que vous utilisez pour créer une application Longhorn. Vous pouvez exécuter MSBuild avec l’option de ligne de commande d’aide pour obtenir des informations détaillées sur son utilisation :

MSBuild /?

Lorsque vous exécutez MSBuild sans aucun argument de ligne de commande, comme indiqué ici, il recherche dans le répertoire de travail actuel un nom de fichier qui se termine par « proj », par exemple.proj, .csproj, etc. Lorsqu’il en trouve un, il génère le projet conformément aux directives de ce fichier.

MSBuild

Lorsque vous avez plusieurs fichiers projet dans le répertoire, vous pouvez spécifier le fichier projet approprié sur la ligne de commande :

MSBuild <ProjectName>.proj

Normalement, MSBuild génère la cible par défaut dans le fichier projet. Vous pouvez remplacer ce paramètre et spécifier la cible que vous souhaitez générer. Par exemple, pour générer la cible nommée CleanBuild, vous appelez MSBuild comme suit :

MSBuild /t:Cleanbuild

Génération de Hello World à l’aide de MSBuild

Examinons les fichiers nécessaires pour créer une application Hello World simple basée sur la navigation. Plus tard, je décrirai l’objectif et l’utilisation de chaque fichier en détail.

Tout d’abord, vous devez définir l’objet Application . Vous effectuez cette opération dans un fichier généralement appelé fichier de définition d’application. Ce fichier HelloWorldApplication.xaml définit mon objet Application .

HelloWorldApplication.xaml

<NavigationApplication xmlns="https://schemas.microsoft.com/2003/xaml" 
                       StartupUri="HelloWorld.xaml" />

Cette définition indique : « Pour mon objet Application, je souhaite utiliser un instance de la classe MSAvalon.Windows.Navigation.NavigationApplication. Au démarrage, l’application doit accéder à l’interface utilisateur définie dans le fichier HelloWorld.xaml et l’afficher. »

Voici le contenu du fichier HelloWorld.xaml. Il s’agit d’une version légèrement plus intéressante de l’exemple de Hello World précédent dans le chapitre 1.

HelloWorld.xaml

<Border xmlns="https://schemas.microsoft.com/2003/xaml">
  <FlowPanel>
    <SimpleText Foreground="DarkRed" FontSize="14">Hello World!</SimpleText>   </FlowPanel>
</Border>

Maintenant que je dispose de tout le « code » pour mon application Hello World simple, j’ai besoin d’un fichier projet qui définit la façon de générer mon application. Voici mon fichier HelloWorld.proj.

HelloWorld.proj

<Project DefaultTargets="Build">
  <PropertyGroup>
    <Property Language="C#" />   
    <Property DefaultClrNameSpace="IntroLonghorn" />
    <Property TargetName="HelloWorld" />
  </PropertyGroup>

  <!--Imports the target which contains all the common targets-->
  <Import Project="$(LAPI)\WindowsApplication.target" />

  <ItemGroup>
    <!-- Application markup -->
    <Item Type="ApplicationDefinition" Include="HelloWorldApplication.xaml" />
   
    <!-- Compiled Xaml Files list -->
    <Item Type="Pages" Include="HelloWorld.xaml"/>      
  </ItemGroup>
</Project>

Placez ces trois fichiers dans un répertoire. Ouvrez une invite de commandes du Kit de développement logiciel (SDK) Longhorn, accédez au répertoire contenant vos fichiers et exécutez MSBuild. Il compilera votre programme dans un exécutable.

Nous examinerons le contenu du fichier de définition d’application plus loin dans ce chapitre. Dans le chapitre 3, je décrit en détail la plupart des éléments XAML (Extensible Application Markup Language) que vous pouvez utiliser pour définir une interface utilisateur. Avant d’examiner plus en détail le fichier projet, examinons la terminologie MSBuild.

Terminologie MSBuild

Établissons des définitions pour certains éléments MSBuild. Une propriété est une paire clé-valeur. La valeur d’une propriété peut provenir d’une variable d’environnement, d’un commutateur de ligne de commande ou d’une définition de propriété dans un fichier projet, comme illustré ici :

<Property OutputDir="bin\" />

Vous pouvez considérer un élément comme un tableau de fichiers. Un élément peut contenir des caractères génériques et peut exclure des fichiers spécifiques. MSBuild utilise l’attribut Type d’un élément pour catégoriser les éléments, comme illustré ici :

<Item Type="Compile" Include="*.cs" Exclude="DebugStuff.cs" />

Une tâche est une unité atomique dans le processus de génération. Une tâche peut accepter des paramètres d’entrée d’éléments Property , d’éléments Item ou de chaînes simples. Le Nom d’une tâche identifie le type de données .NET de build requis pour l’exécution de la tâche. Une tâche peut émettre des élémentsque d’autres tâchesconsomment. MSBuild inclut de nombreuses tâches, qui peuvent toutes être classées en général comme

  • Tâches de l’outil .NET
  • Tâches de déploiement
  • Tâches de l’interpréteur de commande

Par exemple, the Task with a Name of Csc appelle le compilateur C# en tant qu’outil de build, qui compile tous les éléments Item spécifiés dans l’attribut Sources (qui spécifie les éléments Item avec un type de compilation) dans un assembly, et produit l’assembly en tant qu’élément de sortie .

<Task Name="Csc" AssemblyName="$(OutputDir)\HelloWorld.exe"
                 Sources="@(Compile)" />

Une cible est une étape logique unique du processus de génération. Une cible peut effectuer une analyse d’horodatage. Cela signifie qu’une cible ne s’exécutera pas si elle n’est pas obligatoire. Une cible exécute une ou plusieurs tâchespour effectuer les opérations souhaitées, comme illustré ici :

<Target Name="CopyToServer"
        Inputs="$(OutputDir)\HelloWorld.exe"
        Outputs="\\DeployServer\$(BuildNum)\HelloWorld.exe"
        DependsOnTargets="Compile">

  <Task Name="Copy" ... />
</Target>

Un attribut Condition est à peu près équivalent à une instruction if simple. Une condition peut comparer deux chaînes ou case activée pour l’existence d’un fichier ou d’un répertoire. Vous pouvez appliquer une condition à n’importe quel élément d’un fichier projet. Par exemple, voici un groupe de propriétés qui sont définies uniquement lorsque la propriété Configuration a la valeur Debug :

<PropertyGroup Condition=" '$(Configuration)'=='Debug' " >
    <Property ... />
    <Property ... />
</PropertyGroup>

Une importation équivaut à peu près à une instruction #include C/C++, comme illustré dans l’exemple suivant. Lorsque vous importez un projet, le contenu du projet importé devient logiquement une partie du projet d’importation.

<Import Project="$(LAPI)\WindowsApplication.target" />

Maintenant que la terminologie est hors de propos, examinons un fichier projet classique.

Génération d’une application exécutable Longhorn

Voici un fichier projet simple, mais relativement complet, qui génère une application Longhorn exécutable :

<Project DefaultTargets="Build">
  <PropertyGroup>
    <Property Language="C#" />
    <Property DefaultClrNameSpace="IntroLonghorn" />
    <Property TargetName="MyApp" />
  </PropertyGroup>

  <Import Project="$(LAPI)\WindowsApplication.target" />

  <ItemGroup>
    <Item Type="ApplicationDefinition" Include="MyApp.xaml" />

    <Item Type="Pages" Include="HomePage.xaml" />
    <Item Type="Pages" Include="DetailPage.xaml" />
    <Item Type="Code" Include="DetailPage.xaml.cs"/>

    <Item Type="DependentProjects" Include="MyDependentAssembly.proj" /> 

    <Item Type="Components" Include="SomeThirdParty.dll" />

    <Item Type="Resources" Include="Picture1.jpg"
          FileStorage="embedded" Localizable="False"/>
    <Item Type="Resources" Include="Picture2.jpg"
          FileStorage="embedded" Localizable="True"/>
  </ItemGroup>
</Project>

Élément Project

Tous les fichiers projet commencent par une définition d’élément racine nommée Project. Son attribut DefaultTargets spécifie les noms des cibles que le système doit générer lorsque vous ne spécifiez pas de cible. Dans cet exemple, je spécifie que, par défaut, le système doit générer la cible nommée Build.

Éléments PropertyGroup et Property

Les règles de build peuvent s’exécuter de manière conditionnelle en fonction des valeurs de propriété. Comme mentionné précédemment, la valeur d’une propriété peut provenir d’une variable d’environnement, d’un commutateur de ligne de commande MSBuild ou d’une définition de propriété dans un fichier projet.

Un projet pour une application doit spécifier, au minimum, une valeur pour les propriétés Language et TargetName . Dans cet exemple, je spécifie que la langue est C# et que le nom de l’application résultante doit être MyApp. J’ai également affecté une valeur à la propriété nommée DefaultClrNameSpace.

Le système de build compile chaque fichier XAML dans une définition de classe managée. Par défaut, la classe managée aura le même nom que le nom de fichier de base du fichier source XAML. Par exemple, le fichier Markup.xaml est compilé dans une définition d’une classe nommée Markup. En définissant la propriété DefaultClrNameSpace sur IntroLonghorn, je demande au système de build de préfixer les noms de classes générés avec l’espace de noms IntroLonghorn . Pour cette raison, le système de build produit une classe nommée IntroLonghorn.Markup pour la définition Markup.xaml.

J’ai défini mes propriétés avant d’importer d’autres projets, de sorte que les règles des projets importés utilisent mes valeurs de propriété spécifiées( par exemple, je vais obtenir les règles de build appropriées pour les applications C#, car je définit la propriété Language comme C#.

Élément Import

Les règles de la cible Build produisent le fichier exécutable de mon application Longhorn. La spécification de ces règles de build dans chaque fichier projet serait fastidieuse et répétitive. Un peu plus tard dans le fichier projet, j’utilise la définition suivante pour importer un fichier projet prédéfini nommé WindowsApplication.target :

  <Import Project="$(LAPI)\WindowsApplication.target" />

Ce fichier importé contient les règles de build standard pour la génération d’une application Windows et il définit (indirectement) la cible nommée Build.

Éléments ItemGroup et Item

L’élément ItemGroup et ses éléments Item enfants définissent toutes les parties requises pour générer l’application.

Vous devez avoir un élément avec un typed’applicationDefinition, comme illustré ici. Cet élément spécifie le fichier qui décrit l’objet Application à utiliser pour votre application. L’objet Application est généralement un instance de la classe MSAvalon.Windows.Application ou de la classe MSAvalon.Windows.Navigation.NavigationApplication, que je décris plus loin dans ce chapitre.

<Item Type="ApplicationDefinition" Include="MyApp.xaml" />

Chaque élément avec un type de pages définit un ensemble de fichiers XAML, comme illustré ici. Le système de build compile ces définitions XAML en classes qu’il inclut dans l’assembly résultant.

<Item Type="Pages" Include="HomePage.xaml" />
<Item Type="Pages" Include="DetailPage.xaml" />

Chaque élément avec un type de code représente un fichier source, comme illustré ici. Le système de build compile ces fichiers sources à l’aide du compilateur approprié sélectionné par la propriété Language de votre projet.

<Item Type="Code" Include="DetailPage.xaml.cs"/>

Ce projet peut dépendre d’autres projets. Le système de génération doit compiler ces projets dépendants avant de pouvoir générer ce projet. Vous répertoriez chaque projet dépendant à l’aide d’un élément avec type de projets dépendants :

<Item Type="DependentProjects" Include="MyDependentAssembly.proj" /> 

Le code de ce projet peut utiliser des types dans un assembly prédéfini, également appelé assembly de composant. Pour compiler du code à l’aide de tels assemblys de composants, le compilateur a besoin d’une référence à chaque assembly. En outre, lorsque vous déployez votre application, vous devez également déployer ces assemblys de composants. Vous répertoriez chaque assembly de composant à l’aide d’un élément avec type de composants :

<Item Type="Components" Include="SomeThirdParty.dll" />

Un assembly référencé est quelque peu différent d’un assembly de composant. Dans les deux cas, votre code utilise des types dans un assembly prédéfini. Toutefois, vous n’expédiez pas d’assembly référencé dans le cadre de votre application, alors que vous expédiez un assembly de composants dans le cadre de votre application. Le système de build doit connaître cette distinction.

Vous spécifiez un élément avec un type de références pour indiquer que le compilateur doit référencer l’assembly spécifié au moment de la génération, comme illustré ici, mais que l’assembly ne fera pas partie du déploiement de l’application. Le système de génération inclut automatiquement des références à des assemblys système standard, par exemple, mscorlib.dll, System.dll, PresentationFramework.dll. et bien plus encore, mais vous devrez ajouter n’importe quel assembly non standard que votre application doit référencer.

<Item Type="References" Include="SharedThirdParty.dll" />

Votre application peut également utiliser des ressources. Un élément avec un type de ressources décrit une ressource utilisée par l’application, comme illustré ici. Le système de build peut incorporer la ressource dans l’assembly résultant ou l’inclure en tant que fichier autonome. Le système de génération peut également placer des ressources localisables dans des assemblys satellites.

<Item Type="Resources" Include="Picture1.jpg"
      FileStorage="embedded" Localizable="False"/>
<Item Type="Resources" Include="Picture2.jpg"
      FileStorage="embedded" Localizable="True"/>

Création d’un assembly de bibliothèque Longhorn

Vous souhaiterez également créer des bibliothèques en plus des applications exécutables. Les principales différences entre un projet d’application et un projet de bibliothèque sont les suivantes :

  • Un projet de bibliothèque définit la valeur de la propriété TargetType sur Library.
  • Un projet de bibliothèque n’inclut généralement pas d’élément de définition d’application.

Voici un exemple de fichier projet qui crée une bibliothèque :

<Project DefaultTargets="Build">
  <PropertyGroup>
    <Property Language="C#" />
    <Property DefaultClrNameSpace="IntroLonghorn" />
    <Property TargetName="MyLibrary" />
    <Property TargetType="Library" />
  </PropertyGroup>

  <Import Project="$(LAPI)\WindowsApplication.target" />

  <ItemGroup>
    <Item Type="Pages" Include="ErrorPage.xaml" />
    <Item Type="Code" Include="ErrorPage.xaml.cs"/>
    <Item Type="Code" Include="Utilities.cs"/>

    <Item Type="DependentProjects" Include="MyDependentAssembly.proj" /> 

    <Item Type="Components" Include="SomeThirdParty.dll" />

    <Item Type="Resources" Include="Picture1.jpg"
          FileStorage="embedded" Localizable="False"/>
    <Item Type="Resources" Include="Picture2.jpg"
          FileStorage="embedded" Localizable="True"/>
  </ItemGroup>
</Project>

Création d’un document Longhorn

Vous n’êtes pas limité à la création d’applications avec XAML. Vous pouvez également utiliser des fichiers XAML pour créer un document adaptatif hautement interactif, rendu de manière intelligente et à lire par un utilisateur. Dans ce cas, vos fichiers XAML représentent collectivement des pages d’un document. Vous pouvez utiliser le moteur MSBuild pour générer de tels documents.

Les modifications apportées au fichier projet pour générer un document au lieu d’une application sont mineures :

  • Définissez la valeur de la propriété TargetType sur Document.
  • Importez le projet WindowsDocument.target pour les règles de build appropriées.
  • Incluez tous les autres fichiers projet comme d’habitude.

Il est important de comprendre ce qu’un TargetType de document produit réellement. Lorsque vous générez un document, la sortie de build est un fichier .container, et le système de génération optimise le contenu du conteneur pour le téléchargement plutôt que la vitesse. Un fichier conteneur est une extension du format Stockage structuré Windows, également appelé DocFile. La gestion des conteneurs Longhorn fournit des fonctionnalités qui permettent le rendu des fichiers partiellement téléchargés. Par conséquent, vous n’avez pas besoin que le conteneur entier soit téléchargé avant que l’application ne commence à s’exécuter.

En outre, lorsque vous demandez à MSBuild de créer un fichier conteneur, il compile chaque fichier XAML dans une représentation binaire du XML, appelée XAML binaire (BAML). BAML est beaucoup plus compact que le fichier texte d’origine ou qu’un assembly compilé vers IL. Les fichiers BAML sont téléchargés plus rapidement. Ils sont optimisés pour le téléchargement, mais un interpréteur doit les analyser au moment de l’exécution pour créer des instances des classes décrites dans le fichier. Par conséquent, ces fichiers ne sont pas optimisés pour la vitesse. Jusqu’à présent, j’ai généré des fichiers compilés vers IL (également appelés fichiers CAML, abréviation de XAML compilé).

Voici un exemple de fichier projet qui crée un document électronique :

<Project DefaultTargets="Build">
  <PropertyGroup>
    <Property TargetType="Document" />
      <Property Language="C#" />
      <Property DefaultClrNameSpace="IntroLonghorn" />
      <Property TargetName="MyDocument" />
  </PropertyGroup>
    
  <Import Project="$(LAPI)\WindowsDocument.target" />

  <ItemGroup>
    <Item Type="ApplicationDefinition" Include="MyApp.xaml" />

    <Item Type="Pages" Include="Markup.xaml" />
    <Item Type="Pages" Include="Navigate.xaml" />
    <Item Type="Code" Include="Navigate.xaml.cs"/>

    <Item Type="Resources" Include="Picture1.jpg"
          FileStorage="embedded" Localizable="False"/>
    <Item Type="Resources" Include="Picture2.jpg"
          FileStorage="embedded" Localizable="True"/>
  </ItemGroup>
</Project>

Maintenant que vous avez appris à créer les différents types d’applications et de composants Longhorn, examinons les fichiers XAML plus en détail. Plus précisément, examinons ce que fait le système de build lorsqu’il transforme un fichier XAML en classe .NET.

Un fichier XAML en tant que déclaration de classe

Le fichier de définition d’application est le fichier XAML qui définit la classe de l’objet Application de votre application. Toutefois, en général, un document XAML est simplement un fichier qui définit une classe. La classe produite par la définition XAML dérive de la classe associée au nom d’élément racine du document XML. Par défaut, le système de génération utilise le nom du fichier de base XAML comme nom de classe généré.

Création d’un fichier de définition d’application pour une application de navigation

Rappelez-vous que l’élément Item avec Typed’ApplicationDefinition spécifie le nom du fichier XAML qui définit l’objet Application . En d’autres termes, cet élément spécifie le fichier XAML qui contient le point d’entrée de votre application. La plateforme Longhorn crée un instance du type dérivé de MSAvalon.Windows.Application que vous définissez dans ce fichier et lui permet de gérer le démarrage, l’arrêt et la navigation de votre application.

Dans le chapitre 1, vous avez vu comment créer et utiliser une application instance par programmation. Le fichier XAML suivant utilise le balisage pour définir l’objet Application pour un projet :

<NavigationApplication xmlns="https://schemas.microsoft.com/2003/xaml" 
                       StartupUri="HelloWorld.xaml" />

Je m’attends à ce que la plupart des applications Longhorn soient des applications basées sur la navigation et, par conséquent, réutiliseront souvent simplement l’objet NavigationApplication standard. Vous pouvez réutiliser ce fichier de définition d’application pour la plupart de vos applications basées sur la navigation en modifiant uniquement la valeur de l’attribut StartupUri .

Par exemple, si la définition d’application précédente réside dans le fichier HelloWorldApplication.xaml et que j’utilise le fichier projet HelloWorld.proj répertorié précédemment, le système de génération produit la déclaration de classe suivante :

namespace IntroLonghorn {
  class HelloWorldApplication :
           MSAvalon.Windows.Navigation.NavigationApplication {
.
.
.
   }
 }

L’espace de noms résulte de la déclaration DefaultClrNameSpace dans le fichier projet, le nom de classe déclaré est identique au nom du fichier de base et la classe déclarée étend la classe représentée par l’élément racine dans le fichier XAML.

Personnalisation du code généré à l’aide d’attributs

Lorsque vous déclarez un élément racine dans un fichier XAML, vous pouvez utiliser des attributs sur l’élément racine pour contrôler le nom de la déclaration de classe générée. Vous pouvez utiliser l’un des attributs facultatifs suivants :

  • Définition de préfixe d’espace de noms qui associe un préfixe à un espace de noms nommé Definition. Vous devez définir un préfixe pour cet espace de noms afin d’utiliser les attributs Language et Class . Traditionnellement, le préfixe def est utilisé.
  • Attribut Language (défini dans l’espace de noms Definition ) qui spécifie le langage de programmation utilisé par tout code inline dans le fichier XAML.
  • Attribut Class (défini dans l’espace de noms Definition ) qui spécifie le nom de la classe générée. Lorsque vous spécifiez un nom contenant une ou plusieurs périodes, le système de génération ne préfixe pas le nom avec la valeur DefaultClrNameSpace .

Par exemple, nous allons remplacer le contenu du fichier HelloWorldApplication.xaml par ce qui suit :

<NavigationApplication xmlns="https://schemas.microsoft.com/2003/xaml"
                       xmlns:def="Definition"
                       def:Class="Special.MyApp"
                       def:CodeBehind="HelloWorldApplication.xaml.cs" 
                       StartupUri="HelloWorld.xaml" />

La classe générée est alors la suivante :

namespace Special {
  class MyApp :
           MSAvalon.Windows.Navigation.NavigationApplication {
.
.
.
  }
}

Utilisation du code et du balisage dans la même classe

Presque toutes les applications vous demandent d’écrire du code(par exemple, un gestionnaire d’événements click pour un bouton ou un remplacement de méthode virtuelle) en plus du balisage qui spécifie l’interface utilisateur. Rappelez-vous du chapitre 1 que mon application non basée sur la navigation a dépassé la méthode OnStartingUp pour créer sa fenêtre et ses contrôles. Je vais réécrire cet exemple pour illustrer comment combiner le code d’application et le balisage.

Bien que cet exemple à venir crée une application non-navigation, je tiens à souligner qu’il n’y a vraiment aucune raison convaincante de créer une telle application. Vous pouvez toujours créer une application basée sur la navigation qui ne navigue jamais réellement vers une autre page. Toutefois, l’écriture d’une telle application nécessite que je mélange le code et le balisage dans la même classe fournit donc un bon exemple.

Rappelez-vous que la création d’une application sans navigation vous oblige à définir une classe personnalisée qui hérite de MSAvalon.Windows.Application et qui remplace la méthode OnStartingUp . Le fichier de définition d’application déclare la classe d’objet application que votre programme utilise. Par conséquent, une application sans navigation doit définir sa méthode OnStartingUp de substitution dans la même classe.

À l’exception des modifications suivantes, un fichier de configuration d’application pour une application sans navigation contient les mêmes éléments qu’un fichier de définition pour une application de navigation :

  • L’élément racine est Application au lieu de NavigationApplication.
  • Le fichier doit contenir ou référencer l’implémentation de la méthode OnStartingUp pour votre classe d’application.

Étant donné que je dois utiliser le balisage et le code pour implémenter une classe unique, je dois vous montrer une technique pour associer un fichier de code source à un fichier XAML.

Association d’un fichier Source-Behind à un fichier XAML

Vous souhaiterez souvent développer des parties de votre application à l’aide du balisage et développer d’autres parties à l’aide d’un langage de programmation plus traditionnel. Je vous recommande vivement de séparer l’interface utilisateur et la logique dans des fichiers sources individuels à l’aide de la technique suivante.

Vous pouvez ajouter un élément CodeBehind XAML (défini dans l’espace de noms Definition ) à l’élément racine de n’importe quel fichier XAML et spécifier le nom d’un fichier de code source (également appelé fichier code-behind). Le moteur de build compile les déclarations XAML dans une classe managée. Le système de build compile également le fichier code-behind dans une déclaration de classe managée. L’aspect délicat est que ces deux déclarations de classe représentent des déclarations partielles d’une classe unique.

Voici une définition XAML qui produit une classe d’application sans navigation équivalente au premier exemple du chapitre 1 :

<Application xmlns="https://schemas.microsoft.com/2003/xaml"
             xmlns:def="Definition"
             def:Language="C#"
             def:Class="IntroLonghorn.CodeBehindSample"
             def:CodeBehind="CodeBehind.xaml.cs" />

Ce fichier de définition d’application présente deux aspects notables :

  • L’attribut Language spécifie que le fichier code-behind contient du code source C#.
  • L’attribut CodeBehind spécifie que le nom du fichier source est CodeBehindMySample2.xaml.cs.

Voici le fichier source-behind correspondant :

namespace IntroLonghorn {
  using System;
  using MSAvalon.Windows;
  using MSAvalon.Windows.Controls;
  using MSAvalon.Windows.Media;

  public partial class CodeBehindSample {
    MSAvalon.Windows.Controls.SimpleText txtElement;
    MSAvalon.Windows.Window              mainWindow;

    protected override
    void OnStartingUp (StartingUpCancelEventArgs e) {
      base.OnStartingUp (e);
      CreateAndShowMainWindow ();
    }

    private void CreateAndShowMainWindow () {
      // Create the application's main window
      mainWindow = new MSAvalon.Windows.Window ();

      // Add a dark red, 14 point, "Hello World!" text element
      txtElement = new MSAvalon.Windows.Controls.SimpleText ();
      txtElement.Text = "Hello World!";
      txtElement.Foreground = new
       MSAvalon.Windows.Media.SolidColorBrush (Colors.DarkRed);
      txtElement.FontSize = new FontSize (14,
                                          FontSizeType.Point);
      mainWindow.Children.Add (txtElement);
      mainWindow.Show ();
    }
  }
}

Notez la mot clé partielle dans la déclaration de classe dans le fichier code-behind. Cette mot clé indique que le compilateur doit fusionner cette définition de classe avec d’autres définitions de la même classe. Cela vous permet de fournir plusieurs définitions partielles d’une classe, chacune dans un fichier source distinct, que le compilateur combine en une seule définition de classe dans l’assembly résultant.

Mélange de code source et de balisage dans un seul fichier XAML

Je pense qu’il est juste incorrect de mélanger le code source et le balisage dans le même fichier. J’ai même envisagé de ne pas vous montrer comment le faire. Cependant, un malfaiteur quelque part écrira un exemple de programme à l’aide de cette technique, donc vous devrez peut-être comprendre ce qu’il a fait. En outre, vous pouvez ensuite utiliser l’approche de fichier code-behind décrite précédemment pour débarrasser le monde d’une petite quantité de mal et séparer l’interface utilisateur de la logique.

Voici un fichier de définition d’application avec le code source inséré directement dans le balisage :

<Application xmlns="https://schemas.microsoft.com/2003/xaml"
    xmlns:def="Definition"
    def:Language="C#"
    def:Class="IntroLonghorn.MySample2" >

  <def:Code>
  <![CDATA[
    protected override void OnStartingUp (StartingUpCancelEventArgs e) {
      base.OnStartingUp (e);
      CreateAndShowMainWindow ();
    }
    . . . Remaining methods elided for clarity
  ]]>
  </def:Code>
</Application>

Dans cet exemple, l’attribut Language spécifie que le code source inline est C#. Notez que l’élément Code est un bloc CDATA contenant le code source inline. Il est parfois techniquement nécessaire de placer le code source inline dans un bloc CDATA XML pour garantir que le document est bien formé. En fait, l’analyseur XAML vous oblige toujours à placer le code source inline dans un bloc CDATA, même si l’omission produit un document bien formé.

Je m’excuse encore une fois de vous avoir montré une telle parodie.

Manifeste de l’application

Lorsque vous compilez une application, MSBuild génère le fichier .exe plus deux fichiers manifeste : le manifeste de l’application, *.manifest, et un manifeste de déploiement, *.deploy. Vous utilisez ces manifestes lors du déploiement d’une application ou d’un document à partir d’un serveur. Tout d’abord, copiez l’application, toutes ses dépendances et les deux fichiers manifestes à l’emplacement approprié sur votre serveur. Ensuite, modifiez le manifeste de déploiement afin qu’il pointe vers l’emplacement du manifeste d’application.

Pour plus d’exhaustivité, examinons des exemples de manifestes d’application et de déploiement. Le manifeste d’application, illustré dans l’exemple suivant, n’est pas aussi intéressant que le manifeste de déploiement. Le manifeste d’application définit simplement toutes les parties qui composent une application. MSBuild produit le manifeste d’application lorsqu’il génère votre application, et vous en modifiez généralement peu ou rien.

HelloWorld.manifest

<?xml version="1.0" encoding="utf-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"
          xmlns:asmv2="urn:schemas-microsoft-com:asm.v2"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd">

  <assemblyIdentity name="HelloWorld" version="1.0.0.0"
                    processorArchitecture="x86" asmv2:culture="en-us"
                    publicKeyToken="0000000000000000" />

  <entryPoint name="main" xmlns="urn:schemas-microsoft-com:asm.v2"
              dependencyName="HelloWorld">

    <commandLine file="HelloWorld.exe" parameters="" />
  </entryPoint>

  <TrustInfo xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:temp="temporary">
    <Security>
      <ApplicationRequestMinimum>
        <PermissionSet class="System.Security.PermissionSet" version="1" 
                       ID="SeeDefinition">
          <IPermission 
            class="System.Security.Permissions.FileDialogPermission"
            version="1" Unrestricted="true" />
          <IPermission 
            class="System.Security.Permissions.IsolatedStorageFilePermission" 
            version="1" Allowed="DomainIsolationByUser" UserQuota="5242880" />
          <IPermission
            class="System.Security.Permissions.SecurityPermission"
            version="1" Flags="Execution" />
          <IPermission
            class="System.Security.Permissions.UIPermission" version="1"
            Window="SafeTopLevelWindows" Clipboard="OwnClipboard" />
          <IPermission
            class="System.Security.Permissions.PrintingPermission"
            version="1" Level="SafePrinting" />
          <IPermission
            class="MSAvalon.Windows.AVTempUIPermission, PresentationFramework,
                   Version=6.0.4030.0, Culture=neutral,
                   PublicKeyToken=a29c01bbd4e39ac5" version="1"
                   NewWindow="LaunchNewWindows" FullScreen="SafeFullScreen" />
        </PermissionSet>

        <AssemblyRequest name="HelloWorld"
                         PermissionSetReference="SeeDefinition" />
      </ApplicationRequestMinimum>
    </Security>
  </TrustInfo>

  <dependency asmv2:name="HelloWorld">
    <dependentAssembly>
      <assemblyIdentity name="HelloWorld" version="0.0.0.0"
                        processorArchitecture="x86" />
    </dependentAssembly>

    <asmv2:installFrom codebase="HelloWorld.exe"
                       hash="5c58153494c16296d9cab877136c3f106785bfab" 
                       hashalg="SHA1" size="5632" />
  </dependency>
</assembly>

La plupart du contenu du manifeste d’application doit être relativement évident. L’élément entryPoint spécifie le nom de la méthode de point d’entrée, main et fait référence à la dépendance, nommée HelloWorld, qui contient le point d’entrée. L’élément entryPoint contient également le nom du programme et l’argument de ligne de commande dont l’interpréteur de commandes aura besoin pour exécuter l’application.

L’élément de dépendanceHelloWorld contient les informations (l’élément dependentAssembly) qui spécifie l’assembly dépendant et un élément installFrom qui indique au chargeur où trouver le fichier de l’assembly et le hachage d’origine du fichier. Le chargeur peut utiliser le hachage pour détecter les modifications apportées à l’assembly après la compilation.

Le Gestionnaire de confiance Longhorn utilise l’élément TrustInfo pour déterminer les autorisations de sécurité dont l’application a besoin. Dans l’exemple précédent, mon application HelloWorld définit un ensemble d’autorisations qu’elle nomme le jeu d’autorisations SeeDefinition . Immédiatement après avoir défini le jeu d’autorisations, l’élément AssemblyRequest demande que l’assembly nommé HelloWorld reçoive au moins le jeu d’autorisations dans le jeu nommé SeeDefinition. Les autorisations de cet exemple sont les autorisations normalement accordées aux applications qui s’exécutent dans see, de sorte que l’application Hello World s’exécute sans afficher à l’utilisateur des avertissements de sécurité du Gestionnaire de confiance.

Manifeste de déploiement

Comme mentionné, le manifeste de déploiement est plus intéressant. Le manifeste de déploiement contient, de toute évidence, des paramètres qui contrôlent le déploiement de l’application.

HelloWorld.deploy

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" 
          xmlns:asmv2="urn:schemas-microsoft-com:asm.v2"  
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd">
  
  <assemblyIdentity name="HelloWorld.deploy" version="1.0.0.0" 
                    processorArchitecture="x86" asmv2:culture="en-us" 
                    publicKeyToken="0000000000000000" />

  <description asmv2:publisher="Wise Owl, Inc." 
               asmv2:product="Brent's HelloWorld Application"            
    asmv2:supportUrl="http://www.wiseowl.com/AppServer/HelloWorld/support.asp" 
  />
  
  <deployment xmlns="urn:schemas-microsoft-com:asm.v2" 
              isRequiredUpdate="false">
    <install shellVisible="true" />
    <subscription>
      <update>
        <beforeApplicationStartup />
        <periodic>
          <minElapsedTimeAllowed time="6" unit="hours" />
          <maxElapsedTimeAllowed time="1" unit="weeks" />
        </periodic>
      </update>
    </subscription>
  </deployment>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity name="HelloWorld" version="1.0.0.0" 
                        processorArchitecture="x86" asmv2:culture="en-us" 
                        publicKeyToken="0000000000000000" />
    </dependentAssembly>
    <asmv2:installFrom codebase="HelloWorld.manifest" />
  </dependency>
</assembly>

Le manifeste de déploiement contient les informations dont Longhorn a besoin pour installer et mettre à jour une application. Notez que l’élément assemblyIdentity du manifeste de déploiement fait référence au manifeste d’application. Après tout, le manifeste d’application décrit déjà tous les composants d’une application. Pour installer une application, le manifeste de déploiement indique, en effet, « Voici la description des fichiers dont vous avez besoin pour installer cette application ».

Bien sûr, lorsque vous installez une application, vous avez également besoin de plus d’informations que les fichiers à copier sur un système. L’élément description répertorie les attributs publisher, product et supportUrl ; le système affiche son contenu dans la boîte de dialogue Ajout/Suppression de programmes.

L’élément deployment spécifie comment déployer et mettre à jour l’application après le déploiement. Dans cet exemple, l’application est visible par l’interpréteur de commandes et le système du client case activée pour et, si nécessaire, télécharger une nouvelle version de l’application chaque fois que l’utilisateur démarre l’application. En outre, le système recherche régulièrement une nouvelle version ( au maximum toutes les six heures et pas moins d’une fois par semaine). Lorsque l’case activée périodique localise une nouvelle version, Longhorn télécharge la nouvelle version en arrière-plan et l’installe; elle est alors prête à s’exécuter la prochaine fois que l’utilisateur exécutera l’application.

Exécution de l'application

Normalement, un utilisateur « exécute » le manifeste de l’application pour exécuter l’application directement à partir du serveur sans installer l’application sur l’ordinateur local. Longhorn télécharge les composants de l’application en fonction des besoins. Dans ce cas, le serveur doit être disponible pour exécuter l’application.

Lorsqu’un utilisateur « exécute » le manifeste de déploiement, Longhorn télécharge et installe l’application sur l’ordinateur local. L’application peut installer des icônes sur le bureau, ajouter des éléments de menu Démarrer et devenir généralement une application installée traditionnelle. Bien sûr, vous bénéficiez également des mises à jour automatiques en arrière-plan, de la restauration de version et de la prise en charge de la désinstallation.

Lorsque vous lancez pour la première fois un manifeste de déploiement, Longhorn installe l’application dans le cache de l’application et ajoute une entrée à la liste Ajout/Suppression de programmes de l’Panneau de configuration. Par la suite, chaque fois que vous exécutez le manifeste de déploiement, l’application se charge directement à partir du cache de l’application. Il n’est généralement pas téléchargé à nouveau.

Toutefois, lorsque vous désinstallez l’application du cache à l’aide de l’applet Ajout/Suppression de programmes de l’Panneau de configuration, l’exécution du manifeste de déploiement télécharge et installe à nouveau l’application.

Vous pouvez également modifier le numéro de version de l’application sur le serveur. Ensuite, lorsque vous exécutez le manifeste de déploiement, Longhorn télécharge et installe la nouvelle version côte à côte avec la version actuelle. Les deux versions de l’application s’affichent dans la liste Ajout/Suppression de programmes.

Pourquoi créer un autre système de build ?

J’aime vraiment MSBuild, même si, au moment de la rédaction de cet article, je n’ai eu que quelques semaines d’expérience avec elle. Bien sûr, des années d’expérience avec makefiles rend tout système de construction plus élégant attrayant. À l’heure actuelle, il existe deux systèmes de build alternatifs couramment utilisés : Make et Ant. Il semble naturel de comparer MSBuild à de telles alternatives.

Pourquoi ne pas utiliser Make ?

Pourquoi développer un nouveau système de build alors que de nombreux développeurs sont familiarisés avec un système existant appelé Make ? Make a une intégration médiocre des outils dans le système de build. Make exécute simplement les commandes de l’interpréteur de commandes. Pour cette raison, il n’existe aucune possibilité inhérente pour un outil de communiquer avec un autre outil pendant le processus de génération. MSBuild crée des instances des classes Task et les tâches peuvent communiquer entre elles en passant des types .NET normaux.

Les makefiles ont une syntaxe inhabituelle, sont difficiles à écrire et ne sont pas bien mis à l’échelle, car ils deviennent complexes pour les grands projets. En outre, les outils autres que Make ne peuvent pas facilement traiter un makefile. Les outils autres que MSBuild peuvent facilement générer et analyser la syntaxe XML d’un projet MSBuild.

Enfin, Make n’a pas vraiment de support pour les projets. Il n’existe aucune abstraction du système de fichiers et aucune prise en charge des propriétés en cascade. En outre, il n’existe aucune prise en charge au moment du design pour la génération d’un makefile.

Pourquoi ne pas utiliser la fourmi ?

Une question similaire fréquemment posée est pourquoi développer un nouveau système de build basé sur XML quand il existe un système riche et très performant existant appelé Ant ? Ant est un système de génération Java open source à partir de Apache.org qui a mis au point des fichiers et des tâches de projet XML en tant qu’opération de génération atomique. Il existe également un excellent port .NET d’Ant appelé NAnt disponible à partir de nant.sourceforge.net. En surface, MSBuild et Ant/NAnt sont similaires. Les deux outils utilisent XML comme format de sérialisation de projet, et les deux outils utilisent des tâches comme unité atomique d’opération de génération. Les deux outils ont leurs points forts, mais lorsque vous regardez de plus près, ils ont des objectifs de conception différents.

Ant a pris la décision de la conception de placer beaucoup de fonctionnalités dans un grand ensemble de tâches. MSBuild a un objectif de conception différent, où des fonctionnalités similaires sont encapsulées par le moteur (telles que l’analyse d’horodatage, la communication intertsk via des éléments, le traitement par lot de tâches, les transformations d’éléments, etc.). Les deux approches ont leurs forces et leurs faiblesses.

Le modèle d’Ant permet aux développeurs d’étendre et de contrôler chaque détail de la build, et il est donc très flexible. Néanmoins, il impose également une plus grande responsabilité aux enregistreurs de tâches, car les tâches doivent être beaucoup plus sophistiquées pour fournir des fonctionnalités cohérentes. Le modèle MSBuild réduit la quantité de fonctionnalités que chaque tâche doit implémenter. Les auteurs de projets peuvent donc s’appuyer sur des fonctionnalités cohérentes sur différents projets, cibles et tâches. En outre, les environnements de développement intégrés tels que Visual Studio peuvent également s’appuyer sur ces services pour fournir des résultats cohérents et une expérience utilisateur enrichie, sans avoir à connaître les éléments internes des tâches appelées pendant le processus de génération.

De même, bien qu’Ant ait le concept d’un script de build, il n’a pas le concept d’un manifeste de projet que MSBuild a. Un script de build indique comment créer un ensemble de fichiers, mais ne fournit pas de sémantique supplémentaire décrivant comment les fichiers sont utilisés. Un manifeste décrit également la sémantique des fichiers, ce qui permet à des outils supplémentaires, tels qu’un IDE, de s’intégrer plus profondément au système de génération. À l’inverse, l’absence d’un manifeste de projet signifie qu’un développeur peut plus facilement adapter Ant pour créer de nouveaux types de « choses », car il n’existe aucun schéma contraignant pour le script de build.

Résumé

Vous maîtrisez maintenant les bases. Vous pouvez écrire du code XAML et compiler, déployer et exécuter l’application résultante. Malheureusement, les applications que vous avez appris à écrire jusqu’à présent sont assez ennuyeuses. Le chapitre 3 explore le code XAML en profondeur et vous montre comment utiliser une grande variété d’objets d’interface utilisateur fournis par la plateforme Longhorn. Les chapitres suivants vous montrent un certain nombre d’autres nouvelles technologies que vous pouvez également utiliser dans vos applications.

Passez au Chapitre 3 : Contrôles et XAML.