Порешаем?
Для отображения элементов будем использовать вот такой класс:
public class Node
{
public string Name { get; set; }
public CollectionViewSource Items { get; set; }
}
Заполним его данными:
List<Node> roots = new List<Node>();
roots.Add(
new Node()
{
Name = "aaa",
Items = new CollectionViewSource()
{
Source = new Node[]
{
new Node() { Name = "11"},
new Node() { Name = "21"},
new Node() { Name = "31"},
}
}
});
roots.Add(
new Node()
{
Name = "bbb",
Items = new CollectionViewSource()
{
Source = new Node[]
{
new Node() { Name = "12"},
new Node() { Name = "22"},
new Node() { Name = "32"},
}
}
});
roots.Add(
new Node()
{
Name = "ccc",
Items = new CollectionViewSource()
{
Source = new Node[]
{
new Node() { Name = "13"},
new Node() { Name = "23"},
new Node() { Name = "33"},
}
}
});
Ну и собственно форма для демонстрации:
<StackPanel>
<TextBox x:Name="tbFilter" />
<TreeView x:Name="tvMain">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items.View}" >
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate>
<TextBlock Text="{Binding Name}" />
HierarchicalDataTemplate>
HierarchicalDataTemplate.ItemTemplate>
<TextBlock Text="{Binding Name}" />
HierarchicalDataTemplate>
TreeView.ItemTemplate>
TreeView>
StackPanel>
Вон уже сколько кода написали, а для поддержки фильтрации в изменение события TextChanged у нашего поля ввода надо всего лишь написать вот такую штуку:
private void tbFilter_TextChanged(object sender, TextChangedEventArgs e)
{
foreach (var root in tvMain.Items.Cast<Node>())
{
root.Items.Filter -= Items_Filter;
root.Items.Filter += Items_Filter;
}
}
Ну и метод фильтрации соответственно:
void Items_Filter(object sender, FilterEventArgs e)
{
Node filteredNode = e.Item as Node;
e.Accepted = filteredNode.Name.ToLower().Contains(tbFilter.Text.ToLower());
}
Все. Работать будет вот так:
Вот с фильтром: Ну или так:
Работающий пример можно взять здесь.
Блин, сколько маленьких хитростей в таком небольшом коде. Без кружки кофе не разберешься)
ОтветитьУдалитьДа вроме кроме корявой инициализации исходного массива, все остальное нормально? Или нет?
ОтветитьУдалитьЭтот комментарий был удален автором.
УдалитьИнициализация, вроде, инстинктивно понятна. Не совсем понятно, почему при каждом TextChanged нужно отписывать и подписывать обработчик Items_Filter?
ОтветитьУдалитьЕще вопрос: в разметке:
[HierarchicalDataTemplate ItemsSource="{Binding Items.View}" >
[HierarchicalDataTemplate.ItemTemplate>
[HierarchicalDataTemplate>
[TextBlock Text="{Binding Name}" />
[/HierarchicalDataTemplate>
[/HierarchicalDataTemplate.ItemTemplate>
[TextBlock Text="{Binding Name}" />
[/HierarchicalDataTemplate]
верхнему шаблону ItemsSource задается как Items.View. Items.View я так понимаю, это какой-то тип привязки, а не коллекция. Если это так, то что такое View в привязываемом List roots? И почему шаблон для второго уровня дерева обходится без такой же строчки ItemsSource, берет ее у родителя?
Пардоньте, если вопросы нубские)
А еще blogspot съедает пробелы и разметку)
УдалитьОтписывание/подписывание: Проблема в том, что данный для фильтра являются внешними по отношению к CollectionViewSource, поэтому о том, что надо применить фильтр снова, он (источник) не знает. Я нашел такой способ применить фильтр заново.
УдалитьПро View: Дело в том, что Binding по умолчанию смотрит на объект который лежит у текущего компонента в DataContext. В данном же случае, у текущего элемента, для которого мы и описываем шаблон, в DataContext лежит объект типа Node. У этого объекта есть поле Items (типа CollectionViewSource), а .View говорит, что у объекта типа CollectionViewSource есть свойство View, из которого мы и пытаемся взять деток. Собственно в этом свойсте CollectionViewSource и выдает отфильтрованную/сгрупперованную/отсортированную часть коллекции переданной в конструктор.
У дочернего шаблона нет ItemsSource, в связи с тем, что дерево двух уровневое. Соответственно элементам второго уровня источник дочерних элементов не нужен.
Доступно? Если нет, то что? Я попробую подробнее объяснить...
Вона как, спасибо.
ОтветитьУдалить