Un nouvel espace dédié au designer de Visual Studio 2010 pour WPF et Silverlight vient d'ouvrir ses portes.
On pourra y trouver des resources (blog, articles, webcast) sur tout ce qui concerne WPF et Silverlight dans Visual Studio 2010.
Hop ! Ca se passe par là : http://windowsclient.net/wpfdesigner/
Voici une des démonstrations que j'avais présenté à la BDC 2008 afin de démystifier Blend. L'objectif était de transformer une barre de progression en jauge, tout ça en 10 minutes.
Pour réaliser cette démonstration, j'ai "simplement" créé un ControlTemplate avec Blend que j'ai ensuite appliquer à la barre de progression.
L'image ci-dessous présente la barre de progression avant et après :
Télécharger le code source
A noter que le format écrit n'est pas le meilleur format : un WebCast devrait arriver pour présenter la solution sous Blend. Mes explications ici seront assez expéditives :)
Visualiser l'utilisation du CPU
Pour rendre la barre de progression un peu plus dynamique, elle affiche l'utilisation du CPU. J'ai donc créé une DependencyProperty, nommée CpuUsage, au niveau de la fenêtre :
public int CpuUsage
{
get { return (int)GetValue(CpuUsageProperty); }
set { SetValue(CpuUsageProperty, value); }
}
public static readonly DependencyProperty CpuUsageProperty = DependencyProperty.Register("CpuUsage", typeof(int), typeof(SolutionWindow), new UIPropertyMetadata((int)0));
Et "bindée" cette propriété au niveau d'une ProgressBar :
<ProgressBar Value="{Binding Path=CpuUsage, ElementName=Window}" />
Il ne reste plus qu'à utiliser la classe PerformanceCounter pour récupérer le pourcentage d'utilisation du CPU, et d'un DispatcherTimer afin de mettre à jour la valeur de la propriété CpuUsage :
private void CreatePerformanceCounter()
{
cpuCounter = new PerformanceCounter();
cpuCounter.CategoryName = "Processor";
cpuCounter.CounterName = "% Processor Time";
cpuCounter.InstanceName = "_Total";
timer = new DispatcherTimer();
timer.Tick += OnTimerTicker;
timer.Interval = TimeSpan.FromMilliseconds(1000);
timer.Start();
}
private void OnTimerTicker(Object sender, EventArgs e)
{
this.CpuUsage = (int)cpuCounter.NextValue();
}
A ce stade, nous avons une barre de progression qui affiche l'utilisation du CPU.
Notez que dans la solution téléchargeable j'ai utilisé une animation afin de rendre le changement plus progressif et fluide.
Créer le Template
Le Template va permettre de tranformer la barre de progression en jauge, et notamment d'afficher la valeur de la ProgressBar sous la forme d'une aiguille qui tourne. La contrainte étant de pouvoir recréer ce Template en 5 minutes.
La première chose est de réfléchir à la structure graphique de la jauge, la figure ci-dessous montre le découpage de la jauge :

