Как отфильтровать иерархию дерева просмотра wpf, используя ICollectionView?

У меня есть гипотетическое древовидное представление, которое содержит эти данные:

RootNode
   Leaf
   vein
SecondRoot
   seeds
   flowers

Я пытаюсь отфильтровать узлы, чтобы показать только те узлы, которые содержат определенный текст. Скажем, если я укажу «L», дерево будет отфильтровано и покажет только RootNode-> Leaf и SecondRoot-> flowers (потому что они оба содержат букву L).

Следуя шаблону m-v-vm, у меня есть базовый класс TreeViewViewModel, подобный этому:

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);
        }
    }
    ...
}

И базовая TreeViewItemViewModel:

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 ... 
    } 
    ...
}

Все настроено в xaml, поэтому я вижу дерево и окно поиска.

Когда этот код выполняется, фильтр применяется только к корневым узлам, что недостаточно. Есть ли способ заставить фильтр работать в иерархии узлов так, чтобы мой предикат вызывался для каждого узла? Другими словами, может ли фильтр применяться к TreeView в целом?

 Aaron McIver31 дек. 2010 г., 22:52
Что ты в итоге делал? Любая информация о производительности вы можете передать или другое решение?

Ответы на вопрос(5)

используяItemContainerGenerator и когда у вас есть, вы можете установить фильтр.

который я нашел, чтобы сделать это (что-то вроде хака), это создать ValueConverter, который конвертирует из IList в IEnumerable. в ConvertTo () вернуть новый CollectionViewSource из переданного в IList.

Если есть лучший способ сделать это, я хотел бы услышать это. Это похоже на работу, хотя.

Решение Вопроса

невозможно автоматически применить один и тот же фильтр ко всем узлам. Фильтр - это свойство (не DP) элемента ItemsCollection, которое не является DependencyObject, и поэтому наследование DP-значения отсутствует.

Каждый узел в дереве имеет свою собственную коллекцию ItemsCollection, которая имеет свой собственный фильтр. Единственный способ заставить его работать - это вручную настроить их всех на вызов одного и того же делегата.

Простейшим способом было бы выставить свойство Filter типа Predicate <object> в вашем ToolBoxViewModel и в его установщике инициировать событие. Тогда ToolboxItemViewModel будет нести ответственность за использование этого события и обновление его фильтра.

Не очень, и я не уверен, как будет выглядеть производительность для большого количества элементов в дереве.

упомянутую здесь:http://www.codeproject.com/KB/WPF/versatile_treeview.aspx

И применил фильтр к нему, как показано здесь:http://www.hardcodet.net/2008/02/programmatically-filtering-the-wpf-treeview

Я не мог рекомендовать это достаточно :)

TreeView:

У меня есть класс:

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;
         }             
    }
}

Тогда я мог бы отфильтровать результаты как:

// 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");

Ваш ответ на вопрос