Accueil
Rechercher:
sur developpez.com sur les forums
Forums | Tutoriels | F.A.Q's | Participez | Hébergement | Contacts
Club Emploi Blogs   TV   Dév. Web PHP XML Python Autres 2D-3D-Jeux Sécurité Windows Linux PC Mac
Accueil Conception Java DotNET Visual Basic  C  C++ Delphi MS-Office SQL & SGBD Oracle  4D  Business Intelligence
FORUMS .NET FAQs .NET TUTORIELS .NET SOURCES .NET LIVRES .NET OUTILS .NET BLOG .NET DOTNET TV

Templates avancés en Silverlight

Date de publication : 05/05/2008 , Date de mise à jour : 17/06/2008

Par Benjamin Roux (Retour aux articles)
 

Dans cet article nous allons voir une utilisation avancée des templates en Silverlight.
Silverlight

1. Template de contrôle avancé
1-1. Introduction
1-2. Template de bouton
1-2-1. Le visuel
1-2-2. Les animations
1-2-3. Conclusion
2. Création d'un contrôle templatisable
2-1. La classe
2-2. Eléments templatisables
2-3. Propriétés de binding
2-4. Template de test
2-5. Les états (ouvert/fermé)
2-6. Template final
2-7. Test online
2-8. Conclusion
3. Conclusion
4. Remerciements


1. Template de contrôle avancé


1-1. Introduction

Silverlight 2 fait apparaître avec lui de nombreuses nouveautés (introduites ici : fr Introduction à Silverlight 2) et avec ces dernières la notion de Template pour les contrôles.

Mais comment personnaliser un contrôle de A à Z ? C'est ce que nous allons voir immédiatement.

Depuis la beta 2 de Silverlight, la personnalisation des contrôles a été simplifiée grâce à Expression Blend, cet article s'adresse donc aux développeurs, qui, comme moi qui ne savent utiliser qu'une seule application de graphisme : Paint.


1-2. Template de bouton

Pour illustrer ce concept, nous allons personnaliser un contrôle de type bouton.

Pour tout ce qui est réalisation de template, votre meilleure amie est sans nul doute la MSDN. En effet vous trouverez sur cette dernière le nom des différents états (par exemple MouseOver pour un bouton), ainsi que des exemples.
Voici le lien répertoriant les différentes pages qui sont utiles : en http://msdn2.microsoft.com/en-us/library/cc278075(VS.95).aspx

Pour notre exemple de bouton le lien est celui-ci : en http://msdn2.microsoft.com/en-us/library/cc278069(VS.95).aspx

On apprend le nom des différents états d'un bouton ainsi que le groupe auxquels ils appartiennent.

Etat Groupe Description
Normal CommonStates L'état normal du bouton
MouseOver CommonStates L'état du bouton lorsque l'utilisateur passe la souris dessus
Pressed CommonStates L'état du bouton que l'utilisation clique dessus
Disabled CommonStates L'état du bouton lorsqu'il est désactivé
Focused FocusStates L'état du bouton lorsqu'il prend le focus
Unfocused FocusStates L'état du bouton lorsqu'il perd le focus
Nous avons tout ce dont nous avons besoin pour réaliser le template de notre contrôle.

Pour cet article, nous allons rendre notre contrôle semblable visuellement aux boutons présents dans la suite Office 2007 (bleu et orange).


1-2-1. Le visuel

Nous allons commencer par le visuel de notre bouton. Notre élément racine se nommera RootElement.

<Grid x:Name="RootElement">
    
</Grid>
Pour savoir où placer ce code, vous pouvez regarder ici : http://broux.developpez.com/articles/csharp/introduction-silverlight-2/#L9

Maintenant nous allons mettre un peu de couleur, à savoir une bordure bleue et un background en bleu dégradé.

<Grid x:Name="RootElement">                                                       
    <Border x:Name="VisualElement" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" CornerRadius="4" BorderThickness="1">
        <Border.BorderBrush>
            <SolidColorBrush Color="#FF9BB7E0"/>
        </Border.BorderBrush>
        <Border.Background>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop x:Name="Color1" Color="#FFC8DBEE" Offset="0.260"/>
                <GradientStop x:Name="Color2" Color="#FF84AFE6" Offset="0.530"/>
                <GradientStop x:Name="Color3" Color="#FFC8DBEE" Offset="0.80"/>
            </LinearGradientBrush>
        </Border.Background>                                                               
    </Border>                                                        
