2011-04-01 2 views
-1

J'ai écrit mon propre "Multiselect" -Treeview. Il utilise une interface ITreeViewItem pour tous les éléments de la base de données. Cette arborescence ne fonctionnera pas sans que les éléments liés implémentent cette interface.WPF Multiselect TreeView - presque entièrement implémenté, mais

public interface ITreeViewItem : INotifyPropertyChanged 
{ 
    bool IsExpanded { get; set; } 
    bool IsSelected { get; set; } 
    bool IsEnabled { get; set; } 
    Visibility Visibility { get; set; } 

    ITreeViewItem GetParentTreeViewItem(); 
} 

Ceci est ma première question: Quelqu'un a une idée de comment ne pas forcer la mise en œuvre de cette interface? Cela ne semble pas être propre. La principale raison pour laquelle j'ai décidé d'implémenter cette interface est qu'il semble impossible de trouver le ItemsControl correspondant à l'élément de données sans (dans le pire des cas) l'expansion de l'arbre entier. Ce qui dans le cas de données chargées paresseuses entraînera le chargement de toutes ces données.

Une autre raison est la coloration de l'arrière-plan des éléments sélectionnés. Si je veux mettre en évidence l'arrière-plan, j'ai besoin de certaines propriétés à lier. Un déclencheur normal sur IsSelected ne semble pas fonctionner car seul un élément à la fois peut avoir cette valeur définie (ou?). WPF TreeViewItem Background Le problème avec la réponse donnée est, que le modèle par défaut n'a pas les couleurs par défaut (au premier clic sur un élément la couleur sera violette, à la seconde il sera bleu , ce qui est un autre problème). Alors, comment puis-je régler la couleur de tous les éléments sélectionnés sur gris clair ou de l'élément réellement mis au point sur bleu après que l'arborescence ait perdu son focus?

Si vous avez besoin de plus d'informations sur ma mise en œuvre, n'hésitez pas à demander.

EDIT: @Snowbear JIM compilateur:

internal static bool ExecuteOnTreeViewItem(this TreeView tree, ITreeViewItem dataItem, Action<TreeViewItem> action) 
    { 
     Stack<ITreeViewItem> pathToRoot = GetPathToRoot(dataItem); 
     TreeViewItem firstVisibleItem = tree.SyncTreeLevels(pathToRoot); 

     if (firstVisibleItem == null) 
      return false; // can't find any item in path which is currently selectable 

     if (pathToRoot.Count != 0) // expand the first item if nextLevelItems will follow 
      firstVisibleItem.IsExpanded = true; 

     ExecuteOnTreeViewItem(tree, pathToRoot, action, firstVisibleItem); 
     return true; 
    } 

    private static void ExecuteOnTreeViewItem(TreeView tree, Stack<ITreeViewItem> pathToRoot, Action<TreeViewItem> action, TreeViewItem treeViewItem) 
    { 
     if (pathToRoot.Count == 0) 
     { 
      action(treeViewItem); 
      return; 
     } 

     var nextLevelItem = pathToRoot.Pop(); 

     if (pathToRoot.Count != 0) 
      nextLevelItem.IsExpanded = true; // make children visible to create the item containers 

     if (treeViewItem.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) 
     { 
      EventHandler eventHandler = null; 

      if (!treeViewItem.IsExpanded) 
       treeViewItem.IsExpanded = true; 

      eventHandler = delegate 
      { 
       if (treeViewItem.ItemContainerGenerator.Status == GeneratorStatus.Error 
        || treeViewItem.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) 
        treeViewItem.ItemContainerGenerator.StatusChanged -= eventHandler; 

       if (treeViewItem.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) 
       { 
        var nextTreeViewItem = treeViewItem.ItemContainerGenerator.ContainerFromItem(nextLevelItem) as TreeViewItem; 
        ExecuteOnTreeViewItem(tree, pathToRoot, action, nextTreeViewItem); 
       } 
      }; 

      treeViewItem.ItemContainerGenerator.StatusChanged += eventHandler; 
     } 
     else 
     { 
      var nextTreeViewItem = treeViewItem.ItemContainerGenerator.ContainerFromItem(nextLevelItem) as TreeViewItem; 
      ExecuteOnTreeViewItem(tree, pathToRoot, action, nextTreeViewItem); 
     } 
    } 

    internal static Stack<ITreeViewItem> GetPathToRoot(ITreeViewItem item) 
    { 
     Stack<ITreeViewItem> items = new Stack<ITreeViewItem>(); 
     ITreeViewItem parent = item; 

     while (parent != null) 
     { 
      items.Push(parent); 
      parent = parent.GetParentTreeViewItem(); 
     } 

     return items; 
    } 

    /// <summary> 
    /// Returns the first item in stack which is visible in tree view. This is used 
    /// for the case that the first Item of ITreeViewItem is not the first bound item 
    /// </summary> 
    /// <param name="tree"></param> 
    /// <param name="hierachyItems"></param> 
    /// <returns></returns> 
    internal static TreeViewItem SyncTreeLevels(this TreeView tree, Stack<ITreeViewItem> hierachyItems) 
    { 
     TreeViewItem item = null; 

     while (item == null && hierachyItems.Count > 0) 
      item = tree.ItemContainerGenerator.ContainerFromItem(hierachyItems.Pop()) as TreeViewItem; 

     return item; 
    } 

La mauvaise chose au sujet de ce code est que l'action est exécutée seulement après avoir quitté l'événement d'appel dans le TreeViewItem hérité ... mais il fonctionne.

+0

Vous pouvez vérifier le type de ItemSource et lancer une exception si le type est incorrect. À propos de la deuxième - n'utilisez pas la propriété IsSelected de la classe ListViewItem, essayez d'utiliser des événements et définissez d'une manière ou d'une autre la propriété IsSelected d'un élément. – vorrtex

+0

J'ai créé une arborescence multisélectable en utilisant des idées comme la vôtre. Vérifiez-le ici: http://stackoverflow.com/a/13412801/166452 –

Répondre

0

probablement il ne sera pas répondre à toutes vos questions, mais encore:

qu'il semble impossible de trouver le correspondant ItemsControl à l'élément de données sans (dans le pire des cas) l'élargissement arbre entier

Pour autant que j'ai eu votre intention ItemContainerGenerator.ContainerFromItem devrait vous aider. Il renvoie TreeViewItem pour une donnée donnée.

+0

Oui, j'utilise déjà ces fonctions. Le proplem est que chaque TreeViewItem est un ItemContainer. ItemContainerGenerator.ContainerFromItem renvoie uniquement les éléments du premier niveau. Donc, vous devez faire quelque chose comme je vais mettre à jour dans le post ci-dessus – SACO