2009-08-21 10 views
15

que tienen una vista de árbol hipotéticos que contiene estos datos:Cómo filtrar una jerarquía de vista jerárquica wpf usando un ICollectionView?

RootNode 
    Leaf 
    vein 
SecondRoot 
    seeds 
    flowers 

Estoy tratando de filtrar los nodos con el fin de mostrar sólo los nodos que contienen un determinado texto. Digamos que si especifico "L", el árbol será filtrado y mostrará solo RootNode-> Leaf y SecondRoot-> flowers (porque ambos contienen la letra L).

Siguiendo el modelo MV-VM, que tienen una clase básica TreeViewViewModel así:

public class ToolboxViewModel 
{ 
    ... 
    readonly ObservableCollection<TreeViewItemViewModel> _treeViewItems = new ObservableCollection<TreeViewItemViewModel>(); 
    public ObservableCollection<TreeViewItemViewModel> Headers 
    { 
     get { return _treeViewItems; } 
    } 

    private string _filterText; 
    public string FilterText 
    { 
     get { return _filterText; } 
     set 
     { 
      if (value == _filterText) 
       return; 

      _filterText = value; 

      ICollectionView view = CollectionViewSource.GetDefaultView(Headers); 
      view.Filter = obj => ((TreeViewItemViewModel)obj).ShowNode(_filterText); 
     } 
    } 
    ... 
} 

Y un TreeViewItemViewModel básica:

public class ToolboxItemViewModel 
{ 
    ... 
    public string Name { get; private set; } 
    public ObservableCollection<TreeViewItemViewModel> Children { get; private set; } 
    public bool ShowNode(string filterText) 
    { 
     ... return true if filterText is contained in Name or has children that contain filterText ... 
    } 
    ... 
} 

todo está configurado en el xaml así que ver la vista de árbol y cuadro de búsqueda.

Cuando se ejerce este código, el filtro solo se aplica a los nodos de raíz que es insuficiente. ¿Hay alguna manera de hacer que el filtro gotee en la jerarquía de nodos para que se llame a mi predicado para cada nodo? En otras palabras, ¿se puede aplicar el filtro a TreeView como un todo?

+2

Lo que acabaste haciendo ? ¿Alguna información de rendimiento que pueda transmitir u otra solución? –

Respuesta

3

Lamentablemente, no hay forma de hacer que el mismo filtro se aplique a todos los nodos automáticamente. El filtro es una propiedad (no un DP) de ItemsCollection que no es DependencyObject y, por lo tanto, la herencia del valor DP no está allí.

Cada nodo en el árbol tiene su propia ItemsCollection que tiene su propio filtro. La única forma de hacerlo funcionar es configurarlos manualmente para llamar al mismo delegado.

La manera más simple sería exponer la propiedad Filtro del tipo Predicado <objeto> en su ToolBoxViewModel y en su setter desencadenar un evento. Entonces ToolboxItemViewModel será responsable de consumir este evento y actualizar su filtro.

No estoy muy seguro de cómo sería el rendimiento de grandes cantidades de elementos en el árbol.

2

La única manera que he encontrado para hacer esto (que es un poco un hack), es crear un ValueConverter que convierta de IList a IEnumerable. en ConvertTo(), devuelve un nuevo CollectionViewSource del pasado en IList.

Si hay una mejor manera de hacerlo, me encantaría escucharlo. Esto parece funcionar, sin embargo.

0

Usted puede obtener TreeViewItem para un elemento dado en un árbol usando ItemContainerGenerator y una vez que tenga eso, podrá establecer el filtro.

6

Éste es cómo filtrado los artículos en mi TreeView:

tengo la clase:

class Node 
{ 
    public string Name { get; set; } 
    public List<Node> Children { get; set; } 

    // this is the magic method! 
    public Node Search(Func<Node, bool> predicate) 
    { 
     // if node is a leaf 
     if(this.Children == null || this.Children.Count == 0) 
     { 
      if (predicate(this)) 
       return this; 
      else 
       return null; 
     } 
     else // Otherwise if node is not a leaf 
     { 
      var results = Children 
           .Select(i => i.Search(predicate)) 
           .Where(i => i != null).ToList(); 

      if (results.Any()){ 
       var result = (Node)MemberwiseClone(); 
       result.Items = results; 
       return result; 
      } 
      return null; 
     }    
    } 
} 

Entonces podría filtrar los resultados como:

// initialize Node root 
// pretend root has some children and those children have more children 
// then filter the results as: 
var newRootNode = root.Search(x=>x.Name == "Foo"); 
Cuestiones relacionadas