</Grid>
Voici le résultat de notre bouton avec ce code pour l'instant

<Button Width="155" Height="50" Content="Bouton Style Word 2007" 
        Style="{StaticResource ButtonRectangle}" VerticalAlignment="Center" 
        FontFamily="Verdana" FontSize="12" />
On voit que le contenu de notre bouton (ici du texte) n'est pas visible.

Nous allons simplement rajouter un objet de type ContentPresenter, dont le contenu sera bindé sur le contenu de notre bouton, autrement dit, tout ce qui sera dans la propriété Content (du texte, un contrôle...) de notre bouton sera représenté dans notre ContentPresenter.

<Grid x:Name="RootElement">                        
    <Border x:Name="VisualElement" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" CornerRadius="4" BorderThickness="1">
        <Border.BorderBrush>
            <SolidColorBrush Color="#FF9BB7E0"/>
        </Border.BorderBrush>
        <Border.Background>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop x:Name="Color1" Color="#FFC8DBEE" Offset="0.260"/>
                <GradientStop x:Name="Color2" Color="#FF84AFE6" Offset="0.530"/>
                <GradientStop x:Name="Color3" Color="#FFC8DBEE" Offset="0.80"/>
            </LinearGradientBrush>
        </Border.Background>                                
        <ContentPresenter Content="{TemplateBinding Content}"                                                  
                          ContentTemplate="{TemplateBinding ContentTemplate}"
                          FontFamily="{TemplateBinding FontFamily}"
                          FontSize="{TemplateBinding FontSize}"
                          FontStretch="{TemplateBinding FontStretch}"
                          FontStyle="{TemplateBinding FontStyle}"
                          FontWeight="{TemplateBinding FontWeight}"
                          Foreground="Black"
                          Padding="{TemplateBinding Padding}"
                          TextAlignment="{TemplateBinding TextAlignment}"
                          TextDecorations="{TemplateBinding TextDecorations}"
                          TextWrapping="{TemplateBinding TextWrapping}"
                          VerticalContentAlignment="Center"
                          HorizontalContentAlignment="Center"
                          Margin="0" />                             
    </Border>                                                        
</Grid>
Et maintenant voici le résultat avec le même code que précédemment.

Il reste un problème : quand on passe la souris dessus ou même que l'on clique dessus il ne se passe rien du tout.

Pour ça nous allons maintenant spécifier ses comportements.


1-2-2. Les animations

Le gestion des états avec les templates, se fait grâce au VisualStateManager, il nous faut donc l'utiliser.

On commencer par rajouter le namespace

xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
On rajoute maintenant les balises nécessaires :

<Grid x:Name="RootElement">
    <vsm:VisualStateManager.VisualStateGroups>
        <vsm:VisualStateGroup x:Name="CommonStates">
        </vsm:VisualStateGroup>
    </vsm:VisualStateManager.VisualStateGroups>
	[...]
</Grid>
warning Dans la beta 2 de Silverlight, Visual Studio ne reconnait pas VisualStateManager.VisualStateGroups, mais cela fonctionne quand même.
Et maintenant nous allons mettre nos étas uns à uns.

Commençons par l'état MouseOver.

<Grid x:Name="RootElement">
    <vsm:VisualStateManager.VisualStateGroups>
        <vsm:VisualStateGroup x:Name="CommonStates">
            <vsm:VisualState x:Name="MouseOver">
                <Storyboard>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color1" Storyboard.TargetProperty="Color">
                        <DiscreteColorKeyFrame KeyTime="0:0:0" Value="#FFFDF2CA" />
                    </ColorAnimationUsingKeyFrames>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color2" Storyboard.TargetProperty="Color">
                        <DiscreteColorKeyFrame KeyTime="0:0:0" Value="#FFFFD048" />
                    </ColorAnimationUsingKeyFrames>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color3" Storyboard.TargetProperty="Color">
                        <DiscreteColorKeyFrame KeyTime="0:0:0" Value="#FFFDF2CA" />
                    </ColorAnimationUsingKeyFrames>
                </Storyboard>
            </vsm:VisualState>
            <vsm:VisualState x:Name="Disabled" />
        </vsm:VisualStateGroup>
    </vsm:VisualStateManager.VisualStateGroups>                                                                                                         
