IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Windows Phone 7.


précédentsommairesuivant

VI. Les différents contrôles

- Contrôles disponibles.
Border
Button
Canvas
CheckBox
ContentControl
ContentPresenter
Control
Grid
HyperlinkButton
Image
InkPresenter
ListBox
MediaElement
MultiScaleImage
Panel
PasswordBox
ProgressBar
RadioButton
ScrollViewer
Slider
StackPanel
TextBlock
TextBox
Viewbox
UserControl

Tous les contrôles ne sont pas disponibles dans la boîte à outils.

On peut si on le désire ajouter un contrôle dans la boîte à outils. Dans celle-ci, clic droit sur « Windows Phone Controls », puis dans le menu « Choisir les éléments » et cocher un contrôle à ajouter dans la liste de la boîte à outils.

Image non disponible

- Contrôles Silverlight différents dans Windows Phone.
ScroolBarr
ToolsTip
ComboBox
RichTextBox


- Ne sont pas disponibles dans Windows Phone.
System.Windows.Controls.OpenFileDialog
System.Windows.Controls.SaveFileDialog

- Ne sont pas disponibles les contrôles qui sont dans le Silverlight SDK. 
Calendar
ChildWindow
DataGrid
DatePicker
Frame
GridSplitter
Label
Page
TabControl
TreeView



On peut ajouter le ToolKit Silverlight for Windows Phone Toolkit pour Mango 7.1 :http://silverlight.codeplex.com/.

- Cela ajoute :
AutoCompleteBox
ContextMenu
DatePicker
GestureService/GestureListener
ListPicker
LongListSelector
Page Transitions
PerformanceProgressBar
TiltEffect
TimePicker
ToggleSwitch
WrapPanel

Il faut les ajouter à la boîte à outils. Voir le chapitre sur le Toolkit.

VI-A. Page

Une application très simple peut se composer d'une page principale (nommée « MainPage » par défaut).
C'est le cas quand on crée une application en choisissant « Application Windows Phone » :

Image non disponible


Ensuite on peut créer d'autres pages (menu « Projet » puis « Ajouter un nouvel élément »).

Image non disponible


Dans la page principale on peut ouvrir une autre page à l'aide du code suivant :

 
Sélectionnez
 NavigationService.Navigate(new Uri("/PageSecondaire.xaml", UriKind.Relative));

Quand on crée une page :
Elle est dans la Frame de l'application.
Cette Page contient une grille principale (nommée LayoutRoot).
Elle contient un StackPanel (nommé TitlePanel) comprenant le titre de l'application et le titre de la page.
Dessous il y une grille nommée ContentPanel dans laquelle il faudra mettre le contenu de la Page.

 
Sélectionnez
<phone:PhoneApplicationPage 
    x:Class="MyProjet.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">

    <!--LayoutRoot est la grille racine où tout le contenu de la page est placé-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

<!--TitlePanel contient le nom de l'application et le titre de la page-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="MON APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="nom de la page" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <!--ContentPanel - placez tout contenu supplémentaire ici-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
          
        </Grid>
    </Grid>
 
    <!--Exemple de code illustrant l'utilisation d'ApplicationBar-->
    <!--<phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Bouton 1"/>
            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Bouton 2"/>
            <shell:ApplicationBar.MenuItems>
                <shell:ApplicationBarMenuItem Text="ÉlémentMenu 1"/>
                <shell:ApplicationBarMenuItem Text="ÉlémentMenu 2"/>
            </shell:ApplicationBar.MenuItems>
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>-->

</phone:PhoneApplicationPage>

Une page fait 800 * 480 px. Le SystemTray en haut fait 32 px
(il peut y avoir des écrans de 480 * 320).

Image non disponible

Si on regarde le code C#, on constate que l'espace de noms est celui du projet (comme pour le code XAML) ; il y a une classe partielle « MainPage » (la classe MainPage est donc composée du code XAML et de la classe partielle en C#).

 
Sélectionnez
using System.Windows.Shapes;
using Microsoft.Phone.Controls;

namespace MyProjet
{
    public partial class MainPage : PhoneApplicationPage
    {
        // Constructeur
        public MainPage()
        {
            InitializeComponent();
        }
    }
}

Cette class MainPage hérite de la classe PhoneApplicationPage qui est la page standard.
Dans cette classe MainPage, il y a une fonction « public MainPage() » qui est le constructeur de la page ; on y initialise les composants de la page ; on peut y ajouter du code qui sera exécuté lors de la création de la page.

Dans une page, on peut utiliser des évènements ; Loaded, par exemple.
Dans ce cas il faut ajouter « Loaded=… »

 
Sélectionnez
<phone:PhoneApplicationPage 
    x:Class="CalculeTout.Inflation"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 ...
  Loaded="PhoneApplicationPage_Loaded">

Quand la page est chargée, la routine suivante, dans le code C#, est exécutée :

 
Sélectionnez
 private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
        {

        }

On verra qu'il est plus judicieux, pour mettre du code quand la page est chargée, d'utiliser une surcharge de OnNavigatedTo :

 
Sélectionnez
 protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
        }

Il y a deux autres manières de structurer une application Windows Phone :
En utilisant le Pivot ou le Panorama.

VI-B. Pivot et Panorama

Utilisons une page « Pivot ».

C'est le cas quand on crée une application en choisissant « Application Pivot Windows Phone » :

Image non disponible


Voici un exemple de pages pivots :

Image non disponible

Une application « Pivot » contient plusieurs pages, on passe de l'une à l'autre en glissant horizontalement ou en tapant sur le nom grisé en haut de la page désirée.

Image non disponible


Dans le code XAML on voit que le contrôle « Pivot » contient des PivotItem ayant un titre (Header).

 
Sélectionnez
<controls:Pivot Title="MON APPLICATION">
           
            <controls:PivotItem Header="premier">
            </controls:PivotItem>
            
            <controls:PivotItem Header="second">
            </controls:PivotItem>
</controls:Pivot>


Code complet avec dans chaque PivotItem une ListBox :

 
Sélectionnez
    <!--LayoutRoot est la grille racine où tout le contenu de la page est placé-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <!--Contrôle Pivot-->
        <controls:Pivot Title="MON APPLICATION">
            <!--Élément un de tableau croisé dynamique-->
            <controls:PivotItem Header="premier">
                <!--Liste double trait avec habillage du texte-->
                <ListBox x:Name="FirstListBox" Margin="0,0,-12,0" ItemsSource="{Binding Items}">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                          <StackPanel Margin="0,0,0,17" Width="432">
    <TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
    <TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0" 
    Style="{StaticResource PhoneTextSubtleStyle}"/>
                          </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </controls:PivotItem>
 
            <!--Élément deux de tableau croisé dynamique-->
            <controls:PivotItem Header="second"> 
                <!--Liste triple trait, aucun habillage du texte-->
                    <ListBox x:Name="SecondListBox" Margin="0,0,-12,0" ItemsSource="{Binding Items}">
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <StackPanel Margin="0,0,0,17">
<TextBlock Text="{Binding LineOne}" TextWrapping="NoWrap" Margin="12,0,0,0" 
  Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock Text="{Binding LineThree}" TextWrapping="NoWrap" Margin="12,-6,0,0" 
  Style="{StaticResource PhoneTextSubtleStyle}"/>
                                </StackPanel>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
            </controls:PivotItem>
        </controls:Pivot>
    </Grid>

Utilisons maintenant un « Panorama ».

C'est le cas quand on crée une application en choisissant « Application Panorama Windows Phone ».

Image non disponible


Voici une page panorama :

Image non disponible

Une application « Panorama » contient une page unique très large, on se déplace en glissant horizontalement. Le titre est large et s'étale sur toute la grande page. Le début du contenu de la page suivante de droite est légèrement visible.

Image non disponible


Le contrôle Panorama contient des PanoramaItem ayant un titre (Header). Mis côte à côte les PanoramaItem forment la grande page virtuelle.

 
Sélectionnez
<controls:Panorama Title="mon application">
            <controls:PanoramaItem Header="premier élément">
            </controls:PanoramaItem>
            <controls:PanoramaItem Header="second élément">
            </controls:PanoramaItem>
</controls:Panorama>


Code complet avec dans chaque PanoramaItem une ListBox :

 
Sélectionnez
     <!--Contrôle Panorama-->
        <controls:Panorama Title="mon application">
            <controls:Panorama.Background>
                <ImageBrush ImageSource="PanoramaBackground.png"/>
            </controls:Panorama.Background>
 
            <!--Élément un de panorama-->
            <controls:PanoramaItem Header="premier élément">
                <!--Liste double trait avec habillage du texte-->
                <ListBox Margin="0,0,-12,0" ItemsSource="{Binding Items}">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Margin="0,0,0,17" Width="432">
<TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0" 
       Style="{StaticResource PhoneTextSubtleStyle}"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </controls:PanoramaItem>
 
            <!--Élément deux de panorama-->
            <!--Utilisez 'Orientation="Horizontal"' pour activer un panneau qui s'affiche horizontalement-->
            <controls:PanoramaItem Header="second élément">
                <!--Liste double trait avec espace réservé pour une image et habillage du texte-->
                <ListBox Margin="0,0,-12,0" ItemsSource="{Binding Items}">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal" Margin="0,0,0,17">
                                <!--Remplacer le rectangle par l'image-->
                                <Rectangle Height="100" Width="100" Fill="#FFE5001b" Margin="12,0,9,0"/>
                                <StackPanel Width="311">                                    
<TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0" 
  Style="{StaticResource PhoneTextSubtleStyle}"/>
                                </StackPanel>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </controls:PanoramaItem>
        </controls:Panorama>

VI-C. Les Conteneurs

Les conteneurs peuvent contenir plusieurs contrôles et permettent de les positionner.

VI-C-1. Les Grid

En Silverlight une Grid est un conteneur, comprenant des cellules servant à aligner les contrôles (et pas une grille de données comme dans les Windows forms).

Aller chercher une Grid dans la boîte à outils à gauche et la mettre sur la page.

Image non disponible
 
Sélectionnez
<Grid Height="520" HorizontalAlignment="Left" Margin="81,51,0,0" Name="grid1" VerticalAlignment="Top" Width="335">
</Grid>

On a une grille mais avec une seule cellule.

En fait quand on ajoute une page vide à un projet, la fenêtre contient déjà une Grid.

Comportement d'une Grid a une seule cellule.
Ce n'est pas recommandé mais ajoutons deux TextBlock, une image et une ellipse dans la cellule unique :

 
Sélectionnez
 <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        <TextBlock Text="TextBlock aligné à droite et en haut" 
 HorizontalAlignment="Right"
VerticalAlignment="Bottom"/>
            <TextBlock Text="TextBlock aligné à gauche et en bas"
HorizontalAlignment="Left"
VerticalAlignment="Top" />
            <Image Source="c:\vshaghet.jpg" />
            <Ellipse Stroke="{StaticResource PhoneAccentBrush}"
StrokeThickness="24" />

Cela donne :

Image non disponible


On se rend compte que les éléments comme l'image et l'ellipse sont centrés et occupent le maximum de place.
Les TextBox sont placés en fonction de leur alignement.
Enfin on remarque que les éléments se superposent : l'ellipse qui est dans la dernière ligne du code XAML est au dessus.


Pour ajouter des lignes et des colonnes dans la grille (dans le designer), il faut cliquer dans les barres grises horizontales ou verticales de la grille et positionner les traits à la souris.

Image non disponible

Cela génère le code XAML suivant :

 
Sélectionnez
<Grid Height="520" HorizontalAlignment="Left" Margin="81,51,0,0" 
Name="grid1" VerticalAlignment="Top" Width="335">
                <Grid.RowDefinitions>
                    <RowDefinition Height="265*" />
                    <RowDefinition Height="255*" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="190*" />
                    <ColumnDefinition Width="145*" />
                </Grid.ColumnDefinitions>
            </Grid>

On voit dans le code XAML les largeurs des colonnes (Grid.ColumnDefinition) et les hauteurs des lignes (Grid.RowDefinition).

On peut aussi vouloir définir le nombre de lignes et de colonnes sans définir leurs largeurs et hauteurs : simplement mettre trois lignes, deux colonnes.

 
Sélectionnez
<Grid>
      <Grid.RowDefinitions>
       <RowDefinition/>
       <RowDefinition/>
       <RowDefinition/>
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
      </Grid.ColumnDefinitions>
    </Grid>

Dans la définition des dimensions des lignes et colonnes : on peut, comme on l'a vu, donner dans le designer la taille que l'on veut aux cellules.

Mais on peut, dans le code XAML, donner les dimensions des cellules :
- un chiffre ("150", par exemple) indique la largeur ou hauteur en pixels ;
- '*' signifie autant que possible' (cette ligne reçoit une proportion pondérée de l'espace disponible , 2* indiquant deux fois la proportion) ;
- 'Auto' force à prendre la taille des éléments qu'elle contient.

Exemple de colonnes avec Auto :

 
Sélectionnez
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
Image non disponible

Exemple de lignes avec des étoiles :

 
Sélectionnez
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="2*" />
Image non disponible

Si on fait :

 
Sélectionnez
<Grid.RowDefinitions>
    <RowDefinition Height="*" />
    <RowDefinition Height="*" />
    <RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
    <ColumnDefinition Width="*" />
    <ColumnDefinition Width="*" />
    <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

On aura trois lignes d'égale hauteur et trois colonnes d'égale largeur. Cela donne neuf cellules carrées.

On peut rendre les lignes de séparation visibles :

 
Sélectionnez
ShowGridLines="True"

On peut ensuite ajouter des contrôles dans les cellules de la grille pour les positionner.

Pour chaque contrôle ajouté dans une cellule de la Grid, on définira la cellule où le contrôle se trouve, ses marges et son ancrage si nécessaire.

Ici on va ajouter une ListBox en bas à droite de la grille, voyons ce que cela donne dans le code XAML.

 
Sélectionnez
<Grid Height="279" Name="Grid1">
<Grid.RowDefinitions>
<RowDefinition Height="74*" />
<RowDefinition Height="84*" />
<RowDefinition Height="121*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="37*" />
<ColumnDefinition Width="28*" />
<ColumnDefinition Width="31*" />
<ColumnDefinition Width="176*" />
</Grid.ColumnDefinitions>

<ListBox Grid.Column="3" Grid.Row="2"  Name="ListBox1">
<ListBoxItem>toto</ListBoxItem>
<ListBoxItem>lululu</ListBoxItem>
</ListBox>
</Grid>

ListBox Grid.Column="3" Grid.Row="2" indiquent dans quelle cellule est la ListBox.

Attention la première colonne est Column=0 et la première ligne est Row=0.

On aurait pu ajouter Grid.ColumnSpan=2 pour indiquer que la ListBox occupe deux colonnes (la 3 et la 4 dans ce cas).

Il existe aussi Grid.RowSpan pour étendre le contrôle sur plusieurs lignes de la Grid.


On aurait pu ajouter :
Margin="22,26,21,19" pour indiquer les distances au conteneur autour de la ListBox.

Comment faire la même chose en C# ?
La Grid1 étant présente, ajoutons une ListBox comme ci dessus :

 
Sélectionnez
ListBox lb = new ListBox();
lb.Margin = new Thickness(22, 26, 11, 19);
Grid.SetColumn(lb, 3);
Grid.SetRow(lb, 2);
grid1.Children.Add(lb);

VI-C-2. Les StackPanel

