A la découverte de XAML avec C#.
Date de publication : 04/01/2005
Par
Jean-Alain Baeyens (autres articles)
Cet article est un premier aperçu de la technologie XAML pour la gestion de
l'affichage. Rappelons que la technologie XAML sera introduite par Microsoft
sur ses futures OS. Pour réaliser cet aperçu, j'ai utilisé le produit Xamlon.
I. Avertissement
II. Remerciements
III. Qu'est ce que XAML
IV. Installation de Xamlon
V. Analyse de Xolitaire
A. Premiers regards
B. La procédure principale
C. Une "sous procédure" XAML
D. La classe C# Xamlon.Xolitaire
VI. Notre première réalisation
A. Le code XAML
B. Le code C#
VII. Une autre approche de la liaison XAML C#
VIII. Compiler une application XAML, C#
IX. XAML et Internet Explorer.
X. Conclusion
I. Avertissement
Il ne s'agit ni d'un test de Xamlon ni
d'évaluer les performances ou les avantages de la technologie
XAML (pronnoncez zammel). Il s'agit uniquement d'une première
approche de XAML et surtout de son interaction avec C#.
L'exposé se veut le compte-rendu de ma première expérience en
la matière.
II. Remerciements
Je remercie la société
Xamlon pour m'avoir autorisé à utiliser son
exemple Xolitaire dans cet article. Je remercie également David
Pédehourcq pour son avis éclairé et Jennifer Moulard ainsi que
mon épouse pour le travail de correction.
III. Qu'est ce que XAML
XAML, Extensible Application Markup Language, est un langage
déclaratif basé sur XML. Il a pour vocation la déclaration de
l'interface graphique. Typiquement, chaque page d'interface est
décrite dans un fichier XAML. Une page XAMl décrit la classe qui
sera générée lors du runtime.
Cette technologie sera partie intégrante du futur OS de Microsoft
baptisé actuellement Longhorn. Pour rappel, Longhorn introduit
un nouvel interface graphique vectoriel nommé actuellement Avalon.
XAML pourra être utilisé aussi bien en Intranet, Internet via IE
que pour les applications WinForm. Une nouvelle version de Visual
Studio, nom de code ORCA, permettra la manipulation de XAML.
Microsoft a annoncé que cette technologie sera également
disponible sur les anciennes versions de Windows via l'installation
d'un kit complémentaire. Il sera également possible de générer des
applications avec XAML en utilisant Visual Studio Whidbey après
installation du Longhorn SDK.
Aujourd'hui, il est déjà possible via le produit Xamlon d'utiliser
cette technologie sans installer le Longhorn SDK. Il existe aussi
d'autres initiatives comme MyXAML mais si le principe est le même,
le codage XML est différent.
IV. Installation de Xamlon
Vous devez télécharger la version d'essai 30 jours du produit à
l'adresse
http://www.xamlon.com/software/xamlonpro/winforms/.
L'installation est classique et ne demande pas d'explication
particulière. Toutefois, avant de vous lancer dans cette
installation, n'oubliez pas d'attendre votre numéro de série qui
vous sera transmis par email.
Sur la machine de test, je ne dispose que de SharpDevelop 1.0.1
et de Visual C# 2005 bêta. L'AddIn d'intégration à Visual studio
Net 2003 n'a dès lors pu être installé. Je me suis donc concentré sur
l'analyse du code de l'un des exemples fournis,
en l'occurrence Xolitaire.
V. Analyse de Xolitaire
A. Premiers regards
Première surprise, on ne retrouve pas d'exe dans le
répertoire de l'exemple. L'application est lancée depuis
le fichier XAML Xolitaire. Ce type de fichier est associé à un viewer.
La partie C# est quand à elle compilée sous forme d'une dll.
Nous verrons en fin d'article qu'il ne s'agit pas d'une
fatalité.
B. La procédure principale
| Xolitaire.xaml |
<?xml version="1.0"?>
<?Mapping
XmlNamespace="events"
ClrNamespace="Xamlon"
Assembly="Xolitaire"?>
<Window ID="container"
xmlns="http://schemas.microsoft.com/2003/xaml"
xmlns:def="Definition"
def:Class="Xamlon.Xolitaire"
Width="720" Height="500"
Text="Xolitaire"
Load="OnLoad" MouseDown="MouseDown" MouseMove="MouseMove" MouseUp="MouseUp"
Resize="Resize" KeyPress="KeyPress">
<Frame ID="root" Source="Playfield.xaml" />
</Window> |
Je ne m'attacherai pas ici à la syntaxe XAML qui n'est pas
l'objet de cet article. De plus on peut raisonnablement
imaginer que le code XAML étant dédié à l'interface
graphique, il sera généré par votre RAD.
Nous pouvons toutefois identifier 4 éléments importants.
Le fichier fait référence à la dll via Assembly="Xolitaire"
Le code XAML fait référence à une class Xamlon.Xolitaire qui est une classe définie dans l'assembly.
Cette classe est écrite en C#.
Des événements sont associés à des méthodes de la classe définie.
Le fichier fait appel au fichier Playfield.xaml
 |
