Lorsque votre application grossit (son style aussi), il est intéressant de séparer vos styles dans différents fichiers pour gagner en clareté. Il vous suffit ainsi de déclarer vos fichiers dans le fichier App.xaml comme ceci :
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Color.xaml" />
<ResourceDictionary Source="Control.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Ceci ne fonctionne que si le fichier Control.xaml n’utilise pas de ressources déclarées dans Color.xaml et inversement. Contrairement à WPF qui tient compte de l’ordre de déclaration (Color.xaml est avant Control.xaml), Silverlight charge les ressources de manière indépendante interdisant ainsi les références entre les diffèrents dictionnaires.
Dans notre exemple, Color.xaml contient :
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="MyColor">#FFF98110</Color>
</ResourceDictionary>
Alors que Control.xaml contient :
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="TextBlock">
<Setter Property="Foreground"
Value="{StaticResource MyColor}" />
</Style>
</ResourceDictionary>
Control.xaml fait donc référence à une ressource déclarée dans Color.xaml. Pour faire fonctionner ce code, il faut déclarer Color.xaml dans Control.xaml comme ceci :
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Color.xaml" />
</ResourceDictionary.MergedDictionaries>
Imaginer ceci dans 10 autres fichiers de style faisant référence à MyColor déclarée dans Color.xaml. Le contenu de Color.xaml serait duppliqué et inclus 10 fois (1 fois par fichier). Inutile de dire que cette pratique est plutôt mauvaise d’un point de vue performance. K.M s’est basé sur cet article pour créer une classe fonctionnant en Silverlight :
public class SharedResourceDictionary : ResourceDictionary
{
public static Dictionary<Uri, ResourceDictionary> _sharedDictionaries =
new Dictionary<Uri, ResourceDictionary>();
private Uri _sourceUri;
public new Uri Source
{
get { return _sourceUri; }
set
{
_sourceUri = value;
if (!_sharedDictionaries.ContainsKey(value))
{
Application.LoadComponent(this, value);
_sharedDictionaries.Add(value, this);
}
else
{
CopyInto(this, _sharedDictionaries[value]);
}
}
}
private static void CopyInto(ResourceDictionary copy, ResourceDictionary original)
{
foreach (var dictionary in original.MergedDictionaries)
{
var mergedCopy = new ResourceDictionary();
CopyInto(mergedCopy, dictionary);
copy.MergedDictionaries.Add(mergedCopy);
}
foreach (DictionaryEntry pair in original)
{
copy.Add(pair.Key, pair.Value);
}
}
}
Explications :
Au lieu de recharger le code de Color.xaml dans chaque fichier, il sera chargé la 1re fois puis stocké dans un Dictionaire (Dictionary<> C#). Lorsqu’un autre fichier essayera d’inclure Color.xaml, son contenu sera directement récupéré depuis le Dictionary<>.
Le code précédemment ajouté à Control.xaml devient :
<ResourceDictionary.MergedDictionaries>
<shared:SharedResourceDictionary Source="/MergedDictionary;Component/Color.xaml" />
</ResourceDictionary.MergedDictionaries>
sans oublier la déclaration du namespace shared :
xmlns:shared="clr-namespace:MergedDictionary"
Attention, dans la propriété Source du SharedResourceDictionary, « Color.xaml » ne fonctionne plus, il faut impérativement mettre « /MergedDictionary;Component/Color.xaml« .
Projet utilisé dans l’article : ici.
Code et explications originals : ici.
Le 24 avril Microsoft a officialisé la prochaine version de Windows Phone 7. Au programme : Internet explorer 9, multi-tâches, support des groupes, intégration du chat Facebook et environ 500 nouveautés. A noter que les développeurs auront accès à Mango dans moins d’un mois alors que le grand public aura cette mise à jour qu’à l’automne.
Petite nouveauté de Silverlight 5 plutôt intéressante bien que mineure : la possibilité de mettre un nom par défaut lorsque vous souhaitez sauvegarder un fichier grâce à un SaveFileDialog.
Avec l’annonce de Mango, Microsoft sort les outils de développement pour Mango. Il est maintenant possible d’utiliser le GPS ainsi que l’acceléromètre dans l’émulateur pour faciliter le développement de vos applications.
Toujours suite à l’annonce de Mango, Microsoft annonce une version web du MarketPlace avec la possibilité d’installer des applications via Email ou SMS. Cette nouveauté devrait sortie courant d’année 2011.
Par défaut il n’est pas possible de prendre une capture d’écran sur un Windows Phone 7. Cette fonctionnalité plutôt utile pour les auteurs de livres, pour publier vos applications sur le MarketPlace, etc. Voici un morceau de code permettant de faire des captures d’écran.
En Silverlight, la molette de la souris fonctionne très bien sous tous les principaux navigateurs (Internet Explorer, Firefox, Chrome, etc.) lorsque le Windowless n’est pas activé. Lorsque ce dernier est activé, la tâche se complique puisque les navigateurs qui se basent sur NAPI (Firefox et Chrome) ne permettent plus à Silverlight de gérer la molette de la souris. Cet article permet grâce au DOM de récupérer l’événement lié à la molette auprès du navigateur.
Cet article se base sur le code publié sur Compiled Experience auquel j’ai ajouté quelques modifications pour qu’il prenne en compte les éléments dérivant de ItemsControl.
Pour faciliter l’intégration de ce comportement dans votre application, nous allons créer un « Behavior » à ajouter à votre Listbox, Combobox, etc.
Le code se divise en 2 parties :
Le code « Helper »
public class MouseWheelEventArgs : EventArgs
{
public Point Location { get; private set; } public double Delta { get; private set; }
public bool Handled { get; set; }
public MouseWheelEventArgs(double delta)
: this(delta, new Point())
{
}
public MouseWheelEventArgs(double delta, Point location)
{
Delta = delta;
Location = location;
}
}
public class MouseWheelHelper
{
private static Worker MouseWheelWorker;
private bool isMouseOver;
public MouseWheelHelper(UIElement element)
{
if (MouseWheelWorker == null)
MouseWheelWorker = new Worker();
MouseWheelWorker.Moved += HandleMouseWheel;
element.MouseEnter += HandleMouseEnter;
element.MouseLeave += HandleMouseLeave;
element.MouseMove += HandleMouseMove;
}
public event EventHandler<MouseWheelEventArgs> Moved;
private void HandleMouseWheel(object sender, MouseWheelEventArgs args)
{
if (isMouseOver)
Moved(this, args);
}
private void HandleMouseEnter(object sender, EventArgs e)
{
isMouseOver = true;
}
private void HandleMouseLeave(object sender, EventArgs e)
{
isMouseOver = false;
}
private void HandleMouseMove(object sender, EventArgs e)
{
isMouseOver = true;
}
private class Worker
{
public Worker()
{
if (!HtmlPage.IsEnabled)
return;
HtmlPage.Window.AttachEvent("DOMMouseScroll", HandleMouseWheel);
HtmlPage.Window.AttachEvent("onmousewheel", HandleMouseWheel);
HtmlPage.Document.AttachEvent("onmousewheel", HandleMouseWheel);
}
public event EventHandler<MouseWheelEventArgs> Moved;
private void HandleMouseWheel(object sender, HtmlEventArgs args)
{
double delta = 0;
var eventObj = args.EventObject;
if (eventObj.GetProperty("wheelDelta") != null)
{
delta = ((double)eventObj.GetProperty("wheelDelta")) / 120;
if (HtmlPage.Window.GetProperty("opera") != null)
delta = -delta;
}
else if (eventObj.GetProperty("detail") != null)
{
delta = -((double)eventObj.GetProperty("detail")) / 3;
if (HtmlPage.BrowserInformation.UserAgent.IndexOf("Macintosh") != -1)
delta = delta * 3;
}
if (delta == 0 || Moved == null)
return;
var wheelArgs = new MouseWheelEventArgs(delta);
Moved(this, wheelArgs);
if (wheelArgs.Handled)
args.PreventDefault();
}
}
}
Pour récupérer ces informations on s’abonne aux événements JavaScript DOMMouseScroll pour Firefox et onmousewheel pour Chrome et les autres. Lorsque la molette est utilisée, les informations sont transmises du JavaScript vers le Silverlight.
Le code suivant représente le « Behavior » qui sera appliqué aux contrôles en XAML. Dans l’article de base, ce « Behavior » ne s’appliquait qu’à un ScrollViewer, ce qui rend l’utilisation de la molette sur un TreeView impossible. En appliquant ce « Behavior » sur n’importe quel UIElement, on permet ce comportement.
En revanche, pour bénéficier des propriétés VerticalOffset, HorizontalOffset et des méthodes ScrollToVerticalOffset et ScrollToHorizontalOffset, il nous faut un objet de type ScrollViewer. Heureusement, pour les contrôles dérivant de ItemsControl (TreeView, ListBox, etc.) le toolkit Silverlight fournit la méthode d’extension GetScrollHost() permettant de récupérer leurs objets ScrollViewer. N’oubliez pas d’ajouter une référence vers la DLL System.Windows.Controls.Toolkit à votre projet.
public class MouseWheelScrollBehavior : Behavior<UIElement>
{
private MouseWheelHelper helper;
protected override void OnAttached()
{
base.OnAttached();
helper = new MouseWheelHelper(AssociatedObject);
helper.Moved += OnMouseWheelMoved;
}
private void OnMouseWheelMoved(object sender, MouseWheelEventArgs e)
{
ScrollViewer sv;
double verticalOffset;
double horizontalOffset;
if (AssociatedObject is ItemsControl)
{
ItemsControl tr = AssociatedObject as ItemsControl;
sv = tr.GetScrollHost();
}
else
{
sv = AssociatedObject as ScrollViewer;
}
if (sv != null)
{
verticalOffset = sv.VerticalOffset;
horizontalOffset = sv.VerticalOffset;
sv.ScrollToVerticalOffset(sv.VerticalOffset + (e.Delta * -10));
sv.ScrollToHorizontalOffset(sv.HorizontalOffset + (e.Delta * -10));
}
}
protected override void OnDetaching()
{
base.OnDetaching();
helper.Moved -= OnMouseWheelMoved;
}
}
Une fois le « Behavior » créé, il faut l’appliquer à chaque ListBox, TreeView, ComboBox, etc… de l’application comme ceci :
<ListBox>
<i:Interaction.Behaviors>
<behaviors:MouseWheelScrollBehavior/>
</i:Interaction.Behaviors>
<!-- Code -->
</ListBox>
Où i représente System.Windows.Interactivity (n’oubliez pas d’ajouter la référence au projet) et behavior représente le namespace qui contient le « Behavior » créé ci-dessus.
Pour éviter cette tâche fastidieuse et sujette à l’erreur, la meilleure solution est de créer un style par défaut pour ces contrôles :
<Style TargetType="ListBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<ScrollViewer>
<i:Interaction.Behaviors>
<behaviors:MouseWheelScrollBehavior/>
</i:Interaction.Behaviors>
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Ainsi toutes les ListBox de l’application auront ce comportement.
Le projet utilisé pour cet article est disponible ici.
Vous souhaitez pouvoir adapter votre page à la taille de la fenêtre du navigateur en ajoutant ou supprimant des contrôles lors du changement de la taille ? Samuel Blanchard vous explique comment faire.
Silverlight permet de rendre vos applications web riches. Oren Gal propose un article expliquant la démarche pour proposer à l’utilisateur de trouver un itinéraire routier entre 2 points sur une carte.
Encore un lien sur les nouveautés de Silverlight 5, mais cette fois-ci en vidéo avec des démos pour chaque nouveauté (3D, Debug du XAML, etc.).
Que ce soit pour débugger ou analyser les performances de votre application Silverlight, SilverlightSpy est un outil qui fournit un ensemble d’informations sur l’application (Nombre de frames par seconde, événements déclenchés, arbre des contrôles, etc.). Seul inconvénient, ce logiciel est payant.
Microsoft a annoncé la date de la présentation de la 1re version majeure de Windows Phone 7, ce sera le 24 mai à New York. Au programme de cette mise à jour : multitâche, Bing Audio et bien d’autres.
Le fichier AppManifest.xaml est un fichier comportant les informations nécessaires au déploiement de l’application : la version de Silverlight nécessaire, le point d’entrée de l’application, les DLL externes, etc.
La gestion des ressources est un point crucial dans une application Silverlight. Ajouter directement les ressources (images par exemple) dans le fichier XAP, charger les ressources à la demande pour réduire la taille du XAP ou encore les placer dans une assembly, à vous de choisir.
INotifyPropertyChanged est une interface très importante dans la mise en place du MVVM. Son but est de notifier la vue (View) qu’une propriété a été changée dans le ViewModel. Si vous utilisez des outils comme MVVM Light, cette implémentation est déjà faite pour vous. Si vous souhaitez l’implémenter par vous-même, voici un exemple.
Kevin Dockx a commencé une série d’articles sur l’utilisation des collections avec WCF RIA Services.