Arrange, empile les contrôles sur une même ligne qui peut être horizontale ou verticale (on parle d'empilement).

En C# :

 
Sélectionnez
StackPanel instance =new StackPanel();

En XAML :

 
Sélectionnez
<StackPanel> </StackPanel>

On a ajouté dans le StackPanel trois boutons.

En XAML :

 
Sélectionnez
<StackPanel Height="362" HorizontalAlignment="Left" Margin="29,51,0,0" Name="stackPanel1" 
VerticalAlignment="Top" Width="390">
                <Button Content="Button" Height="85" Name="button1" Width="300" />
                <Button Content="Button" Height="85" Name="button2" Width="300" />
                <Button Content="Button" Height="85" Name="button3" Width="300" />
            </StackPanel>

On voit qu'il suffit de mettre les objets dans le StackPanel, ils seront empilés.

Image non disponible

La valeur par défaut de Orientation du StackPanel est Vertical, aussi les contrôles seront positionnés de haut en bas. L'attribut Orientation="Horizontal" permet de mettre les contrôles enfants de gauche à droite.

Image non disponible
 
Sélectionnez
<StackPanel Orientation="Horizontal" Height="362" HorizontalAlignment="Left" Margin="29,51,0,0" 
Name="stackPanel1" VerticalAlignment="Top" Width="390">
                <Button Content="Button" Height="220" Name="button1" Width="100" />
                <Button Content="Button" Height="220" Name="button2" Width="100" />
                <Button Content="Button" Height="220" Name="button3" Width="100" />
</StackPanel>

C'est bien pratique quand on veut mettre sur une même ligne, les différents champs d'un objet (nom, prénom, adresse) :

 
Sélectionnez
<StackPanel Orientation="Horizontal">
                    <TextBlock Padding="5,0,5,0"
                    <Text="{Binding FirstName}" />
                    <TextBlock Text="{Binding LastName}" />
                    <TextBlock Text=", " />
                    <TextBlock Text="{Binding Address}" />
</StackPanel>


En C#, voici un autre exemple :

 
Sélectionnez
StackPanel StackPanel = new StackPanel();

StackPanel.Orientation = System.Windows.Controls.Orientation.Vertical;

StackPanel.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;

StackPanel.VerticalAlignment = System.Windows.VerticalAlignment.Center;

Button MyButton1 = new Button();

Button MyButton2 = new Button();

MyButton1.Content = "bouton1";

MyButton2.Content = "bouton2";

StackPanel.Children.Add(MyButton1);

StackPanel.Children.Add(MyButton2);

this.ContentPanel.Children.Add  (StackPanel);

On a instancié des boutons que l'on ajoute à la collection Children du StackPanel.

Enfin, on met donc le StackPanel dans une grille nommée ContentPanel.

On peut insérer un bouton3 supplémentaire à une position définie :

 
Sélectionnez
Button MyButton3 = new Button();
MyButton3.Content = "bouton3";
StackPanel.Children.Insert(1, MyButton3);  //1 indique la position d'insertion.

Le bouton2 est automatiquement repositionné.

VI-C-3. Les Canvas

Il existe un contrôle Canvas qui permet de positionner des contrôles en indiquant leurs coordonnées (comme dans les Windows Forms), mais il n'y a pas de repositionnement automatique quand on modifie les dimensions du Canvas.

On positionne les contrôles avec les propriétés attachés Canvas.Top et Canvas.Left qui se trouvent dans le contrôle enfant.

Image non disponible

En XAML :

 
Sélectionnez
<Canvas Margin="72,65,118,302">
                <CheckBox Canvas.Top="80" Canvas.Left="0">Check Me</CheckBox>
                <RadioButton Canvas.Top="0" Canvas.Left="80">Yes</RadioButton>
</Canvas>

Les Canvas ont une propriété attachée ZIndex permettant d'imposer l'ordre de superposition des éléments enfants (syntaxe dans l'élément enfant : Canvas.ZIndex = "2").

VI-C-4. Les ScrollViewer

Représente une zone défilante qui peut contenir d'autres éléments visibles bien plus grands ; on fait défiler le contenu qui est dans le ScrollViewer au doigt, il apparaît des ScrollBarr si nécessaire.

Une utilisation habituelle est de mettre dans un petit ScrollViewer qui utilise la place disponible, un grand TextBlock, contenant un long texte.

Image non disponible
 
Sélectionnez
<ScrollViewer Height="232" Width="448" HorizontalScrollBarVisibility="Auto" >
  <TextBlock Width="530" TextWrapping="Wrap" 
    Text="texte très long...." />
</ScrollViewer>

HorizontalScrollBarVisibility et VerticalScrollBarVisibility permettent de forcer ou non l'affichage de la ScrollBarr ; ici il est à « Auto» .

Voici comment on positionne le ScrollViewer (qui reste sur l'écran) et le TextBlock interne (plus grand que l'écran) dans le designer :

Image non disponible

Le ScrollViewer peut aussi être utilisé comme conteneur de tous les objets de la page ; si on passe en mode paysage les objets du bas de la page ne sont plus visibles mais on peut remonter la page pour en voir le bas.

VI-C-5. WrapPanel

Le WrapPanel qui est un conteneur où les éléments sont mis à la suite l'un de l'autre mais où il y a "retour à la ligne" quand on atteint la limite du conteneur. Les éléments se positionnent de gauche à droite et de haut en bas.
Voir le chapitre sur le Toolkit.

VI-C-6. Positionnement dynamique : les grandes règles

Layout dynamique : permet un positionnement quel que soit la résolution de l'écran.
On utilise un StackPanel ou surtout une Grid comme conteneur.
Dans les cellules, on met des contrôles avec les propriétés Width et Height= Auto. Ils occupent toutes 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 *.

VI-D. Les Boutons

VI-D-1. Les Button

Pour ajouter un bouton, on clique sur le bouton dans les outils à gauche puis sur le formulaire, on appuie (bouton gauche de la souris), on déplace puis on lâche. Le bouton apparaît.

Image non disponible

En bas à droite, on a la fenêtre de propriétés du bouton.

Le nom du contrôle est en haut, après « Name ».

Dans le designer, la propriété « Content » contient le texte à afficher sur le bouton.

Si on double-clique sur un bouton, par exemple, on se retrouve dans la fonction évènement correspondante qui est Button1_Click.

Créons un bouton avec du code XAML.

Il faut taper dans la fenêtre XAML :

 
Sélectionnez
<Button>Ok</Button>

Cela crée un bouton sur lequel est affiché « Ok ». Il apparaît en haut.

Faisons plus complet :

 
Sélectionnez
<Button Height="150" Margin="55,50,79,0" Name="Button1" VerticalAlignment="Top" Click="OnClick5">Ok</Button>

Voyons le détail de cette ligne :

La balise « Button » crée un bouton.

Name="Button1" indique le nom du contrôle dans le designer.

VerticalAlignment="Top" indique que le contrôle est ancré en haut (il reste toujours à la même distance du bord sur lequel il est ancré. Valeur possible : Top, Bottom, Center.

HorizontalAlignment="Left" même chose pour l'alignement horizontal.

Margin définit la distance par rapport aux bords du conteneur.

Width="" Height="" indiquent les dimensions du bouton (largeur, hauteur).

On peut aussi ajouter MinWidth MinHeight MaxWidth et MaxHeight qui indiquent les tailles minimales et maximales.

On aurait pu utiliser l'attribut Content="Ok" pour mettre un texte dans le bouton.

On peut positionner le « Content » dans le bouton en indiquant des alignements et marges internes (Padding) :

 
Sélectionnez
<Button Content="Cliquez ici"
Padding="70 100"
HorizontalContentAlignment="Right"
VerticalContentAlignment="Bottom" />

Click="OnClick5" indique que l'action de cliquer sur le bouton déclenche la fonction OnClick5.

Cela crée automatiquement, dans le code C#, la routine suivante :

 
Sélectionnez
private void OnClick5(object sender, RoutedEventArgs e)
        {

        }

sender est l'objet qui a déclenché l'évènement.

e contient les arguments de l'évènement, remarquons que e est de type RoutedEventArgs (et pas EventArgs comme dans les Windows Forms).

On peut ajouter du code dans la routine, pour modifier les propriétés du bouton, par exemple :

 
Sélectionnez
private void OnClick5(object sender, RoutedEventArgs e)
        {
Button1.FontSize = 16; 
Button1.Content = "Ok Ok..";
}

Mettre des formes dans un bouton puis y mettre une image.

 
Sélectionnez
<Button>
  <StackPanel>
    <Ellipse Height="40" Width="40" Fill="Blue"/>
    <TextBlock TextAlignment="Center">Button</TextBlock>
  </StackPanel>
</Button>

<Button>
  <Rectangle Height="40" Width="40" Fill="Blue"/>
</Button>



<Button Height="130" Margin="9,362,0,0" Name="Button2" VerticalAlignment="Top" >
                <Image Source="wphone.jpg"></Image>
</Button>

Cela donne :

Image non disponible

Pour mettre une image dans un bouton il faut avoir le fichier image, l'ajouter au projet (menu « Projet » puis « Ajouter un élément existant ») ajouter Image et renseigner la source.

On peut choisir un fichier GIF avec un fond transparent ce qui permet de ne pas voir le fond de l'image.

Comment mettre un texte et une image dans un bouton ?

Il faut mettre un StackPanel dans le bouton (puisque celui-ci ne peut contenir qu'un seul objet), dans ce StackPanel mettre un TextBlock et une Image. Le faire en tapant du code XAML (dans le designer VB c'est difficile de mettre un StackPanel dans un Button, il se met dessus et pas dedans, donc copier/coller le code XAML). De plus l'image doit être dans les ressources.

 
Sélectionnez
<Button Height="170" Margin="31,377,-22,0" Name="Button2" VerticalAlignment="Top" >
                <StackPanel Height="212">
                    <TextBlock HorizontalAlignment="Center">Ok</TextBlock>
                <Image Source="wphone.jpg" Height="84" Width="96"></Image>
                </StackPanel>
</Button>

Cela donne :

Image non disponible

Voir aussi le chapitre sur les ressources.

Créons un bouton avec du code C# :

 
Sélectionnez
Button myButton = new Button();    //Création du bouton
myButton.Content = "ok";           //Afficher 'Ok' dans le bouton
myButton.Background = new SolidColorBrush(Colors.Blue);      //Fond en bleu
ContentPanel.Children.Add(myButton);   //Ajouter le bouton à la grid
myButton.Click += new RoutedEventHandler(buttonClick);//Indiquer que le click sur le bouton
                                    //doit exécuter 'buttonClick'


        

        private void buttonClick(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("ok");
        }                                      'doit exécuter la Sub nommée 'Click'

On remarque que, comme on crée par code, il faut soi même écrire la gestion des évènements.

Quand on crée le bouton en mode designer, et que l'on double-clique sur le bouton, la routine myButton_Click est automatiquement affichée.

VI-D-2. HyperLinkButton

Représente un bouton qui affiche un lien hypertexte.

 
Sélectionnez
<HyperlinkButton Content="Cliquer ici pour voir Silverlight.net"
    NavigateUri="http://www.silverlight.net" TargetName="_blank" Margin="10,60,0,0"/>
Image non disponible

NavigateUri donne l'adresse.
TargetName a la valeur :
_blank, _media ou _search, cela charge le document dans une nouvelle fenêtre vide.
_parent, _self, _top, ou "" cela charge la page dans la fenêtre dans laquelle l'utilisateur a cliqué sur le lien (la fenêtre active).
L'effet n'est pas visible.

Si on met une adresse Internet, cliquer sur le lien ouvre le navigateur Web.

On peut aussi naviguer vers une autre page :

 
Sélectionnez
<HyperlinkButton NavigateUri="MySecondPage.xaml" />

VI-E. Les contrôles contenant du texte

Les contrôles permettant de voir ou de modifier du texte sont :

  • les TextBlock ;
  • les TextBox ;
  • les RichTextBox ;
  • les PasswordBox.

VI-E-1. Les TextBlock


Permettent d'afficher du texte dans une page en lecture seule, sans possibilité de le modifier. Il est bien adapté pour afficher une ou au maximum quelques lignes.

Prendre un TextBlock dans les outils et le mettre dans un formulaire.

Image non disponible

Pour mettre rapidement un petit texte dedans en C#, utiliser la propriété Text.

 
Sélectionnez
TextBlock1.Text= "Mon texte";

La même chose en XAML :

 
Sélectionnez
<TextBlock> 
Mon texte 
</TextBlock>
 
Sélectionnez
TextWrapping="Wrap"

Permet de passer automatiquement à la ligne.


On peut modifier le texte (Text), la hauteur des caractères (FontSize), la couleur du texte (Foreground) :

 
Sélectionnez
 <TextBlock Name="TextBlock1"  Text="Hauteur:" FontSize="46" Foreground="Cyan" Height="55" Width="308" />
Image non disponible

Un TextBlock a une propriété Foreground (couleur du texte) mais pas de Background (pas de couleur de fond), pas de bords.

Pour mettre une couleur de fond, il faut mettre le TextBlock dans un Rectangle.
Pour avoir un bord, mettre le TextBlock dans un Border (dans le TextBox mettre Margin="0").

 
Sélectionnez
<Border  Margin="12,189,0,352" BorderThickness="2" BorderBrush="Bisque">
            <TextBlock Name="resultat"  HorizontalAlignment="Left" Margin="0"  TextAlignment="Center">           
            </TextBlock>
</Border>

On peut ajouter des sauts de lignes simplement grâce à \n.

 
Sélectionnez
TextBlock1.Text= "Mon texte  \n  Seconde ligne ";


On peut ajouter dans le code XAML plusieurs lignes (avec la balisee Run qui correspond à un morceau de texte) avec des couleurs, des tailles, des polices différentes et des retours à la ligne avec des LineBreak.

 
Sélectionnez
<TextBlock Height="151" HorizontalAlignment="Left" Margin="65,243,0,0" Name="textBlock4"  
VerticalAlignment="Top" Width="290">
  <LineBreak/>
  <Run Foreground="Maroon" FontFamily="Courier New" FontSize="24">Courier New 24</Run>
  <LineBreak/>
  <Run Foreground="Teal" FontFamily="Times New Roman" FontSize="18" FontStyle="Italic">Times New Roman Italic 18</Run>
  <LineBreak/>
  <Run Foreground="SteelBlue" FontFamily="Verdana" FontSize="14" FontWeight="Bold">Verdana Bold 14</Run>         
</TextBlock>
Image non disponible


On peut le faire en C# :

 
Sélectionnez
using System.Windows.Documents;
 
Sélectionnez
         TextBlock textlong = new TextBlock();
        Run r = new Run();
        r.Text = "mon texte";
        r.FontSize = 20;
        r.FontWeight = FontWeights.Bold;
        r.Foreground = new SolidColorBrush(Colors.Orange);
        textlong.Inlines.Add(r);
        textlong.Inlines.Add(new LineBreak());

On déclare un « Run », on modifie ses propriétés ; on l'ajoute à la collection de lignes (InLines). On peut aussi ajouter un saut de ligne.

Exercice : j'ai une string mySting, je veux l'ajouter à un TextBlock avec des passages à la ligne et mettre la première ligne en orange.

 
Sélectionnez
string myString= "Titre:?Première ligne.?Seconde ligne.";

J'ai ajouté un caractère '¤' signifiant retour à la ligne.
Je vais découper la string grâce à Split puis avec une boucle je vais ajouter à la collection InLines un Run (un texte ) puis un LineBreak.

 
Sélectionnez
            int i=0;
            foreach (var line in myString.Split('?'))
            {

                if (i == 0)
                {
                    var r = new Run();
                    r.Text = line;
                    r.FontSize = 20;
                    r.FontWeight = FontWeights.Bold;
                    r.Foreground = new SolidColorBrush(Colors.Orange);
                    TextLong.Inlines.Add(r);
                    TextLong.Inlines.Add(new LineBreak());
                    i++;
                }
                else
                {
                    TextLong.Inlines.Add(line);
                    TextLong.Inlines.Add(new LineBreak());
                }
            }

VI-E-2. Les TextBox

Permettent d'afficher du texte, il est modifiable par l'utilisateur.

La police de caractères, sa taille, la couleur, l'enrichissement des caractères affectent la totalité du texte. Il n'est pas possible d'enrichir (gras, italique…) une partie du texte seulement.

Image non disponible

Créons un TextBox en XAML :

 
Sélectionnez
<TextBox></TextBox>

Donnons lui un nom :

 
Sélectionnez
<TextBox Name="TextBox1"></TextBox>

Mettons un texte dedans :

 
Sélectionnez
<TextBox Name="TextBox1">Ceci est le texte</TextBox>

En C#

 
Sélectionnez
TextBox1.Text="le texte";

TextBox1.Text contient le texte affiché dans la TextBox.

TextBox1.IsReadOnly=true  interdit les modifications du texte.

TextBox1.AcceptEnter=true  autorise le passage à la ligne quand on tape « Enter ». Si AcceptEnter=false, on ne peut saisir qu'une seule ligne.

VerticalScrollBarVisibility=true  affiche une ScrollBar verticale.

TextBox1.MaxLength permet de définir le nombre de caractères. 0 pour une saisie illimitée.

Si l'utilisateur a sélectionné du texte, il est dans TextBox1.SelectedText.

TextBox1.SelectionStart, TextBox1.SelectionLength indique la position du premier caractère sélectionné (le premier caractère du texte étant le caractère 0) et le nombre de caractères sélectionnés.
On peut utiliser ces propriétés pour sélectionner du texte avec du code ou utiliser TextBox1.Select(3, 2), il existe enfin SelectAll qui selectionne la totalité du texte.

Si le texte est modifié cela déclenche :

 
Sélectionnez
private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
        {

        }

Double_cliquez sur le TextBox, cela fera apparaître le code qui précède.

Quand l'utilisateur frappe une touche cela déclenche les évènements KeyDown puis KeyUp (pas de keyPress !)

Pour ajouter la gestion de l'évènement KeyUp, cliquer sur le bouton. Dans la fenêtre des propriétés cliquer sur l'onglet « Évènements ».

Image non disponible

Puis dans la liste double-cliquez sur « KeyUp » vous voyez apparaître :

 
Sélectionnez
 <TextBox  Name="textBox1" Text="TextBox1"  KeyUp="textBox1_KeyUp" />

Et dans le code C# :

 
Sélectionnez
  private void textBox1_KeyUp(object sender, KeyEventArgs e)
        {
        
             }

On note que la fonction a un paramètre e de type KeyEventArgs qui a la propriété Key qui contient la touche tapée. Malheureusement e.Key est en lecture seule ! On ne peut donc pas modifier le caractère tapé !
De plus le contenu de e.Key est bizarre ! Des expressions comme if (e.Key==Key.B)… ne fonctionnent pas car e.Key ne contient pas ce qui est attendu.


Pour modifier les caractères tapés on les modifie directement dans la propriété text :

 
Sélectionnez
  private void textBox1_KeyUp(object sender, KeyEventArgs e)
        {
            TextBox TextBoxControl = (TextBox)sender;
            string keys;
            keys = "1234567890";

            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;
                }
            }

        }

Quand on tape sur une TextBox et qu'elle prend le focus, il apparaît automatiquement un clavier virtuel en bas.

Le clavier par défaut, si on n'indique rien est le clavier "Default".
Il permet de taper des minuscules et donne accès aux majuscules et aux chiffres :

Image non disponible

Ce petit clavier à l'écran est appelé "panneau de saisie logiciel" ou SIP.

Mais on peut avoir besoin d'afficher un autre clavier virtuel.
Voici le clavier TelephoneNumber permettant de taper des numéros de téléphone :

Image non disponible

Comment l'obtenir ?
En utilisant un InputScope.

 
Sélectionnez
<TextBox Name="entree1" InputScope="TelephoneNumber" Height="75"   Text="" >
</TextBox>


Ci dessous une syntaxe plus longue mais permettant d'avoir de l'aide à la saisie, la liste des valeurs s'affiche quand on saisit NameValue :

 
Sélectionnez
 <TextBox Height="90" Name="textBox1" Text="" Width="197" Margin="79,105,181,412">
                <TextBox.InputScope>
                    <InputScope>
                        <InputScopeName NameValue="Digits"></InputScopeName>
                    </InputScope>
                </TextBox.InputScope>
            </TextBox>

En C# :

 
Sélectionnez
InputScope scope = new InputScope();
InputScopeName name = new InputScopeName();

// Saisir des dates
name.NameValue = InputScopeNameValue.Date;

// Saisir un numéro de téléphone 
name.NameValue = InputScopeNameValue.TelephoneNumber; 

// Saisir un montant
name.NameValue = InputScopeNameValue.CurrencyAmount; 
                
scope.Names.Add(name);
MyTextBox.InputScope = scope;

Il y a aussi DateDay, DateMonth, DateYear, Address, Chat, Digits, FileName, Number, OneChar, PostalCode, Text, Time, Url, Xml…
Voici quelques exemples :

Text : la première lettre tapée est en majuscule puis le clavier passe en minuscules.
Quand on tape au moins une lettre la zone au dessus du clavier propose des mots sur lesquels on peut taper.
En plus de ces suggestions de texte, il y a la correction automatique, l'auto-apostrophe, l'auto-accentuation et l'auto capitalisation.
Chat est le même clavier (contrairement à la version anglaise et a ce qui est dit par ailleurs).

Image non disponible


Url :
permet de saisir des adresses Internet.
L'appui long sur « .com » donne accès à d'autres nom de domaine.

Image non disponible


Digit :
pour saisir les chiffres de 0 à 9 et le point (même si on est en clavier français).
CurrencyAmmont et Number sont le même clavier en français.

Image non disponible


CurrencyAmountAndSymbol :
permet de saisir chiffres et symboles.

Image non disponible

Contrairement à ce qui est dit ailleurs les inputscope sont différents de ceux de la version anglaise.
Sous Mango, l'InputScope « Number », représente maintenant uniquement un clavier numérique, alors qu'auparavant c'était le clavier chiffre+symboles.


En cours de saisie, il ne faut pas oublier de filtrer les caractères tapés (voir chapitre « Comment faire »).

VI-E-3. Les RichTextBox

Rich Text veut dire Texte enrichi.
Le contrôle RichTextBox permet d'afficher, d'entrer et de manipuler du texte mis en forme. Il effectue les mêmes tâches que le contrôle TextBox, mais il peut également afficher des polices, des couleurs pour une partie du texte et des liens, charger du texte et des images incorporées à partir d'un fichier, ainsi que rechercher des caractères spécifiques.

En Wp RichTextBlox est ReadOnly, donc pas de possibilité d'écrire dedans ! ce qui lui enlève son intérêt.

Ce contrôle n'apparaît pas dans la liste d'outils ; il faut l'ajouter :

Dans la boîte à outils, clic droit sur « Windows Phone Controls », puis clic sur la ligne « Choisir les éléments ».

Image non disponible

Là on peut cocher le contrôle « RichTextBox » à ajouter dans la liste de la boîte à outils.

En XAML :
on peut ajouter un texte dans la balise Paragraph, l'enrichir avec les balises Bold, Italic, Underline, Hyperlink.
On peut aller à la ligne avec LineBreak.

 
Sélectionnez
<RichTextBox Name="rtb" Height="169" HorizontalAlignment="Left" Margin="42,6,0,0"  VerticalAlignment="Top" Width="348">
                <Paragraph>
                    Un RichTextBox avec du texte en <Bold> gras </Bold> en
                    <Italic>italique</Italic> en
                    <Underline>souligné</Underline>
                    <LineBreak/>
                    <Hyperlink NavigateUri="http://search.msn.com">avec un lien"</Hyperlink>.
                </Paragraph>
</RichTextBox>
Image non disponible

Noter la syntaxe du lien qui montre que dans la balise on peut ajouter des attributs.
On va voir un autre exemple avec un Border et l'utilisation d'attributs pour modifier la couleur et la taille des caractères :

 
Sélectionnez
<RichTextBox Name="rtb" Height="339" HorizontalAlignment="Left" Margin="42,6,0,0" 
 VerticalAlignment="Top" Width="348" BorderThickness="4">
                <RichTextBox.BorderBrush>
                    <SolidColorBrush Color="Red" />
                </RichTextBox.BorderBrush>
                
                <Paragraph>
                    <Run Foreground="Red">RichTextBox avec du texte en rouge</Run>
                     <LineBreak/>
                    <Run FontSize="50">C'est beau.</Run>
                </Paragraph>
            </RichTextBox>

Cela donne :

Image non disponible


En C# c'est un peu plus difficile :
On crée des « Run » (portion de texte) qu'on met dans la collection Inlines des « Paragraph ». Ensuite on met les paragraphes dans la collection Blocks du RichTextBox.

 
Sélectionnez
//Créer un RichTextBox 
    RichTextBox MyRTB = new RichTextBox();
    //VertalScrollBar à auto
    MyRTB.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;

    // Créer deux 'Run' de texte et un 'Bold' avec du texte en gras.
    Run myRun1 = new Run();
    myRun1.Text = "Un RichTextBox avec ";
    Bold myBold = new Bold();
    myBold.Inlines.Add("du texte en gras ");
    Run myRun2 = new Run();
    myRun2.Text = "dedans.";

    // Créer un 'Paragraph' et y ajouter les 2 Run et le Bold.
    Paragraph myParagraph = new Paragraph();
    myParagraph.Inlines.Add(myRun1);
    myParagraph.Inlines.Add(myBold);
    myParagraph.Inlines.Add(myRun2);

    // Ajouter le paragraph au RichTextBox.
    MyRTB.Blocks.Add(myParagraph);

    //Ajouter le RichTextBox à la grille.
    ContentPanel.Children.Add(MyRTB);

VI-E-4. Les PasswordBox

Permet de saisir un mot de passe.

La propriété PasswordChar détermine le caractère affiché à la place des caractères tapés.

En XAML :

 
Sélectionnez
<PasswordBox Name="pwdBox" MaxLength="64" PasswordChar="#" PasswordChanged="PasswordChanged" />

Ici le fait de taper un mot de passe déclenche la fonction PasswordChanged.

En C# on récupère le mot de passe dans :

 
Sélectionnez
pwdBox.Password

VI-F. Les cases à cocher et RadioButton

VI-F-1. Case à cocher

Image non disponible

Créons une case à cocher.

C'est une CheckBox. L'utilisateur peut la cocher ou non en cliquant dessus.

Dans le designer, la propriété Content contient le texte à afficher à côté de la case.

Les évènements les plus utiles sont  : Checked et Unchecked.

 
Sélectionnez
<!-- CheckBox à 2 états -->
<CheckBox x:Name="cb1" Content="CheckBox à 2 états" 
          Checked="HandleCheck" Unchecked="HandleUnchecked" Margin="5" />
<TextBlock x:Name="text1" Margin="5" />

<!-- CheckBox à 3 états -->
<CheckBox x:Name="cb2" Content="CheckBox à 3 états" 
    IsThreeState="True" Checked="HandleCheck" 
    Indeterminate="HandleThirdState" Unchecked="HandleUnchecked" Margin="5" />
<TextBlock x:Name="text2" Margin="5" />
 
Sélectionnez
private void checkBox1_Checked(object sender, RoutedEventArgs e)
        {

        }

Dans le code XAML IsChecked="True" permet de cocher un CheckBox. Dans le code C#, la propriété IsChecked permet de voir si la case est cochée ou non.

 
Sélectionnez
 if (MyradioButton1.IsChecked == true)
            { conv = 0.6; }

VI-F-2. RadioButton

Les RadioButton peuvent être cochés ou non.

Image non disponible

Ils sont généralement regroupés pour offrir aux utilisateurs un choix unique parmi plusieurs options, un seul bouton à la fois peut être coché. Si on clique sur un RadioButton dans un groupe, on le sélectionne, cela désélectionne les autres.

Vous pouvez regrouper des contrôles RadioButton en les plaçant dans un parent commun ou en leur attribuant un nom de groupe (GroupName).

Quand un RadioButton est sélectionné, l'événement Checked est déclenché. Comme le montre l'exemple de code suivant, si votre application doit faire une action quand la sélection de RadioButton change, vous pouvez gérer l'événement Checked.

 
Sélectionnez
<RadioButton Name="MyRadioBUtton" 
            IsChecked="True"
            Checked="Myroutine"
            GroupName="MyGroupe">
 Texte    
</RadioButton>

VI-G. Les ListBox

Une ListBox est un contrôle qui contient une collection de ListBoxItem. Chaque ListBoxItem a une propriété « Content ».

Créons une ListBox avec trois ListBoxItem en XAML :

 
Sélectionnez
<ListBox Height="176" HorizontalAlignment="Left" Margin="19,27,0,0" Name="listBox1" VerticalAlignment="Top" Width="400">
                <ListBoxItem Content="Paris" />
                <ListBoxItem Content="Lyon" />
                <ListBoxItem Content="Marseille" /> 
</ListBox>
Image non disponible

Créer en C# une ListBox puis ajouter des éléments à la ListBox :

 
Sélectionnez
ListBox ListBox1 = new ListBox();
ListBox1.Items.Add("Paris");
ListBox1.Items.Add("Lyon");
ListBox1.Items.Add("Marseille");
ContentPanel.Children.Add(ListBox1);

On peut créer en C#, une collection « Villes », y ajouter des éléments et ensuite faire un binding :

 
Sélectionnez
    List<string> Villes = new List<string>
    {
        "Paris",
        "Lyon",
        "Marseille"
    };
            listBox1.ItemsSource = Villes;

Dans ce cas dans le code XAML ItemsSource doit indiquer qu'il y a binding.

 
Sélectionnez
 <ListBox  ItemsSource="{Binding}"   Name="listBox1" />

On peut effacer la liste.

 
Sélectionnez
ListBox.Items.Clear();

SelectedIndex indique l'index de l'élément sélectionné (-1 si pas de sélection).
SelectedItem indique l'item (objet) sélectionné. On peut l'utiliser pour forcer par code la sélection.

 
Sélectionnez
ListBox.SelectedItem = ListBox.Items[2];


On peut ajouter un bord autour du ListBox :

 
Sélectionnez
<ListBox  BorderThickness="5" BorderBrush="Red"  />

Maintenant on veut personnaliser chaque Item.
Pour cela, on va utiliser un style pour les ListBoxItem.
On va créer dans les ressources de la grille racine un style pour les ListBoxItem ; ce style définira un template qui affiche un bord pour chaque Item.
Enfin on créera la ListBox en utilisant pour chaque ListBoxItem le style précédent.

 
Sélectionnez
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <Grid.Resources>
                <Style x:Key="ListBoxTemplate" TargetType="ListBoxItem">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="ListBoxItem">
                                <Border BorderThickness="3" BorderBrush="Azure" Height="50" Margin="4">
                                   
                                    
                                        <ContentPresenter  HorizontalAlignment="Center"
                              VerticalAlignment="Center" Content="{TemplateBinding Content}"/>
                                    
                                </Border>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>

               
            </Grid.Resources>
    <ListBox  BorderThickness="5" BorderBrush="Red"  Height="192" HorizontalAlignment="Left" Margin="19,27,0,0" 
            Name="listBox1" VerticalAlignment="Top" Width="400" SelectionChanged="listBox1_SelectionChanged">

                <ListBoxItem Content="Paris"  Style="{StaticResource ListBoxTemplate}" />
                <ListBoxItem Content="Lyon"  Style="{StaticResource ListBoxTemplate}" />
                <ListBoxItem Content="Marseille"  Style="{StaticResource ListBoxTemplate}" /> 
            </ListBox>
        </Grid>
    </Grid>

On remarque que le Template du ListBoxItem contient dans le Border un ContentPresenter ; dans ce ContentPresenter, on indique que le Content doit être lié au content du ListBoxItem.

Image non disponible

On peut aussi modifier l'item template pour, au lieu d'afficher les items les uns au dessus des autres, les afficher de gauche à droite avec passage à la ligne (comme dans le Media Library). Il faut pour cela utiliser un WrapPanel (voir dans le chapitre sur le Toolkit).

Mettre des boutons, des cases à cocher dans une ListBox :

Les CheckedListBox n'existent pas en WP ? On va les créer dans une ListBox1 :

 
Sélectionnez
<ListBox  BorderThickness="5" BorderBrush="Red"  Height="192"  Name="listBox2"  
  SelectionChanged="listBox1_SelectionChanged">
                <CheckBox Content="Option1"></CheckBox>
                <CheckBox Content="Option2"></CheckBox> 
</ListBox>
Image non disponible

De la même manière, on peut créer une liste de boutons…

Utilisation du binding sur une ListBox.

On reprend l'exemple du chapitre sur le binding en le complétant.
Dans Class1.cs on crée une classe nommée « ElementDeListe ».

 
Sélectionnez
using System;
    using System.ComponentModel;
    using System.Diagnostics;
    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 listboxbinding // Espace de noms = nom du programme
    {
        public class ElementDeListe  // la classe se nomme 'ElementDeListe'
        {
            private string _Titre;  // Variable privée
            
            public string Titre     // Variable public avec son get et son set
            {
                get
                {
                    return _Titre;
                }
                set
                {
                    if (value != _Titre)
                    {
                        _Titre = value;
                       
                    }
                }
            }
            private string _Formule;

            public string Formule
            {
                get
                {
                    return _Formule;
                }
                set
                {
                    if (value != _Formule)
                    {
                        _Formule = value;

                    }
                }
            }

            private string _Mode;

            public string Mode
            {
                get
                {
                    return _Mode;
                }
                set
                {
                    if (value != _Mode)
                    {
                        _Mode = value;

                    }
                }
            }
          
            public ElementDeListe(string t, string n, string m) { _Titre = t; _Formule = n; _Mode = m; }  
        }
  
  }

Dans MainPage.xaml.cs on déclare une Collection nommée « ListMath » de type List, on y met quelques éléments.

 
Sélectionnez
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;
using Microsoft.Phone.Controls;

namespace listboxbinding
{
    public partial class MainPage : PhoneApplicationPage
    {
        public List<ElementDeListe> ListeMath { get; set; } //la collection sera public
        // Constructeur
        public MainPage()
        {
            InitializeComponent();

            ListeMath = new List<ElementDeListe>();

            //On va créer de nouveaux ElementDeliste en utilisant le constructeur à 3 paramètres.
            ListeMath.Add(new ElementDeListe("Francs-Euros", "C", "C"));
            ListeMath.Add(new ElementDeListe("Celsius-Fahrenheit", "Celsius", "C"));
            ListeMath.Add(new ElementDeListe("Celsius-Kelvin", "Celsius", "C"));

            listBox1.DataContext = ListeMath;// en prime on ajoute un binding (une liaison) sur une listbox
        }
    }
}

En fin de code on lie la collection à la ListBox grâce à sa propriété DataContext.

Dans MainPage.xaml on crée une ListBox nommée « listBox1 », on indique que la source des items est une liaison.

 
Sélectionnez
 <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <ListBox ItemsSource="{ Binding}" 
                Height="193" HorizontalAlignment="Left" Margin="40,32,0,0" Name="listBox1" VerticalAlignment="Top" Width="369" />
        </Grid>

Cela donne :

Image non disponible

La ListBox affiche les types !
C'est normal elle affiche ce que retourne la méthode ToString().

On a une première méthode pour éviter l'affichage du type : on surcharge la méthode ToString de la classe ElementdeListe :

 
Sélectionnez
public override string ToString()
    {
        return Titre + " - " + Formule;
    }

Cela marche bien mais uniquement avec du texte.

Une autre méthode (plus générale) pour afficher Titre et Formule, on va ajouter un ItemTemplate contenant un StackPanel horizontal qui contient trois TextBlock. Le premier et le troisième sont liés au propriétés « Titre » et « Formule ».

 
Sélectionnez
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <ListBox ItemsSource="{ Binding}" 
    Height="193" HorizontalAlignment="Left" Margin="40,32,0,0" Name="listBox1" VerticalAlignment="Top" Width="369" >
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding Titre}" />
                            <TextBlock Text=", " />
                            <TextBlock Text="{Binding Formule}" />
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
Image non disponible

Utilisation d'ItemTemplate complexe.

On veut afficher pour chaque Item une petite sphère, la propriété « Titre » en gros et la propriété « Formule » dessous en plus petit :

 
Sélectionnez
<ListBox Name="listBox1"  Margin="10,10,23,-9" ItemsSource="{Binding}" 
Height="524" Width="400"  MouseLeftButtonUp="ListBoxConversion_Appel"  >
 <ListBox.ItemTemplate>
  <DataTemplate>
    <StackPanel Orientation="Horizontal" Margin="0,0,0,17">
<!--La petite sphère-->
        <Ellipse Height="30" Width="30"  Margin="12,0,9,0">
        <Ellipse.Fill>
        <RadialGradientBrush Center="0.4 0.4" 
        GradientOrigin="0.4 0.4">
        <GradientStop Offset="0" Color="White" />
        <GradientStop Offset="1" Color="Blue"  />
        </RadialGradientBrush>
        </Ellipse.Fill>
        </Ellipse>
        <StackPanel Width="311">
<!--Les 2 lignes Titre et Formule-->
    <TextBlock Foreground="Azure" Text="{Binding Titre}" TextWrapping="Wrap" 
    Style="{StaticResource PhoneTextLargeStyle}"   />
    <TextBlock Text="{Binding Formule}" TextWrapping="Wrap" Margin="12,-6,12,0" 
    Style="{StaticResource PhoneTextSubtleStyle}" />
        </StackPanel>
    </StackPanel>
  </DataTemplate>
 </ListBox.ItemTemplate>
