Заполнение TreeView из списка путей в WPF
Необходимо заполнить TreeView из некоего списка:
var paths = new List<string>
{
@"DD\flora\green_tree\tree_0",
@"DD\flora\green_tree\tree_1",
@"DD\flora\dead_tree\Dtree_2",
@"DD\flora\green_tree\tree_3",
@"DD\flora\green_tree\tree_4",
@"DD\wall\wooden_wall\wall_0",
@"DD\window\wooden_window\window_0",
@"DD\wall\wooden_wall\wall_1",
@"DD\wall\wooden_wall\wall_2",
@"DD\flora\green_bush\bush_0",
@"BD\flora\dead_bush\Dbush_1",
@"BD\flora\green_bush\bush_1"
};
Сейчас использую следующий код:
FillingTreeView(MyTreeView, paths, '\\');
private static void FillingTreeView(TreeView treeView, IEnumerable<string> paths, char separator)
{
TreeViewItem? lastNode = null;
string subPathAgg;
foreach (string path in paths)
{
lastNode = null;
subPathAgg = "";
foreach (string subPath in path.Split(separator))
{
subPathAgg += subPath + separator;
TreeViewItem? nodes = SearchTreeView(subPathAgg, treeView.Items);
if (nodes == null)
{
if (lastNode == null)
{
treeView.Items.Add(new TreeViewItem() { Header = subPath, Tag = subPathAgg });
lastNode = (TreeViewItem)treeView.Items[^1];
}
else
{
lastNode.Items.Add(new TreeViewItem() { Header = subPath, Tag = subPathAgg });
lastNode = (TreeViewItem)lastNode.Items[^1];
}
}
else
{
lastNode = nodes;
}
}
}
}
private static TreeViewItem? SearchTreeView(string subPathAgg, ItemCollection Nodes)
{
TreeViewItem? returnValue = null;
foreach (TreeViewItem node in Nodes)
{
if (string.Equals(((HeaderedItemsControl)node).Tag.ToString(), subPathAgg) == true)
{
returnValue = node;
return returnValue;
}
if (node.Items.Count > 0) returnValue = SearchTreeView(subPathAgg, node.Items);
}
return returnValue;
}
Все работает почти как нужно, но иногда получается вот так:
Как можно увидеть, есть повторяющиеся пути "wooden_wall", которые должны быть одним узлом. Я с WPF работаю впервые, надеюсь на вашу помощь.
Ответы (1 шт):
Много лишнего делаете. Всё может стать проще, если использовать привязки.
Добавлю класс для поддержки динамических привязок
public class NotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null!)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Сделаю вот такую ноду как модель данных
public class TreeNode<T> : NotifyPropertyChanged
{
private T _value;
public T Value
{
get => _value;
set
{
_value = value;
OnPropertyChanged();
}
}
public ObservableCollection<TreeNode<T>> Children { get; }
public TreeNode(T value)
{
_value = value;
Children = new();
}
}
Ну и дальше очень простой код, вот вам окно целиком
public partial class MainWindow : Window
{
public ObservableCollection<TreeNode<string>> Nodes { get; } = new();
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var paths = new List<string>
{
@"DD\flora\green_tree\tree_0",
@"DD\flora\green_tree\tree_1",
@"DD\flora\dead_tree\Dtree_2",
@"DD\flora\green_tree\tree_3",
@"DD\flora\green_tree\tree_4",
@"DD\wall\wooden_wall\wall_0",
@"DD\window\wooden_window\window_0",
@"DD\wall\wooden_wall\wall_1",
@"DD\wall\wooden_wall\wall_2",
@"DD\flora\green_bush\bush_0",
@"BD\flora\dead_bush\Dbush_1",
@"BD\flora\green_bush\bush_1"
};
FillNodes(paths);
}
private void FillNodes(List<string> paths)
{
foreach (var path in paths)
{
string[] tokens = path.Split('\\');
var children = Nodes;
foreach (string token in tokens)
{
// ищем, есть ли уже такая нода
TreeNode<string> node = children.FirstOrDefault(x => x.Value == token);
if (node is null)
{
// если нет, то добавляем новую
node = new(token);
children.Add(node);
}
children = node.Children;
}
}
}
}
Вот XAML целиком
<Window x:Class="WpfAppTreeView.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfAppTreeView"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" Loaded="Window_Loaded"
d:DataContext="{d:DesignInstance Type={x:Type local:MainWindow}, IsDesignTimeCreatable=False}">
<Grid>
<TreeView ItemsSource="{Binding Nodes}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Value}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Window>
Вот результат
Соответственно string
можно поменять на что угодно, на любую модель данных и сверстать внутри HierarchicalDataTemplate
любую XAML разметку для отображения этих данных.
Дополнительная прелесть этих привязок и такой реализации в том, что вы в процессе работы приложения можете в коллекцию с нодами вносить любые изменения, и они сразу же отобразятся в режиме реального времени в TreeView
.