2010-03-26 13 views
10

Problème
J'ai un contrôle onglet personnalisé à l'aide des onglets en forme de Chrome qui se fixe à un ViewModel. En raison de la forme, les bords se chevauchent un peu. J'ai une fonction qui définit le ZIndex de tabItem sur TabControl_SelectionChanged qui fonctionne très bien pour sélectionner les onglets, et glisser/déposer des onglets, cependant quand j'ajoute ou ferme un onglet via une commande de relais, je reçois des résultats inhabituels. Quelqu'un a-t-il une idée?WPF - CUMUL onglets personnalisés dans un TabControl et ZIndex

vue par défaut:

Retrait Tabs:

Ajout de 2 ou plusieurs onglets dans une rangée:

L'ajout de plus d'un onglet à la fois ne réinitialisera pas le zindex des autres onglets ajoutés récemment pour qu'ils passent derrière l'onglet de droite, et les onglets de fermeture ne rendront pas correctement l'index ZIndex de SelectedTab qui le remplace et apparaîtra derrière le onglet sur sa droite.

code pour définir ZIndex

private void PrimaryTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e) 
    { 
     if (e.Source is TabControl) 
     { 
      TabControl tabControl = sender as TabControl; 
      ItemContainerGenerator icg = tabControl.ItemContainerGenerator; 
      if (icg.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated) 
      { 
       foreach (object o in tabControl.Items) 
       { 
        UIElement tabItem = icg.ContainerFromItem(o) as UIElement; 
        Panel.SetZIndex(tabItem, (o == tabControl.SelectedItem ? 100 : 
         90 - tabControl.Items.IndexOf(o))); 
       } 
      } 
     } 
    } 

En utilisant des points d'arrêt que je peux voir qu'il est le réglage correct du ZIndex à ce que je veux qu'il, mais la mise en page ne montre pas les changements. Je sais que certains des changements sont en vigueur parce que si aucun d'entre eux ne fonctionnait, alors les bords des onglets seraient inversés (les onglets de droite seraient dessinés en haut de ceux de gauche). Cliquer sur un onglet définira correctement le zindex de tous les onglets (y compris celui qui doit être dessiné sur le dessus) et les faire glisser/les déposer pour les réorganiser rendra également les rendus correctement (ce qui supprime et réinsère l'élément tabulation). La seule différence que je peux penser est que j'utilise le modèle de conception MVVM et les boutons que les onglets Ajouter/Fermer sont des commandes de relais.

Quelqu'un at-il une idée de pourquoi cela se produit et comment je peux le réparer?

p.s. J'ai essayé de définir un ZIndex dans mon ViewModel et de le lier, cependant la même chose se produit lors de l'ajout/suppression de tabulations via la commande de relais.

+0

Je pense maintenant que c'est un problème avec WPF et pas mon code. J'ai ajouté un bouton qui affiche le zIndex des éléments de l'onglet après qu'ils ont été dessinés et le zIndex est définitivement correct sur chacun d'eux, ils ne sont pas dessinés correctement. – Rachel

+0

Salut à tous. Donner des onglets dans un TabControl la forme trapézoïdale de type Chrome et de les amener à se comporter correctement semble en effet être assez difficile dans WPF. Y a-t-il une chance de partager le XAML que vous avez utilisé pour le gabarit? J'ai une solution ici, mais ce n'est pas particulièrement élégant - curieux de savoir ce que vous avez fait! À votre santé. – Noldorin

+0

Bien sûr, le code est un peu long à poster ici mais je vais essayer de l'expliquer. Si vous avez des questions n'hésitez pas à m'envoyer un courriel à [email protected] Chaque onglet est en fait une grille avec 3 cellules - Gauche et droite contiennent un chemin qui dessine la courbe et le milieu contient les données. Chaque grille a une marge négative définie pour les faire se chevaucher, et le zIndex est utilisé pour définir lequel devrait être en haut. Je ne sais pas si vous ajoutez un glisser/déposer ou un défilement, mais dans mon cas, tous les onglets sont contenus dans un ScrollViewer pour le défilement et le DragDrop est une propriété jointe pour ItemsControl du TabControl. – Rachel

Répondre

5

Merci Abe, votre deuxième commentaire m'amène à ma solution! J'ai ajouté tabItem.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate); à chaque itération de la boucle.

Je serais toujours intéressé d'apprendre si quelqu'un d'autre trouve un moyen de contourner cela sans rafraîchir chaque tabItem à chaque changement. J'ai essayé d'actualiser tout le contrôle onglet à la fin de la boucle, mais cela ne fonctionnait que pour la fermeture des onglets, ne pas les ajouter. Je sais que Panel.ZIndex est réglé correctement, il ne respecte pas cette propriété lors du rendu.

EDIT: La ligne de code ci-dessus provoquait un scintillement inhabituel lorsque vous glissiez/supprimiez les onglets qui montraient brièvement l'onglet derrière celui qui était déplacé. J'ai déplacé le code vers une fonction séparée et l'ai appelée à une priorité inférieure du répartiteur, ce qui a corrigé le problème. Le code final est ci-dessous:

private void PrimaryTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e) 
    { 
     if (e.Source is TabControl) 
     { 
      TabControl tabControl = sender as TabControl; 

      tabControl.Dispatcher.BeginInvoke(
       new Action(() => UpdateZIndex(sender as TabControl)), 
       DispatcherPriority.Background); 
     } 
    } 

    private void UpdateZIndex(TabControl tabControl) 
    { 
     ItemContainerGenerator icg = tabControl.ItemContainerGenerator; 

     if (icg.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated) 
     { 
      foreach (object o in tabControl.Items) 
      { 
       UIElement tabItem = icg.ContainerFromItem(o) as UIElement; 
       if (tabItem != null) 
       { 
        // Set ZIndex 
        Panel.SetZIndex(tabItem, (o == tabControl.SelectedItem ? 100 : 
         90 - tabControl.Items.IndexOf(o))); 
       } 
      } 
     } 
    } 
+1

La mise à niveau vers .Net 4.0 a également résolu le problème – Rachel

1

Il semble que vous ayez juste besoin de relancer votre algorithme lorsque la collection change. Puisque vous testez la propriété ItemContainerGenerator.Status, l'algorithme peut ne pas s'exécuter. Vous souhaiterez peut-être envisager d'écouter l'événement StatusChanged et, lorsqu'il sera remplacé par ContainersGenerated, réexécuter l'algorithme.

+0

J'ai essayé ceci et il n'apparaît toujours pas correctement. En outre, l'événement ItemContainerGenerator.StatusChanged s'exécute avant que l'élément SelectedItem soit mis à jour. Par conséquent, le paramètre définit toujours l'onglet sélectionné sur le dernier onglet sélectionné. En utilisant des points d'arrêt, je peux voir que le Panel.ZIndex est définitivement réglé correctement lors de l'ajout/suppression de tabulations en utilisant l'événement TabControl_SelectionChanged, mais il ne les dessine pas comme il le devrait. Pour moi, cela n'a aucun sens puisque les onglets Glisser/Déposer (qui supprime et ajoute à nouveau le tabItem) dessinent correctement l'ordre des onglets. – Rachel

+2

Oui, c'est étrange. La seule différence que je peux penser entre d & d et juste ajouter un nouvel élément serait le moment où l'algorithme z-index est exécuté. Peut-être l'invoquer sur le Dispatcher à une priorité inférieure (comme DispatcherPriority.Input) serait une solution (hacky). –