</ListBox>
Image non disponible

Bien respecter les majuscules et minuscules dans les liaisons car si on se trompe d'une lettre, rien ne signale une erreur mais cela ne marche pas.

On voit que l'ItemTemplate est beaucoup plus puissant, on peut afficher dans la ListBox du texte mais aussi des images…

Et les évènements !

Il est indiqué partout d'utiliser l'évènement SelectionChanged :

 
Sélectionnez
<ListBox ItemsSource="{ Binding}" Height="193" HorizontalAlignment="Left"  
Name="listBox1"  SelectionChanged="ListBoxSelChange"/>

Ainsi quand l'utilisateur tape sur une ligne de la ListBox l'évènement SelectionChanged se produit et la routine suivante est exécutée.

 
Sélectionnez
 private void ListBoxSelChange(object sender, RoutedEventArgs e)
       {
        if (e.AddedItems.Count > 0)
        string TexteSelectionné = e.AddedItems[0].ToString();
       }

L'inconvénient est que si je tape deux fois de suite sur le même item de la ListBox, la seconde fois ne déclenche rien puisque la sélection n'a pas changé.

J'utilise plutôt MouseLeftButtonUp.

 
Sélectionnez
<ListBox ItemsSource="{Binding}" Height="193" HorizontalAlignment="Left"  
Name="listBox1" MouseLeftButtonUp="ListBox_Appel"/>

Quand l'utilisateur tape sur une ligne de la ListBox l'évènement MouveLeftButtonUp se produit et la routine suivante est exécutée.
Je peux utiliser l'item sélectionné : SelectedItem.

 
Sélectionnez
 private void ListBoxConversion_Appel(object sender, RoutedEventArgs e)
          {//sender est l'objet qui a déclenché l'évènement, je le cast en listbox
        ListBox list = sender as ListBox;
        //SelectedItem est aussi un objet qui doit être casté
        ElementDeListe  ItemSelectionne = list.SelectedItem as ElementDeListe;
        if (ItemSelectionne == null)
            return;
            }
            //....Ici on peut utiliser ItemSelectionne
            }

VI-H. Les Slider

Un Slider est un curseur qu'on peut déplacer avec le doigt :

 
Sélectionnez
<StackPanel>
    <Slider Name="Slider1" Value="5" Maximum="200" Minimum ="0" Margin="0,294,0,172" />
    <Rectangle Fill="Blue" Stroke="Red"  StrokeThickness="3" Width="97" 
    Height="{Binding ElementName=Slider1, Path=Value}"/>
</StackPanel>

Ici on utilise un Slider pour modifier la hauteur d'un rectangle grâce au binding.

Image non disponible

VI-I. Les MessageBox

Comment afficher un message pour l'utilisateur (sans sortir de la page) ?

 
Sélectionnez
MessageBox.Show("Erreur sur le format de la date");

Cela donne :

Image non disponible

Il y a un bouton « Ok » par défaut pour sortir du MessageBox.

Sinon on peut ajouter un titre et un bouton « Annuler ».

 
Sélectionnez
MessageBox.Show("Poursuivre?", "Exemple MessageBox", MessageBoxButton.OKCancel);
Image non disponible


MessageBox peut retourner dans une variable de type MessageBoxResult le résultat de l'action de l'utilisateur :
A-t-il tapé sur Ok ou Annuler ?


MessageBoxResult.Ok
ou
MessageBoxResult.Cancel
sera retourné suivant le bouton qui a été tapé par l'utilisateur.

 
Sélectionnez
 MessageBoxResult result = MessageBox.Show("Poursuivre?",
     "Exemple MessageBox", MessageBoxButton.OKCancel);

            if (result == MessageBoxResult.OK)
            {
                MessageBox.Show("Vous avez tapé sur ok.");
            }


En WP7 il n'y a que deux boutons possibles.
Les InputBox n'existent pas.

VI-J. Image, Vidéo, Son

On peut ajouter un contrôle « Image ».

Comment mettre une image dans un contrôle image ?
En C# :
(image qui est sur Internet puis image locale) :

 
Sélectionnez
using System.Windows.Media;
using System.Windows.Media.Imaging;

//Image sur Internet
//On instancie un Uri adresse internet d'une image
Uri uri = new Uri("http://www.MonSite/MonImage.jpg");

//On instancie un BitmapImage on charge l'image
BitmapImage bmp = new BitmapImage(uri);

//On met le Bitmap dans le contrôle image
Image.Source = bmp;


//Image locale (avec comme 'option de génération'= 'Contenu')
Image.Source = new BitmapImage(new Uri("MyImage.jpg", UriKind.Relative));


//Image locale (avec comme 'option de génération'= 'Resource')
Image.Source = new BitmapImage(new Uri("NomProjet;component/MyImage.jpg", UriKind.Relative));


Dans le code XAML :

 
Sélectionnez
<Image x:Name="Image" Source="/Images/MyImage.jpg" Width="60" Height="60" />

Ici l'image (option de génération = Contenu) est dans un répertoire « Images » du projet.

Si l'image n'existe pas, il n'y a pas de levée d'exception. Pas d'arrêt du logiciel avec un code d'erreur. Cool !

Par contre, si on veut, on peut intercepter l'erreur en s'abonnant à l'évènement ImageFailed.


L'image doit être mise dans le projet : menu « Projet », « Ajouter un élément existant », choisir l'image puis « Ok », elle apparaît dans la fenêtre de l'explorateur de solution à droite.
Dans ses propriétés en bas à droite, il faut mettre « Option de génération » à « Contenu » ou le laisser à « Resource » (et Copier… à « Copier si plus récent »).

On se souvient que le chemin de l'application est « C:\Users\Philippe\Documents\Visual Studio 2010\Projects\Mon application ».

Utiliser des fichiers JPEG plutôt que des PNG (le décodage des PNG se fait au niveau logiciel alors que celui des JPEG se fait au niveau matériel).
Par contre pas de transparence dans les JPEG.
Pas de GIF dans WP.

On peut donner un chemin absolu :

 
Sélectionnez
<Button  HorizontalAlignment="Left" Margin="4,0,0,0" Name="Button1" VerticalAlignment="Top" Width="150"  Height="100">
    <Image Source="C:/test.JPG" />
</Button>

On peut aussi utiliser les ressources pour mettre l'image (voir chapitre Ressources).

On peut ajouter l'attribut Opacity dont la valeur va de 0 à 1.

 
Sélectionnez
<Image Source="C:/test.JPG" Opacity="0.5"/>

L"image est semi-transparente.

L'attribut Stretch permet de gérer les dimensions de l'image ; par défaut elle a la valeur « Uniform », ainsi la totalité de l'image rentre dans le contrôle sans déformation.
'UniformToFill' conserve les proportions mais ne force pas la totalité de l'image a être affichée.

'Fill' adapte l'image au conteneur, mais il peut y avoir des déformations.
« None » respecte les dimensions en pixel de l'image, l'image peut être plus petite ou plus grande que le conteneur ; on peut ne pas la voir en totalité.

 
Sélectionnez
<Image Margin="10,10,10,10" Source="fr.png" Stretch="UniformToFill" />

StretchDirection = UpOnly ne permet que l'agrandissement, DownOnly permet le contraire. Par défaut sa valeur est égale à Both.

On peut appliquer une transformation à une image (ici une rotation de 90 degrés) :

 
Sélectionnez
<Rectangle Width="200" Height="100" Stroke="Black" StrokeThickness="1">
  <Rectangle.Fill>
    <ImageBrush Stretch="UniformToFill">
      <ImageBrush.ImageSource>
        <BitmapImage UriSource="sampleImages\myimage.jpg" />
      </ImageBrush.ImageSource>
      <ImageBrush.RelativeTransform>
        <RotateTransform CenterX="0.5" CenterY="0.5" Angle="90" />
      </ImageBrush.RelativeTransform>
    </ImageBrush>
  </Rectangle.Fill>
</Rectangle>

Pour le son, la vidéo : on peut lire des Windows Media Video (WMV), Windows Media Audio (WMA), et fichier MP3.

 
Sélectionnez
<MediaElement Name="media"
Source="silverlight.wmv" />

On peut arrêter, mettre en pause, démarrer une vidéo :

 
Sélectionnez
private void StopMedia(object sender, RoutedEventArgs e)
{
media.Stop();
}
private void PauseMedia(object sender, RoutedEventArgs e)
{
media.Pause();
}
private void PlayMedia(object sender, RoutedEventArgs e)
{
media.Play();
}

On peut ajouter une propriété « Volume » avec une valeur allant de 0 à 1 (défaut=0.5) en échelle logarithmique.

On peut aussi ajouter une propriété « AutoPlay », elle est à true par défaut. Il y a aussi Stretch et IsMuted.

VI-K. Formes

Le rectangle 

 
Sélectionnez
<Rectangle Fill="Blue" Stroke="Red"  StrokeThickness="3"  Width="97" Heightc="20"/>
Image non disponible

Fill : couleur de remplissage.
Stroke : couleur du bord.
StrokeThickness : épaisseur du bord.
RadiusX et RadiusY : arrondir les angles.

L'ellipse 

exemple : une sphère, on remplit un cercle (une ellipse aussi haute que large) avec un RadialGradientBrush :

 
Sélectionnez
<Grid >
                <Ellipse Height="200" Width="200" Margin="12,0,9,0">
                    <Ellipse.Fill>
                        <RadialGradientBrush Center="0.4 0.4"
                                            GradientOrigin="0.4 0.4">
                            <GradientStop Offset="0" Color="White" />
                            <GradientStop Offset="1" Color="YellowGreen"  />
                        </RadialGradientBrush>
                    </Ellipse.Fill>
                </Ellipse>
            </Grid>
Image non disponible

On peut ajouter un bord autour d'un conteneur (ou d'un contrôle) :

 
Sélectionnez
<Border BorderBrush="White" BorderThickness="2"  CornerRadius="20" Height="47">
                    <TextBlock  Margin="2,4">Rectangle</TextBlock>
</Border>

L'attribut CornerRadius permet d'arrondir les angles.

Voici un exemple en code c# :

 
Sélectionnez
Ellipse monEllipse = new Ellipse();
monEllipse.Stroke = new SolidColorBrush(Colors.Cyan);
monEllipse.Fill = new SolidColorBrush(Colors.DarkGray);
monEllipse.HorizontalAlignment = HorizontalAlignment.Left;
monEllipse.VerticalAlignment = VerticalAlignment.Center;
monEllipse.Width = 70;
monEllipse.Height = 95;
ContentPanel.Children.Add(monEllipse);

On remarque que pour modifier le bord, le tour de la forme, on utilise Stroke (couleur) et StrokeThickness (épaisseur).

Polygone 
On définit des points, il y a une ligne entre un point et le suivant, le dernier point étant relié au tout premier.

 
Sélectionnez
<Polygon
      Fill="Blue"
      Stroke="Black"
      StrokeThickness="2"
      Points="10,200,60,140,130,140,180,200" />
Image non disponible


Il y a une collection « PointCollection » dans lequel vous ajoutez les « Point » pour former la collection.
Polylines fait de même sauf que le dernier point n'est pas relié au premier.

Path (chemin) 
Permet de dessiner un chemin complexe :

 
Sélectionnez
 <Path Stroke="Pink" StrokeThickness="4"
    Data="M 140,200 C 100,25 400,350 400,175 V 140 H 140" />

Data contient :
M : coordonnées (absolu) du point de départ ('m' si coordonnées relative) ;
C : courbe de Bézier commençant à la position de départ, se terminant à 400, 175 (2 dernières valeurs) ;
Passant pas les points 100,25 et 400,350 ;
V : ligne vertical se terminant à l'ordonnée 140 ;
H : ligne horizontale se terminant à l'abscisse 140.

Image non disponible

Exemple plus complexe : dans un bouton, dessiner une loupe, l'agrandir grâce à RenderTransform :

 
Sélectionnez
<Button   Name="btnStart" Click="btnStart_Click" Width="260" Margin="98,284,98,239">
    <Button.Content>
        <StackPanel Orientation="Horizontal">
        <Path Fill="Blue" Data="F1 M 2.339,6.489 C 1.193,5.343 1.193,3.485 2.339,2.339 C 3.485,1.192 5.344,1.193 6.489,2.339 
        C 7.635,3.485 7.635,5.343 6.489,6.489 C 5.344,7.636 3.485,7.636 2.339,6.489 Z M 11.711,10.209 L 8.185,6.684 
        C 9.207,4.986 9.000,2.757 7.535,1.293 C 5.812,-0.431 3.017,-0.431 1.293,1.293 
        C -0.431,3.017 -0.431,5.812 1.293,7.536 C 2.757,8.999 4.988,9.206 6.685,8.185 
        L 10.209,11.710 L 11.711,10.209 Z" Margin="0,0,5,0" Height="36" Width="17">
                            <Path.RenderTransform>
                                <ScaleTransform ScaleX="3" ScaleY="3" />
                            </Path.RenderTransform>
                        </Path>
                        <TextBlock Foreground="White" Text="Chercher" Margin="20 0 0 0" />
                    </StackPanel>
        </Button.Content>
</Button><br/>
Image non disponible

Line 
Elle va du point X1,Y1 à X2,Y2.

En XAML :

 
Sélectionnez
<Line  StrokeThickness="4" X1="150" Y1="150" X2="100" Y2="100" />

En C# :

 
Sélectionnez
maLine = new Line();
maLine.Stroke = System.Windows.Media.Brushes.Blue;
maLine.X1 = 2;
maLine.X2 = 55;
maLine.Y1 = 1;
maLine.Y2 = 60;
maLine.HorizontalAlignment = HorizontalAlignment.Left;
maLine.VerticalAlignment = VerticalAlignment.Center;
maLine.StrokeThickness = 2;
maGrid.Children.Add(myLine);

VI-L. Barre System en haut

Le SystemTray est la bande en haut qui contient les icônes de charge, de réseau, de type de réseau, l'heure.

Image non disponible

Elle fait 32 pixels en vue portrait et 72 pixels en vue paysage. En mode paysage la barre est sur le rebord gauche de l'écran et pas en haut.
Par défaut, elle est blanche ou noire en fonction du thème. La grille « LayoutRoot » commence dessous.


Dans le XAML, dans la balise « PhoneApplicationPage », on charge l'espace de noms « Microsoft.Phone.Shell » (il l'est pas défaut).
Puis on peut modifier le SystemTray.

On peut ne pas afficher le SystemTray.

 
Sélectionnez
<phone:PhoneApplicationPage 
 xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
 ..
 shell:SystemTray.IsVisible="False">

Il est possible de modifier la couleur des icônes et du fond. Cela ne marche qu'à partir de WP7.1 (Mango).

 
Sélectionnez
shell:SystemTray.ForegroundColor="Red" 
shell:SystemTray.BackgroundColor="Blue"

Il y a un bug et pour mettre du blanc il faut écrire : shell:SystemTray.ForegroundColor="#FEFEFE".

On peut modifier l'opacité (de 0 à 1).

 
Sélectionnez
shell:SystemTray.Opacity="0.5">

L'opacité n'a pas d'incidence sur les icônes.
Si on modifie l'opacité, la page débute sur le bord de l'écran (en arrière du SystemTray) on voit donc la page par transparence à travers le SystemTray.

Image non disponible

Sinon la page débute en dessous du SystemeTray.

Image non disponible

En C# on peut modifier l'opacité de la bande (0 à 1) et les couleurs :

 
Sélectionnez
using Microsoft.Phone.Shell;

            SystemTray.SetIsVisible(this, false);
            SystemTray.SetOpacity(this, 0.5);
            SystemTray.SetBackgroundColor(this, Colors.Purple);
            SystemTray.SetForegroundColor(this, Colors.Yellow);

Ici on indique la page en premier paramètre.
Il existe aussi GetIsVisible… pour lire la propriété.

Quand on a un arrière fond clair et si on a modifié la taille de la Grid LayoutRoot, cela fait une bande blanche en haut ; pour éviter cela, il ne faut pas afficher le System Tray et surtout ne pas donner de hauteur à la grille, ainsi elle occupera tout l'espace (on peut donner, sans le vouloir, une valeur en déplaçant par erreur la grille) :

 
Sélectionnez
shell:SystemTray.IsVisible="False"

 <!--LayoutRoot est la grille racine où tout le contenu de la page est placé-->
    <Grid x:Name="LayoutRoot" >