</Grid>
Comme vous pouvez le voir c'est extrêmement simple, il suffit de déclarer un VisualState et d'associer à la propriété x:Name le nom de l'état auquel on veut qu'il soit associé. Puis il suffit de mettre un Storyboard pour notre animation.

Et voilà, désormais lorsque l'utilisateur passera la souris sur le bouton le dégradé bleu en fond changera en dégradé orange.

Rajoutons ensuite les autres états.

<Grid x:Name="RootElement">
    <vsm:VisualStateManager.VisualStateGroups>
        <vsm:VisualStateGroup x:Name="CommonStates">
            <vsm:VisualState x:Name="Normal">
                <Storyboard>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color1" Storyboard.TargetProperty="Color">
                        <LinearColorKeyFrame KeyTime="0:0:0.35" Value="#FFC8DBEE" />
                    </ColorAnimationUsingKeyFrames>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color2" Storyboard.TargetProperty="Color">
                        <LinearColorKeyFrame KeyTime="0:0:0.35" Value="#FF84AFE6" />
                    </ColorAnimationUsingKeyFrames>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color3" Storyboard.TargetProperty="Color">
                        <LinearColorKeyFrame KeyTime="0:0:0.35" Value="#FFC8DBEE" />
                    </ColorAnimationUsingKeyFrames>
                </Storyboard>
            </vsm:VisualState>
            <vsm:VisualState x:Name="MouseOver">
                <Storyboard>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color1" Storyboard.TargetProperty="Color">
                        <DiscreteColorKeyFrame KeyTime="0:0:0" Value="#FFFDF2CA" />
                    </ColorAnimationUsingKeyFrames>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color2" Storyboard.TargetProperty="Color">
                        <DiscreteColorKeyFrame KeyTime="0:0:0" Value="#FFFFD048" />
                    </ColorAnimationUsingKeyFrames>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color3" Storyboard.TargetProperty="Color">
                        <DiscreteColorKeyFrame KeyTime="0:0:0" Value="#FFFDF2CA" />
                    </ColorAnimationUsingKeyFrames>
                </Storyboard>
            </vsm:VisualState>
            <vsm:VisualState x:Name="Pressed">
                <Storyboard>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color1" Storyboard.TargetProperty="Color">
                        <DiscreteColorKeyFrame KeyTime="0:0:0" Value="#FFFCB16D" />
                    </ColorAnimationUsingKeyFrames>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color2" Storyboard.TargetProperty="Color">
                        <DiscreteColorKeyFrame KeyTime="0:0:0" Value="#FFFF8D06" />
                    </ColorAnimationUsingKeyFrames>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color3" Storyboard.TargetProperty="Color">
                        <DiscreteColorKeyFrame KeyTime="0:0:0" Value="#FFFCB16D" />
                    </ColorAnimationUsingKeyFrames>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="VisualElement" Storyboard.TargetProperty="(Border.BorderBrush).Color">
                        <DiscreteColorKeyFrame KeyTime="0:0:0" Value="#FF7B6645" />
                    </ColorAnimationUsingKeyFrames>
                </Storyboard>
            </vsm:VisualState>
            <vsm:VisualState x:Name="Disabled" />
        </vsm:VisualStateGroup>
    </vsm:VisualStateManager.VisualStateGroups>                                 
</Grid>
Lorsque l'utilisateur cliquera sur le bouton la couleur de ce dernier passera en orange foncé (Discrete animation), lorsque le bouton repassera dans l'état normal (MouseOut par exemple), la couleur reviendra progressivement (Linear animation) au bleu et pour finir je n'ai pas mis d'animation pour l'état désactivé.

Voici donc le code final.

