WPF Explorer View with TreeView
In this post we will see how to use WPF’s TreeView with the WPF Tookit’s DataGrid to present an Explorer-like view of a file system folder. We will use databinding with the HierarchicalDataTemplate, a sprinkling of LINQ and as little code as possible.
You can download the source code here.
User Interface Layout
As in Explorer, we will present a window with two vertical panes. The left pane will show the directory structure as a tree; the right pane will show the files in the folder that is selected in the left pane.
The layout is very simple. It contains a Grid with three columns: the left and right panels and a GridSplitter in the middle. The left panel contains a TreeView (directoryView) to represent the directory structure, the right panel contains a DataGrid (fileView), from the WPF Toolkit, to represent the list of files in the selected directory.
Left Pane : Directory View
Here is the markup for the TreeView:
<TreeView Grid.Column="0" Name="directoryView">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:DirectoryRecord}"
ItemsSource="{Binding Directories}" >
<TextBlock Text="{Binding Info.Name}"/>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
As you can see I have used the HierarchicalDataTemplate to format the nodes of the TreeView. See also this post about HierarchicalDataTemplate. Each node binds to an instance of the DirectoryRecord, which is a wrapper of the DirectoryInfo class. The wrapper is required in order to expose the GetFiles and GetDirectories methods as properties.
Here it is:
class DirectoryRecord
{
public DirectoryInfo Info { get; set; }
public IEnumerable<FileInfo> Files
{
get
{
return Info.GetFiles();
}
}
public IEnumerable<DirectoryRecord> Directories
{
get
{
return from di in Info.GetDirectories("*", SearchOption.AllDirectories)
select new DirectoryRecord { Info = di };
}
}
}
Right Pane: File View
This is the markup for the DataGrid
<wf:DataGrid Grid.Column="2"
Name="fileView"
ItemsSource="{Binding ElementName=directoryView,
Path=SelectedItem.Files}"></wf:DataGrid>
The databinding is interesting. I have bound the ItemsSources to the Files property of the DirectoryRecord that is selected in the directoryView.
The default value of AutoGenerateColumns is true, but I filter the columns with the following delegate which is attached to the AutoGeneratingColumn event of directoryView
private void fileView_AutoGeneratingColumn(
object sender, DataGridAutoGeneratingColumnEventArgs e)
{
List<string> requiredProperties = new List<string>
{
"Name", "Length", "LastWriteTime"
};
if (!requiredProperties.Contains(e.PropertyName))
e.Cancel = true;
else
{
e.Column.Header = e.Column.Header.ToString().MixedCaseToSpace();
}
}
Finally, I added some styling for the DataGrid as follows:
<Window.Resources>
<Style TargetType="{x:Type wf:DataGrid}">
<Setter Property="Background" Value="White"></Setter>
<Setter Property="GridLinesVisibility" Value="Vertical"></Setter>
<Setter Property="RowHeaderWidth" Value="0"></Setter>
<Setter Property="VerticalGridLinesBrush"
Value="{Binding ElementName=splitter, Path=Background}"></Setter>
</Style>
</Window.Resources>
Putting It All Together
In the constructor of the Window we install events for Loaded and the AutoGeneratingColumn of the fileView.
public Window1()
{
InitializeComponent();
this.Loaded += Window_Loaded;
fileView.AutoGeneratingColumn += fileView_AutoGeneratingColumn;
}
The folder to be viewed in the window is currently hard coded. You can easily add a browse button to allow the user to select the folder to view.
In the Loaded event we create the root node and bind it to the directoryView.
string folder = @"..\..\";
void Window_Loaded(object sender, RoutedEventArgs e)
{
var root = new ObservableCollection<DirectoryRecord>{
new DirectoryRecord {
Info = new DirectoryInfo(folder)
}
};
directoryView.ItemsSource = root;
Title = "Explorer View (" + Path.GetFullPath(folder) + ")";
}
Da Da!
Performance
I think you will agree that the we have achieved quite a lot with very little code.
However, if you try to use the application to view the contents of your entire c: drive, you will wait a long time until the TreeView is filled.
Does anyone have any simple ideas to improve the performance?