On peut aussi ajouter un ProgressIndicator.
Si IsIndeterminate = true cela affiche les points qui bougent de gauche à droite.
Si IsIndeterminate = false cela affiche une barre rouge de gauche à droite. La valeur de « Value » donne donne la longueur de la barre : 0= pas de barre, 1= barre complète.

 
Sélectionnez
 public partial class MainPage : PhoneApplicationPage
    {
        ProgressIndicator prog;
        // Constructeur
        public MainPage()
        {
            InitializeComponent();

            prog = new ProgressIndicator();

            SystemTray.SetIsVisible(this, true);
            SystemTray.SetBackgroundColor(this, Colors.Gray);
            SystemTray.SetForegroundColor(this, Colors.Yellow);

           
            prog.IsVisible = true;
            prog.IsIndeterminate = true; //Affiche les points qui bougent de gauche à droite
                                         //false nécessite de donner des valeurs croissantes à 'Value'
            prog.Text = "Clicker...";

            SystemTray.SetProgressIndicator(this, prog);
     
        }
Image non disponible

VI-M. Application Barre

L'application Barre permet de mettre une barre en bas avec des icônes (quatre maximum) et même d'ajouter un menu qu'on ouvre (avec les trois petits points).
Le code de l'ApplicationBar est déjà donné par Microsoft (il est en commentaire en bas de la page), j'ai ajouté la gestion des clics.

 
Sélectionnez
 <!--Exemple de code illustrant l'utilisation d'ApplicationBar-->
    <phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
            <shell:ApplicationBarIconButton IconUri="ApplicationBar.Cancel.png" Text="Annuler" Click="bouton1"/>
            <shell:ApplicationBarIconButton IconUri="ApplicationBar.Check.png" Text="Ok" Click="bouton2"/>
            <shell:ApplicationBar.MenuItems>
                <shell:ApplicationBarMenuItem Text="Menu 1" Click="menu1"/>
                <shell:ApplicationBarMenuItem Text="Menu 2" Click="menu2"/>
            </shell:ApplicationBar.MenuItems>
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>

</phone:PhoneApplicationPage>
 
Sélectionnez
 private void menu2(object sender, EventArgs e)
        {
            MessageBox.Show("menu2");
        }

        private void menu1(object sender, EventArgs e)
        {
            MessageBox.Show("menu1");
        }

        private void bouton1(object sender, EventArgs e)
        {
            MessageBox.Show("bouton1");
        }

        private void bouton2(object sender, EventArgs e)
        {
            MessageBox.Show("bouton2");
        }
Image non disponible

L'option de génération des fichiers images doit avoir la valeur « Contenu » et l'option « Copier dans… » la valeur « Copier si plus récent ».
Les icônes n'apparaîssent pas dans l'IDE, il faut tester avec l'émulateur.

On peut trouver des icônes dans le SDK, ici "C:\Program Files\Microsoft SDKs\Windows Phone\v7.1\Icons\dark", utiliser uniquement le répertoire dark, WP se chargera d'adapter l'image au thème.
On peut en trouver aussi sur Internet ou les dessiner soi-même. Microsoft conseille des images de 48 x 48 px avec le fond graphique au centre de 26 x 26 px. Couleur d'avant plan blanche et fond transparent ; pas de cercle qui sera dessiné par WP sur le bouton.

Si on veut désactiver le second bouton dans le code-behind :

 
Sélectionnez
//Inclure l'espace de noms
using Microsoft.Phone.Shell;


(this.ApplicationBar.Buttons[1] as ApplicationBarIconButton).IsEnabled = false;

VI-N. AdControl pour la publicité

À partir de Mango, on peut ajouter dans une application de la publicité rémunérée proposée par Microsoft.
Aller sur https://pubcenter.microsoft.com pour s'inscrire.
Une fois inscrit vous devrez choisir la taille de votre bannière (480*80 ou 300*50) et vous obtiendrez deux identifiants (ApplicationId et AdUnitId).

À partir de la boîte à outils glisser/déposer un contrôle AdControl :

Image non disponible


Cela donne dans l'XAML :

 
Sélectionnez
 <my:AdControl AdUnitId="Image480_80" ApplicationId="test_client" Height="80"  Margin="-12,179,0,0" 
 Name="adControl1"  Width="480" />

Bien mettre les Id récupérés sur le site.
Le contrôle ici a une taille de 480*80.
Il est impératif de respecter la taille et de ne pas modifier l'opacité.

VI-O. Le Toolkit

Le Silverlight Toolkit pour Windows Phone est un projet open source où l'équipe Microsoft Silverlight partage de nouveaux composants et fonctionnalités qui ne sont pas dans le noyau de Silverlight pour Windows Phone.

Le Toolkit ajoute donc des contrôles indispensables.


Se rendre sur le site codeplex :

http://silverlight.codeplex.com/.

Charger Silverlight for Windows Phone Toolkit pour Mango 7.1.


Ajouter la référence (la DLL) du ToolKit au projet. Dans l'explorateur de solution à droite, clic droit sur référence puis dans le menu clic sur « Ajouter une référence ».

Image non disponible


Puis cliquer sur « Microsoft.Phone.Controls.ToolKit ».

Image non disponible

Dans le code XAML ajouter l'espace de noms « Microsoft.Phone.Controls.Toolkit » :

 
Sélectionnez
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"


Maintenant on peut ajouter un contrôle du ToolKit directement dans le code XAML :

 
Sélectionnez
<toolkit:DatePicker />

On peut si on le désire ajouter le contrôle dans la boîte à outils. Dans celle-ci, click droit sur « Windows Phone Controls », puis dans le menu « Choisir les éléments ».

Image non disponible

Là on peut cocher le contrôle à ajouter dans la liste de la boîte à outils.

Le ToolKit (version d'octobre 2011) fournit les contrôles suivant :
AutoCompleteBox, ContextMenu, DateTimePicker, DateTimeConverters, ExpanderView, GestureService/GestureListener, HeaderedItemsControl, HubTile, ListPicker, LocalizedResources, LockablePivot, LongListSelector, MultiselectList, Page Transitions, PerformanceProgressBar, PhoneTextBox, TiltEffect, TimePicker, ToggleSwitch, WrapPanel, LockablePivots .

AutoCompleteBox permet d'avoir une TextBox proposant une liste de mots dès qu'on tape des lettres.

 
Sélectionnez
<toolkit:AutoCompleteBox Margin="25,42,0,0" Name="autoCompleteBox1"  />

On va charger la liste des mots dans le code-behind :

 
Sélectionnez
List<string> villes = new List<string>();
villes.Add("Barcelona");
villes.Add("Bélize");
villes.Add("Beaune");
villes.Add("Londres");
villes.Add("Paris");
villes.Add("Prague");
villes.Add("Madrid");
villes.Add("Rome");
this.autoCompleteBox1.ItemsSource = villes;
Image non disponible


On peut modifier quelques propriétés :

 
Sélectionnez
//La liste apparaît après la frappe de 3 caractères
this.autoCompleteBox1.MinimumPrefixLength = 3;

//Tient compte de la casse de la première lettre
this.autoCompleteBox1.FilterMode = AutoCompleteFilterMode.StartsWithCaseSensitive;

On peut ajouter un filtre, afficher des images… voir le libre blanc.

Le DatePicker permet de saisir une date.

 
Sélectionnez
<toolkit:DatePicker x:Name="datePicker" Value="2/4/2012"/>

En C# :

 
Sélectionnez
using Microsoft.Phone.Controls;
DatePicker datePicker = new DatePicker();

On a dans la page ce contrôle :

Image non disponible

Quand on clique dessus, la page de saisie de date s'ouvre :

Image non disponible

Il faut mettre dans le répertoire « Toolkit.Content » les fichiers « ApplicationBar.Cancel.png » et « ApplicationBar.Check.png ».

Où trouver les fichiers ? Aller sur le site http://silverlight.codeplex.com/releases. Dans « Other Available Downloads » charger le ZIP « Source Code Silverlight for Windows Phone Toolkit Source and Sample - Nov 2011.zip ». Dans le ZIP les fichiers PNG des icônes sont dans « Source and Samples\PhoneToolkitSample\Toolkit.Content ».

Il faut créer avec l'explorer, un répertoire « Toolkit.Content » à la racine du projet et y mettre les icônes « ApplicationBar.Cancel.png » et « ApplicationBar.Check.png » puis ajouter le répertoire au projet (dans l'explorateur de solution, clic sur l'icône « Afficher tous les fichiers », clic droit sur le répertoire puis « Ajouter », « Ajouter le répertoire ») ; enfin dans les propriétés de chaque fichier image, « Action de génération » doit avoir la valeur « contenu ». Copier doit avoir la valeur « Copier si plus récent ».

Image non disponible

ToggleSwitch est un interrupteur :

 
Sélectionnez
 <toolkit:ToggleSwitch x:Name="toggle" Content="ToggleSwitch est on"
IsChecked="True" Header="DemoToggleSwitch"/>

Ici on le met à On et on ajoute un petit titre au-dessus :

Image non disponible


En C# :

 
Sélectionnez
ToggleSwitch toggleSwitch = new ToggleSwitch();

On peut ajouter les EventHandler « Checked » et « Unchecked » et les fonctions correspondantes qui modifient le texte et la couleur du contrôle en fonction de son état « on » « off» :

 
Sélectionnez
namespace PhoneApp2
{
    public partial class MainPage : PhoneApplicationPage
    {
        // Constructeur
        public MainPage()
        {
            InitializeComponent();
        this.toggle.Checked += new EventHandler<RoutedEventArgs>(toggle_Checked);
        this.toggle.Unchecked += new EventHandler<RoutedEventArgs> (toggle_Unchecked);
}
void toggle_Unchecked(object sender, RoutedEventArgs e)
{
this.toggle.Content = "ToggleSwitch est off";
this.toggle.SwitchForeground = new SolidColorBrush(Colors.Red);
}
void toggle_Checked(object sender, RoutedEventArgs e)
{
this.toggle.Content = "ToggleSwitch est on";
this.toggle.SwitchForeground = new SolidColorBrush(Colors.Green);
}


    }
}

On peut simplement tester l'état du ToggleSwitch :

 
Sélectionnez
if (this.toggle.IsChecked == true)
{ MessageBox.Show("On"); }

Dans le Toolkit il y a aussi le WrapPanel qui est un conteneur où les éléments sont mis à la suite l'un de l'autre mais où il y a "retour à la ligne" quand on atteint la limite du conteneur.
Les éléments se positionnent de gauche à droite et de haut en bas.

C'est pratique pour afficher une série de photos comme dans la Media Library de WP.
Exemple du blog d'Audrey Petit, il faut rajouter les binding.
Le template par défaut d'une ListBox n'est autre qu'un StackPanel avec une orientation verticale.
C'est pour cela que les items d'une ListBox se positionnent par défaut les uns en dessous des autres. Nous allons donc modifier ce template afin de lui faire utiliser un WrapPanel et positionner nos éléments les uns à côté des autres, avec passage à la ligne lorsqu'une ligne est complète.

 
Sélectionnez
xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"


<phone:PhoneApplicationPage.Resources>
    <DataTemplate x:Key="DataTemplatePhoto">
      <Grid Width="150" Height="150">
        <Image x:Name="imageItem" Margin="5"/>
      </Grid>
    </DataTemplate>
    <ItemsPanelTemplate x:Key="PanelTemplatePhoto">
      <toolkit:WrapPanel />
    </ItemsPanelTemplate>
</phone:PhoneApplicationPage.Resources>

<ListBox x:Name="collectionPhoto" ItemTemplate="{StaticResource DataTemplatePhoto}" 
ItemsPanel="{StaticResource PanelTemplatePhoto}" />



La Toolbox permet aussi d'ajouter des effets comme le 'Tilt' effect, effet d'appui sur un élément tapé. Cela rend visible les contrôles tapés par le doigt.

 
Sélectionnez
<ListBox toolkit:TiltEffect.IsTiltEnabled="True">

Le Toolkit ajoute aussi une gestion des gesrures (voir chapitre sur la saisie tactile et les gestures).

Consulter la bible (en anglais) du ToolKit, il y a tout en détail :http://www.windowsphonegeek.com/upload/ebooks/Windows%20Phone%20Toolkit%20Aug%202011%20in%20depth-v1.pdf.

VI-P. MessageBox

Comment afficher un message pour l'utilisateur sans sortir de la page ?
En utilisant la méthode Show de MessageBox.

 
Sélectionnez
MessageBox.Show("Erreur sur le format de la date");

Cela donne :

Image non disponible

Il y a un bouton « Ok » par défaut pour sortir du MessageBox.

Sinon on peut ajouter un titre et un bouton annuler.

 
Sélectionnez
MessageBox.Show("Poursuivre?", "Exemple MessageBox", MessageBoxButton.OKCancel);
Image non disponible


MessageBox peut retourner dans une variable de type MessageBoxResult :
MessageBoxResult.Ok ou MessageBoxResult.Cancel, suivant le bouton qui a été tapé par l'utilisateur.

 
Sélectionnez
 MessageBoxResult result =
     MessageBox.Show("Poursuivre?",
     "Exemple MessageBox", MessageBoxButton.OKCancel);

            if (result == MessageBoxResult.OK)
            {
                MessageBox.Show("Vous avez tapé sur ok.");
            }


En WP7 il n'y a que deux boutons possible.

VII. Grandes fonctions WP

VII-A. Données

Image non disponible

VII-A-1. État de l'application

On peut enregistrer temporairement des données simples (de type clé/valeur) dans le dictionnaire d'état qui est temporaire (elles sont conservées en désactivation et en tombstoned, on perd les infos si le logiciel s'arrête).
Le dictionnaire d'état est accessible par la classe PhoneApplicationService et permet de conserver l'état de l'application (données, Web Service, etc.).
Voir le chapitre sur le tombstone.

Il faut au préalable inclure l'espace de noms Microsoft.Phone.Shell.

 
Sélectionnez
using Microsoft.Phone.Shell;

C'est aussi bien pratique, comme on l'a vu, pour passer des données simples d'une page à l'autre.
On enregistre (avec la clé « ToDo ») :

 
Sélectionnez
private void todoTb_TextChanged(object sender, TextChangedEventArgs e)
{
    PhoneApplicationService.Current.State["Todo"] = todoTb.Text;
}

Pour récupérer les informations, on peut le faire dans le constructeur de l'autre page par exemple :

 
Sélectionnez
MainPage.xaml.cs


public MainPage()
{
    InitializeComponent();

    if (PhoneApplicationService.Current.State.ContainsKey("CurrentTodo"))
        todoTb.Text = (string)PhoneApplicationService.Current.State["Todo"];
       
    ...
}

Autre exemple : pour conserver l'état d'une page, on peut aussi l'enregistrer dans le dictionnaire d'état.
Là on va enregistrer dans la surcharge de la méthode OnNavigatedFrom et lire dans la surcharge de la méthode OnNavigatedTo.
En haut de la classe MainPage, on instancie MyappService de type PhoneApplicationService.
Pour lire, on utilise la méthode TryGetValue qui permet d'éviter les problèmes si la clé n'existe pas.

 
Sélectionnez
public partial class MainPage : PhoneApplicationPage

    {
        PhoneApplicationService MyappService = PhoneApplicationService.Current;
       
       
        // Constructeur
        public MainPage()
        {
            InitializeComponent();

        }

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs args)
{
MyappService.State["LeText"] = MyTextBox.Text;

base.OnNavigatedFrom(args);
}

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs args)
{
object text;
if (MyappService.State.TryGetValue("LeText", out text))
MyTextBox.Text = text as string;

base.OnNavigatedTo(args);
}


    }

VII-A-2. Isolated Storage

Pour enregistrer des données, il est préférable d'utiliser l'Isolated Storage.
L'enregistrement est permanent.
'Isoladed' car seule votre application aura accès aux données, pas les autres applications.
Votre application ne pourra pas lire les données d'autres applications.

On peut utiliser 200 Mo par application.
Il y a deux manières d'utiliser l'Isolated Storage :
- l'IsolatedStorageSetting pour enregistrer des clé/valeurs ;
- l'IsolatedStorageFile pour enregistrer des répertoires et des fichiers.

Il faut au préalable inclure l'espace de noms :

 
Sélectionnez
using System.IO.IsolatedStorage;


L'IsolatedStorageSetting pour enregistre des clé/valeurs.

Exemple : enregistrer un nom dans le Setting sous la clé « name ».
Il faut instancier un objet de type IsolatedStorageSettings.ApplicationSettings. La méthode Add permet ensuite d'ajouter une clé/valeur. On peut ensuite lire la valeur correspondant à la clé.

 
Sélectionnez
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;
using Microsoft.Phone.Controls;
using System.IO.IsolatedStorage; // Ajouter cette ligne

namespace IsolatedStorage
{
    public partial class MainPage : PhoneApplicationPage
    {
         //Instanciation de userSettings+++++++++++++++++
         private IsolatedStorageSettings userSettings = IsolatedStorageSettings.ApplicationSettings;
    
        // Constructeur
        public MainPage()
        {
            InitializeComponent();
        }

