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
- Lecture d?un morceau MP3
- Pause
- Arrêt
- Parcourir de fichier sur le PC pour sélectionner le fichier à lire
- Un éditeur de tags pour modifier les tags associés au morceau MP3 en cours de lecture
- 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
- .Net Framework 3.0
- Direct X 9.0 (DirectX.AudioVideoPlayBack.dll, mais aussi DirectX.dll) SDK DirectX 9
Pour réaliser l'application
- 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 :
- La couche graphique (classe Dialogue) ;
- La couche métier (classes Contrôle) ;
- 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.