David Sackstein's Blog

"The more that you learn, the more places you'll go.”, Dr. Seuss

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!

Explorer View with TreeView

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?

Comments

WPF Explorer View with TreeView - David Sackstein's Blog · WPFdc said:

Pingback from  WPF Explorer View with TreeView - David Sackstein&#39;s Blog &middot; WPFdc

# September 3, 2009 2:11 PM

Aviad P. said:

You can improve the GUI responsiveness (not the overall performance) by using PriorityBinding.

# December 6, 2009 12:54 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Enter the numbers above: