Initiation à WPF

Hervé Labénère

Au travers de la réalisation d'un mini lecteur MP3, ce tutoriel aborde différents concepts de la technologie WPF comme le DataBinding, les styles et les thèmes ainsi que le format de tag ID3Tag(1) (1)

N'hésitez pas à commenter cet article ! Commentez

- Titre : Initiation à WPF

- Auteur : Hervé Labénère

- Parution : 23 février 2009

- Licence : 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 ® 2008 Hervé Labénère. 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.

Sommaire

- I. SPÉCIFICATIONS

- II. PRE-REQUIS

- III. ARCHITECTURE

- IV. RÉALISATION

- IV-A. Conception de la couche Entité (EntiteMorceau.cs)

- IV-B. Conception de la couche Contrôle (ControleEditeurTags.cs et ControleLecture.cs))

- IV-C. Conception de la couche Dialogue (DialogueIHMLecteur.xaml)

- IV-C-1. Liaison de données (DataBinding) avec une source Xml, en WPF

- IV-C-2. Lier une propriété à la valeur d'un contrôle

- IV-C-3. Mettre un peu de style dans notre application (DialogueIHMLecteur.xaml)

- V. CONCLUSION

I - SPÉCIFICATIONS

L'application se présente de la façon suivante : Un lecteur MP3 rudimentaire reposant sur la brique WPF pour l'aspect visuel et DirectX.AudioVideoPlayBack pour les fonctionnalités audios, plus quelques fonctions d'édition de tags MP3.

Fonctionnalités requises

1. Lecture d?un morceau MP3

2. Pause

3. Arrêt

4. Parcourir de fichier sur le PC pour sélectionner le fichier à lire

5. Un éditeur de tags pour modifier les tags associés au morceau MP3 en cours de lecture

6. Une image d'album associée au morceau MP3, qui peut être redimensionnée à la volée via un slider

Liste des composants dans la partie Lecteur audio

Nom Composant

Type

Titre

Lecteur

DirectX.AudioVideoPlayBack.Audio

btnParcourir

Button

…

lTitre

Label

Titre de la chanson

btnPause

Button

Pause

btnArret

Button

Arrêt

btnLecture

Button

Lecture

imgAlbum

Image

sliderImg

Slider

cbxStyle

comboBox

Style Garçon / Style Fille

Liste des composants dans la partie Editeur de tags MP3

Nom Composant

Type

Titre

txtbTitre

TextBox

txtbAuteur

TextBox

btnOk

Button

Enregistrer

II - PRE-REQUIS

Pour exécuter l'application

1. .Net Framework 3.0

2. Direct X 9.0 (DirectX.AudioVideoPlayBack.dll, mais aussi DirectX.dll) SDK DirectX 9

Pour réaliser l'application

1. Visual C# Express 2008 ici

III - ARCHITECTURE

Pour construire une application modulaire et évolutive, l'approche MDA conseille de distinguer trois couches au sein de l'application, qui interagissent entre elles :

1. La couche graphique (classe Dialogue) ;

2. La couche métier (classes Contrôle) ;

3. La couche données (classe Entité).

Ça tombe bien, car WPF en décrivant les interfaces grâce à une syntaxe déclarative (le Xaml), isole la couche graphique du reste de l'application. En ce qui concerne la couche métier, on recense deux classes, une concernant les fonctions de lecture et une concernant les fonctions d'édition de tags.

La réalisation de l'application suivra le sens suivant : Classe d'entité -> Classes de contrôle -> Classe de dialogue.
Car la classe de dialogue encapsule (et donc dépend de) les classes de contrôle qui elles-mêmes encapsulent la classe d'entité. En effet, la classe DialogueIHMLecteur appellera les méthodes des classes ControleLecture et ControleEditeur, suivant les actions de l'utilisateur.

IV - RÉALISATION

Ouvrir Visual C# Express 2008. Choisir dans le Menu File -> New Project -> WPF Application

IV-A - Conception de la couche Entité (EntiteMorceau.cs)

Cette couche ne contient que la définition de la structure strTagsMP3 qui est une représentation d'un fichier MP3 avec ses tags titre, artiste, et genre

