VIII. Comment faire▲
VIII-A. SplashScreen▲
C'est une fenêtre qui s'ouvre au démarrage d'un programme, qui montre simplement une belle image, ensuite la fenêtre « Splash » disparaît et la fenêtre principale apparaît.
En WP c'est simple :
Il y a dans l'explorateur de solution un fichier nommé SplashScreenImage.jpg, il fait 480*800 pixels. Il suffit de double-cliquer dessus pour se retrouver dans Paint et modifier le SplashScreen.
Dans ses propriétés, « Action de génération » doit être à « contenu ».
S'il n'y a pas ce fichier, il n'y a pas de SplashScreen.
Si on veut que le SplashScreen reste plus longtemps il faut, dans le constructeur de la page (sous InitializeComponent) mettre une Thread.Sleep (voir chapitre « perdre du temps »).
using
System.
Threading;
namespace
customcontrol
{
public
partial
class
MainPage :
PhoneApplicationPage
{
// Constructeur
public
MainPage
(
)
{
InitializeComponent
(
);
Thread.
Sleep
(
500
);
VIII-B. Image de fond de page, icône▲
Dans le projet il y a déjà par défaut :
ApplicationIcon.png : icône de l'application (62*62 pixels) qui sera affichée dans la liste des applications ;
Background.png : icône qui sera affichée lorsque l'application est "épinglée" (pinned) sur la page d'accueil du téléphone (173*173 pixels).
Dans les propriétés du projet (menu "Projet" puis "Propriétés de") on peut modifier cela.
Dans l'explorateur de solution à droite, il faut double-cliquer sur le nom d'une de ces deux images pour se retrouver dans Paint (ou Paint.Net ici) et ainsi la modifier.
Pour ses deux icônes si on veut un fond ayant la couleur du thème il faut, au lieu de mettre un fond coloré, mettre un fond transparent, un avant plan blanc et sauvegarder votre image en .PNG avec support de la transparence (sauvegarde en 32 bits). J'utilise Paint.Net pour cela.
Ici on a la liste des applications avec les icônes (ApplicationIcon.png de 63x63 px), WP ajoute le nom de l'application à droite.
Ici, pour l'icône épinglée (Background.png 173x173 px) WP ajoute sur l'image le nom de l'application ('My app' ici) avec des caractères de 20 px de hauteur, à 7 px du bord gauche, et 7 px du bas.
Comment mettre une image de fond dans une page Panorama ou Pivot ?
<
controls:
Panorama Title=
"mon application"
>
<
controls:
Panorama.
Background>
<
ImageBrush ImageSource=
"Panoramafond.png"
/>
</
controls:
Panorama.
Background>
Pour un panorama, si l'image à une largeur de deux pages mais que le panorama a quatre pages, le fond se déroulera lentement et s'affichera sur les quatre pages.
Comment mettre un fond sur une page simple ?
Il faut mettre l'image dans le background de la grille racine :
<!--LayoutRoot est la grille racine où tout le contenu de la page est placé-->
<Grid
x
:
Name
=
"LayoutRoot"
>
<Grid.Background>
<ImageBrush
ImageSource
=
"PanoramaBackground.png"
Stretch
=
"UniformToFill"
/>
</Grid.Background>
Le problème est que parfois on a (si on utilise un thème avec arrière-fond clair) une bande blanche en haut.
Dans ce cas il ne faut pas afficher le SystemTray et il ne faut pas mettre de taille dans la Grid LayoutRoot (ainsi elle remplira la totalité de l'espace).
Si on a modifié la Grid à la souris, il apparaît la propriété Height, ensuite on a du mal à gérer, il vaut donc mieux enlever « Height=" " ».
shell:SystemTray.IsVisible="False"
<!--LayoutRoot est la grille racine où tout le contenu de la page est placé-->
<Grid
x
:
Name
=
"LayoutRoot"
>
Comment mettre en fond de page une couleur aléatoire :
ContentPanel.
Background =
new
SolidColorBrush
(
Color.
FromArgb
(
255
,
(
byte
)rand.
Next
(
256
),
(
byte
)rand.
Next
(
256
),
(
byte
)rand.
Next
(
256
)));
VIII-C. Positionnement dynamique des contrôles▲
Layout Absolu
Dans un positionnement en Layout Absolu, les éléments sont placés dans un canvas à une position exacte absolue relative à leur élément parent.
Cela ne prend pas en compte les dimensions de l'écran.
Layout dynamique
Permet un positionnement quel que soit la résolution de l'écran.
On utilise un StackPanel ou surtout une Grid.
Dans les cellules, on met des contrôles avec les propriétés Width et Height= Auto. Ils occupent toute la cellule (Margin permet de laisser un espace autour).
S'il y a un texte à afficher en totalité on donne une valeur à MinHeight et MinWidth pour que le texte ne soit pas tronqué.
On donne des dimensions relatives pour les colonnes et les lignes de la Grid (dans RowDefinition et ColumnDefinition) à l'aide des *.
L'utilisateur utilise son doigt pour toucher les éléments visuels ; il est conseillé d'agrandir la zone de touché bien au-delà de la zone visuelle. Les éléments trop rapprochés les uns des autres introduisent un risque pour l'utilisateur d'appuyer sur le mauvais bouton. Les contrôles de Silverlight sont déjà optimisés par nature, en ayant naturellement une Margin importante.
Il y a des contraintes de taille et Microsoft donne les conseils suivants :
une cible doit être au moins un carré de 34 px avec un espacement d'au moins 8 px. L'agrandissement de la zone de touché doit être d'au moins 60 % de la taille de l'élément. Pour les éléments textuels touchables, la taille de police doit être d'au moins 15 pts.
VIII-D. Navigation entre pages, transfert de paramètres▲
La page de démarrage est par défaut « MainPage.xaml ».
Si on veut en mettre une autre, il faut dans l'explorateur de solution, cliquer sur « Properties » puis double-cliquer sur « WMAppManifest.xml » et modifier la ligne :
<DefaultTask Name
=
"_default"
NavigationPage
=
"MainPage.xaml"
/>
Comment dans une page ouvrir une autre page nommée « PageConvert.xaml » ?
En C# :
NavigationService.
Navigate
(
new
Uri
(
"/PageConvert.xaml"
,
UriKind.
Relative));
La Classe NavigationService contient ce qu'il faut pour naviguer. Les méthodes Navigate mais aussi GoBack, GoForward…
En XAML : on peut aussi utiliser un hyperlinkButton :
<HyperlinkButton
NavigateUri
=
"/PageConvert.xaml"
Content
=
"Aller à pageconvert"
/>
Exemple complet
On met trois boutons dans une page, cliquer sur un des boutons ouvre la page correspondante.
Code XAML des boutons :
<Button
x
:
Name
=
"ButtonRouge"
Content
=
"Rouge"
Click
=
"Button_Click"
Width
=
"200"
Height
=
"75"
/>
<Button
x
:
Name
=
"ButtonVert"
Content
=
"Vert"
Click
=
"Button_Click"
Width
=
"200"
Height
=
"75"
/>
<Button
x
:
Name
=
"ButtonBleue"
Content
=
"Bleue"
Click
=
"Button_Click"
Width
=
"200"
Height
=
"75"
/>
Code C# :
private
void
Button_Click
(
object
sender,
RoutedEventArgs e)
{
Button clickedButton =
sender as
Button;
switch
(
clickedButton.
Name)
{
case
"ButtonRouge"
:
NavigationService.
Navigate
(
new
Uri
(
"/PageRouge.xaml"
,
UriKind.
Relative));
break
;
case
"ButtonVert"
:
NavigationService.
Navigate
(
new
Uri
(
"/PageVerte.xaml"
,
UriKind.
Relative));
break
;
case
"ButtonBleue"
:
NavigationService.
Navigate
(
new
Uri
(
"/PageBleue.xaml"
,
UriKind.
Relative));
break
;
}
}
Comment passer un texte ?
Quand on clique sur button1, ouvrir SecondPage.xaml. Passer « montexte » de la page actuelle à la page SecondPage.xaml.
On va utiliser les QueryString : pour l'appel d'une nouvelle page avec NavigationService.Navigate, on ajoute le texte à l'URI (querystring= 'msg', valeur="montexte"). C'est comme pour une page Web, on utilise le point d'interrogation suivi d'une clé et de sa valeur. Dans la page qui s'ouvre on récupère le texte par NavigateContext.QueryString :
private
void
button1_Click
(
object
sender,
RoutedEventArgs e)
{
NavigationService.
Navigate
(
new
Uri
(
"/SecondPage.xaml?msg="
+
"montexte"
,
UriKind.
Relative));
}
Dans SecondPage :
protected
override
void
OnNavigatedTo
(
System.
Windows.
Navigation.
NavigationEventArgs e)
{
base
.
OnNavigatedTo
(
e);
string
msg =
""
;
if
(
NavigationContext.
QueryString.
TryGetValue
(
"msg"
,
out
msg))
//msg contient montexte.
}
La méthode OnNavigatedTo de la page s'exécute lorsque la page devient active. Elle est préférable à la méthode « Loaded » qui peut s'exécuter plusieurs fois.
On surcharge donc cette méthode pour récupérer le QueryString.
On peut mettre plusieurs textes séparés par '&'.
private
void
button1_Click
(
object
sender,
RoutedEventArgs e)
{
NavigationService.
Navigate
(
new
Uri
(
"/SecondPage.xaml?msg=montexte&msg2=montexte2"
,
UriKind.
Relative));
}
Comment passer un objet ?
En utilisant le dictionnaire d'état temporaire avec PhoneApplicationService.Current.State permettant de stocker les données de l'application, un objet en particulier.
Il faut au préalable inclure l'espace de noms Microsoft.Phone.Shell.
using Microsoft.Phone.Shell;
Dans la page de départ :
Personne MyPersonne;
// soit un objet MyPersonne de type Personne
PhoneApplicationService.
Current.
State[
"ItemEnCours"
]
=
MyPersonne;
Ouvrir la seconde page et surcharger OnNavigateTo dans celle-ci. Récupérer l'objet dans les données de l'application.
using
Microsoft.
Phone.
Shell;
namespace
CalculeTout
{
public
partial
class
PageConvert :
PhoneApplicationPage
{
//Variable
ElementDeListe currentCalcul;
Boolean IsInverse;
public
PageConvert
(
)
{
InitializeComponent
(
);
}
protected
override
void
OnNavigatedTo
(
System.
Windows.
Navigation.
NavigationEventArgs e)
{
//Recupération de l'objet de type personne
base
.
OnNavigatedTo
(
e);
MyLocalPersonne =
(
personne) PhoneApplicationService.
Current.
State[
"ItemEnCours"
];
{
}
}
Comment retourner à la page précédente avec du code ?
NavigationService.
GoBack
(
);
Bien sûr il y a le bouton de gauche qui est le bouton « back ».
Comment détourner l'usage du « back Button », exécuter du code et annuler son effet ?
En surchargeant la méthode OnBackKeyPress :
protected
override
void
OnBackKeyPress
(
System.
ComponentModel.
CancelEventArgs e)
{
//votre code.
e.
Cancel =
true
;
//annule l'effet 'back'.
}
VIII-E. Validation de caractère dans un TextBox▲
Problème récurrent.
Je veux permettre la saisie des chiffres de 0 à 9 (uniquement) et permettre l'effacement (back) dans un TextBox nommé « entree1 », comment faire ?
Premièrement, il faut bien choisir le clavier virtuel (InputScope). On utilise « Digit ».
<TextBox
Name
=
"entree1"
KeyUp
=
"TesteTouche"
>
<TextBox.InputScope>
<InputScope>
<InputScopeName
NameValue
=
"Digit"
/>
</InputScope>
</TextBox.InputScope>
</TextBox>
Quand le TextBox à le focus, le clavier suivant apparaît :
Ce clavier ne permet de saisir que des chiffres mais parfois il y a d'autres caractères possible qu'il faut éliminer (le point ici).
On va donc intercepter l'évènement KeyUp (KeyUp="TesteTouche" dans le XAML précédent) qui se déclenche quand on appuie sur une touche, cela exécutera « TesteTouche » ; cette routine va accepter les seules touches permises.
private
void
TesteTouche
(
object
sender,
KeyEventArgs e)
{
//Controle validité des touches
//touches permisses
string
keys;
keys =
"1234567890"
;
TextBox TextBoxControl=
(
TextBox) sender;
for
(
int
j =
0
;
j <
TextBoxControl.
Text.
Length;
j++
)
{
if
(
!
(
keys.
Contains
(
TextBoxControl.
Text.
Substring
(
j,
1
))))
{
TextBoxControl.
Text=
TextBoxControl.
Text.
Remove
(
j,
1
);
TextBoxControl.
SelectionStart =
TextBoxControl.
Text.
Length;
}
}
}
keys est une string qui contient les touches permises.
Je caste le sender en TextBox.
Je regarde si chaque caractère du TextBox fait partie des caractères autorisés ; sinon je l'élimine.
Enfin je remets le curseur à la fin.
Si la saisie doit permettre les nombres fractionnaires, il faut accepter le bon séparateur décimal. Une méthode est de lire ce séparateur ('.' ou ',') dans la currentCulture et de l'ajouter à keys.
//touches permisses
string
keys;
keys =
"1234567890"
;
if
(
Thread.
CurrentThread.
CurrentCulture.
NumberFormat.
NumberDecimalSeparator ==
","
)
{
keys =
keys +
","
;
}
else
{
keys =
keys +
";"
;
}
Comment remplacer un caractère par un autre au cours de la saisie ?
Ici on va utiliser TextChanged :
<TextBox
Name
=
"entree2"
TextChanged
=
"ChangeCar"
>
</TextBox>
Et dans le code behind :
private
void
ChangeCar
(
object
sender,
TextChangedEventArgs e)
{
if
(
entree2.
Text.
Contains
(
"1"
))
{
entree2.
Text =
entree2.
Text.
Replace
(
"1"
,
"2"
);
entree2.
SelectionStart =
entree2.
Text.
Length;
}
}
Une autre méthode se trouve sur Internet.
Dans la routine C# on déclare un tableau contenant les Key (touches du clavier) à accepter.
Ensuite on teste si la touche tapée (qui est dans e.Key) est dans le tableau.
Si IndexOf retourne -1 , la touche n'y est pas.
Dans ce cas on indique d'annuler le traitement de la touche (e.Handled= true) :
private
void
entree1_KeyDown
(
object
sender,
KeyEventArgs e)
{
//Controle validité des touches
//touches permisses
Key[]
numeric =
new
Key[]
{
Key.
Back,
Key.
NumPad0,
Key.
NumPad1,
Key.
NumPad2,
Key.
NumPad3,
Key.
NumPad4,
Key.
NumPad5,
Key.
NumPad6,
Key.
NumPad7,
Key.
NumPad8,
Key.
NumPad9 };
// handles non numeric
if
(
Array.
IndexOf
(
numeric,
e.
Key) ==
-
1
)
{
e.
Handled =
true
;
}
}
Elle ne fonctionne pas si on utilise les codes touche keyD1, KeyD2… Il faut utiliser Key.NumPad1, KeyNumPad2… (e.Key retourne la touche mais avec les InputScope, le même code est retourné pour plusieurs touches ! Cela fonctionne avec l'exemple précédent mais pas avec toutes les touches).
Autre exemple plus complexe :
L'utilisateur tape du texte dans une TextBox nommée entree.
Eliminer '*', '#', " ".
Si le séparateur décimal est '.' , remplacer ',' par '.' et vice versa.
private
void
ChangeCar
(
object
sender,
TextChangedEventArgs e)
{
string
entr =
entree.
Text;
// Le séparateur est point ou virgule? Remplacer dans la saisie ; par , ou l'inverse
//Pour L'affichage ToString affiche suivant la culture
if
(
Thread.
CurrentThread.
CurrentCulture.
NumberFormat.
NumberDecimalSeparator ==
","
)
{
if
(
entr.
Contains
(
"."
) ||
entr.
Contains
(
"*"
) ||
entr.
Contains
(
"#"
) ||
entr.
Contains
(
" "
))
{
entr =
entr.
Replace
(
"."
,
","
);
entr =
entr.
Replace
(
"*"
,
""
);
entr =
entr.
Replace
(
"#"
,
""
);
entr =
entr.
Replace
(
" "
,
""
);
entree.
Text =
entr;
entree.
SelectionStart =
500
;
}
}
else
{
if
(
entr.
Contains
(
","
) ||
entr.
Contains
(
"*"
) ||
entr.
Contains
(
"#"
) ||
entr.
Contains
(
" "
))
{
entr =
entr.
Replace
(
","
,
"."
);
entr =
entr.
Replace
(
"*"
,
""
);
entr =
entr.
Replace
(
"#"
,
""
);
entr =
entr.
Replace
(
" "
,
""
);
entree.
Text =
entr;
entree.
SelectionStart =
500
;
}
}
}
VIII-F. Créer ses propres contrôles▲
Contrôle étendu
On peut utiliser un contrôle et étendre ses possibilités, on peut aussi créer son propre contrôle.
On va créer un contrôle étendu nommé « TextBoxNumerique » qui est une TextBox qui n'accepte que les chiffres de 0 à 9 et permet l'effacement.
On va créer une classe.
Menu « Projet » puis « Ajouter une classe ».
Notre classe va hériter de TextBox :
public
class
TextBoxNumerique :
TextBox
{}
On va surcharger la méthode OnKeyDown
(qui est exécutée quand l'évènement KeyDown va se produire) :
public
class
TextBoxNumerique :
TextBox
{
protected
override
void
OnKeyDown
(
KeyEventArgs e)
{
}
}
On y teste la touche appuyée, si elle n'est pas dans le tableau des touches valides (0 à 9 et Back), on annule la touche.
Voici le code complet :
using
System;
using
System.
Net;
using
System.
Windows;
using
System.
Windows.
Controls;
using
System.
Windows.
Documents;
using
System.
Windows.
Ink;
using
System.
Windows.
Input;
using
System.
Windows.
Media;
using
System.
Windows.
Media.
Animation;
using
System.
Windows.
Shapes;
namespace
customcontrol
{
public
class
TextBoxNumerique :
TextBox
{
private
readonly
Key[]
KeyValide =
new
Key[]
{
Key.
Back,
Key.
NumPad0,
Key.
NumPad1,
Key.
NumPad2,
Key.
NumPad3,
Key.
NumPad4,
Key.
NumPad5,
Key.
NumPad6,
Key.
NumPad7,
Key.
NumPad8,
Key.
NumPad9 };
public
TextBoxNumerique
(
)
{
this
.
InputScope =
new
InputScope
(
);
this
.
InputScope.
Names.
Add
(
new
InputScopeName
(
) {
NameValue =
InputScopeNameValue.
TelephoneLocalNumber }
);
}
protected
override
void
OnKeyDown
(
KeyEventArgs e)
{
if
(
Array.
IndexOf
(
KeyValide,
e.
Key) ==
-
1
)
{
e.
Handled =
true
;
}
base
.
OnKeyDown
(
e);
}
}
}
Dans le constructeur on indique l'InputScope qui est « TelephoneLocalNumber » ce qui ouvre le clavier téléphone pour l'utilisateur.
On peut maintenant ajouter le contrôle dans l'UI.
Menu « Build » puis « Générer la solution » et on retrouve le contrôle dans la boîte à outils en haut.
Il faut inclure l'espace de noms (ici avec un alias nommé « local ») puis mettre le customcontrol « TextBoxNumerique » dans le code XAML.
xmlns:local="clr-namespace:customcontrol"
<!--.............-->
<!--ContentPanel - placez tout contenu supplémentaire ici-->
<Grid
x
:
Name
=
"ContentPanel"
Grid.
Row
=
"1"
Margin
=
"12,0,12,0"
>
<
local
:
TextBoxNumerique
x
:
Name
=
"MonTB"
Margin
=
"0,43,183,470"
/>
</Grid>
</Grid>
On peut utiliser les champs et méthodes hérités de TextBox dans le code C# ( MonTB.Text, par exemple).
Le Custom Control hérite d'un contrôle ; il hérite aussi des propriétés et méthodes de ce contrôle.
On peut surcharger les méthodes pour modifier le comportement ou ajouter des champs et de nouvelles méthodes.
UserControl : contrôle utilisateur.
Là on va créer un UserControl qui est un contrôle composite contenant plusieurs contrôles et réutilisable.
Il dérive de la classe UserControl ; il a une propriété « Content » où on mettra les contrôles.
On va créer un UserControl contenant deux TextBox permettant de saisir un nom, un mot de passe, un bouton « ok » qui déterminera si le mot de passe est valide.
Créons le UserControl : menu « Projet » puis « Ajouter un nouvel élément ».
Choisir « Contrôle utilisateur Windows Phone » puis en bas donner le nom « MyControl », puis cliquer sur ajouter.
Il s'ouvre un onglet nommé « MyControl.xaml » ; dans le concepteur il n'y a même pas de bord.
À l'aide de la boîte à outils ajouter les contrôles nécessaires :
Cela donne le code XAML correspondant suivant :
<UserControl
x
:
Class
=
"Test.Mycontrol"
xmlns
=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns
:
x
=
"http://schemas.microsoft.com/winfx/2006/xaml"
xmlns
:
d
=
"http://schemas.microsoft.com/expression/blend/2008"
xmlns
:
mc
=
"http://schemas.openxmlformats.org/markup-compatibility/2006"
mc
:
Ignorable
=
"d"
FontFamily
=
"{StaticResource PhoneFontFamilyNormal}"
FontSize
=
"{StaticResource PhoneFontSizeNormal}"
Foreground
=
"{StaticResource PhoneForegroundBrush}"
d
:
DesignHeight
=
"480"
d
:
DesignWidth
=
"480"
>
<Grid
x
:
Name
=
"LayoutRoot"
>
<StackPanel
HorizontalAlignment
=
"Center"
>
<StackPanel.Resources>
<!--Create a Style for a TextBlock.-->
<Style
TargetType
=
"TextBlock"
x
:
Key
=
"TextBlockStyle"
>
<Setter
Property
=
"Foreground"
Value
=
"Aquamarine"
/>
<Setter
Property
=
"FontSize"
Value
=
"20"
/>
<Setter
Property
=
"VerticalAlignment"
Value
=
"Bottom"
/>
</Style>
<!--Create a Style for a TextBlock.-->
<Style
TargetType
=
"TextBox"
x
:
Key
=
"TextBoxStyle"
>
<Setter
Property
=
"Width"
Value
=
"200"
/>
<Setter
Property
=
"Height"
Value
=
"30"
/>
<Setter
Property
=
"Margin"
Value
=
"4"
/>
<Setter
Property
=
"FontSize"
Value
=
"10"
/>
<Setter
Property
=
"Background"
Value
=
"Beige"
/>
</Style>
</StackPanel.Resources>
<TextBlock
FontSize
=
"18"
Text
=
"Identification."
/>
<StackPanel
Orientation
=
"Vertical"
Height
=
"74"
Width
=
"207"
>
<TextBlock
Style
=
"{StaticResource TextBlockStyle}"
>
Nom:
</TextBlock>
<TextBox
Name
=
"_Nom"
Style
=
"{StaticResource TextBoxStyle}"
Height
=
"50"
FontSize
=
"15"
/>
</StackPanel>
<StackPanel
Orientation
=
"Vertical"
Height
=
"75"
>
<TextBlock
Style
=
"{StaticResource TextBlockStyle}"
>
Mot de passe:
</TextBlock>
<TextBox
Name
=
"_MotdePasse"
Style
=
"{StaticResource TextBoxStyle}"
Margin
=
"6,4,4,4"
Height
=
"50"
FontSize
=
"15"
/>
</StackPanel>
<Button
Name
=
"Bouton"
Width
=
"108"
Content
=
"Ok"
Height
=
"70"
Click
=
"Bouton_Click"
/>
</StackPanel>
</Grid>
</UserControl>
Il y a un TextBox nommé « _Nom », un autre nommé « _MotDePasse » et un bouton « Bouton ».
Maintenant on va créer le code propre au UserControl, dans les fonctions privées du UserControl.
Le code contenu dans les propriétés et méthodes privées va piloter le ou les contrôles internes. Ce code fournit un comportement propre au UserControl.
On va déclarer une variable bool nommée « _IsValid ». La fonction Bouton_Click (exécutée lorsque l'utilisateur clique sur « Ok ») contrôle la validité du nom et du mot de passe et si tout est valide cela entraîne _IsValid= true. Cela donne dans MyControl.xaml.cs.
using
System;
using
System.
Collections.
Generic;
using
System.
Linq;
using
System.
Net;
using
System.
Windows;
using
System.
Windows.
Controls;
using
System.
Windows.
Documents;
using
System.
Windows.
Input;
using
System.
Windows.
Media;
using
System.
Windows.
Media.
Animation;
using
System.
Windows.
Shapes;
namespace
Test
{
public
partial
class
Mycontrol :
UserControl
{
public
bool
_IsValid;
public
Mycontrol
(
)
{
InitializeComponent
(
);
_IsValid =
false
;
}
private
void
Bouton_Click
(
object
sender,
RoutedEventArgs e)
{
//Validation mot de passe
if
(
_Nom.
Text==
"toto"
&
_MotdePasse.
Text==
"pass"
)
{
_IsValid =
true
;
}
}
}
Notez bien que la validation est triviale ; ce n'est pas notre propos ici.
Bien, le UserControl fonctionne mais pour le moment il n'y a pas de liaison avec l'extérieur.
Les contrôles qui sont dans le UserControl sont privés au UserControl et leurs propriétés ou méthodes ne sont pas accessibles à l'extérieur. On ne peut donc pas avoir accès à la propriété « Text » du contrôle « _Nom » par exemple. Il va falloir rajouter dans le UserControl des membres et des méthodes public qui seront accessibles à l'utilisateur du composant. Ces champs public (avec get et set) et ces méthodes publiques créeront une interface.
Ajoutons donc l'interface du UserControl
On va donc créer une propriété « public » « IsValid » pour que l'utilisateur du contrôle puisse savoir si « nom/mot de passe » sont valides, une propriété public « Nom » cela peut être utile.
Enfin une méthode Clear « public » pour réinitialiser le UserControl :
public
bool
IsValid
{
get
{
return
_IsValid;
}
set
{
if
(
value
!=
_IsValid)
{
_IsValid =
value
;
}
}
}
public
string
Nom // Variable public avec son get et son set
{
get
{
return
_Nom.
Text;
}
set
{
if
(
value
!=
_Nom.
Text)
{
_Nom.
Text =
value
;
}
}
}
public
void
Clear
(
)
{
_Nom.
Text =
""
;
_MotdePasse.
Text =
""
;
_IsValid =
false
;
}
Le UserControl est terminé, un coup de menu « Build » puis « Générer » ; maintenant on va pouvoir utiliser le UserControl.
Utilisons le UserControl « MyControl ».
Dans la page MainPage, j'ouvre la boîte à outils ; le UserControl « MyControl » est là, on peut j'ajouter à la page :
Cela donne le code XAML suivant :
<
phone:
PhoneApplicationPage
x:
Class=
"test.MainPage"
xmlns=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:
x=
"http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:
phone=
"clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:
shell=
"clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:
d=
"http://schemas.microsoft.com/expression/blend/2008"
xmlns:
mc=
"http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:
Ignorable=
"d"
d:
DesignWidth=
"480"
d:
DesignHeight=
"768"
FontFamily=
"{StaticResource PhoneFontFamilyNormal}"
FontSize=
"{StaticResource PhoneFontSizeNormal}"
Foreground=
"{StaticResource PhoneForegroundBrush}"
SupportedOrientations=
"Portrait"
Orientation=
"Portrait"
shell:
SystemTray.
IsVisible=
"True"
xmlns:
my=
"clr-namespace:test"
>
<!--
LayoutRoot est la grille racine où tout le contenu de la page est placé-->
<
Grid x:
Name=
"LayoutRoot"
Background=
"Transparent"
>
<!--...........-->
<
my:
Mycontrol x:
Name=
"mycontrol1"
Height=
"260"
HorizontalAlignment=
"Left"
VerticalAlignment=
"Top"
Width=
"262"
/>
<
Button Content=
"Button"
Height=
"72"
HorizontalAlignment=
"Left"
Margin=
"112,379,0,0"
Name=
"button1"
VerticalAlignment=
"Top"
Width=
"160"
Click=
"button1_Click_1"
/>
</
Grid>
</
phone:
PhoneApplicationPage>
On note que cela a ajouté la ligne « xmlns:my="clr-namespace:test » qui ajoute le namespace « test » (l'application), ce qui permet ensuite d'utiliser « my:Mycontrol ».
Dans le code C# de MainPage on peut accéder à la méthode Clear et aux propriétés IsValid et Nom du contrôle « mycontrole1 »
MessageBox.
Show
(
mycontrol1.
IsValid.
ToString
(
));
Affiche true si la saisie Nom/Mot de passe est valide.
VIII-G. Gérer le BackButton▲
On peut simuler l'appui du bouton « back » par code :
private
void
BtnBack_Click
(
object
sender,
RoutedEventArgs e)
{
NavigationService.
GoBack
(
);
}
Si un texte a été tapé dans le TextBox « MonTexte », demander confirmation avant de quitter la page si l'utilisateur appuie sur le bouton « Back ».
Pour cela, on surcharge OnBackKeyPress
protected
override
void
OnBackKeyPress
(
System.
ComponentModel.
CancelEventArgs e)
{
base
.
OnBackKeyPress
(
e);
if
(
MonTexte.
Text !=
string
.
Empty)
{
var
result =
MessageBox.
Show
(
"Vous étes sur le point de perdre votre saisie. Continuer?"
,
"Attention"
,
MessageBoxButton.
OKCancel);
if
(
result !=
MessageBoxResult.
OK)
{
e.
Cancel =
true
;
}
}
}
On remarque qu'on peut annuler l'effet de la touche « Back » par e.Cancel = true.
Quand on veut forcer un « back » par code, on peut tester avant si c'est possible :
private
void
BtnBack_Click
(
object
sender,
RoutedEventArgs e)
{
if
(
NavigationService.
CanGoBack)
NavigationService.
GoBack
(
);
}
Enfin, on peut interdire l'action de la touche « Back ».
this
.
goBackButton.
IsEnabled =
false
;
VIII-H. Saisie tactile, gesture▲
Rappel des différents gestures (dessin de Microsoft) :
Un événement Tap a lieu lorsqu'un doigt touche l'écran et le quitte avant 1,1 secondes sans se déplacer beaucoup. Si deux événements Tap se succèdent rapidement, le deuxième est traité comme un événement DoubleTap. Un événement Hold (tenir) a lieu lorsqu'un doigt appuie sur l'écran et reste au même endroit pendant environ 1,1 secondes. L'événement Hold est généré à la fin de cet intervalle de temps sans attendre que le doigt se soulève.
Note : l'émulateur ne supporte pas le multitouche.
VIII-H-1. Événements souris▲
On utilise habituellement les événements souris.
Un doigt qui touche un bouton déclenche un événement Click.
Soit un bouton en XAML :
<Button
Click
=
"Button_Click>Ok</Button>
Le Click exécute :
private
void
Button_Click
(
object
sender,
RoutedEventArgs e)
{
}
Un doigt qui touche un objet visuel (UIElement) est converti en un évènement de souris ; événement MouseLeftButtonDown lorsque vous placez votre doigt, événement MouseLeftButtonUp lorsque vous levez votre doigt, MouseMove lorsque vous faites glisser votre doigt.
Il y a aussi MouseLeave (sortie de la zone) MouseEnter (entrée dans la zone).
Les arguments de ces événements sont : Sender l'objet d'où vient l'événement et e de type MouseButtonEventArgs.
Exemple, gestion de l'événement MouseLeftButtonUp sur un rectangle :
<Rectangle
Height
=
"67"
Name
=
"rectangle1"
Width
=
"331"
Fill
=
"Transparent"
MouseLeftButtonUp
=
"Rectangle_MouseLeftButtonUp"
/>
Noter que pour que cela fonctionne, il faut remplir le rectangle ; ici on met une couleur transparente grâce à Fill.
La méthode suivante sera exécutée lors du « levé » du doigt sur le rectangle.
private
void
Rectangle_MouseLeftButtonUp
(
object
sender,
MouseButtonEventArgs e)
{
}
VIII-H-2. Événements de bas niveau▲
La classe System.Windows.Input.Touch fournit un service au niveau de l'application qui traite l'entrée tactile et déclenche l'événement FrameReported.
L'argument TouchFrameEventArgs de l'événement a la méthode GetPrimaryTouchPoint qui retourne le point tactile (TouchPoint) actuel par rapport à l'élément spécifié (null pour l'écran).
TouchPoint.Action obtient la dernière action qui s'est produite à cet emplacement. TouchPOint.TouchDevice obtient le périphérique tactile qui a généré ce TouchPoint.
Voici l'exemple d'un tap sur un TextBlock nommé « txBlock » :
public
partial
class
MainPage :
PhoneApplicationPage
{
// Constructeur
public
MainPage
(
)
{
InitializeComponent
(
);
// On utilise Touch.FrameReported l'événement de bas niveau
Touch.
FrameReported +=
OnTouchFrameReported;
}
void
OnTouchFrameReported
(
object
sender,
TouchFrameEventArgs args)
// Méthode exécutée lors d'un événement sur l'écran
{
TouchPoint MyTouchPoint =
args.
GetPrimaryTouchPoint
(
null
);
if
(
MyTouchPoint !=
null
&&
MyTouchPoint.
Action ==
TouchAction.
Down)
{
if
(
MyTouchPoint.
TouchDevice.
DirectlyOver ==
txBlock)
{
txBlock.
Text =
"Clicker"
;
txBlock.
Foreground =
new
SolidColorBrush
(
Colors.
Green);
}
}
}
}
VIII-H-3. Événements de haut niveau▲
Ces événements de haut niveau sont exécutés sur les objets dérivés de UIElement ; il y a les événements de gesture : Tap, DoubleTap, Hold.
Exemple avec Tap.
<
Ellipse Height=
"109"
Fill=
"red"
Tap=
"buttonTap"
Name=
"ellipse1"
Stroke=
"Black"
StrokeThickness=
"1"
Width=
"326"
/>
private
void
buttonTap
(
object
sender,
System.
Windows.
Input.
GestureEventArgs e)
{
Point p =
e.
GetPosition
(
null
);
// Coordonnées dans l'écran
Point p1 =
e.
GetPosition
(
ellipse1);
// Coordonnées relative à l'ellipse1
MessageBox.
Show
(
p.
ToString
(
));
}
On peut utiliser l'appuie prolongé avec « Hold ».
<Ellipse
Height
=
"109"
Fill
=
"red"
Hold
=
"ellipse2_Hold"
Name
=
"ellipse2"
Stroke
=
"Black"
StrokeThickness
=
"1"
Width
=
"326"
Margin
=
"58,409,72,89"
/>
On peut aussi utiliser les événements de manipulation :
ManipulationStarted
intervient quand la manipulation démarre.
ManipulationCompleted
intervient quand la manipulation s'arrête.
ManipulationDelta
intervient quand le doigt change de position et survient de multiples fois au cours de la manipulation.
Par exemple, pour effectuer une action si l'utilisateur pose le doigt sur un contrôle comme une ellipse.
<Ellipse
ManipulationStarted
=
"Manip"
Height
=
"109"
Fill
=
"red"
Name
=
"ellipse1"
/>
private
void
Manip
(
object
sender,
ManipulationStartedEventArgs e)
{
Point p =
e.
ManipulationOrigin;
// Coordonnées de la manipulation dans l'objet
MessageBox.
Show
(
p.
ToString
(
));
}
Le second paramètre est de type ManipulationStartedEventArgs.
L'événement ManipulationDelta permet de « lire » les mouvements de « Translation » et de « Scale » (pas de rotation en WP).
Ici un glissé horizontal du doigt permet d'élargir ou de réduire l'ellipse.
<Ellipse
Height
=
"109"
Fill
=
"red"
ManipulationDelta
=
"ManipDelta"
Name
=
"ellipse1"
Stroke
=
"Black"
StrokeThickness
=
"1"
Width
=
"326"
/>
Pour une événement ManipulationDelta le second paramètre est de type ManipulationDeltaEventArgs.
La propriété DeltaManipulation contient les modifications qui se sont produites depuis le précédent événement ManipulationDelta.
private
void
ManipDelta
(
object
sender,
ManipulationDeltaEventArgs e)
{
Point p =
e.
DeltaManipulation.
Translation;
ellipse1.
Width =
ellipse1.
Width +
p.
X;
}
Dans le code C# on peut surcharger la méthode OnManipulationStarted (et les autres événements de manipulation) de la page :
Si l'utilisateur pose le doigt sur l'écran.
// Dans le code de la page
protected
override
void
OnManipulationStarted
(
ManipulationStartedEventArgs args)
{
// Code a effectuer quand l'utilisateur tape sur l'écran.
args.
Complete
(
);
base
.
OnManipulationStarted
(
args);
}
VIII-H-4. Gestures du toolkit▲
Le Toolkit fournit des fonctionnalités très puissantes.
Il y a dans le Toolkit la classe GestuleService qui permet d'attacher un GestureListener à un élément.
Cela permet de gérer les Tap, DoubleTap, Hold, PinchStarted, PinchDelta, PinchCompleted, Flick, DragStarted, DragDelta, DragCompleted, GestureBegin, GestureCompleted.
Il faut installer le Toolkit, ajouter dans les références « Microsoft.Phone.Controls.Toolkit.dll ».
Dans le code XAML, dans la balise d'ouverture de la page, il faut taper :
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
Maintenant on peut utiliser les gestures du toolkit : on retrouve Tap DoubleTap et Hold.
Exemple simple avec Tap ; on va appliquer un GestureService.GestureListener à la grille principale « ContentPanel » et on va « écouter » l'événement Tap.
<!--ContentPanel - placez tout contenu supplémentaire ici-->
<Grid
x
:
Name
=
"ContentPanel"
Grid.
Row
=
"1"
Margin
=
"12,0,12,0"
>
<
toolkit
:
GestureService.GestureListener>
<
toolkit
:
GestureListener
Tap
=
"OnGestureListenerTap"
/>
</
toolkit
:
GestureService.GestureListener>
<Ellipse
Height
=
"109"
Fill
=
"red"
Name
=
"ellipse1"
Stroke
=
"Black"
StrokeThickness
=
"1"
Width
=
"326"
/>
<Button
Content
=
"Button"
Height
=
"86"
HorizontalAlignment
=
"Left"
Name
=
"button1"
Width
=
"280"
Click
=
"button1_Click"
/>
<Ellipse
Height
=
"109"
Fill
=
"red"
Name
=
"ellipse2"
Width
=
"326"
Margin
=
"58,409,72,89"
/>
</Grid>
private
void
OnGestureListenerTap
(
object
sender,
System.
Windows.
Input.
GestureEventArgs e)
{
Point p =
e.
GetPosition
(
ellipse2);
MessageBox.
Show
(
p.
X.
ToString
(
) +
" "
+
p.
Y.
ToString
(
));
}
Un Tap exécute OnGestureService qui ici ouvre une simple MessageBox.
Noter que le Tap fonctionne sur les ellipses et le bouton, pas sur le fond autour.
GetPosition retourne un Point ici par rapport à ellipse2 (qui a été donné en argument), l'origine étant le coin supérieur gauche d'un rectangle contenant l'ellipse. Pour que Tap soit actif même sur le fond, il faut un Background sur la grille.
<!--ContentPanel - placez tout contenu supplémentaire ici-->
<Grid
x
:
Name
=
"ContentPanel"
Grid.
Row
=
"1"
Margin
=
"12,0,12,0"
Background
=
"Transparent"
>
On peut écrire cela en C# :
Ici on a un rectangle, quand on clique dessus, il devient blanc.
Rectangle rectg;
public
MainPage
(
)
{
InitializeComponent
(
);
this
.
rectg =
new
Rectangle
(
);
rectg.
Height =
100
;
rectg.
Width =
100
;
rectg.
Fill =
new
SolidColorBrush
(
Colors.
Cyan);
this
.
ContentPanel.
Children.
Add
(
rectg);
GestureListener gl =
GestureService.
GetGestureListener
(
rectg);
gl.
Tap +=
new
EventHandler<
Microsoft.
Phone.
Controls.
GestureEventArgs>(
GestureListener_Tap);
}
private
void
GestureListener_Tap
(
object
sender,
Microsoft.
Phone.
Controls.
GestureEventArgs e)
{
this
.
rectg.
Fill =
new
SolidColorBrush
(
Colors.
White);
}
Passons rapidement sur GestureBegin et GestureCompleted qui permettent de voir l'objet qui a déclenché la gesture (OriginalSource) et les coordonnés (GetPosition).
<Grid
x
:
Name
=
"ContentPanel"
Grid.
Row
=
"1"
Margin
=
"12,0,12,0"
Background
=
"Transparent"
>
<
toolkit
:
GestureService.GestureListener>
<
toolkit
:
GestureListener
GestureBegin
=
"OnGestureBegin"
/>
</
toolkit
:
GestureService.GestureListener>
<Ellipse
Height
=
"109"
Fill
=
"red"
Name
=
"ellipse1"
Stroke
=
"Black"
StrokeThickness
=
"1"
Width
=
"326"
/>
</Grid>
private
void
OnGestureBegin
(
object
sender,
Microsoft.
Phone.
Controls.
GestureEventArgs e)
{
Object o =
e.
OriginalSource;
Point p =
e.
GetPosition
(
ellipse1);
MessageBox.
Show
(
"Taptc"
);
}
Une séquence Drag (traîner) est constituée d'un événement DragStarted, de zéro ou plusieurs événements DragDelta et d'un événement DragCompleted (lorsqu'un doigt touche l'écran, se déplace, puis quitte l'écran).
DragStarted permet, outre l'objet et les coordonnées, de voir la direction de départ (retourne une « Orientation » qui est une enum ayant deux valeurs : Horizontal et Vertical).
<Grid
x
:
Name
=
"ContentPanel"
Grid.
Row
=
"1"
Margin
=
"12,0,12,0"
Background
=
"Transparent"
>
<
toolkit
:
GestureService.GestureListener>
<
toolkit
:
GestureListener
DragStarted
=
"OnGestureDrag"
/>
</
toolkit
:
GestureService.GestureListener>
private
void
OnGestureDrag
(
object
sender,
Microsoft.
Phone.
Controls.
DragStartedGestureEventArgs e)
{
Object o =
e.
OriginalSource;
Point p =
e.
GetPosition
(
rectg);
Orientation orient =
e.
Direction;
MessageBox.
Show (
orient.
ToString
(
));
// Horizontal ou Vertical
}
DragDelta, outre les informations précédentes, permet de voir le déplacement horizontal et vertical (positif si le déplacement se fait à droite et en bas).
<Grid
x
:
Name
=
"ContentPanel"
Grid.
Row
=
"1"
Margin
=
"12,0,12,0"
Background
=
"Transparent"
>
<
toolkit
:
GestureService.GestureListener>
<
toolkit
:
GestureListener
DragDelta
=
"OnGestureDragDelta"
/>
</
toolkit
:
GestureService.GestureListener>
private
void
OnGestureDragDelta
(
object
sender,
Microsoft.
Phone.
Controls.
DragDeltaGestureEventArgs e)
{
Object o =
e.
OriginalSource;
Point p =
e.
GetPosition
(
rectg);
Orientation or =
e.
Direction;
MessageBox.
Show (
or.
ToString
(
));
//Horizontal ou Vertical
double
x =
e.
HorizontalChange;
double
y =
e.
VerticalChange;
MessageBox.
Show
(
x.
ToString
(
) +
" "
+
y.
ToString
(
));
}
DragCompleted présente les mêmes informations (sur quel objet on lâche) mais avec en plus HorizontalVelocity et VerticalVelocity.
L'événement Flick a lieu lorsqu'un doigt quitte l'écran alors qu'il se déplace encore, ce qui suggère que l'utilisateur souhaite obtenir une inertie ; je le fais lorsque j'enlève vers le haut l'écran de démarrage. Les arguments comprennent une valeur Angle (de 0 à 360 °, mesuré dans le sens des aiguilles d'une montre à partir de l'axe des X positifs horizontal à droite), ainsi que des valeurs HorizontalVelocity et VerticalVelocity (mesurées en pixels par seconde).
On va l'appliquer à une ellipse.
<Ellipse
Height
=
"109"
Fill
=
"red"
Name
=
"ellipse1"
Stroke
=
"Black"
StrokeThickness
=
"1"
Width
=
"326"
>
<
toolkit
:
GestureService.GestureListener>
<
toolkit
:
GestureListener
Flick
=
"OnFlick"
/>
</
toolkit
:
GestureService.GestureListener>
</Ellipse>
private
void
OnFlick
(
object
sender,
FlickGestureEventArgs e)
{
double
x =
e.
HorizontalVelocity;
double
y =
e.
VerticalVelocity;
double
a =
e.
Angle;
MessageBox.
Show
(
x.
ToString
(
) +
" "
+
y.
ToString
(
) +
" "
+
a.
ToString
(
));
}
La séquence Pinch (pincement) a lieu lorsque deux doigts touchent l'écran ; elle est généralement interprétée pour développer ou réduire un objet. Pinch correspond aux deux doigts qui s'écartent ou se rapprochent.
La position actuelle d'un doigt (le doigt principal) est toujours disponible avec la méthode GetPosition.
Il y a PinchStarted.
<Ellipse
Height
=
"109"
Fill
=
"red"
Name
=
"ellipse1"
Stroke
=
"Black"
StrokeThickness
=
"1"
Width
=
"326"
>
<
toolkit
:
GestureService.GestureListener>
<
toolkit
:
GestureListener
PinchStarted
=
"PinchS"
/>
</
toolkit
:
GestureService.GestureListener>
</Ellipse>
private
void
PinchS
(
object
sender,
PinchStartedGestureEventArgs e)
{
double
a =
e.
Angle;
double
d =
e.
Distance;
}
Il y a aussi PinchDelta.
<Ellipse
Height
=
"109"
Fill
=
"red"
Name
=
"ellipse1"
Stroke
=
"Black"
StrokeThickness
=
"1"
Width
=
"326"
>
<
toolkit
:
GestureService.GestureListener>
<
toolkit
:
GestureListener
PinchDelta
=
"PinchD"
/>
</
toolkit
:
GestureService.GestureListener>
</Ellipse>
On peut voir le « DistanceRatio » rapport de la distance actuelle entre les deux doigts et la distance de départ. Il y a aussi le « TotalAngleDelta » différence de l'angle actuel entre les deux doigts et l'angle de départ.
private
void
PinchD
(
object
sender,
PinchGestureEventArgs e)
{
double
ratio =
e.
DistanceRatio;
double
a =
e.
TotalAngleDelta;
}
Il existe aussi PinchCompleted qui n'apporte rien de plus.
VIII-I. Perdre du temps▲
Parfois on a besoin de perdre du temps, suspendre l'exécution durant 1/2 s par exemple.
Il faut inclure l'espace de nom System.Threading puis utiliser la méthode Sleep du Thread (avec en paramètre 500 ms).
using
System.
Threading;
Thread.
Sleep
(
500
);
VIII-J. Sortir de l'application▲
Logiquement, le fait d'utiliser le bouton « Back » quand on est sur la première page de l'application permet de sortir de l'application.
Si on quitte l'application avec le bouton central, elle se met à l'état dormant et se fermera automatiquement si on s'y revient pas.
Il n'y a donc pas besoin de méthode pour quitter l'application.
Si on veut vraiment, Application.Exit() n'existant pas, il faut déclencher une exception non gérée ! Ce n'est pas élégant !
private void Exit(object sender, RoutedEventArgs e)
{
throw new Exception("Exit");
}
VIII-K. Application démonstration ou payée▲
On peut distribuer une application gratuite en démonstration ; elle pourra ensuite être achetée et ainsi donner accès à des niveaux ou fonctions supplémentaires.
La Méthode IsTrial de LicenceInformation indique si on est en mode démo.
//Ajouter en haut
using
Microsoft.
Phone.
Marketplace;
//instancier une licenceinformation
LicenseInformation licInfo =
new
LicenseInformation
(
);
//Tester si on est en version démo ou payée
if
(!
licInfo.
IsTrial
(
))
{
//Faire seulement pour les users ayant payé.
}
else
{
//Faire pour tous.
}
Code non testé.
VIII-L. Mes trucs▲
Sans prétention !
Attention au nom de l'application !
Si je crée une application se nommant « calcul » puis une variable « calcul » et si je me fais aider pour l'écriture (IntelliSense), je peux avoir des confusions entre espace de noms « calcul » et variable « calcul » :
De même si le nom de l'application a le nom d'une classe.
L'enfer des accolades.
Dans le code C#, quand on a plusieurs classes dans un espace de noms ou si on a des if imbriqués, il y a plein d'accolades ouvrantes et fermantes.
C'est l'horreur pour savoir où ajouter une fonction ou une classe ou un else ; si on se trompe et qu'on met une fonction une accolade trop haut, les messages d'erreur ne sont pas toujours clairs.
Le plus simple pour s'y retrouver est de cliquer sur une accolade ouvrante cela surligne l'accolade fermante correspondante.
Groupe de contrôles
Avant l'univers .NET, en VB6 par exemple, on pouvait créer des groupe de contrôles, les contrôles d'un groupe avaient les mêmes fonctions évènements et pour un clic sur différents boutons on exécutait la même fonction.
Pour simuler les « groupes de contrôles » qui n'existent pas en WP on a un moyen simple : en XAML, pour que plusieurs boutons exécutent la même fonction, on peut indiquer Click="MyButtonClick" pour plusieurs boutons.
<Button
Name
=
"Button1"
Click
=
"MyButtonClick"
>
<Button
Name
=
"Button2"
Click
=
"MyButtonClick"
>
Ensuite dans la fonction MyButtonClick il suffit de regarder le « sender » pour voir quel bouton a été cliqué (il faut le caster en bouton avant de tester son nom).
On peut aussi utiliser un StackPanel « parent » pour gérer l'évènement Click de tous les boutons enfants dans le StackPanel (comme il n'y a pas de « Click= » dans les boutons, l'évènement Click remonte dans le StackPanel) :
<StackPanel
Background
=
"LightGray"
Orientation
=
"Horizontal"
Button.
Click
=
"ClickHandlerCommun"
>
<Button
Name
=
"ButtonOui"
Width
=
"Auto"
>
Oui</Button>
<Button
Name
=
"ButtonNon"
Width
=
"Auto"
>
Non</Button>
</StackPanel>
IX. Compilation, test, distribution▲
IX-A. Compilation▲
La compilation c'est transformer un code source (le code XAML et le code C# que vous avez tapé), en code exécutable et distribuable.
En haut on peut choisir Windows Phone Emulator ou Windows Phone Device pour lancer l'application sur l'émulateur ou le portable connecté (si on est enregistré) :
Puis on peut choisir Debug (permettant le débogage) ou Release (pour la distribution).
Si on clique sur le bouton d'exécution (ou F5) on lance le débogage.
Par le menu « Build » puis « Générer la solution » ou F6, on compile l'application.
Cela crée un répertoire « Bin » avec un sous répertoire « Debug » ou « Release » contenant un fichier .xap (archive compressée contenant l'exécutable, les DLL, les ressources, images…).
Le fichier .xap est en fait un fichier .zip (vous pouvez renommer ce fichier en .zip et ensuite aller voir ce qu'il y a dedans).
Pour tester le .xap passer par le menu « Build» puis « Déployer » ; il s'exécutera dans l'émulateur.
On se souvient où se trouvent les sources des applications.
Pour mon application qui se nomme « CalculTout » voir dans 'C:\Users\Philippe\Documents\Visual Studio 2010\Projects\CalculTout'.
Il y a les fichiers « CalculTout.sln » et « CalculTout.suo ».
Il y a dessous un répertoire nommé aussi « CalculTout » qui contient les fichiers « .xaml » et « .xaml.cs » et les répertoires « Bin » et « Obj » qui contiennent chacun les répertoires « Debug » et « Release ».
Quand on compile il y a création des fichiers « .g.cs » et « .g.i.cs » en C# pour chaque page XAML. On n'a théoriquement pas besoin de s'en occuper sauf quand il y a un bogue dans le code XAML et que VS s'arrête sur une instruction de ces fichiers ; lire l'erreur donne une indication sur la correction nécessaire dans le code XAML.
IX-B. Test sur un Windows Phone▲
Il est possible de voir tourner votre application dans l'émulateur.
Impossible de charger simplement sur votre Windows Phone une application que vous venez de développer.
Impossible donc de tester une application sur votre portable ou de faire une application uniquement pour les membres de votre entreprise.
La solution : enregistrez-vous comme développeur chez Microsoft (99 $) et là vous aurez la possibilité de charger sur votre portable une application pour la tester.
Pour déboguer, il faut choisir l'émulateur en haut au milieu :
Si on clique sur le bouton d'exécution (ou F5) on compile l'application, on charge l'émulateur WP7 et on exécute l'application dans l'émulateur.
La souris permet de simuler le doigt sur l'écran.
À sa gauche il y a une barre d'outils qui apparaît quand le curseur de la souris s'approche.
Les boutons de haut en bas :
- fermer l'émulateur ;
- réduire l'émulateur ;
- faire pivoter l'émulateur pour passer en mode paysage ;
- faire pivoter mais dans l'autre sens.
- ajuster l'émulateur à la taille de l'écran ;
- définir le zoom de l'émulateur ;
- autres.
- accéléromètre ;
- localisation ;
- copie d'écran.
Sur la droite de l'écran, il y a des compteurs :
En détails :
De haut en bas ils indiquent
- Thread de composition (Render Thread).
3 chiffres. En nombre de frames par seconde (maximum : 120)
Bonne valeur = 60.
Mauvaise valeur si inférieur à 30 (devient rouge).
- Thread UI (User Interface Thread).
3 chiffres. En nombre de frames par seconde (si la valeur est faible, la réaction de l'UI est moins bonne).
Bonne valeur si supérieur à 15.
Devient rouge si inférieur à 15.
- Textures (mémoire utilisée par les textures)
6 chiffres.
- Compteur de surfaces
Nombre de surfaces passées au chip graphique.
- Compteur IRT (Intermediate Texture Count).
- Screen Fill rate (nombre d'écrans dessinés par frame).
Un écran représente 480 x 800 pixels.
si> 3 mauvaises performances.
si inférieur à 2, bonnes performances.
Dans le constructeur de la classe App de votre application (App.xaml.cs), vous pouvez faire disparaître ces compteurs.
Application.
Current.
Host.
Settings.
EnableFrameRateCounter =
false
;
C'est bien pour faire des copies d'écran.
Un profiler dédié au projet WP7 a été ajouté, il n'est pas à l'heure actuelle dans sa version définitive, mais il permet de pouvoir cibler certains problèmes de frame rate et d'utilisation de thread sur la CPU/GPU.
Passez par le menu « déboguer » puis « Démarrer l'analyse des performances de Windows Phone ».
IX-C. La distribution▲
Il faut préparer des images.
Trois images pour le MarketPlace :
Large Mobile App Tile : 173*173 px 96 DPI, fichier .PNG ;
Small Mobile App Tile : 99*99 px 96 DPI, fichier .PNG ;
Large PC App Tile : 200*200 px 96 DPI, fichier .PNG.
Une (minimum) à huit captures d'écran (Screenshots) 480*800 px 96 DPI, fichier .PNG ;
Une image en option (Background art) utilisée si l'application devient l'application du jour 1000*800 px 96 DPI, fichier .PNG.
On peut charger à partir du site CodePlex un logiciel gratuit (http://wpiconmaker.codeplex.com/) permettant, très simplement, de créer les icônes à partir d'une image.
On se souvient que dans l'application il y a les 2 icônes :
Background.png, 173×173 px (utilisé sur l'écran de démarrage).
ApplicationIcon.png, 62x62 px (utilisée dans la liste des applications installées).
La distribution se fait uniquement par le Marquet Place.
Merci à Tomlev pour sa relecture ses corrections et ses conseils sur une partie de ce document, il m'a été d'un grand secours.
Merci à jacques jean pour sa relecture orthographique d'une grosse partie du document. Merci aux autres.
A suivre…
Philippe LASSERRE.