Dans cette jauge, il y a deux choses qui ne peuvent pas (enfin ci ... mais bon) être faites avec Blend :
- Les graduations. Pour afficher les graduations, j'ai créé un petit contrôle qui affiche des rectangles à intervalles réguliers.
- La conversation de l'utilisation du CPU exprimée en pourcentage en une valeur angulaire. Ceci sera fait par un Converter nommé PercentageConverter.
Vous pourrez retrouver ces deux éléments au niveau de la solution.
Pour le reste, il suffit d'ouvrir Blend, de créer un ControlTemplate vierge, et de superposer les différents éléments graphiques identifiables dans la figure précédentes à savoir :
- Un effet chromé : une ellipse avec un RadialGradientBrush
- Un fond noir : une ellipse avec un LineraGradientBrush
- Les graduations : le contrôle de graduations
- L'aiguille qui tournera : un rectangle
- Le centre de l'aiguille : encore une ellipse
- Et enfin le petit reflet : perso j'ai utilisé un Path
La subtilité de ce Template réside au niveau de l'aiguille qui doit :
- Etre centrée au milieu de la jauge
- Tourner à sa base en fonction de la propriété Value de la ProgressBar.
Pour résoudre la première problématique, on peut utiliser une grille avec deux lignes et positionnée l'aiguille dans la première ligne et centrée horizontalement au milieu (c'est bien plus simple à comprendre sous Blend) :
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.5*"/>
<RowDefinition Height="0.5*"/>
</Grid.RowDefinitions>
<Rectangle Width="3" HorizontalAlignment="Center" />
</Grid>
Pour le problème de rotation de l'aiguille, il suffit d'utiliser une RotateTransform sur le rectangle. On n'oublie pas de modifier la propriété RenderTransformOrigin afin que la rotation se fasse à la base du rectangle :
<Rectangle RenderTransformOrigin="0.5,1" Width="3" HorizontalAlignment="Center" >
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform>
<RotateTransform.Angle>
<Binding Path="Value" RelativeSource="{RelativeSource TemplatedParent}">
<Binding.Converter>
<converters:PercentageConverter/>
</Binding.Converter>
</Binding>
</RotateTransform.Angle>
</RotateTransform>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
Enfin, on utilise une expression de Binding. Cette expression permet de lier la valeur de la propriété Value de la ProgressBar (qui représente le pourcentage CPU) à l'angle de la rotation de l'aiguille.
On n'utilise le PercentageConverter décrit plus haut afin de transformer la valeur d'utilisation CPU en valeur angulaire.
Enjoy !!
La classe FlowDocument vous permet d'afficher n'importe quel type de contenu : texte, image, lien hypertexte, etc., extrêmement pratique ! Toutefois, elle présente un léger problème lorsqu'on utilise un HyperLink.
Exemple : le code ci-dessous permet d'afficher le texte "Voici un lien" :
1: <FlowDocument>
2: <Paragraph>
3: <Run>Voici un</Run>
4: <Hyperlink Command="local:Window1.MaCommande">lien</Hyperlink>
5: </Paragraph>
6: </FlowDocument>
1: public partial class Window1 : Window
2: { 3: public static readonly RoutedCommand MaCommande = new RoutedCommand("MaCommande", typeof(Window1)); 4: ...
5: }
La particularité de ce code est la présence d'une commande au niveau de l'HyperLink.
Si vous exécutez ce code, que vous sélectionnez tout le texte et faites un Copier (ou un Drag), vous allez avoir une belle "NotSupportedException" : 'CommandConverter' is unable to convert 'System.Windows.Input.RoutedCommand' to 'System.String'.
Comme le souligne le texte de l'Exception, WPF n'arrive pas à convertir une Command en String. En utilisant Reflector, on peut s'apercevoir que le code de la méthode ConvertTo de la classe CommandConverter n'autorise que la conversion des commandes fournies par WPF !
La solution la plus simple pour contourner le problème est donc d'utiliser une des commandes de WPF:
- Dans la classe System.Windows.Input.ApplicationCommands
- Ou bien System.Windows.Input.NavigationCommands
Exemple :
1: <Hyperlink Command="input:NavigationCommande.GoToPage">lien</Hyperlink>
D'autres solutions existent, comme recréér la classe HyperLink ou ne pas utiliser les Commandes, mais j'avoue que j'aime bien la solution facile :p
Si comme beaucoup de gens vous vous posez encore la question "WCF est-il plus performant que Remoting (ou WSE) ?" - oui il y a des gens qui se posent encore la question - je vous conseille d'aller voir ce petit comparatif de l'MSDN :
http://msdn.microsoft.com/en-us/library/bb310550.aspx
Rien de plus simple en WPF, je pense que le code ci-dessous se passe de commentaire :
1: private void Screenshoot(FrameworkElement element, String filename)
2: { 3: RenderTargetBitmap targetBitmap = new RenderTargetBitmap((int)element.ActualWidth,
4: (int)element.ActualHeight,
5: 96d,
6: 96d,
7: PixelFormats.Default);
8:
9: targetBitmap.Render(element);
10:
11: JpegBitmapEncoder encoder = new JpegBitmapEncoder();
12: encoder.Frames.Add(BitmapFrame.Create(targetBitmap));
13:
14: using (FileStream stream = File.Open(filename, FileMode.Create))
15: { 16: encoder.Save(stream);
17: }
18: }
Ce qu'il y a de bien avec WPF, c'est toutes ces petites choses qu'on trouve coder en dur.
Prenez par exemple cette ListView avec un joli rectangle rouge de 20*20
<ListView DataContext="{StaticResource ds}" ItemsSource="{Binding}">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Width="20" Header="">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Rectangle Width="20" Height="20" Fill="Red" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
...
Si on exécute une telle ListView, on se rend compte que notre joli rectangle ne fait que 8 pixels de largeur !
Après avoir chercher dans tous les ControlTemplate où pouvait bien se cacher ce ?*ù!$ padding, j'ai démarré mon outil préféré : Reflector.
Et voila ce que j'y ai trouvé dans la classe GridViewRowPresenter :
_defalutCellMargin = new Thickness(6.0, 0.0, 6.0, 0.0);
Hop, je sors ma calculatrice : 8 + 6 + 6 = 20 ... tiens drôle de coïncidence !? Malheureusement le hasard n’y est pour rien, la valeur de padding est bel et bien fixée en dur dans le code.
Il n’y a aucun moyen de modifier cette valeur proprement, une solution pour contourner le problème est d’affecter une marge de (-6,0,-6,0) au CellTemplate de la colonne, et hop le tour est joué !
<DataTemplate>
<Rectangle Margin="-6,0,-6,0" Width="20" Height="20" Fill="Red" />
</DataTemplate>
Les 10 derniers blogs postés
-
[Refactoring] ReSharper pour Visual Studio 2010 (Preview) par
Thomas Jaskula le il y a 1 heure et 38 minutes
-
[Refactoring] Analyser vos exceptions avec ReSharper Exceptional par
Thomas Jaskula le il y a 2 heures et 52 minutes
-
SharePoint 2007 : patterns & practices SharePoint Guidance par
Philippe Sentenac [MVP SharePoint] le il y a 16 heures et 31 minutes
-
[Visual Studio 2010] Les tests cases c’est bien, mais je vais devoir tout réécrire ? par
Etienne Margraff le il y a 17 heures et 28 minutes
-
MVP[Gribouillon].AddYear par
The Grib's Lair [Sébastien PICAMELOT - MVP SharePoint] le il y a 17 heures et 43 minutes
-
Clinique INSIA - Projet de fin d’Etudes (Silverlight 3 MVVM et OutOfBrowser, WCF, TFS) - Part 1 par
David REI le 07-02-2009, 23:38
-
C’est la crise ? Bah pourquoi cramer du budget pub alors ? par
Nix's Blog le 07-02-2009, 15:31
-
Soyons MVP ! par
TheSaib .NET blog le 07-02-2009, 12:15
-
SharePoint : Gestion des Erreurs 6398, 7076 et 6482 par
Blog Technique de Romelard Fabrice le 07-02-2009, 11:53
-
EF avec WPF par
Matthieu MEZIL le 07-02-2009, 10:18