Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Aurélien GALTIER

The dark world of .Net

TabControl, Surface et look Metro

Avec l'apparition du SDK Surface 2, je me suis demandé ce que je pouvais réaliser. Parallèlement j'ai un projet personnel NeoBD qui me permet de gérer ma collection de B.D. Pour donner le contexte, ce projet utilise une base centralisée exposée par des services WCF.

Après le téléchargement du SDK je me suis posé la question : Qu'est ce que je peux faire ? Alors j'ai créé une petite maquette rapide et voilà le résultat :

image

La maquette n'est pas complète et ne représente pas le fonctionnement entier de l'application. Mais j'ai décidé de faire un menu de taille fixe sur la gauche et un espace libre qui prend le reste de l'écran. Le but, pouvoir glisser plusieurs fiches de Séries. Je vais parler dans ce poste du menu de gauche où j'utilise un TabControl pour gérer les modules "mes B.D.", "manquantes", "menu".

Comment faire pour customiser le TabControl pour qu'il ressemble au menu de gauche.

Son utilisation :

  1:         <TabControl x:Name="tabcontrol">
  2:             <TabItem Header="mes B.D.">
  3:             </TabItem>
  4:             <TabItem Header="manquantes">
  5:             </TabItem>
  6:             <TabItem Header="menu">
  7:             </TabItem>
  8:         </TabControl>

On vas changer le template du TabControl :

  1:         <Style TargetType="TabControl">
  2:             <Setter Property="Template">
  3:                 <Setter.Value>
  4:                     <ControlTemplate TargetType="TabControl">
  5:                         <Grid KeyboardNavigation.TabNavigation="Local">
  6:                             <mesbdControl:MenuTabPanel  
  7:                             Panel.ZIndex="1"
  8:                             Margin="0,0,4,-1"
  9:                             IsItemsHost="True"
 10:                             KeyboardNavigation.TabIndex="1"
 11:                             Background="Transparent" />
 12:                             <Border Margin="70,75,0,0" Panel.ZIndex="100">
 13:                                 <ContentPresenter
 14:                             x:Name="PART_SelectedContentHost"
 15:                               Margin="4"
 16:                               ContentSource="SelectedContent" />
 17:                             </Border>
 18:                         </Grid>
 19:                     </ControlTemplate>
 20:                 </Setter.Value>
 21:             </Setter>
 22:         </Style>
 23: 

Dans ce template j'ai ajouté un mesbdControl:MenuTabPanel qui est un contrôle héritant du MenuTabPanel de WPF. Et j'ai ajouté un ContentPresenter pour le contenu du TabItem.

Que fait le contrôle MenuTabPanel ? Il place les onglets. Ce contrôle ressemble à un contrôle Panel, j'ai donc override les méthodes "MeasureOverride" et "ArrangeOverride"

  1:         protected override Size MeasureOverride(Size constraint)
  2:         {
  3:             double height = 50;
  4:             foreach (UIElement item in this.Children)
  5:             {
  6:                 item.Measure(constraint);
  7:  
  8:                 if (item is TabItem)
  9:                 {
 10:                     var tabItem = item as TabItem;
 11:                     if (!tabItem.IsSelected)
 12:                     {
 13:                         height += tabItem.DesiredSize.Width;
 14:                     }
 15:                 }
 16:             }
 17:             constraint.Height = height;
 18:             return constraint;
 19:         }

MeasureOverride : Je calcul la hauteur nécessaire a l'affichage correcte des éléments.

  1:         protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize)
  2:         {
  3:             double height = 75;
  4:  
  5:             foreach (var item in this.Children)
  6:             {
  7:                 if (item is TabItem)
  8:                 {
  9:                     var tabItem = item as TabItem;
 10:                     if (tabItem.IsSelected)
 11:                     {
 12:                         tabItem.Arrange(new Rect(new Size(this.DesiredSize.Width, 50)));
 13:                         Animate(tabItem, 50, 0, 0);
 14:                     }
 15:                     else
 16:                     {
 17:                         height += tabItem.DesiredSize.Width;
 18:                         tabItem.Arrange(new Rect(new Size(tabItem.DesiredSize.Width, 50)));
 19:                         Animate(tabItem, 0, height, -90);
 20:                     }
 21:                 }
 22:             }
 23:  
 24:             return arrangeSize;
 25:         }

ArrangeOverride : Je place et modifie l'angle des TabItem correctement. Pour cela lorsque le TabItem est sélectionné je mets un angle de 0 (Ce qui correspond à un affichage horizontal) et je mets la position sur une valeur fixe x:50 et y:0.

Pour tous les autres TabItem je mets un angle de -90 (Ce qui correspond à un affichage vertical) et je mets la position sur une valeur variable en hauteur calculée sur la somme des largeurs des TabItem non sélectionnées.

La méthode Animate va ajouter une petite animation des TabItems ce qui va donner un effet de changement.

  1:         private void Animate(TabItem item, double x, double y, double angle)
  2:         {
  3:             DoubleAnimation animAngle = new DoubleAnimation(angle, new Duration(TimeSpan.FromMilliseconds(500)));
  4:             DoubleAnimation animX = new DoubleAnimation(x, new Duration(TimeSpan.FromMilliseconds(500)));
  5:             DoubleAnimation animY = new DoubleAnimation(y, new Duration(TimeSpan.FromMilliseconds(500)));
  6:  
  7:             TransformGroup tc = null;
  8:             RotateTransform rt = null;
  9:             TranslateTransform tt = null;
 10:  
 11:             if (item.RenderTransform is TransformGroup)
 12:             {
 13:                 tc = item.RenderTransform as TransformGroup;
 14:                 rt = tc.Children[0] as RotateTransform;
 15:                 tt = tc.Children[1] as TranslateTransform;
 16:             }
 17:             else
 18:  
 19:             {
 20:                 tc = new TransformGroup();
 21:                 rt = new RotateTransform();
 22:                 tt = new TranslateTransform();
 23:                 tc.Children.Add(rt);
 24:                 tc.Children.Add(tt);
 25:                 item.RenderTransform = tc;
 26:             }
 27:  
 28:             tt.BeginAnimation(TranslateTransform.XProperty, animX);
 29:             tt.BeginAnimation(TranslateTransform.YProperty, animY);
 30:             rt.BeginAnimation(RotateTransform.AngleProperty, animAngle);
 31:         }

