populate treeview from list of file paths in wpf
There are several examples of how to populate a tree view from a collection of file paths such as this or this other example. I cannot seem to find such example for WPF. I know I can integrate windows forms and use a different control in order to make it work but it will be nice if I could do the same thing with a wpf treeview control. The tree view that I want to construct consists of about 50,000 files therefore I think it will be better if it is bind it to something. But first before binding it, I think it will be helpful to construct one based on a List of strings (strings contains the paths of files).
Solution 1:
I was intrigued by the question and threw this together. As a first pass I think I'm pretty close to what you're looking for. Talking about 50,000 items though makes me think that lazy loading may be appropriate. Anyway, here is the simple version based on an article by Josh Smith. I put all of the code here, but the magic really takes place with the data templates.
Given a few classes to represent the objects we're working with...
using System.Collections.Generic;
namespace WpfTreeViewBinding.Model
{
public class Item
{
public string Name { get; set; }
public string Path { get; set; }
}
}
and...
namespace WpfTreeViewBinding.Model
{
public class FileItem : Item
{
}
}
and...
namespace WpfTreeViewBinding.Model
{
public class DirectoryItem : Item
{
public List<Item> Items { get; set; }
public DirectoryItem()
{
Items = new List<Item>();
}
}
}
I created a recursive method to load up some directories/files...
using System.Collections.Generic;
using System.IO;
using WpfTreeViewBinding.Model;
namespace WpfTreeViewBinding
{
public class ItemProvider
{
public List<Item> GetItems(string path)
{
var items = new List<Item>();
var dirInfo = new DirectoryInfo(path);
foreach(var directory in dirInfo.GetDirectories())
{
var item = new DirectoryItem
{
Name = directory.Name,
Path = directory.FullName,
Items = GetItems(directory.FullName)
};
items.Add(item);
}
foreach(var file in dirInfo.GetFiles())
{
var item = new FileItem
{
Name = file.Name,
Path = file.FullName
};
items.Add(item);
}
return items;
}
}
}
From there it's just a matter of getting the data...
using System.Windows;
namespace WpfTreeViewBinding
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var itemProvider = new ItemProvider();
var items = itemProvider.GetItems("C:\\Temp");
DataContext = items;
}
}
}
And displaying it...
<Window x:Class="WpfTreeViewBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Model="clr-namespace:WpfTreeViewBinding.Model"
Title="MainWindow"
Height="350" Width="525">
<Window.Resources>
<HierarchicalDataTemplate DataType="{x:Type Model:DirectoryItem}"
ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Path=Name}" ToolTip="{Binding Path=Path}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type Model:FileItem}">
<TextBlock Text="{Binding Path=Name}" ToolTip="{Binding Path=Path}" />
</DataTemplate>
</Window.Resources>
<Grid Margin="8">
<TreeView ItemsSource="{Binding}" />
</Grid>
</Window>
All of the magic really happens with the data templates. I guess the key to the whole thing is using the HierarchicalDataTemplate for any items with hierarchy (i.e. directories).
NOTE 1: I haven't extensively tested this. It hasn't been profiled for performance. I would welcome any feedback though since this is a problem I tried to solve long ago and gave up on. Thanks!
NOTE 2: You'll need to set the hard-coded path to something that makes sense on your system.
Here is a screenshot showing directories and files at different levels...
Solution 2:
Small extension for previous solution: I added xaml code to support the icons, and support for switching between icons for opened and closed folder:
<HierarchicalDataTemplate DataType="{x:Type viewModels:SourceControlDirecoryViewModel}"
ItemsSource="{Binding Items}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Width="16"
Height="16"
Source="{StaticResource ImageSourceFolderClosed16x16}"
x:Name="img" />
<TextBlock Text="{Binding Path=Name}"
ToolTip="{Binding Path=Path}"
Grid.Column="2" />
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsExpanded, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type TreeViewItem}}}"
Value="True">
<Setter Property="Source"
TargetName="img"
Value="{StaticResource ImageSourceFolderOpened16x16}" />
</DataTrigger>
</DataTemplate.Triggers>
</HierarchicalDataTemplate>