David Sackstein's Blog

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

September 2009 - Posts

Received my MCT Credentials Today

Earlier this year I completed the MCPD certification - today I earned my MCT !

Over the year I have trained classes on C++, .Net 2.0, WPF, WF, WCF, Silverlight and Design Patterns.

This has just got to be the fastest and most effective way to learn : )

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?