Les trois premiers points définissent comment XAML va communiquer avec C#.
|
C. Une "sous procédure" XAML
PlayField.xaml est une sous procédure de Xolitaire.xaml.
Il contient le code XAML permettant l'affichage du fond
d'écran et la définition des zones du jeux.
Aucun appel à C# n'est généré depuis cette partie du code.
Comme nous avons pu le voir plus haut, l'appel se fait avec
le code suivant.
| Chargement de Playfield.xaml |
<Frame ID="root" Source="Playfield.xaml" /> |
| Le code complet de Playfield.xaml |
<?xml version="1.0"?>
<TransformDecorator
xmlns="http://schemas.microsoft.com/2003/xaml"
xmlns:def="Definition"
ID="zoom"
AffectsLayout="false">
<Canvas ID="board" Width="720" Height="500" Background="Green">
<Canvas.Resources>
<Style>
<Rectangle
Fill="{shadowGrad}"
Canvas.Left="4"
Canvas.Top="4"
RectangleWidth="79"
RectangleHeight="113"
RadiusX="4"
RadiusY="4"
Stroke="#334EFF00"
/>
</Style>
<LinearGradientBrush def:Name="shadowGrad" StartPoint="0.5,0"
EndPoint="0.5,1">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#00000000" Offset="0" />
<GradientStop Color="#33000000" Offset="0.95" />
<GradientStop Color="#66000000" Offset="1" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Canvas.Resources>
<Canvas ID="deck" Canvas.Left="10" Canvas.Top="10">
<Rectangle />
</Canvas>
<Canvas ID="discard" Canvas.Left="110" Canvas.Top="10">
<Rectangle />
</Canvas>
<Canvas ID="ace1" Canvas.Left="310" Canvas.Top="10">
<Rectangle />
</Canvas>
<Canvas ID="ace2" Canvas.Left="410" Canvas.Top="10">
<Rectangle />
</Canvas>
<Canvas ID="ace3" Canvas.Left="510" Canvas.Top="10">
<Rectangle />
</Canvas>
<Canvas ID="ace4" Canvas.Left="610" Canvas.Top="10">
<Rectangle />
</Canvas>
<Canvas ID="stack1" Canvas.Left="10" Canvas.Top="150">
<Rectangle />
</Canvas>
<Canvas ID="stack2" Canvas.Left="110" Canvas.Top="150">
<Rectangle />
</Canvas>
<Canvas ID="stack3" Canvas.Left="210" Canvas.Top="150">
<Rectangle />
</Canvas>
<Canvas ID="stack4" Canvas.Left="310" Canvas.Top="150">
<Rectangle />
</Canvas>
<Canvas ID="stack5" Canvas.Left="410" Canvas.Top="150">
<Rectangle />
</Canvas>
<Canvas ID="stack6" Canvas.Left="510" Canvas.Top="150">
<Rectangle />
</Canvas>
<Canvas ID="stack7" Canvas.Left="610" Canvas.Top="150">
<Rectangle />
</Canvas>
</Canvas>
</TransformDecorator> |
Nous reviendrons sur ce code ultérieurement.
D. La classe C# Xamlon.Xolitaire
| Vue synthétique de la classe Xamlon.Xolitaire |
using System;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Threading;
using Xamlon.CardStacks;
namespace Xamlon
{
public class Xolitaire : System.Windows.Forms.Panel,
System.Windows.Serialization.IPageConnector,
Xamlon.Windows.Forms.IIEViewerEvents
{
public static Xolitaire FirstInstance = null;
+ Fields
+ Properties
+ Constructors
+ Methods
+ Event Handlers
+ IPageConnector Members
+ IIEViewersEvent Members
}
} |
Remarquons les références à System.Windows.Controls et
System.Windows.Media qui sont définies dans l'assembly
Xamlon.dll et non pas dans le framework.
Nous pouvons aussi remarquer dans le code que notre classe
hérite de System.Windows.Forms.Panel et implémente les
interfaces System.Windows.Serialization.IPageConnector
et Xamlon.Windows.Forms.IIEViewerEvents.
Vous trouverez sans problèmes la classe Panel dans l'aide du
framework mais par contre l'interface IPageConnector est
défini dans l'assembly Xamlon.dll. Il décrit la méthode
Connect que l'on retrouve dans la région IPageConnector
Members. Sous Longhorn, ces classes seront intégrées dans le
système d'exploitation lui même. Les dll fournies par Xamlon
réalisent l'extension nécessaire aux versions actuelles de Windows.
| La méthode Connect |
#region IPageConnector Members
public bool Connect(string id, object target)
{
this._ids[id] = target;
return false;
} |
La méthode se contente de placer des objets dans une HashTable.
Pour l'interface IIEViewerEvents on implémente:
| La méthode DoAction |
#region IIEViewerEvents Members
public void DoAction(string action)
{
switch(action)
{
case "NewGame":
FirstInstance.NewGame();
break;
}
} |
 |