        private void Enregistrer_Click(object sender, RoutedEventArgs e)
        {
        userSettings.Add("name", "Philippe");// On ajoute la clé/valeur
        }

        private void Lire_Click(object sender, RoutedEventArgs e)
        {
         string name = (string)userSettings["name"]; //On lit la valeur de la clé "name"
          textBlock1.Text=name;     
        }
    }
}

On peut voir si une clé existe :

 
Sélectionnez
if (userSettings.Contains("name"))

{ textBlock1.Text="clé 'name' présente"; }

On peut ensuite modifier la valeur d'une clé :

 
Sélectionnez
 userSettings["name"] = "Paul";

On peut effacer une clé :

 
Sélectionnez
userSettings.Remove("name");

On peut effacer toutes les clés ou compter le nombre de clés.

 
Sélectionnez
userSettings.Clear();
        
tbResults.Text = "Count: " + userSettings.Count();

Exemple complet 
On enregistre :

 
Sélectionnez
private void btnSaveSetting_Click(object sender, RoutedEventArgs e)
{
IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
// txtInput is a TextBox defined in XAML.
if (!settings.Contains("userData"))
{
settings.Add("userData", txtInput.Text);
}
else
{
settings["userData"] = txtInput.Text;
}
settings.Save();
}

Les données qui sont dans le Setting sont enregistrées automatiquement quand l'application (ou la page) qui utilise la classe se termine. On peut forcer l'enregistrement immédiat avec la méthode Save (voir dernière ligne de code qui précède).

On peut aussi enregistrer des objets. Attention, les objets doivent être « serializables ». La sérialisation consiste à transformer l'état d'une information qui est en mémoire sous la forme d'une suite d'informations qui pourra être enregistrée ou transportée.

Si on écrit soi-même la classe, il faut la rendre serializable : mettre [DataContract] avant la classe et [DataMember] avant les champs publics.

Il faut ajouter :

 
Sélectionnez
using System.Runtime.Serialization;
 