En une demie seconde je vais faire un déplacement et une rotation de mon TabItem. Attention si les transformations existent déjà je les réutilise, si non votre control ne s'animera pas de son emplacement précédent.

Il reste a modifier le template du TabItem :

  1:         <Style TargetType="TabItem">
  2:             <Setter Property="Template">
  3:                 <Setter.Value>
  4:                     <ControlTemplate TargetType="TabItem">
  5:                         <Grid>
  6:                             <VisualStateManager.VisualStateGroups>
  7:                                 <VisualStateGroup x:Name="SelectionStates">
  8:                                     <VisualState x:Name="Unselected">
  9:                                     </VisualState>
 10:                                     <VisualState x:Name="Selected">
 11:                                     </VisualState>
 12:                                 </VisualStateGroup>
 13:                                 <VisualStateGroup x:Name="CommonStates">
 14:                                     <VisualState x:Name="Normal" />
 15:                                     <VisualState x:Name="MouseOver" />
 16:                                     <VisualState x:Name="Disabled">
 17:                                     </VisualState>
 18:                                 </VisualStateGroup>
 19:                             </VisualStateManager.VisualStateGroups>
 20:                             <TextBlock FontSize="40" FontFamily="Segoe WP"
 21:                                 VerticalAlignment="Top"
 22:                                 HorizontalAlignment="Left" Foreground="White">
 23:                             <ContentPresenter x:Name="ContentSite"
 24:                                               ContentSource="Header"
 25:                                               Margin="20,0,0,0"
 26:                                 RecognizesAccessKey="True" />
 27:                             </TextBlock>
 28:                         </Grid>
 29:                         <ControlTemplate.Triggers>
 30:                             <Trigger Property="IsSelected" Value="True">
 31:                                 <Setter Property="Panel.ZIndex" Value="100" />
 32:                             </Trigger>
 33:                         </ControlTemplate.Triggers>
 34:                     </ControlTemplate>
 35:                 </Setter.Value>
 36:             </Setter>
 37:         </Style>
 38: 

Et pour le moment j'obtiens quelque chose qui ressemble à cela :

image
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 :
Posted: mercredi 20 juillet 2011 22:27 par agaltier
Classé sous : , ,

Commentaires

azra a dit :

Sympa le look!

Si tu veux une BDD assez complète de BD attaquable, fais moi signe =&gt; j'ai un projet similaire avec une bonne communauté derrière :)

# juillet 20, 2011 22:37

agaltier a dit :

Merci pour la proposition. Je reparlerais du projet (Version Web, Version WP7, ...) dans de future post pour le moment j'utilise l'API d'Amazon ;)

# juillet 21, 2011 06:41

NeuroCypher a dit :

@aurelien: tu comptes faire partager ?

@azra: j'etais sur que t'allais poster !!!

# juillet 21, 2011 23:07

azra a dit :

@NeuroCypher: c'est clair, j'ai passé 10min a chercher mon mot de passe codes sources pour poster :p

# juillet 22, 2011 08:58

agaltier a dit :

@NeuroCypher : Oui je compte faire partager au moins les applications. Je réfléchis encore pour le moment si j'ouvre les sources.

# juillet 25, 2011 10:22

azra a dit :

"Mode lourd": Si tu veux, j'ai une communauté pour brander et pousser tes applications :P

# juillet 25, 2011 15:53
Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- ProcDump 6.0 : support du filtrage sur messages d'exceptions .NET, des filtres multiples et du ciblage par nom de service par CoqBlog le il y a 7 heures et 59 minutes

- Votez pour le TOP 10 des influenceurs SharePoint francophones ! par Le blog de Patrick [MVP SharePoint] le il y a 9 heures et 51 minutes

- [Conf’SharePoint] Dernier rappel ! :-) par Le blog de Patrick [MVP SharePoint] le il y a 13 heures et 41 minutes

- [ #SharePoint 2013 ] les modèles de sites standards… par Le blog de Patrick [MVP SharePoint] le il y a 13 heures et 47 minutes

- 10 erreurs de compréhension concernant SharePoint… par Le blog de Patrick [MVP SharePoint] le il y a 14 heures et 23 minutes

- Conf’SharePoint : 10 bonnes raisons pour ne pas la rater par Le petit blog de Pierre / Pierre's little blog le 05-14-2013, 02:24

- [Event] Soirée de lancement Agile .NET France à Lyon par Blog Agile/ALM de Vincent THAVONEKHAM le 05-13-2013, 01:29

- .NET / Debug : inspection de la mémoire d'applications .NET (dump ou processus live) : première livraison d'une librairie .NET par Microsoft par CoqBlog le 05-11-2013, 22:21

- SharePoint : Incompatibilité avec Internet Explorer 10 (IE10) par Blog Technique de Romelard Fabrice le 05-08-2013, 16:29

- AutoSPInstaller pour SharePoint 2013 maintenant disponible en “RTM” par Julien Chable le 05-06-2013, 23:30