[Silverlight/WPF]Un contrôle très pratique,
Vous vous êtes déjà dit que créer un UserControl juste pour afficher un bloc UI était un peu dommage ? Vous rêvez de pouvoir utiliser du Xaml de la même façon qu’un png ? Alors vous aller adorer le XamlLoader !
Principe :
Le XamlLoader est un contrôle permettant de charger dynamiquement un fichier contenant du Xaml.
L’intérêt réside dans le fait que lorsqu’on désire avoir des icônes en mode vectoriel (comprendre en Xaml), la plupart du temps, on créera un contrôle avec un fichier de code behind vide… ou qui ne contiendrait que la « logique » destinée aux évènements souris.
Cela n’est maintenant plus nécessaire, le fichier Xaml à lui seul suffit. En effet, la description simple en Xaml du contenu de l’icône peut être placée dans un fichier au sein du projet et chargée dynamiquement.
Conditions :
Pour cela il faut respecter certaines conventions :
- Le fichier doit être une « Page » du projet : pour ce faire, il suffit de faire un click droit sur le fichier et changer la « Build Action » de celui-ci en Page.
- Ce fichier est au fait un fichier XML. A ce titre (et contrairement aux fichiers XAML normaux) il nécessite une entête XML
<?xml version="1.0" encoding="utf-8"?>
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Ici nous n’avons que le strict minium mais on peut bien sûr importer ce que l’on veut…
- Il est aussi nécessaire de donner une taille fixe à l’élément racine. On pourrait comparer cela à ce que l’on fait avec une image de type Bitmap ; la taille est fixe.
Utilisation :
L’utilisation du XamlLoader est relativement simple, il suffit de le mettre dans le corps de votre Xaml et de lui donner le chemin d’accès au fichier à charger. Attention toutefois à lui donner ce chemin de façon absolue (/Sample;component/XamlImages/icon_upload.xaml). En effet, si vous donner le chemin du fichier relativement à partir du contrôle en cours d’édition, lorsque le XamlLoader essaiera de le charger, depuis (bien souvent une toute autre position dans la hiérarchie de fichier de votre application. Cela permet aussi d’avoir les fichiers Xaml dans un autre projet, et donc de partager facilement les icônes.
Le XamlLoader offre aussi les possibilités suivantes :
- Une propriété « Stretch » empruntée à la ViewBox du Toolkit Silverlight qui permet de spécifier comment adapter la taille du contenu par rapport à la taille du container.
En pratique, cela permet de spécifier le Stretch mode à :
- None : peu importe la taille du XamlLoader, la taille de l’icône chargée sera celle spécifiée dans le fichier Xaml.
- Fill : Ici on va adapter la taille du contenu à celle du contrôle, sans se préoccuper du ratio.
- Uniform : On s’arrange pour prendre la taille la plus grande possible tout en respectant l’aspect ratio. On ne sortira donc jamais des limites du contrôle.
- UniformToFill : Similaire à Uniform mais en remplissant complètement le XamlLoader. On dépassera donc toujours (sauf en cas d’une taille identique) soit en largeur soit en hauteur les limites.
- Une propriété IsEnabled. Celle-ci fait essentiellement 2 choses :
- Elle applique un GreyscaleEffect (trouvé sur le net et qui n’est autre qu’un pixel shader effect) à l’élément en lui-même permettant de griser l’icône.
- Elle parcourt tous les enfants du contrôle afin de trouver tous les éléments de type Control et de mettre à jour les propriétés IsEnabled.
Comment ca marche ?
Pour faire charger et générer le Xaml, rien de bien sorcier, Silverlight nous offre cette fonctionnalité out of the box avec le XamlReader
Voici donc comment l’utiliser :
public static object LoadFromXaml(Uri uri)
{
StreamResourceInfo streamInfo = Application.GetResourceStream(uri);
if ((streamInfo != null) && (streamInfo.Stream != null))
{
using (StreamReader reader = new StreamReader(streamInfo.Stream))
{
return XamlReader.Load(reader.ReadToEnd());
}
}
return null;
}
Pour ce qui est des la gestion de la taille, on utilise des Transformation pour s’adapter au Stretch et à la taille choisie.
private void CheckSize()
{
double currentWidth = ActualWidth;
if (!double.IsNaN(Width))
if (Width > ActualWidth)
currentWidth = Width;
double currentHeight = ActualHeight;
if (!double.IsNaN(Height))
if (Height > ActualHeight)
currentHeight = Height;
if (loadedElement != null && loadedElement.Width > 0 && loadedElement.Height > 0 && currentWidth > 0 &&
currentHeight > 0)
{
double scaleX = (currentWidth/loadedElement.Width);
double scaleY = (currentHeight/loadedElement.Height);
double centerX = loadedElement.Width/2;
double centerY = loadedElement.Height/2;
loadedElement.SetValue(LeftProperty, (currentWidth/2) - (loadedElement.Width/2));
loadedElement.SetValue(TopProperty, (currentHeight / 2) - (loadedElement.Height / 2));
switch (Stretch)
{
case Stretch.None:
break;
case Stretch.Fill:
loadedElement.RenderTransform = new ScaleTransform
{
ScaleX = scaleX,
ScaleY = scaleY,
CenterX = centerX,
CenterY = centerY
};
break;
case Stretch.Uniform:
double smallest = scaleY;
if (scaleX < scaleY)
smallest = scaleX;
loadedElement.RenderTransform = new ScaleTransform
{
ScaleX = smallest,
ScaleY = smallest,
CenterX = centerX,
CenterY = centerY
};
break;
case Stretch.UniformToFill:
double biggest = scaleY;
if (scaleX > scaleY)
biggest = scaleX;
loadedElement.RenderTransform = new ScaleTransform
{
ScaleX = biggest,
ScaleY = biggest,
CenterX = centerX,
CenterY = centerY
};
break;
}
}
}
Et mes events alors ?
Comme je le disais au début de cet article, on utilise généralement un UserControl pour bénéficier du support des event et ainsi permettre de modifier l’état du contrôle. Avec Silverlight 3 on peut maintenant tirer parti des librairies Interactivity (Microsoft.Expression.Interactivity et System.Windows.Interaction) fournie dans le SDK de Blend 3.
Cela nous permet entre autre de faire ce qui suit.
- Créer les storyboard dont on a besoin :
<Storyboard x:Key="FlipIn" >
<DoubleAnimation Duration="0:0:0.5" To="45" Storyboard.TargetName="projection" Storyboard.TargetProperty="RotationX" />
</Storyboard>
<Storyboard x:Key="FlipOut" >
<DoubleAnimation Duration="0:0:0.5" To="0" Storyboard.TargetName="projection" Storyboard.TargetProperty="RotationX" />
</Storyboard>
- Ensuite des triggers pour démarrer les storyboards:
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="MouseEnter">
<Media:ControlStoryboardAction Storyboard="{StaticResource FlipIn}" ControlStoryboardOption="Play" />
</Interactivity:EventTrigger>
<Interactivity:EventTrigger EventName="MouseLeave">
<Media:ControlStoryboardAction Storyboard="{StaticResource FlipOut}" ControlStoryboardOption="Play" />
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
Je prépare un petit post sur les Triggers et Behaviors pour les jours à venir car ce que je fais ici est assez simple mais ces derniers sont bien plus puissants qu’il n’y parrait.
Résultat:
Cette belle télévision provient de la : http://advertboy.wordpress.com/blendcandy/
Conclusion :
Personnellement j’use et j’abuse de ce contrôle dans beaucoup de situations.
Ce n’est pas encore parfait et de multiples améliorations pourrait y être amenées mais déjà tellement pratique.
J’espère qu’il vous simplifiera la vie tout autant qu’à moi !
Enfin, je vous prépare égallement un post tirant parti des possibilités de ce contrôle…
A bientôt !
DjoDjo
Technorati Tags:
Silverlight,
WPF
PS : Je vous mets les sources rapidement !! Dès que je trouve comment faire…
EDIT : Les sources sont maintenant attachées au post ;-)
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :