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

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

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 в целом?

Ответы [ 5 ]

7 голосов
/ 12 сентября 2012

Вот как я отфильтровал элементы на моем 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");
4 голосов
/ 07 сентября 2009

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

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

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

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

3 голосов
/ 22 августа 2009

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

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

2 голосов
/ 28 июня 2011

Я решил использовать древовидную структуру Филиппа Суми, упомянутую здесь: http://www.codeproject.com/KB/WPF/versatile_treeview.aspx

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

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

0 голосов
/ 19 января 2012

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...