<Grid x:Name="RootElement">
    <vsm:VisualStateManager.VisualStateGroups>
        <vsm:VisualStateGroup x:Name="CommonStates">
            <vsm:VisualState x:Name="Normal">
                <Storyboard>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color1" Storyboard.TargetProperty="Color">
                        <LinearColorKeyFrame KeyTime="0:0:0.35" Value="#FFC8DBEE" />
                    </ColorAnimationUsingKeyFrames>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color2" Storyboard.TargetProperty="Color">
                        <LinearColorKeyFrame KeyTime="0:0:0.35" Value="#FF84AFE6" />
                    </ColorAnimationUsingKeyFrames>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color3" Storyboard.TargetProperty="Color">
                        <LinearColorKeyFrame KeyTime="0:0:0.35" Value="#FFC8DBEE" />
                    </ColorAnimationUsingKeyFrames>
                </Storyboard>
            </vsm:VisualState>
            <vsm:VisualState x:Name="MouseOver">
                <Storyboard>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color1" Storyboard.TargetProperty="Color">
                        <DiscreteColorKeyFrame KeyTime="0:0:0" Value="#FFFDF2CA" />
                    </ColorAnimationUsingKeyFrames>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color2" Storyboard.TargetProperty="Color">
                        <DiscreteColorKeyFrame KeyTime="0:0:0" Value="#FFFFD048" />
                    </ColorAnimationUsingKeyFrames>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color3" Storyboard.TargetProperty="Color">
                        <DiscreteColorKeyFrame KeyTime="0:0:0" Value="#FFFDF2CA" />
                    </ColorAnimationUsingKeyFrames>
                </Storyboard>
            </vsm:VisualState>
            <vsm:VisualState x:Name="Pressed">
                <Storyboard>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color1" Storyboard.TargetProperty="Color">
                        <DiscreteColorKeyFrame KeyTime="0:0:0" Value="#FFFCB16D" />
                    </ColorAnimationUsingKeyFrames>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color2" Storyboard.TargetProperty="Color">
                        <DiscreteColorKeyFrame KeyTime="0:0:0" Value="#FFFF8D06" />
                    </ColorAnimationUsingKeyFrames>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="Color3" Storyboard.TargetProperty="Color">
                        <DiscreteColorKeyFrame KeyTime="0:0:0" Value="#FFFCB16D" />
                    </ColorAnimationUsingKeyFrames>
                    <ColorAnimationUsingKeyFrames Storyboard.TargetName="VisualElement" Storyboard.TargetProperty="(Border.BorderBrush).Color">
                        <DiscreteColorKeyFrame KeyTime="0:0:0" Value="#FF7B6645" />
                    </ColorAnimationUsingKeyFrames>
                </Storyboard>
            </vsm:VisualState>
            <vsm:VisualState x:Name="Disabled" />
        </vsm:VisualStateGroup>
    </vsm:VisualStateManager.VisualStateGroups>                                                    
    <Border x:Name="VisualElement" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" CornerRadius="4" BorderThickness="1">
        <Border.BorderBrush>
            <SolidColorBrush Color="#FF9BB7E0"/>
        </Border.BorderBrush>
        <Border.Background>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop x:Name="Color1" Color="#FFC8DBEE" Offset="0.260"/>
                <GradientStop x:Name="Color2" Color="#FF84AFE6" Offset="0.530"/>
                <GradientStop x:Name="Color3" Color="#FFC8DBEE" Offset="0.80"/>
            </LinearGradientBrush>
        </Border.Background>                                
        <ContentPresenter Content="{TemplateBinding Content}"                                                  
                          ContentTemplate="{TemplateBinding ContentTemplate}"
                          FontFamily="{TemplateBinding FontFamily}"
                          FontSize="{TemplateBinding FontSize}"
                          FontStretch="{TemplateBinding FontStretch}"
                          FontStyle="{TemplateBinding FontStyle}"
                          FontWeight="{TemplateBinding FontWeight}"
                          Foreground="Black"
                          Padding="{TemplateBinding Padding}"
                          TextAlignment="{TemplateBinding TextAlignment}"
                          TextDecorations="{TemplateBinding TextDecorations}"
                          TextWrapping="{TemplateBinding TextWrapping}"
                          VerticalContentAlignment="Center"
                          HorizontalContentAlignment="Center"
                          Margin="0" />                                
    </Border>                                                        
</Grid>
Vous pouvez aussi voir que je n'ai pas utilisé l'élément pour indiquer le focus sur le bouton (en général une bordure en pointillés).

Voici enfin un lien vers un test online :