Pour rappel FirstInstance est une propriété publique et
statique du type Xolitaire. Vous pourrez voir dans le
constructeurs qu'elle contient la première instance de la
classe.
|
Si l'on détaille les fields définis,nous pouvons constater la
présence de types de données inconnues. Certains de ces types
sont propres à l'application. D'autres sont définis dans les
espaces de nom System.Windows.Controls et
System.Windows.Media. Il s'agit de Canvas,
TransformDecorator, Frame, Matrix et Point. Il n'y a rien
d'étonnant à ce que nous retrouvions ces termes dans le
fichier PlayField.XAML. Nous venons de découvrir le premier
pas pour communiquer avec les objets définis dans les
balises.
| Définition des fields de la classe |
#region Fields
Hashtable _ids;
TransformDecorator _zoom;
Canvas _board;
Frame _root;
Matrix _toUserSpace = Matrix.Identity;
CardStack[] _allStacks;
Deck _deck;
Waste _waste;
Foundation[] _foundations;
Tableau[] _tableaux;
CardStack _originalStack;
CardInfo[] _currentCards;
Point[] _diffs;
#endregion |
L'étude du constructeur ne nous apporte pas d'information complémentaire.
| Le constructeur de la classe |
#region Constructors
public Xolitaire()
{
if(FirstInstance == null)
{
FirstInstance = this;
}
this._ids = new Hashtable();
this._board = null;
this._allStacks = new CardStack[13];
this._deck = null;
this._waste = null;
this._foundations = new Foundation[4];
this._tableaux = new Tableau[7];
this._originalStack = null;
this._currentCards = new CardInfo[0];
this._diffs = new Point[0];
} |
Regardons maintenant la méthode OnLoad associée à
l'événement Load de notre fichier XAML.
| La méthode OnLoad associée à l'événement Load |
public void OnLoad(object sender, EventArgs e)
{
this._root = (Frame) this._ids["root"];
FindChildIDs(this._root);
this._board = (Canvas) this._ids["board"];
this._zoom = (TransformDecorator) this._ids["zoom"];
this._deck = new Deck(this, (Canvas) this._ids["deck"]);
this._waste = new Waste(this, (Canvas) this._ids["discard"]);
this._foundations[0] = new Foundation(this, (Canvas) this._ids["ace1"]);
this._foundations[1] = new Foundation(this, (Canvas) this._ids["ace2"]);
this._foundations[2] = new Foundation(this, (Canvas) this._ids["ace3"]);
this._foundations[3] = new Foundation(this, (Canvas) this._ids["ace4"]);
this._tableaux[0] = new Tableau(this, (Canvas) this._ids["stack1"]);
this._tableaux[1] = new Tableau(this, (Canvas) this._ids["stack2"]);
this._tableaux[2] = new Tableau(this, (Canvas) this._ids["stack3"]);
this._tableaux[3] = new Tableau(this, (Canvas) this._ids["stack4"]);
this._tableaux[4] = new Tableau(this, (Canvas) this._ids["stack5"]);
this._tableaux[5] = new Tableau(this, (Canvas) this._ids["stack6"]);
this._tableaux[6] = new Tableau(this, (Canvas) this._ids["stack7"]);
this._allStacks[0] = this._deck;
this._allStacks[1] = this._waste;
this._allStacks[2] = this._foundations[0];
this._allStacks[3] = this._foundations[1];
this._allStacks[4] = this._foundations[2];
this._allStacks[5] = this._foundations[3];
this._allStacks[6] = this._tableaux[0];
this._allStacks[7] = this._tableaux[1];
this._allStacks[8] = this._tableaux[2];
this._allStacks[9] = this._tableaux[3];
this._allStacks[10] = this._tableaux[4];
this._allStacks[11] = this._tableaux[5];
this._allStacks[12] = this._tableaux[6];
this.NewGame();
this.Resize(sender, e);
} |
Nous pouvons voir sans problèmes que la méthode Connect a
servit à collecter les différents objets de l'écran.
La méthode OnLoad va entre autres réassigner ces éléments
maintenant contenus dans une Hashtable vers les fields
correctement typés.
Pour finir, les autres méthodes vont manipuler ces objets de
manière à effectuer les traitements voulus mais c'est une
autre histoire qui dépasse le cadre de cet article.
VI. Notre première réalisation
Essayons de réaliser un écran simple.
A. Le code XAML
| le minimum pour une fenêtre |
<?xml version="1.0"?>
<Window xmlns:wfi="wfi"
xmlns:wf="wf"
Width="200" Height="150" Text="Mon application">
</Window> |
Ce code génére une fenêtre vierge.
| Préparer la fenêtre à recevoir des contrôles |
<?xml version="1.0"?>
<Window xmlns:wfi="wfi"
xmlns:wf="wf"
Width="200" Height="150" Text="Mon application">
<Canvas>
<wfi:WindowsFormsHost>
</wfi:WindowsFormsHost>
</Canvas>
</Window> |
Si nous ajoutons les
tag Canvas et WindowsFormHost, notre fenêtre est prête pour
recevoir des contrôles.
| Ajout d'un bouton |
<?xml version="1.0"?>
<Window xmlns:wfi="wfi"
xmlns:wf="wf"
Width="200" Height="150" Text="Mon application">
<Canvas>
<wfi:WindowsFormsHost>
<wf:Button Top="55" Left="60" Width="80" Height="18" Text="Hello!" />
</wfi:WindowsFormsHost>
</Canvas>
</Window> |
Le bouton est bien là mais occupe l'intégralité de la fenêtre !
Il nous faut encore ajouter le tag Panel
| le tag Panel |
<?xml version="1.0"?>
<Window xmlns:wfi="wfi"
xmlns:wf="wf"
Width="200" Height="150" Text="Mon application">
<Canvas>
<wfi:WindowsFormsHost>
<wf:Panel>
<wf:Button Top="55" Left="60" Width="80" Height="18"
Text="Hello!" />
</wf:Panel>
</wfi:WindowsFormsHost>
</Canvas>
</Window> |
Nous obtenons enfin le résultat voulu.
B. Le code C#
Nous allons associer une action au bouton. Pour cela nous
allons ajouter l'événement Click à notre bouton et y
associer la méthode OnClick que nous définirons dans C#.
Nous devons aussi faire le lien avec l'assembly via le tag
Mapping mais aussi associer notre class C# avec la fenêtre
en complétant notre tag window. Il nous reste encore à
encadrer notre panel contenant le bouton par le tag
WindowsFormsHost.Controls.
| Encore un peu de XAML |
<?xml version="1.0"?>
<?Mapping
XmlNamespace="events"
ClrNamespace="Xamlon"
Assembly="Test Xamlon"?>
<Window xmlns:wfi="wfi"
xmlns:wf="wf"
xmlns:def="Definition"
def:Class="Xamlon.MyClass"
Width="200" Height="150" Text="Mon application">
<Canvas>
<wfi:WindowsFormsHost>
<wfi:WindowsFormsHost.Controls>
<wf:Panel>
<wf:Button Top="55" Left="60" Width="80" Height="18"
Text="Hello!" Click="OnClick" />
</wf:Panel>
</wfi:WindowsFormsHost.Controls>
</wfi:WindowsFormsHost>
</Canvas>
</Window> |
| Notre code C# |
using System;
using System.Windows.Controls;
using System.Collections;
namespace Xamlon
{
public class MyClass:System.Windows.Forms.Panel
, System.Windows.Serialization.IPageConnector
{
Hashtable lienXAML;
public MyClass()
{
lienXAML = new Hashtable();
}
public bool Connect(string id, object target)
{
this.lienXAML[id] = target;
return false;
}
public void OnClick(object sender, EventArgs e)
{
System.Windows.Forms.Button bt = (System.Windows.Forms.Button)sender;
if (bt.Text == "Hello!")
{
bt.Text="Bye.";
}
else
{
bt.Text="Hello!";
}
}
}
} |
Notre première fenêtre est terminée.
VII. Une autre approche de la liaison XAML C#
Il est possible d'intégrer le code C# directement dans le
fichier XAML. Notre fichier XAML devient alors:
<?xml version="1.0"?>
<Window xmlns:wfi="wfi"
xmlns:wf="wf"
xmlns:def="Definition"
Width="200" Height="150" Text="Mon application">
<def:Code>
<![CDATA[
public void OnClick(object sender, EventArgs e)
{
System.Windows.Forms.Button bt = (System.Windows.Forms.Button)sender;
if (bt.Text == "Hello!")
{
bt.Text="Bye.";
}
else
{
bt.Text="Hello!";
}
}
]]>
</def:Code>
<Canvas>
<wfi:WindowsFormsHost>
<wfi:WindowsFormsHost.Controls>
<wf:Panel>
<wf:Button Top="55" Left="60" Width="80" Height="18"
Text="Hello!" Click="OnClick" />
</wf:Panel>
</wfi:WindowsFormsHost.Controls>
</wfi:WindowsFormsHost>
</Canvas>
</Window> |
Le code est ainsi très simplifié, mais cette méthode mélange
différents types de codes ce qui rend la lecture plus difficile.
 |