public struct strTagsMP3
{
public string tagOK;        // 03 car
public String titre;       // 30 car
public String artiste;     // 30 car
public short genre;        // 01 car
// total                     128 car
}

La classe EntiteMorceau implémente cette structure et contient simplement les accesseurs pour accéder en lecture / modification à chacun des tags.

public class EntiteMorceau
{

private strTagsMP3 TagsMP3;

public strTagsMP3 accTagsMP3
{
get
{
return TagsMP3;
}

set
{
TagsMP3.tagOK = value.tagOK;
TagsMP3.titre = value.titre;
TagsMP3.artiste = value.artiste;
TagsMP3.genre = value.genre;
}

}

public string accTagOk
{
get
{
return TagsMP3.tagOK;
}
set
{
TagsMP3.tagOK = value;
}
}

public String accTitre
{
get
{
return TagsMP3.titre;
}
set
{
TagsMP3.titre = value;
}
}

public String accArtiste
{
...
}

public short accGenre
{
...
}
}
}

IV-B - Conception de la couche Contrôle (ControleEditeurTags.cs et ControleLecture.cs))

La classe ControleEditeur contient en particulier les trois méthodes OuvrirFichier, PossedeTag et EcrireTag qui sont des adaptations du code source du hors-série code(r) #4. La méthode PossedeTag ouvre un filestream sur le fichier passé en paramètre, se positionne 128 bits avant la fin du fichier, lit les trois premiers octets du filestream et détecte si le tableau de bits ainsi stocké contient la chaîne « TAG ».
Si la chaîne « TAG » est stockée, c'est que ce fichier MP3 contient effectivement des tags.