Sélectionnez
  [DataContract]
        public class ElementDeListe 
        {
           
            private string _Titre;
           [DataMember]
            public string Titre
            {
                get
                {
                    return _Titre;
                }
                set
                {
                    if (value != _Titre)
                    {
                        _Titre = value;
                       
                    }
                }
            }

Exemple :
soit une classe « ElementDeListe » permettant d'instancier un objet currentCalcul.
On va enregistrer currentCalcul (dans OnNavigatedFrom par exemple) :

 
Sélectionnez
ElementDeListe currentCalcul;
private IsolatedStorageSettings userSettings = IsolatedStorageSettings.ApplicationSettings;


if (userSettings.Contains("currentCalcul"))
{ userSettings.Remove("currentCalcul"); }
userSettings.Add("currentCalcul", currentCalcul);// On ajoute la clé/valeur
userSettings.Save();


On va lire currentCalcul (dans OnNavigatedTo par exemple) :

 
Sélectionnez
currentCalcul = (ElementDeListe)userSettings["currentCalcul"]; //On lit la valeur de la clé "name"


L'IsolatedStorageFile pour enregistrer des fichiers.

Pour pouvoir utiliser l'Isolated Storage il faut récupérer une référence vers le conteneur dédié à votre application. Cela se fait avec la méthode statique GetUserStoreForApplication de la classe IsolatedStorageFile.
À partir d'une instance de la classe IsolatedStorageFile, il est possible de manipuler répertoires et fichiers grâce aux méthodes.

FileExists.
CreateFile.
OpenFile.
DeleteFile.
DirectoryExists.
CreateDirectory.
DeleteDirectory.
Remove.

On peut écrire et lire des fichiers avec des IsolatedStorageFileStream.
Un « Stream » est un flux de données.
Vous pouvez lire à partir des flux. La lecture est le transfert de données d'un flux vers une structure de données (du texte avec ReadLine, par exemple).
Vous pouvez écrire dans les flux. L'écriture est le transfert de données d'une structure de données vers un flux (du texte avec ReadLine, par exemple).

 
Sélectionnez
using System.IO.IsolatedStorage;
using System.IO;

private void EnregistreTexte_Button_Click(object sender, RoutedEventArgs e)
{
    //Obtenir une référence de l'Isolated Storage
IsolatedStorageFile fileStorage = IsolatedStorageFile.GetUserStoreForApplication();

    //Créer un nouveau subdirectory
fileStorage.CreateDirectory("textFiles");

    //Créer un nouveau StreamWriter pour écrire 
StreamWriter fileWriter = new StreamWriter(new IsolatedStorageFileStream("textFiles\\newText.txt", 
  FileMode.OpenOrCreate, fileStorage));
    //Ecrire le contenu de MonTexte.Text dans le fichier.
    fileWriter.WriteLine(MonTexteText.Text);
    //Fermer le StreamWriter.
    fileWriter.Close();
}

private void LitTexte_Button_Click(object sender, RoutedEventArgs e)
{
    //Obtenir une référence de l'Isolated Storage
    IsolatedStorageFile fileStorage = IsolatedStorageFile.GetUserStoreForApplication();
    //Créer un nouveau StreamReader
    StreamReader fileReader = null;

    try
    {
        //Lire le fichier.
        fileReader = new StreamReader(new IsolatedStorageFileStream("textFiles\\newText.txt", FileMode.Open, fileStorage));
        //Lire la ligne.
        string textFile = fileReader.ReadLine();

        //Mettre la ligne lue dans le textBlock.
        MonTexte.Text = textFile;
        fileReader.Close();
    }
    catch
    {
        //Si on lit avant d'avoir enregistrer!!.
        viewText.Text = "Le fichier n'existe pas.";
    }
}

VII-A-3. LINQ to SQL, données sur le Web

Il est possible d'utiliser une base de données locale et de l'interroger avec LINQ to SQL.

Il y a plusieurs réseaux et technologies Web service que vous pouvez utiliser pour récupérer des données distantes :
HTTP classes ;
WCF services ;
WCF Data Services (OData services) ;
Windows Azure Services.

VII-B. Activation, désactivation, Tombstone.

Image non disponible

Terminer une application 
On se souvient que, sur la page principale (la première page) d'une application, si on fait « Retour » (Back bouton de gauche), on sort de l'application elle est fermée.
Si on veut utiliser de nouveau l'application, il faut la relancer.

Suspendre une application
Si par, contre, il y a un appel téléphonique ou si on fait « Démarrer » (bouton du milieu avec logo Windows), l'application est désactivée.
L'utilisateur est censé y revenir et poursuivre l'utilisation de l'application.
Avec le bouton « Back », on peut revenir dans l'application et poursuivre.
(Si on clique sur l'icône du programme, l'application est relancée et redémarre à zéro.)

Durant la désactivation, pendant un certain temps WP conserve toutes les données (c'est l'état dormant) ; si on utilise « Back », on va réactiver l'application et se retrouver dans la même page avec les données affichées dans les TextBox par exemple.

État Tombstone 
Il n'est pas possible que toutes les applications désactivées restent au stade désactivé (dormant) éternellement.
Donc après un certain temps, et sans prévenir, quand WP a besoin de place, il fait une désactivation totale dite Tombstone (pierre tombale) ; l'OS tue le process ; et là, si ensuite on fait « Back », on se retrouve dans la page qu'on avait quittée mais on a perdu les informations contenues dans la page (et les données de l'application) qui est réactivée.

En effet, Windows Phone enregistre la pile de retour d'une application lors d'une désactivation Tombstone. Au moment de l'activation, il restaure uniquement la dernière page qui était active avant la désactivation Tombstone de l'application (mais pas les données, les objets ni les contenus des contrôles, des TextBox…).

L'état persistant de l'application (le contenu des contrôles visuels, les données, les objets) doit être enregistré par l'application avant la désactivation. C'est au programmeur de le faire.

Voici le cycle de vie d'une application WP donné par Microsoft :

Image non disponible

Si on veut réactiver une application qui est tombstoned et poursuivre là où on était, l'OS se chargera de réafficher la dernière page mais il appartient au programmeur d'enregistrer l'état de la page (Page State= contenu des contrôles de la page) et les données de l'application (Application State) lors de la désactivation et de les restituer à la réactivation.

Voyons les évènements que propose WP pour faire cela.

A - Activated, Desactivated de l'application
Dans App.xaml.cs 
Les évènement Launching (lancement) et Closing (fermeture) sont exécutés au lancement et à la fermeture de l'application. C'est classique.
Les évènement Activated (activé) et Desactivated (désactivé) sont moins habituels, ils sont exécutés quand on retourne à l'application et, au cours de l'exécution de votre application, quand on utilise le bouton « démarrer » (bouton du milieu) ou quand le téléphone sonne.

 
Sélectionnez
 // Code à exécuter lorsque l'application démarre (par exemple, à partir de Démarrer)
        // Ce code ne s'exécute pas lorsque l'application est réactivée
        private void Application_Launching(object sender, LaunchingEventArgs e)
        {
        }

        // Code à exécuter lorsque l'application est activée (affichée au premier plan)
        // Ce code ne s'exécute pas lorsque l'application est démarrée pour la première fois
        private void Application_Activated(object sender, ActivatedEventArgs e)
        {
        }

        // Code à exécuter lorsque l'application est désactivée (envoyée à l'arrière-plan)
        // Ce code ne s'exécute pas lors de la fermeture de l'application
        private void Application_Deactivated(object sender, DeactivatedEventArgs e)
        {
        }

        // Code à exécuter lors de la fermeture de l'application (par exemple, lorsque l'utilisateur clique sur Précédent)
        // Ce code ne s'exécute pas lorsque l'application est désactivée
        private void Application_Closing(object sender, ClosingEventArgs e)
        {
        }

Si l'application est lancée (launching) Application_Activated n'est pas exécuté. L'inverse est vrai.
Idem pour Deactivated et Closing.

Utilisez les événements Deactivated et Activated de l'application pour les données et objets de l'application.

On utilise dans ce cas l'application Setting.
Bien se souvenir que le stockage dans l'application Setting est temporaire et ne dure que le temps pendant lequel l'application est tombstoned. Quand l'application est totalement arrêtée, on perd l'application Setting. Si on veut un enregistrement persistant il faut utiliser l'IsolatedStorage.

Exemple simple utilisant l'ApplicationSettings :

 
Sélectionnez
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
    PhoneApplicationService.Current.State["MonObjet"] = MonObjet ;
}

Pour la réactivation on peut utiliser « private void Application_activated ».

 
Sélectionnez
private void Application_Activated(object sender, DeactivatedEventArgs e)
{

    if (PhoneApplicationService.Current.State.ContainsKey("MonObjet"))
    {
        MonObjet = (string)PhoneApplicationService.Current.State["MonObjet"];

    }
}

B - OnNavigatedFrom, OnNavigatedFrom de la page
On a dans la page les évènement OnNavigatedFrom et OnNavigatedTo que l'on peut surcharger.
OnNavigatedTo est exécuté lors de toutes les navigations vers la page (lorsque l'utilisateur arrive sur la page).
OnNavigatedFrom est exécuté lors de toutes les navigations sortant de la page (lorsque l'utilisateur quitte la page).
Le constructeur de page est appelé, lui, lors de la première navigation sur cette page (appelé une seule fois).

Image non disponible
 
Sélectionnez
using Microsoft.Phone.Shell;
using System.Threading;

namespace CalculeTout
{
    public partial class PageMath : PhoneApplicationPage
    {
      
        public PageMath()
        {
        //constructeur de la page, exécuté lors de la PREMIERE navigation vers la page
            InitializeComponent();
            
        }

        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            //exécuté lors de toutes les navigations vers la page
            base.OnNavigatedTo(e);
            
         }
    protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
        {
            //exécuté lors de toutes les navigations sortant de la page
            base.OnNavigatedFrom(e);
            
         }     
    }
    }

On utilise OnNavigatedFrom et OnNavigatedTo pour l'état de page (contenu des contrôles).

Je l'utilise aussi pour les objets propres à la page.

Exemple :

 
Sélectionnez
    protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
        {
           if (phoneApplicationPage.State.ContainsKey("MyText"))
        {
            phoneApplicationPage.State.Remove("MyText");
        }
        // sauvegarder le contenu de textbox1
        phoneApplicationPage.State.Add(MyText, textbox1.text);

            
            base.OnNavigatedFrom(e);
            
         }
         
         protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            //Restaurer
             if (phoneApplicationPage.State.ContainsKey("MyText"))
        {
            //Remettre le texte dans la textbox1
            textbox1.text= phoneApplicationPage.State["MyText];
        }

            
            base.OnNavigatedTo(e);
            
         }

Comment savoir si l'application désactivée a un état Tombstone ?
Avec la méthode IsApplicationInstancePreserved :

 
Sélectionnez
private void Application_Activated(object sender, ActivatedEventArgs e)
{
if (e.IsApplicationInstancePreserved)
{
// instance de l'application préservée, pas de tombstone, donc pas de récupération de données.
}
else
{
//Etat tombstone: il faut récupérer l'état
// Teste si une clé est dans State dictionary.
if (PhoneApplicationService.Current.State.ContainsKey("ApplicationDataObject"))
{
//
ApplicationDataObject = PhoneApplicationService.Current.State["ApplicationDataObject"] as string;
}
}

Dans le cas où on utilise un pattern MVVM, on peut sauvegarder et restaurer l'objet ViewModel lui-même.

 
Sélectionnez
public partial class MainPage : PhoneApplicationPage

{

    public MainPageViewModel MainPageViewModel { get; set; }

    private bool isFirstInstance;

 

    // Constructor

    public MainPage()

    {

        InitializeComponent();

        

        if (!IsolatedStorageSettings.ApplicationSettings.Contains("ViewModel"))

        {

            MainPageViewModel=new MainPageViewModel();

            DataContext = MainPageViewModel;

            isFirstInstance = true;

        }

    }

 

    protected override void OnNavigatedFrom(NavigationEventArgs e)

    {

        IsolatedStorageSettings.ApplicationSettings["ViewModel"] = MainPageViewModel;

        base.OnNavigatedFrom(e);

    }

 

    protected override void OnNavigatedTo(NavigationEventArgs e)

    {

        if (isFirstInstance) { return; }

 

        MainPageViewModel = 

            IsolatedStorageSettings.ApplicationSettings["ViewModel"] as MainPageViewModel;

        DataContext = MainPageViewModel;

        base.OnNavigatedTo(e);

    }

}

Le site CodePlex fourni en open Source TombstoneHelper.dll qui permet de sauvegarder et restaurer automatiquement l'état de la page, le contenu des contrôles d'une page .

Voir : tombstonehelper

Il faut charger tombstonehelper.dll dans le projet. Comme elle vient d'Internet et que Windows interdit d'utiliser une DLL non sure, Visual Studio refuse de l'utiliser, il faut donc cliquer droit sur la DLL dans l'explorateur, choisir « propriétés » ; en bas il y a un bouton qui débloque la DLL à vos risques et périls (attention, il y a des copies de la DLL dans les répertoires bin et release du projet, il faut les débloquer aussi si on ne la pas fait au départ).

Ensuite il faut ajouter la référence au projet : clic droit sur « Références» dans l'explorateur de solution puis « Ajouter une référence » ; ajouter tombstonehelper.dll.

Enfin il faut ajouter l'espace de noms nécessaire :

 
Sélectionnez
using TombstoneHelper;


Pour sauver et restaurer la totalité des contrôles (TextBox, PasswordBoxe, CheckBoxe, RadioButton, Slider, ListBoxe et ScrollViewer, PAS les TextBlock !) c'est simple :

 
Sélectionnez
using TombstoneHelper;

protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
    this.SaveState(e);  // <- first line
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    this.RestoreState();  // <- second line
}

Pour un type de contrôle :

 
Sélectionnez
   this.SaveState(typeof(ScrollViewer));
//ou 
    this.SaveState(typeof(TextBox), typeof(PasswordBox), typeof(CheckBox));
//ou
    this.SaveState<TextBox, PasswordBox, CheckBox>();

Attention, les contrôles doivent avoir un nom (attribut « Name »).
On se souvient que les TextBlock ne sont pas sauvés (pour certains résultats j'ai préféré utiliser des TextBox avec l'attribut IsReadOnly à true plutôt que des TextBlock).

Microsoft.Phone.Shell.PhoneApplicationService.Current.ApplicationIdleDetectionMode = Microsoft.Phone.Shell.IdleDetectionMode.Disabled :
permet à votre application de ne pas se faire tombstonner quand le téléphone se met en veille, en clair votre application tourne derrière le lockscreen. C'est le comportement le plus proche d'un thread d'arrière-plan.
Microsoft.Phone.Shell.PhoneApplicationService.Current.UserIdleDetectionMode = Microsoft.Phone.Shell.IdleDetectionMode.Disabled :
permet de désactiver purement et simplement le lockscreen !

Pour tester dans l'émulateur l'effet Tombstone il faut passer par le menu « projet » puis « propriété de », onglet « déboguer ».

Image non disponible

Si on ne coche pas, il y a désactivation sans Tombstone.

VII-C. Le pattern Model-View-ViewModel

Un pattern est un modèle de structure d'une application.

Le pattern M-V-VM (Model-View-ViewModel) permet une séparation entre
la couche dédiée aux objets métiers (Model),
à l'interface graphique (View),
à la logique (ViewModel).

C'est ce pattern qu'il est conseillé d'utiliser sur WP.

Il y a de très bon article sur le Web :

http://www.aymericlagier.com/2010/12/20/wp7-bien-commencer-a-developper-pour-windows-phone-7/.

Je dois vous avouer que, pour moi, cela semble très obscur, complexe et difficile à gérer. Mais cela n'engage que moi !

VII-D. Orientation

Le WP peut fonctionner :
en Portrait ou en LandScape (paysage).

Image non disponible

Dans le code XAML de la page, on a les orientations supportées (Portrait, LandScape, PortraitOrLandscape).
Il y a aussi l'orientation initiale(Portrait, LandScape).

 
Sélectionnez
SupportedOrientations="Portrait"
Orientation="Portrait"

Il est possible de détecter un changement d'orientation en surchargeant la méthode OnOrientationChanged et en regardant l'OrientationChangedEventArgs.

 
Sélectionnez
public partial class MainPage :
PhoneApplicationPage
{
// Constructeur
public MainPage()
{
InitializeComponent();
}
protected override void OnOrientationChanged(OrientationChangedEventArgs e)
{
OrientationTextBlock.Text = "On passe en mode
" + e.Orientation ;
base.OnOrientationChanged(e);
}
}

e.Orientation a une des valeurs de l'énumération PageOrientation (Bit Flags) :
- PageOrientation.PortraitUp ;
- PageOrientation.PortraitDown ;
- PageOrientation.LandscapeLeft ;
- PageOrientation.LandscapeRight.

L'énumération comporte aussi les valeurs PageOrientation.Portrait et PageOrientation.Landscape.
En utilisant un « ou » entre e.Orientation et PageOrientation.Landscape on determine si on est en LandscapeLeft ou LandscapeRight.

 
Sélectionnez
if ((e.Orientation & PageOrientation.Landscape) != 0)
{
}

C'est l'équivalent de :

 
Sélectionnez
if (e.Orientation == PageOrientation.Landscape ||
e.Orientation == PageOrientation.LandscapeLeft ||
e.Orientation == PageOrientation.LandscapeRight)
{
}

C'est l'endroit parfait pour modifier les dimensions et positions des contrôles en fonction de l'orientation.

 
Sélectionnez
protected override void OnOrientationChanged(OrientationChangedEventArgs e)
{

// Landscape
if ((e.Orientation & PageOrientation.Landscape) != 0)
{
    //Positionner en mode paysage
}
// Portrait
else
{
   //Positionner en mode portrait
}
base.OnOrientationChanged(e);
}

Le plus simple pour positionner les contrôles dans les deux orientations est de les mettre dans un StackPanel qui lui-même sera dans un ScrollViewer. Comme cela, en mode paysage, les contrôles peuvent déborder en bas.

VII-E. Lanceur d'applications internes

Impossible de lancer, à partir de votre application, une de vos autres applications ou même une application présente sur votre WP comme Internet Explorer (sécurité oblige).

Seules exceptions : Les Launchers (lanceurs) et Choosers (sélecteurs) permettent d'accéder aux Windows Phone applications. Par exemple, si vous voulez chercher un contact à partir de votre application, il faut utiliser EmailAddressChooserTask.

On peut ainsi, à partir de votre application, envoyer un mail ou un coup de téléphone, prendre une photo, ouvrir une page Web.

La différence entre Launchers et Choosers est que les Launchers ne retournent pas de données contrairement aux Choosers. L' EmailComposeTask Launcher par exemple démarre l'email application qui se fermera simplement. La CameraCaptureTask Chooser démarre l'application « camera », à sa sortie cette application fournit la photo qui à été prise.

Il faut ajouter :

 
Sélectionnez
using Microsoft.Phone.Tasks;

Launchers (lanceur). Voici les lanceurs :

EmailComposeTask permet d'envoyer un mail ;
MarketplaceDetailTask permet de lancer le « Windows Phone Marketplace client application » et affiche le détail d'un produit du Market Place ;
MarketplaceHubTask Permet de lancer le « Windows Phone Marketplace client application » ;
MarketplaceSearchTask permet de lancer le « Windows Phone Marketplace client application » et de voir le résultat d'une recherche sur un thème ;
MediaPlayerLauncher permet de lancer le media player ;
PhoneCallTask permet de lancer la « Phone application » et de faire un appel téléphonique ;
SearchTask permet de lancer le « web search application » pour rechercher sur le Web ;
SmsComposeTask permet de lancer le « Messaging application » et de faire un nouveau message SMS ;
WebBrowserTask permet de lancer le browser Internet.

Il faut instancier le lanceur, renseigner certaines propriétés puis appliquer la méthode Show.

Exemple 1 : appeler un numéro à partir de votre application.

 
Sélectionnez
// Instancier le launcher.
PhoneCallTask phoneCallTask = new PhoneCallTask();
// Renseigner les propriétés.
phoneCallTask.PhoneNumber = "01234567";
phoneCallTask.DisplayName = "Philippe";
// Lancer la 'phone dialer application'.
phoneCallTask.Show();


On a l'écran :

Image non disponible

Puis la page habituelle du téléphone.

Exemple 2 : faire une recherche sur le Web.

 
Sélectionnez
// Instancier le launcher.
SearchTask searchTask = new SearchTask();
// Renseigner la propriété SearchQuery avec le texte à chercher.
searchTask.SearchQuery = "Cours Windows Phone";
// Lancer la recherche.
searchTask.Show();

On a l'écran :

Image non disponible


On a ensuite les réponses à la recherche "cours Windows Phone" sur le Web :

Image non disponible

Choosers (sélecteurs). Voici les Choosers 

CameraCaptureTask lance la « Camera application ». Permet de récupérer la photo prise ;
EmailAddressChooserTask lance la « Contacts application ». On récupère l'adresse du contact sélectionné par l'utilisateur ;
PhoneNumberChooserTask lance la « Contacts application ». On récupère le numéro de téléphone du contact sélectionné par l'utilisateur ;
PhotoChooserTask lance la « Photo Chooser application ». Retourne la photo sélectionnée ;
SaveEmailAddressTask permet de sauvegarder une adresse mail ;
SavePhoneNumberTask permet de sauvegarder un numéro de téléphone ;
GameInviteTask permet d'accepter un joueur dans un jeu multi joueur ;
SaveRingtoneTask utilise le « save ringtone task » pour permettre à l'utilisateur de sauvegarder un fichier audio file dans le « system ringtones » list (les sonneries).

Il faut instancier le Chooser, instancier un EventHandle sur l'évènement Completed du Chooser, renseigner certaines propriétés puis appliquer la méthode Show.
Il faut récupérer la valeur retournée par le Chooser dans la fonction évènement déclenchée par Completed (Completed survient quand l'application Chooser est terminée).

Exemple 1 : récupérer un numéro de téléphone dans les contacts.

 
Sélectionnez
public partial class MainPage : PhoneApplicationPage
{
// Instanciation d'un PhoneNumberChooserTask
PhoneNumberChooserTask numberChooser;
// Constructor
public MainPage()
{
InitializeComponent();
numberChooser = new PhoneNumberChooserTask();
// Instanciation d'un EventHandler sur l'évènement Completed du Chooser
numberChooser.Completed +=
new EventHandler<PhoneNumberResult>(numberChooser_Completed);
}

void numberChooser_Completed(object sender, PhoneNumberResult e)
{
// Est exécuté lorsque l'évènement Completed sur le Chooser survient.
// Check if TaskResult is a success
if (e.TaskResult == TaskResult.OK)
{
// Affiche le numéro de téléphone dans le TextBlock txtPhoneNumber
txtPhoneNumber.Text += e.PhoneNumber;//Récupérer le numéro de téléphone du contact
}
}


//Lancer la liste des contacts pour récuperer un numéro de téléphone
private void ButtonChooser_Click(object sender, RoutedEventArgs e)
{
try
{
// Démarre le Chooser.
numberChooser.Show();
}
catch (System.InvalidOperationException ex)
{
// Catch l' exception, pas besoin de traitement.
}
}
}

Exemple 2 : à partir de mon application, prendre une photo puis dans mon application, l'afficher.

 
Sélectionnez
//Instanciation d'un CameraCaptureTask
CameraCaptureTask PhotoCaptureTask;
PhotoCaptureTask = new CameraCaptureTask();
// Instanciation de l' EventHandler sur Completed
PhotoCaptureTask.Completed += new EventHandler<PhotoResult>(PhotoCaptureTask_Completed);

//Lancer la prise de photo. 
try
{
    PhotoCaptureTask.Show();
}
catch (System.InvalidOperationException ex)
{
    MessageBox.Show("Erreur, impossible de prendre la photo.");
}


Maintenant, on a la fonction évènement qui survient quand la tache « photo » est terminée.

 
Sélectionnez
void PhotoCaptureTask_Completed(object sender, PhotoResult e)
{
    if (e.TaskResult == TaskResult.OK)
    {
        //Message box indiquant la longueur de l'image 
        MessageBox.Show(e.ChosenPhoto.Length.ToString());

        //Afficher l'image prise dans l'image controle nommé myImage.
        System.Windows.Media.Imaging.BitmapImage bmp = new System.Windows.Media.Imaging.BitmapImage();
        bmp.SetSource(e.ChosenPhoto);
        myImage.Source = bmp;
    }
}

Exemple 3 : à partir de mon application, enregistrer un contact dans les contacts.

 
Sélectionnez
//Instanciation d'un SaveContactTask
SaveContactTask saveContactTask;
saveContactTask = new SaveContactTask();
// Instanciation de l' EventHandler sur Completed
saveContactTask.Completed += new EventHandler<SaveContactResult>(saveContactTask_Completed);

//Lancer l'enregistrement du contact. 
try
{
    saveContactTask.FirstName = "Philippe";
    saveContactTask.LastName = "Lasserre";
    saveContactTask.MobilePhone = "012345678";

    saveContactTask.Show();
}
catch (System.InvalidOperationException ex)
{
    MessageBox.Show("Impossible d'enregister le contact .");
}


Maintenant, on a la fonction évènement qui survient quand la tache est terminée.

 
Sélectionnez
void saveContactTask_Completed(object sender, SaveContactResult e)
{
    switch (e.TaskResult)
    {
        //contact enregistré
        case TaskResult.OK:
            MessageBox.Show("Contact enregistré.");
            break;

        //Annuler par l'utilisateur
        case TaskResult.Cancel:
            MessageBox.Show("Enregistrement annulé.");
            break;

        //Impossible d'enregistrer
        case TaskResult.None:
            MessageBox.Show("Impossible d'enregister.");
            break;
    }
}

Dans le chapitre « Utiliser un WebBrowser » il y a un exemple d'utilisation du WebBrowseTask pour visionner des pages du Web.

VII-F. Les thèmes

Dans les paramètres du Windows Phone on peut choisir :
l'arrière plan light ou dark (clair ou sombre),
la couleur d'accentuation (11 couleurs proposées).

Image non disponible

Voici les 10 couleurs version 7.1 (en 7.0 magenta et lime sont légèrement différents), en plus il y a une onzième couleur réservée à l'opérateur ou au fabriquant (sur le mien c'est « Orange F »).

Image non disponible

Ces couleurs se retrouvent dans les ressources Windows Phones : la couleur de fond est : PhoneBackgroundColor.
La couleur d'accentuation est : PhoneAccentColor.

Pour le texte : PhoneForegroundColor.


Dans le code XAML quand on utilise des ressources comme couleur, ce sont les « Brush » qu'il faut utiliser et pas les couleurs.

PhoneAccentBrush :Brush d'accentuation définie par le thème.

PhoneForegroundBrush :Brush utilisée pour les éléments de premier plan tels que le texte ou les icônes.

PhoneBackgroundBrush :Brush utilisée pour les éléments d'arrière-plan.

PhoneContrastBackgroundBrush :Brush de fond utilisée pour mettre en contraste (fond des menus contextuels par exemple).

PhoneContrastForegroundBrush :Brush de premier plan utilisée pour les éléments à mettre en contraste. Par exemple les textes des menus contextuels.

PhoneDisabledBrush :Brush des éléments désactivés.

PhoneSubtleBrush :Brush partiellement transparente qui permet de mettre en retrait des éléments visuels moins importants.

PhoneBorderBrush :Brush de bordure des contrôles TextBox, CheckBox, PasswordBox, et RadioButton.

PhoneSemitransparentBrush :Brush presque transparente permettant de faire ressortir des éléments de couleurs similaires (par exemple du texte blanc sur une image claire).

Exemple :

 
Sélectionnez
 <TextBlock Height="434" Name="TextLong" Foreground="{StaticResource PhoneAccentBrush} />"


On peut aussi utiliser les styles :
PhoneTextNormalStyle qui utilise judicieusement une couleur (PhoneForegroundBrush) qui contraste bien avec la PhoneBackgroundColor du thème. Si le background est dark, PhoneTextNormalStyle donnera du texte blanc (FontSize : 20).
Il y a aussi PhoneTextTitle1Style (FontSize : 72, PhoneForegroundBrush), PhoneTextTitle2Style (FontSize : 32, PhoneForegroundBrush)…

 
Sélectionnez
<TextBox Name="resultat" Style="{StaticResource PhoneTextNormalStyle}" />

On se rend compte qu'à partir de la couleur d'accentuation et du fond, les styles s'adaptent pour être lisibles et cohérents.



Sélecteur de ressource : dans la fenêtre « Propriétés », pour donner une valeur ressource à la propriété d'un contrôle (BackGround par exemple), on peut ouvrir le sélecteur de ressource en cliquant sur le carré à côté du nom de la propriété :

Image non disponible

Dans le menu, choisir « Appliquer la ressource ».

Image non disponible

Double-cliquer sur la ressource désirée.



Comment utiliser les couleurs du thème choisi par l'utilisateur ?
On va créer une page avec un fond dégradé à partir des deux couleurs PhoneBackgroundColor et PhoneAccentColor.
Le titre de la page utilisera le style PhoneAccentColor. Le texte affiché aura le Style PhoneTextNormalStyle.

 
Sélectionnez
<!--LayoutRoot est la grille racine où tout le contenu de la page est placé-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--TitlePanel contains the name of the application and page title-->
        <Rectangle Stroke="Black" Grid.RowSpan="2">
            <Rectangle.Fill>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="{StaticResource PhoneBackgroundColor}" Offset="0"/>
                    <GradientStop Color="{StaticResource PhoneAccentColor}" Offset="1"/>
                </LinearGradientBrush>
            </Rectangle.Fill>
        </Rectangle>

        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
        <TextBlock x:Name="ApplicationTitle" Text="Windows Phone" Style="{StaticResource PhoneTextNormalStyle}"/>
        <TextBlock x:Name="PageTitle" Text="Les thèmes" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}">
                <TextBlock.Foreground>
                    <SolidColorBrush Color="{StaticResource PhoneAccentColor}"/>
                </TextBlock.Foreground>
            </TextBlock>
        </StackPanel>

        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <TextBlock Height="601" TextWrapping="Wrap" HorizontalAlignment="Left" Margin="0,6,0,0" x:Name="textBlock1" 
            Style="{StaticResource PhoneTextNormalStyle}"
            Text="Il y a le 'designer' à gauche qui permet de dessiner l'interface que verra l'utilisateur.
            Le designer génère le code XAML correspondant, on le voit à droite, il décrit l'interface en XAML.
            On peut aussi écrire directement du XAML à droite, ce qui modifie l'interface dans le designer.." 
            VerticalAlignment="Top" Width="468" />
        </Grid>
    </Grid>

Voici le résultat : avec un fond foncé et une couleur d'accentuation marron puis avec un fond clair et une couleur d'accentuation bleue.

Image non disponible

Le résultat est parfait, le texte toujours lisible quelque soit le choix du fond et de la couleur d'accentuation.

Si on n'indique rien comme style ou couleur, par défaut le thème courant est utilisé : texte blanc si le fond est « dark » et texte noir si le fond est « light » plus couleur d'accentuation.
Si on indique des couleurs, pour le fond par exemple, il faut bien veiller à ce que la couleur du texte soit lisible et tester son application avec les différents fonds et couleurs d'accentuation.

Comment savoir si on a un Background Dark ou Light ?

 
Sélectionnez
// Determine la visibilité du dark background.
            Visibility darkBackgroundVisibility =
                (Visibility)Application.Current.Resources["PhoneDarkThemeVisibility"];

            // Afficher dans un textbox le theme background .
            if (darkBackgroundVisibility == Visibility.Visible)
            {
                textBlock1.Text = "background = dark";
            }
            else
            {
                textBlock1.Text = "background = light";
            }


Comment connaître la couleur d'accentuation ?

 
Sélectionnez
Color currentAccentColorHex = 
            (Color)Application.Current.Resources["PhoneAccentColor"];

VII-G. Vibreur

Il faut inclure l'espace de noms Microsoft.Devices et instancier un VibrateControler avec la méthode static Default.

 
Sélectionnez
using Microsoft.Devices;

            VibrateController vc = VibrateController.Default;
            vc.Start(TimeSpan.FromMilliseconds(100));

Il y a une exception si l'argument de Start est négatif ou supérieur à cinq secondes.

On a aussi la méthode Stop.


Combien de temps faire vibrer ?
100 ms pour signaler la pression d'une touche.
300 ms pour attirer l'attention de l'utilisateur.
2 s : non !

VII-H. Localisation

Le Windows Phone utilise trois moyens pour se localiser :

  1. avec les relais émetteurs cellulaires ;
  2. avec la Wi-Fi ;
  3. avec les satellites GPS.

Avec les relais c'est rapide mais peu précis. Avec le GPS c'est précis mais lent et cela ne marche pas à l'intérieur. Avec la WiFi, il faut être en ville.
Les informations des relais et de la Wi-Fi arrivent en quelques secondes ; pour le GPS c'est quelques minutes.
Le WP utilise les trois (il faut, dans les paramètres du WP, que la localisation soit activée).



Comment trouver sa localisation (latitude, longitude, vitesse, orientation, altitude) ?

Il faut charger la référence (la DLL) System.Device (clic droit sur « Références » dans l'explorateur de projet puis « Ajouter une référence »).

Puis ajouter l'espace de noms System.Device.Localisation.
Il faut instancier un GeoCoordinateWatcher, créer un EventHandler sur l'évènement PositionChanged.
À chaque fois que la position du WP change, l'évènement PositionChanged est exécuté et donc la fonction « gcw_PositionChanged ».
Il faut démarrer la localisation avec la méthode Start.
On affiche ensuite la latitude, la longitude, la vitesse, la direction en degrés par rapport au nord géographique, l'altitude (dans cinq TextBlock).

 
Sélectionnez
using System.Device.Location;
namespace localisation
{
    public partial class MainPage : PhoneApplicationPage
    {
        GeoCoordinateWatcher GeoCoordinater;
        // Constructeur
        public MainPage()
        {
            InitializeComponent();
           
        GeoCoordinater= new GeoCoordinateWatcher();
        GeoCoordinater.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(gcw_PositionChanged);
        GeoCoordinater.Start();
        }
        void gcw_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
        {
             //Coordonnées
            latitude.Text = e.Position.Location.Latitude.ToString();
            longitude.Text = e.Position.Location.Longitude.ToString();
            
            //Vitesse
            vitesse.Text= e.Position.Location.Speed.ToString("0.0") + " métres par seconde";
            
            //Direction en degrés par rapport au nord géographique. 
            course.Text = e.Position.Location.Course.ToString("0.0");
            
            //Altitude
            altitude.Text = e.Position.Location.Altitude.ToString("0.0")+ " métres";
        }
    }
}


Pour tester l'application dans l'émulateur, il faut ouvrir la fenêtre suivante grâce au bouton « >> » situé à droite de l'émulateur, puis cliquer sur l'onglet « Localisation » :

Image non disponible

Dans la zone de recherche, en haut à gauche, il faut saisir un lieu, cela affiche une carte. Le fait de cliquer sur la carte affiche en bas les coordonnées du lieu.
Ces coordonnées seront utilisées dans l'émulateur (pas de vitesse, direction et altitude).



Afin d'éviter les plantages, on peut gérer le Status du GeoCoorditaneWatcher afin de savoir si la localisation est activée, si le WP reçoit des données :
Il faut créer un évènement :

 
Sélectionnez
// Ajouter en dessous du précédent EventHandler
GeoCoordinater.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(watcher_StatusChanged);

//       
        static void watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
        {
            switch (e.Status)
            {
                case GeoPositionStatus.Initializing:
                    System.Diagnostics.Debug.WriteLine("Localisation en cours");
                    break;

                case GeoPositionStatus.Ready:
                    System.Diagnostics.Debug.WriteLine("Location effectuée");
                    break;

                case GeoPositionStatus.NoData:
                    System.Diagnostics.Debug.WriteLine("Pas de données");
                    break;

                case GeoPositionStatus.Disabled:
                    System.Diagnostics.Debug.WriteLine("Localisation désactivée");
                    break;
            }
        }

On peut ajouter un paramètre lors de l'initialisation pour forcer à plus de précision :

 
Sélectionnez
GeoCoordinateWatcher GeoCoordinater;
 
GeoCoordinater = new GeoCoordinateWatcher(GeoPositionAccuracy.High);

La valeur par défaut est « Default ».
La précision High peut nuire aux performances.

On peut aussi indiquer la distance de déplacement pour déclencher PositionChanged :

 
Sélectionnez
GeoCoordinater.MovementThreshold = 10.0f;

VII-I. Accéléromètre

Windows Phone contient un espace de noms dédié à la gestion et l'exploitation en temps réel des données de l'accéléromètre du téléphone. L'accéléromètre mesure l'intensité et la direction de la force d'accélération appliquée sur le téléphone. La valeur de cette force suivant les axes X, Y, Z, se retrouve dans une variable décimale (valeurs de -1.0 à 1.0), l'unité étant le « g » (1g = attraction de la force terrestre).

Voici le sens des vecteurs X, Y, Z :

Image non disponible


L'attraction terrestre ayant la valeur 1, voici les valeurs x y z pour les divers positions du phone.

Image non disponible

Dans le code il faut inclure l'espace de noms nécessaire :

 
Sélectionnez
using Microsoft.Devices.Sensors;


Il faut ensuite instancier l'accéléromètre et ajouter un EventHandler sur l'évènement :

 
Sélectionnez
    public partial class MainPage : PhoneApplicationPage
    {
        // Constructor
        Accelerometer monaccelerometer = new Accelerometer();
       
        monaccelerometer.Start();
        monaccelerometer.ReadingChanged += new
        EventHandler<AccelerometerReadingEventArgs>(myHandler);
 
Sélectionnez
    void myHandler(object sender, AccelerometerReadingEventArgs e)
    {
        Deployment.Current.Dispatcher.BeginInvoke(() => updateMyScreen(e));
    }
 
Sélectionnez
    void updateMyScreen(AccelerometerReadingEventArgs e)
    {
        // updates the textblocks
        xreadout.Text = e.X.ToString("0.00");
        yreadout.Text = e.Y.ToString("0.00");
        zreadout.Text = e.Z.ToString("0.00");
    }


Pour tester l'application dans l'émulateur, il faut ouvrir la fenêtre suivante grâce au bouton'>>" situé à droite de l'émulateur puis cliquer sur l'onglet « Accéléromètre » :

Image non disponible

En déroulant la liste « Orientation » en bas à gauche, on peut choisir différentes valeurs de X, Y, Z.

VII-J. Internet, WebBrowser, WebClient

Le Windows Phone est-il connecté à internet ?

 
Sélectionnez
using Microsoft.Phone.Net.NetworkInformation;
/...
 MessageBox.Show(NetworkInterface.NetworkInterfaceType.ToString());

NetworkInterfaceType retourne :
None si pas de connexion ;
Wireless80211 si connexion Wi-Fi ;
Ethernet ;
MobileBroadbandGSM ou MobileBroadbandCDMA si réseau téléphone mobile.

Comment charger dans un Browser une page qui est sur le Web ?

On peut utiliser un lanceur de tache pour utiliser l'application « WebBrowser ». Il faut ajouter un espace de noms :

 
Sélectionnez
using Microsoft.Phone.Tasks;

Ensuite il faut utiliser un WebBrowserTask :

 
Sélectionnez
WebBrowserTask webBrowserTask = new WebBrowserTask();

webBrowserTask.Uri = new Uri("http://msdn.microsoft.com", UriKind.Absolute);

webBrowserTask.Show();
Image non disponible

On peut mettre un contrôle « WebBrowser » dans la page.
En allant le chercher dans la boîte à outils :

 
Sélectionnez
<phone:WebBrowser Height="426" HorizontalAlignment="Left" Margin="26,49,0,0" 
 Name="webBrowser1" VerticalAlignment="Top" Width="407" />

En ajoutant la source :

 
Sélectionnez
 <phone:WebBrowser Height="426" HorizontalAlignment="Left" Margin="26,49,0,0" 
 Name="webBrowser1" VerticalAlignment="Top" Width="407"
 Source="http://www.bing.com"  />

ou en instanciant un WebBrowser dans le code C# :

 
Sélectionnez
using Microsoft.Phone.Controls;

WebBrowser webBrowser1 = new WebBrowser();

Ensuite on peut naviguer vers une page.

 
Sélectionnez
webBrowser1.Navigate(new Uri("http://msdn.microsoft.com", UriKind.Absolute));

Pas de GoBack, GoFoward...

Dans ce WebBrowser on peut aussi afficher du HTML grâce à NavigateToString :

 
Sélectionnez
  webBrowser1.NavigateToString("<html><head><meta name='viewport' content='width=480, user-scalable=yes' />
  </head><body>Du texte en HTML</body></html>");

SaveToString retourne une string contenant le code HTML de la page.

Pour charger un fichier situé sur Internet, on peut utiliser un WebClient.
Un WebClient a des méthodes pour l'envoi de données à une ressource locale, intranet ou Internet identifiée par un URI ou pour la réception de données en provenance de cette ressource.

Ici on va charger une image :

 
Sélectionnez
{
WebClient MywebClient = new WebClient();
MywebClient.OpenReadCompleted += OnWebClientOpenReadCompleted;
MywebClient.OpenReadAsync(new Uri("http://www.mysite.com/Media/myimage.jpg"));
}


void OnWebClientOpenReadCompleted(object sender, OpenReadCompletedEventArgs args)
{
if (!args.Cancelled && args.Error== null)
 {
BitmapImage bmp = new BitmapImage();
bmp.SetSource(args.Result);
image.Source = bmp;
}
}

VII-K. Radio FM

Simple pour mettre en marche la radio FM.

 
Sélectionnez
using Microsoft.Devices.Radio;

puis

 
Sélectionnez
FMRadio.Instance.PowerMode=  RadioPowerMode.On;
FMRadio.Instance.Frequency= 100.3;

VII-L. Minuterie

On peut utiliser une minuterie grâce à la classe DispatcherTimer de espace de noms System.Windows.Threading du framework.
Elle déclenche un évènement Tick tous les « Interval » de temps.

Créons une application qui affiche date, heure, minute, seconde et se met à jour toutes les secondes.

 
Sélectionnez
using System.Windows.Threading;
//..

//Instanciation d'un timer.
DispatcherTimer dt = new DispatcherTimer();

//On indique l'intervalle de temps entre les évènements Tick, ici 1 s.
dt.Interval = TimeSpan.FromSeconds(1);

//On indique quelle fonction exécuter quand le Timer déclenche Tick.
dt.Tick += OnTimerSeconde;

//On démarre le Timer
tmr.Start();



void OnTimerSeconde(object sender, EventArgs args)
//Fonction exécutée toutes les secondes
{
//On affiche dans le textBlock nommé 'Clock' la date et heure, minute, seconde
//Mise à jour toutes les secondes donc.
Clock.Text = DateTime.Now.ToString();
}

Les opérations DispatcherTimer sont placées dans la file d'attente Dispatcher. Le moment d'exécution de l'opération DispatcherTimer dépend des autres travaux de la file d'attente et de leur priorité ; ainsi l'évènement Tick se produira peut-être pas exactement durant l'intervalle de temps défini, au moins après un intervalle, parfois plus.
Il s'exécute dans le thread interface utilisateur (UI).

Il existe un autre Timer dans l'espace de noms System.Threading :
Il fonctionne dans un thread d'arrière-plan.

 
Sélectionnez
using System.Threading;


Timer tm = new Timer(TimerCallBack);
            // TimerCallBack est la méthode de rappel à exécuter

tm.Change(100 , 1000);
            //Premier paramètre :
            //Délai d'attente, en millisecondes ou en TimeSpan, avant l'appel de la méthode de rappel 
            //spécifiée au moment de la construction de Timer. 
            //Spécifiez Timeout.Infinite pour empêcher le redémarrage de la minuterie. 
            //Spécifiez zéro (0) pour redémarrer la minuterie immédiatement. 
            //Second paramètre:
            //Intervalle de temps, en millisecondes ou en TimeSpan, entre les appels de la méthode de rappel 
            
            //...
            
public void TimerCallBack(object state)
        { //méthode de rappel exécutée toutes les secondes
            this.Dispatcher.BeginInvoke(delegate() { OnTimer(); });
           
        }


void OnTimer()
        {
            // Méthode affichant l'heure
            Clock.Text = DateTime.Now.ToString();
        }

Si on avait voulu afficher l'heure dans la méthode TimerCallBack, il y aurait eu levée d'une exception « Invalid cross-thread access » car le Timer est dans un thread différent du thread de l'interface. Le thread du Timer doit déléguer le travail au Dispatcher associé au thread d'interface utilisateur grâce à BeginInvoke.

System.Threading.Timer n'est pas recommandé pour les scénarios dans lesquels l'interface d'utilisateur doit être actualisée, parce que ses callback ne se produisent pas sur le thread d'interface d'utilisateur. Système.Windows.DispatcherTimer est un meilleur choix dans ces scénarios, parce que ses événements sont levés sur le thread d'interface d'utilisateur.

VII-M. Applications en tâche de fond : alarme, rappel

La classe « Alarme » de l'espace de noms « Microsoft.Phone.Scheduler » permet de créer une alarme.
On va créer une alarme nommée « MonAlarme » ; elle se déclenchera dans 20 s et affichera "Debout".

 
Sélectionnez
using Microsoft.Phone.Scheduler;



Alarm alarm = new Alarm("MonAlarme");
alarm.BeginTime = DateTime.Now.AddSeconds(20);
alarm.RecurrenceType = RecurrenceInterval.Daily;
alarm.Content = "Debout.";
alarm.Sound = new Uri("sound.wav", UriKind.Relative)
ScheduledActionService.Add(alarm);

RecurrenceInterval peut être Daily, Weekly, Monthly, Yearly, None, EndOfMonth.
ExpirationTime indique à quel moment arrêter le service.
Sound permet d'ajouter l'URI d'un fichier son qui sera exécuté lors de l'alarme. Le fichier son doit être dans le répertoire source et chargé dans l'application comme contenu.

Image non disponible

Si on relance l'application une seconde fois et qu'on veut créer de nouveau une alarme nommée « MonAlarme », il y a levée d'une exception car elle existe toujours (même si on a quitté l'application).
Il est possible de détruire cette alarme :

 
Sélectionnez
ScheduledActionService.Remove("MonAlarme");

On peut aussi utiliser un Reminder ou rappel : chaque 2 juin à 21 h 40 rappeler que c'est l'anniversaire de « Mimi ».

 
Sélectionnez
Reminder reminder = new Reminder("Anniverssaire")
            {
                BeginTime = new DateTime (2012,6,2,21,40,0) ,
                Content = "Anniverssaire de Mimi",
                Title = "Anniversaire",
                RecurrenceType = RecurrenceInterval.Yearly,
                NavigationUri = new Uri("/MainPage.xaml", UriKind.Relative)
            };
            ScheduledActionService.Add(reminder);
Image non disponible

VII-N. Informations diverses

Grâce à DeviceExtendedProperties de l'espace de noms Microsoft.Phone.Info on peut lire des informations sur le téléphone : fabricant, nom, version du Firmware et du Hardware, mémoire totale, mémoire utilisée par l'application, « ApplicationPeakMemoryUsage », ID du téléphone.

 
Sélectionnez
using Microsoft.Phone.Info;
 
Sélectionnez
//Fabriquant du téléphone
MessageBox.Show ( DeviceExtendedProperties.GetValue("DeviceManufacturer").ToString());
//Nom du téléphone
MessageBox.Show ( DeviceExtendedProperties.GetValue("DeviceName").ToString());
//Version du Firmware
MessageBox.Show ( DeviceExtendedProperties.GetValue("DeviceFirmwareVersion").ToString());
//Version du Harsware
MessageBox.Show ( DeviceExtendedProperties.GetValue("DeviceHardwareVersion").ToString());
//Mémoire total du portable
MessageBox.Show ( DeviceExtendedProperties.GetValue("DeviceTotalMemory").ToString());//retourne un long
//Mémoire utilisée par l'application
MessageBox.Show ( DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage").ToString());//retourne un long
           
object tryValue;
           
if (DeviceExtendedProperties.TryGetValue("ApplicationPeakMemoryUsage", out tryValue))
            {
                MessageBox.Show ( tryValue.ToString());
            }

//retourne un tableau de 20 bytes correspondant à l'ID unique du téléphone
MessageBox.Show ( DeviceExtendedProperties.GetValue("DeviceUniqueId").ToString()); 
//A faire : caster le tableau pour le rendre affichable

Il existe aussi DeviceStatus (à partir de WP 7.1) qui permet de voir toutes les informations précédentes avec une syntaxe plus simple.
Mais en plus :
PowerSource qui retourne Battery ou External (secteur) ;
ApplicationMemoryUsageLimit (mémoire que l'application peut utiliser) ;
IsKeyboardPresent IsKeyboardDeployed.

 
Sélectionnez
//On retrouver le précédentes information avec une syntaxe plus simple
MessageBox.Show(DeviceStatus.ApplicationCurrentMemoryUsage.ToString());

//En plus
MessageBox.Show(DeviceStatus.PowerSource.ToString());
MessageBox.Show(DeviceStatus.ApplicationMemoryUsageLimit.ToString());
MessageBox.Show(DeviceStatus.IsKeyboardPresent.ToString()); //et IsKeyboardDeployed

Le portable supporte-t-il le smooth streaming des multi-resolution encoded video ?

 
Sélectionnez
 MessageBox.Show(MediaCapabilities.IsMultiResolutionVideoSupported.ToString());

précédentsommairesuivant

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 © 2011 Lasserre Philippe. Aucune reproduction, même partielle, ne peut être faite de ce site ni 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.