Attention, pour que le programme fonctionne de cette façon,
il faut que les fichiers Xamlon.dll et
WindowsFormsIntegration.dll soient présents dans le répertoire.
|
VIII. Compiler une application XAML, C#
Microsoft a mis au point une nouvelle version du compilateur
MSBuild qui permet de réaliser la compilation d'une application
complète. Le compilateur compile aussi bien le code C# que le
code XAML. Celui-ci est alors intégré dans l'exécutable.
De cette manière, il est possible d'obtenir un exécutable qui
peut être distribué sans les fichiers XAML.
Voici un exemple de fichier permettant la compilation.
<Project DefaultTargets="Build">
<PropertyGroup>
<Property Language="C#" />
<Property DefaultClrNameSpace="MonNameSpace" />
<Property TargetName="MonApplication" />
</PropertyGroup>
<Import Project="$(LAPI)\WindowsApplication.target" />
<Item Type="Reference" Include="WindowsFormsIntegration"
HintPath="C:\WINDOWS\Microsoft.NET\Windows\v6.0.4030\WindowsFormsIntegration.dll"
Name="WindowsFormsIntegration" />
<Item Type="Reference" Include="System" />
<Item Type="Reference" Include="System.Data" />
<Item Type="Reference" Include="System.Xml" />
<Item Type="Reference" Include="System.Windows.Forms" />
<ItemGroup>
<Item Type="ApplicationDefinition" Include="MonApplication.xaml" />
<Item Type="Code" Include="MonApplication.cs"/>
<Item Type="Pages" Include="UneAutreClasse.cs"/>
<Item Type="Code" Include="MaSubForm.xaml"/>
<Item Type="Pages" Include="MaSubForm.cs"/>
</ItemGroup>
</Project> |
On peut considérer ce code comme purement informatif car il est
pratiquement certain que Visual Studio ou tout autre IDE dédié
prendra en charge le génération de ce fichier.
IX. XAML et Internet Explorer.
Du code XAML pourra être directement interprété par la version
d'Internet Explorer intégrée à Longhorn. Par contre ce code ne
pourra intégrer directement du code C#. Toutefois, il sera
possible d'exécuter des applications, préalablement compilées,
directement dans le navigateur et non sous forme d'applications
Windows. Pour cela, il faudra utiliser un mode de compilation
particulier.
X. Conclusion
Nous avons maintenant une idée de ce qui nous attend dans l'avenir.
Bien que le concept soit fortement différent de ce qui se fait
actuellement, je ne pense pas que l'introduction d'XAML boulverse
nos habitudes de développeur. La plus grosse partie du travail
d'adaptation sera réalisé par Visual studio Orca. Un certain
nombre de nouvelles classes feront leurs apparitions alors que
d'autres deviendront obsolètes. Pour un usage courant, il est peu
probable que nous allions nous même modifier le fichier XAMl
généré par Visual studio ou par un autre RAD.
La société Xamlon suit les développements
XAML de Microsoft sans toutefois réaliser un clone d'Avalon.
Par exemple, les espaces de noms sont différents.
On ne peut être sûr de la manière dont
vont évoluer les deux produits et les modifications qui vont
encore y être apportées. On peut aussi s'interroger sur l'avenir
de Xamlon après l'apparition des kits de développement produits
par Microsoft. Pour ma part, je recommande la patience.
Pour celui que l'investissement ne rebute pas et qui souhaite
être à la pointe de la technologie, Xamlon est cependant une
bonne rampe de lancement. Il fait par ailleurs partie de l'offre
DVD partenaires de Microsoft.


Les sources présentées sur cette page sont libres de droits
et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation
constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ©
. Aucune reproduction,
même partielle, ne peut être faite de ce site et de l'ensemble de son contenu :
textes, documents, images, etc. sans l'autorisation expresse de l'auteur.
Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 €
de dommages et intérêts.
Cette page est déposée.