private bool PossedeTag(string fichier)
{
// ouvrir le fichier
FileStream f = new FileStream(fichier, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
// se positionner sur le tag
f.Seek(-128, SeekOrigin.End);

//ouverture d'un binaryreader sur le fichier
BinaryReader br = new BinaryReader(f);

byte[] tagArray = br.ReadBytes(3);
// lire les 3 premiers octets
string tag = System.Text.Encoding.Default.GetString(tagArray);
br.Close();

// si ces octets contiennent "TAG", c'est qu'il y a un tag
if (tag.CompareTo("TAG")==0)
return true;
else
return false;
}

La méthode OuvrirFichier prend en paramètre le chemin du fichier à ouvrir (type String), lit les tags du fichier en se positionnant 128 bits avant la fin du fichier puisque c'est à cet endroit d'un fichier MP3 que les tags sont stockés.
Elle vide la structure de l'objet de type EntiteMorceau de ses éventuelles valeurs en mémoire. Elle appelle la méthode PossedeTag. Si le fichier ne possède pas de tag MP3, elle rend l'objet Morceau tel quel, c'est-à-dire vide.
Sinon, la méthode se positionne 128 bits avant la fin du fichier pour lire les tags (f.seek(-128, SeekOrigin.End)). On lit alors le contenu des tags, en éliminant le caractère « / » qui fonctionne comme un espace vide.
Pour le dernier caractère, on utilisera la méthode PeekChar, pour le lire, sans avancer dans le fichier, sinon une exception est levée.
Puis on attribue grâce aux accesseurs les tags adéquats titre, artiste, genre. Concernant les genres musicaux, on a simplifié la liste en ne gardant que six genres principaux :

- genres[8] => « Jazz » ;

- genres[9] => « Metal » ;

- genres[13] => « Pop » ;

- genres[15] => « Rap »;

- genres[18] => « Techno » ;

- genres[32] => « Classical ».

Si le fichier contient un genre différent, on lui attribue le genre[0] c'est-à-dire « divers »

public class ControleEditeurTags
{
private EntiteMorceau Morceau = null;

public EntiteMorceau OuvrirFichier(String fichier)
{
if (fichier != null)
{
Morceau = new EntiteMorceau();
// on "vide" le tag
Morceau.accTagOk = "TAG";
Morceau.accTitre = "";
Morceau.accArtiste = "";
Morceau.accGenre = -1;

// si le mp3 ne possède pas de tag à lire, on retourne le tag vide
if (!PossedeTag(fichier))
{
return Morceau;
}

// ouvrir le fichier
FileStream f = new FileStream(fichier, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
// se positionner sur le tag
f.Seek(-128, SeekOrigin.End);

//ouverture d'un binaryreader sur le fichier
BinaryReader br = new BinaryReader(f);

char charsToTrim = '/';

// lire chaque élément
Morceau.accTagOk = System.Text.Encoding.Default.GetString(br.ReadBytes(3));
Morceau.accTitre = System.Text.Encoding.Default.GetString(br.ReadBytes(30));
Morceau.accTitre = Morceau.accTitre.TrimEnd(charsToTrim);
Morceau.accArtiste = System.Text.Encoding.Default.GetString(br.ReadBytes(30));
Morceau.accArtiste = Morceau.accArtiste.TrimEnd(charsToTrim);
Morceau.accGenre = (short)br.PeekChar();

//Attribution d'un genre par défaut "Divers", si le fichier n'appartient à aucune des grandes familles de genres musicaux

if (Morceau.accGenre != 8 && Morceau.accGenre != 9 && Morceau.accGenre != 13 && Morceau.accGenre != 15 && Morceau.accGenre != 18 && Morceau.accGenre != 32)
{
Morceau.accGenre = 0;

}

// fermeture du fichier (la fermeture du BinaryReader ferme le FileStream)
br.Close();

// retourner le tag lu
return Morceau;
}
else
{
return null;
}
}

}

Le contenu de notre objet Morceau, généré à partir des boites de dialogue de l'IHM est écrit dans les différents tags. Mais vu que chaque champ de tag a une taille fixe dans le fichier MP3, on complète les espaces vides par le caractère « / », qui sera ignoré à la lecture.

- Titre : 30 car

- Artiste : 30 car

- Genre : 1 car

public void EcrireTag( string fichier)
{
// ouvrir le fichier
FileStream f = new FileStream(fichier, FileMode.Open, FileAccess.Write, FileShare.ReadWrite);
// se positionner suivant qu'il y a un dj tag ou pas
if( PossedeTag(fichier) )
f.Seek(-128, SeekOrigin.End);
else
f.Seek(0, SeekOrigin.End);

// écrire les infos avec un BinaryWriter
BinaryWriter bw = new BinaryWriter(f);
bw.Write(System.Text.Encoding.Default.GetBytes(Morceau.accTagOk));

// pour chaque élément, s'il ne fait pas la longueur voulue, on rajoute le car "/"
bw.Write(System.Text.Encoding.Default.GetBytes(Morceau.accTitre));
for(int i = 0; i < 30-Morceau.accTitre.Length; i ++) bw.Write((char)'/');

bw.Write(System.Text.Encoding.Default.GetBytes(Morceau.accArtiste));
for (int i = 0; i < 30 - Morceau.accArtiste.Length; i++) bw.Write((char)'/');

bw.Write((short) Morceau.accGenre);

bw.Close();

}

On génère l'objet Morceau à partir du stackpanel de l'IHM contenant les contrôles et on appelle la méthode EcrireTag

public void EditerTag(StackPanel objStckPan, string fichierATagger)
{
if (Morceau != null)
{
Morceau.accTitre = ((TextBox)objStckPan.FindName("txtbTitre")).Text;
Morceau.accArtiste = ((TextBox)objStckPan.FindName("txtbAuteur")).Text;
Morceau.accGenre = short.Parse(((ComboBox)objStckPan.FindName("cbxType")).SelectedValue.ToString());

}

EcrireTag(fichierATagger);
}

La classe ControleLecture encapsule un objet DirectX AudioVideoPlayBack.Audio. Les méthodes _lire(), _arreter(), _interrompre(), ne font qu'appeler les méthodes correspondantes de l'objet Audio : play(), stop(), pause().
L'espace de nom à ajouter est bien Microsoft.DirectX.AudioVideoPlayBack. Pour ouvrir le fichier MP3 en lecture, on utilisera la classe OpenFileDialog, sans oublier d'ajouter l'espace de nom System.IO dans les directives Using du fichier. Une énumération typeFichier permet de factoriser la méthode _ouvrir en l'utilisant aussi bien pour l'ouverture de fichiers musicaux que de fichiers d'images.

public class ControleLecture
{
public Audio Lecteur;

public void _lire()
{
if (Lecteur != null)
{
Lecteur.Play();
}

}

public string _ouvrir(typeFichier tf)
{
OpenFileDialog ofd = new OpenFileDialog();
if (tf == typeFichier.musique)
{
ofd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyMusic);
ofd.Filter = "mp3 files (*.mp3)|*.mp3";
ofd.FilterIndex = 2;
ofd.RestoreDirectory = true;

if (ofd.ShowDialog() == DialogResult.OK)
{
try
{
if (ofd.OpenFile() != null)// On attribue le chemin du fichier à lire au
{
Lecteur = new Audio(ofd.FileName, false);
return ofd.FileName;

}
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
return null;
}
}
return null;
}
else if (tf == typeFichier.image)
{
ofd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
ofd.Filter = "JPG (*.jpg)|*.jpg";
ofd.FilterIndex = 2;
ofd.RestoreDirectory = true;

if (ofd.ShowDialog() == DialogResult.OK)
{
try
{
if (ofd.OpenFile() != null)// On attribue le chemin du fichier à lire au
{

return ofd.FileName;

}
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
return null;
}
}
return null;

}
return null;
}

public void _arreter()
{
if (Lecteur != null)
{
Lecteur.Stop();
}
}

public void _interrompre()
{
if (Lecteur != null)
{
Lecteur.Pause();
}
}
}

IV-C - Conception de la couche Dialogue (DialogueIHMLecteur.xaml)

L'IDE Xaml de Visual Studio est scindé en deux horizontalement : une interface RAD où on dépose les contrôles, et une page affichant le code Xaml correspondant.

Dans le fichier DialogueIHMLecteur.xaml, tapez le texte suivant :

<Page x:Class="LecteurAudioXaml.Dialogues.DialogueIHMLecteur"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Page1" Height="505" Width="600">
<Grid>
<TabControl Margin="10,35,36,39" Name="tabControl1">
<TabItem Name="tbiLecteur">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="12*" />
<ColumnDefinition Width="532*" />
</Grid.ColumnDefinitions>
<Button Click="btnLecture_Click" Grid.Column="1" Height="70" HorizontalAlignment="Left"
Margin="140,106.5,0,0" Name="btnLecture" VerticalAlignment="Top" Width="70">Lecture</Button>
<Button Click="btnPause_Click" Grid.Column="1" Height="70" Margin="253,106.5,209,0" Name="btnPause"
VerticalAlignment="Top">Pause</Button>
<Button Click="btnArret_Click" Grid.Column="1" Height="70" HorizontalAlignment="Right" Margin="0,106.5,97,0"
Name="btnArret" VerticalAlignment="Top" Width="70">Arrêt</Button>
<Button Click="btnParcourir_Click" Grid.Column="1" Height="70" HorizontalAlignment="Right" Margin="0,25,97,0"
Name="btnParcourir" VerticalAlignment="Top" Width="70">...</Button>
<Label Grid.Column="1" Height="32" HorizontalAlignment="Left" Margin="12,25,0,0" Name="lTitre"
VerticalAlignment="Top" Width="219">Titre de la chanson</Label>
<Button Click="btnImg_Click" Grid.Column="1" HorizontalAlignment="Right" Margin="0,196,80,145" Name="btnImg" Width="70">
...</Button>
<Image Grid.Column="1" Margin="140,182,192,29" Name="imgAlbum" Stretch="Fill" />
<Slider Grid.Column="1" Height="21" HorizontalAlignment="Right" Margin="0,0,8,97"
Maximum="200" Name="sliderImageAlbum" StyleTickFrequency="1" Value="200"
VerticalAlignment="Bottom" Width="175" />
</Grid>
</TabItem>
<TabItem Name="tbiEditeur">
<StackPanel Name="stckPEditeur">
<Label>Titre</Label>
<TextBox Name="txtbTitre" />
<Label>Auteur</Label>
<TextBox Name="txtbAuteur" />
<Label>Type</Label>
<ComboBox Height="23"  Name="cbxType" Width="120" />
<Button Click="btnOk_Click" Name="btnOk" VerticalAlignment="Top" Width="75">Enregistrer</Button>
</StackPanel>
</TabItem>
</TabControl>
<ComboBox Height="23" HorizontalAlignment="Left" Margin="15,10,0,0" Name="cbxStyle" VerticalAlignment="Top" Width="120">
<ComboBoxItem IsSelected="True">Style Garçon</ComboBoxItem>
<ComboBoxItem>Style Fille</ComboBoxItem>
</ComboBox>
</Grid>
</Page>

ce qui devrait donner un résultat proche de ceci pour l'onglet Lecteur :

et ceci pour l'onglet éditeur :

IV-C-1 - Liaison de données (DataBinding) avec une source Xml, en WPF

Pour peupler notre liste déroulante dans l'onglet éditeur, qui contient les différents styles musicaux, nous allons utiliser un fichier Xml comme source. Voici le fichier type_morceau.xml :

<?xml version="1.0"?>
<types>
<Item>
<titre>divers</titre>
<valeur>0</valeur>
</Item>
<Item>
<titre>jazz</titre>
<valeur>8</valeur>
</Item>
<Item>
<titre>métal</titre>
<valeur>9</valeur>
</Item>
<Item>
<titre>pop-rock</titre>
<valeur>13</valeur>
</Item>
<Item>
<titre>rap</titre>
<valeur>15</valeur>
</Item>
<Item>
<titre>techno</titre>
<valeur>18</valeur>
</Item>
<Item>
<titre>classique</titre>
<valeur>32</valeur>
</Item>
</types>

Il contient un nombre restreint de styles musicaux, mais bien suffisants pour notre démonstration. Nous rajouterons donc comme ressource à notre fichier DialogueIHMLecteur.xaml, le fournisseur Xml (XmlDataProvider) type_morceau.xml dont
l'identifiant désigné par la balise x :key sera dataProvider.

<Window.Resources>
<XmlDataProvider x:Key="dataProvider" XPath="types" Source="../type_morceau.xml"></XmlDataProvider>
</Window.Resources>

Nous n'avons donc plus qu'à assurer le mappage entre la liste déroulante (Text et Value) et les deux champs (titre et valeur) de la source Xml, grâce aux instructions DisplayMemberPath et SelectedValuePath, sans oublier de préciser grâce à la requête XPath que le champ du fichier source concerné est Item.

<ComboBox Height="23" Width="120"  Name="cbxType" ItemsSource="{Binding Source={StaticResource dataProvider},XPath=Item}"
DisplayMemberPath="titre" SelectedValuePath="valeur">
</ComboBox>

Voici donc sans une seule ligne de code (mais uniquement avec des balises Xaml), ce que vous devriez obtenir :

IV-C-2 - Lier une propriété à la valeur d'un contrôle

Un des gros avantages de WPF, c'est que le rendu graphique est entièrement vectoriel. Nous allons illustrer cette idée, par la mise au point d'une image qui diminue à la volée suivant la valeur d'un slider.

Pour faire cela, une simple instruction de binding suffit dans la balise du contrôle Image :

<Image Margin="140,182,192,29" Name="imgAlbum" Stretch="Fill" Width="{Binding ElementName=sliderImageAlbum, Path=Value}"
Height="{Binding ElementName=sliderImageAlbum, Path=Value}"
Grid.Column="1" />
<Slider Height="21" Margin="0,0,8,97" Name="sliderImageAlbum"  Style="{DynamicResource TplSlider}"
Value="200" Maximum="200" TickFrequency="1" HorizontalAlignment="Right"
VerticalAlignment="Bottom" Width="175" Grid.Column="1" />

IV-C-3 - Mettre un peu de style dans notre application (DialogueIHMLecteur.xaml)

Que diriez-vous de personnaliser un peu le style de l'application, comme on peut le voir sur certains sites web où l'utilisateur choisit la feuille de style qui veut appliquer sur la page. Il faut alors définir les propriétés de chaque contrôle dans une page Xaml que l'on désigne par le terme de Theme. Notre fichier s'appelle ThemeGarcon.xaml et voici son contenu :

ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<Style x:Key="TplArrierePlan">
<Setter Property="Control.Background" Value="CornflowerBlue"/>
</Style>

<Style BasedOn="{StaticResource TplArrierePlan}"
TargetType="Button"
x:Key="TplBouton">
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property=" HorizontalAlignment" Value="Left"/>
<Setter Property="Width" Value="70"/>
<Setter Property="Height" Value="70"/>
</Style>
<Style BasedOn="{StaticResource TplArrierePlan}"
TargetType="Label"
x:Key="TplLabel">
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property=" HorizontalAlignment" Value="Left"/>
<Setter Property="Width" Value="300"/>
<Setter Property="Height" Value="32"/>
</Style>
<Style BasedOn="{StaticResource TplArrierePlan}"
TargetType="Slider"
x:Key="TplSlider">
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="32"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="HorizontalAlignment" Value="Right"/>

</Style>

<Style BasedOn="{StaticResource TplArrierePlan}"
TargetType="ComboBox"
x:Key="TplCbx">

</Style>

</ResourceDictionary>

Un style de base dont l'identifiant est TplArrierPlan, qui applique une couleur d'arrière-plan bleue aux contrôles :

<Style x:Key="TplArrierePlan">
<Setter Property="Control.Background" Value="CornflowerBlue"/>
</Style>

Tous les autres styles se réfèrent à ce style de base, pour définir d'autres propriétés plus spécifiques. Ici le style TplBouton dont le contrôle cible est de type Button, définit des propriétés de police, alignement, de longueur, de hauteur.

<Style BasedOn="{StaticResource TplArrierePlan}"
TargetType="Button"
x:Key="TplBouton">
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property=" HorizontalAlignment" Value="Left"/>
<Setter Property="Width" Value="70"/>
<Setter Property="Height" Value="70"/>
</Style>

On définit avec des valeurs différentes le thème Fille dans le fichier ThemeFille.xaml. Ces deux fichiers sont placés dans le répertoire Themes de l'application Par défaut, on attribue le thème
Garçon à l'application, en appliquant cette directive dans le fichier App.xaml

<ResourceDictionary x:Name="Style">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary  Source="Themes\StyleGarcon.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

Maintenant, dans le fichier DialogueIHMLecteur.xaml nous devons lier chaque contrôle avec son style défini dans le thème, grâce à la directive Style, en précisant bien que c'est une ressource dynamique, puisque le style des contrôles est susceptible d'évoluer suivant les actions de l'utilisateur.

<Button Name="btnLecture" Click="btnLecture_Click"   Margin="140,106.5,0,0"  Style="{DynamicResource TplBouton}"
Grid.Column="1" Height="70" VerticalAlignment="Top" HorizontalAlignment="Left" Width="70">Lecture</Button>

<Label  Name="lTitre"  Margin="12,25,0,0" Style="{DynamicResource TplLabel}" Grid.Column="1"
Height="32" VerticalAlignment="Top" HorizontalAlignment="Left" Width="219">Titre de la chanson</Label>

Puis grâce à une liste déroulante, on donne le choix à l'utilisateur de sélectionner son style préféré.

private void cbxStyle_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ResourceDictionary rd = new ResourceDictionary();

ResourceDictionary newDictionary = new ResourceDictionary();
switch (cbxStyle.SelectedIndex)
{
case 0:
newDictionary.Source =  new Uri("Themes/StyleGarcon.xaml",UriKind.Relative);
break;
case 1:
newDictionary.Source =  new Uri("Themes/StyleFille.xaml",UriKind.Relative);
break;
}
System.Windows.Application.Current.Resources.MergedDictionaries[0] = newDictionary;

}

Voici, le style appliqué lorsque l'utilisateur choisit dans la liste déroulante « Style Fille » :

V - CONCLUSION

Par ce bref tutoriel, vous avez appris à réaliser un mini lecteur MP3 en utilisant les technologies WPF et XAML. Ces technologies semblent prometteuses, puisqu'elles ont l'énorme avantage de dissocier totalement la couche Présentation de la couche Métier, idée relativement évidente dans une architecture web, mais plutôt étrangère aux architectures Windows classiques. XAML se propose donc de réunir le meilleur du monde Windows et du monde Web.

(1)Organisme ID3Tag : http://www.id3.org/