In C#, one of the most exciting topic is Shell. As what we've discuss in Displaying System Icon in C#, shelling is a technique of manipulating file system objects in the computer's operating system using the system APIs provided. In the previous version of Microsoft programming language like VB6.0, the common and popular name for this is FSO (or FileSystemObject). With the advent of the technology inventions, doing Shell is very easy compare to what we're doing with the older version of programming languages.
In this blog, we preferred to use the WPF technology since we already engage in some project that already using the WPF. In short, we already have our environment ready for it.
See below the image as what are our target output.
In the above Image, you can see a WPF TreeView control that already filled with the File System information. We just simply get the root drives and get the child directories and files of the drives in a lazy load technique. See below our actual code implementation and explanation.
TreeView Explorer Implementation
First and foremost, we created a new Window and place a TreeView control inside it. See the codes below.
<Window x:Class="CodesDirectory.WIN_TreeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WIN TreeView"
Height="300" Width="300">
<TreeView Name="treeView" Margin="5"></TreeView>
</Window>
In order for us to display the list of the available Drive in the file system, we need to use the System.IO.DriveInfo class to get the list of drives. The method GetDrives method will give us the information of the active system drives. See the code below our implementation.
public void
LoadDirectories()
{
var drives = DriveInfo.GetDrives();
foreach (var drive in drives)
{
this.treeView.Items.Add(this.GetItem(drive));
}
}
In our code above, we created the method LoadDirectories so it can be called during the construction of the Window. You will also notice that there is a method named GetItem passing the drive information. This method will actually return the actual TreeViewItem that binds the DriveInfo object into the TreeViewItem's DataContext and Tag property. See below our implementation.
private TreeViewItem
GetItem(DriveInfo drive)
{
var item = new TreeViewItem
{
Header = drive.Name,
DataContext = drive,
Tag =
drive
};
this.AddDummy(item);
item.Expanded += new RoutedEventHandler(item_Expanded);
return item;
}
Again, the AddDummy method and the item_Expanded method is needed in this implementation. The AddDummy method is used to create a temporary dummy item inside each TreeViewItem (those bound to Directory and Drive). The purpose of this is to let the TreeViewItem be collapsible (you will notice there is an arrow before the item) WITHOUT loading the child directories and files. In the other side, the item_Expanded method is just an event handler to the Expanded event of the TreeViewItem. Later we'll explain how did we used this event when loading the child directories and files.
Additionally, in order for us to determine whether the item is dummy or not we are forced to create a new class that inherits the TreeViewItem. We called it DummyTreeViewItem. See the code below our implementation.
public class DummyTreeViewItem : TreeViewItem
{
public DummyTreeViewItem()
: base()
{
base.Header = "Dummy";
base.Tag = "Dummy";
}
}
private TreeViewItem
GetItem(DirectoryInfo directory)
{
var item = new TreeViewItem
{
Header = directory.Name,
DataContext = directory,
Tag =
directory
};
this.AddDummy(item);
item.Expanded += new RoutedEventHandler(item_Expanded);
return item;
}
private TreeViewItem
GetItem(FileInfo file)
{
var item = new TreeViewItem
{
Header = file.Name,
DataContext = file,
Tag =
file
};
return item;
}
What you can see above is just an overloaded method of what like the GetItem method of the DriveInfo. The only difference is just we used the DirectoryInfo and FileInfo object instead. Notice that in the FileInfo GetItem method there is no AddDummy and Expanded event listener. This is because the File object is not expandable and doesn't contain any children.
Looks like very surprising, till now we're not yet written the AddDummy implementation. We just only would like to be in the proper order before doing so. The code is below is our implementation for the AddDummy method.
private void AddDummy(TreeViewItem item)
{
item.Items.Add(new DummyTreeViewItem());
}
private bool HasDummy(TreeViewItem item)
{
return item.HasItems &&
(item.Items.OfType<TreeViewItem>().ToList().FindAll(tvi
=> tvi is DummyTreeViewItem).Count
> 0);
}
private void RemoveDummy(TreeViewItem item)
{
var dummies = item.Items.OfType<TreeViewItem>().ToList().FindAll(tvi => tvi is DummyTreeViewItem);
foreach (var dummy in dummies)
{
item.Items.Remove(dummy);
}
}
Explore Directories and Files
Exploring directories and files is very easy in C#. With the use of the DirectoryInfo object from System.IO namespace, we can get the list of child directories and files. Please visit Microsoft documentation for your reference.
private void
ExploreDirectories(TreeViewItem item)
{
var directoryInfo = (DirectoryInfo)null;
if (item.Tag is DriveInfo)
{
directoryInfo = ((DriveInfo)item.Tag).RootDirectory;
}
else if (item.Tag is DirectoryInfo)
{
directoryInfo = (DirectoryInfo)item.Tag;
}
else if (item.Tag is FileInfo)
{
directoryInfo = ((FileInfo)item.Tag).Directory;
}
if (object.ReferenceEquals(directoryInfo,
null)) return;
foreach (var
directory in directoryInfo.GetDirectories())
{
var isHidden = (directory.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
var isSystem = (directory.Attributes & FileAttributes.System) == FileAttributes.System;
if (!isHidden && !isSystem)
{
item.Items.Add(this.GetItem(directory));
}
}
}
private void ExploreFiles(TreeViewItem item)
{
var directoryInfo = (DirectoryInfo)null;
if (item.Tag is DriveInfo)
{
directoryInfo = ((DriveInfo)item.Tag).RootDirectory;
}
else if (item.Tag is DirectoryInfo)
{
directoryInfo = (DirectoryInfo)item.Tag;
}
else if (item.Tag is FileInfo)
{
directoryInfo = ((FileInfo)item.Tag).Directory;
}
if (object.ReferenceEquals(directoryInfo,
null)) return;
foreach (var file in directoryInfo.GetFiles())
{
var isHidden = (file.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
var isSystem = (file.Attributes & FileAttributes.System) == FileAttributes.System;
if (!isHidden && !isSystem)
{
item.Items.Add(this.GetItem(file));
}
}
}
Note: The GetFiles() and GetDirectories() method includes the System or Hidden objects in their result. So we must be aware of that before displaying it in the UI.
Also, you will notice that we used the Bitwise comparison for the object Attributes. We first determined whether it is a System or a Hidden object, and if it is not we add it into the parent's TreeViewItem Items property.
Expanded Event
Also, you will notice that we used the Bitwise comparison for the object Attributes. We first determined whether it is a System or a Hidden object, and if it is not we add it into the parent's TreeViewItem Items property.
Expanded Event
Now that we're almost done with our topic. One important thing we need to do is to manage how are we going to load the children of the file system object. We need to determine in what event and what our code is doing. In the TreeViewItem.Expanded event, as you remember, we added the item_Expanded event handler and there we did our job. We first checked whether the current item has a Dummy item and remove it. After that, we loaded the child directories and files with the methods we implemented above. See below our codes.
void item_Expanded(object
sender, RoutedEventArgs e)
{
var item = (TreeViewItem)sender;
if (this.HasDummy(item))
{
this.Cursor = Cursors.Wait;
this.RemoveDummy(item);
this.ExploreDirectories(item);
this.ExploreFiles(item);
this.Cursor = Cursors.Arrow;
}
}
You will also notice that we only call the RemoveDummy, ExploreDirectories and ExploreFiles method only if there is a Dummy node. This is because we used the Dummy object as a flag to the TreeViewItem state whether it is explored or not.
Lastly, we need to call the first method we created named LoadDirectories in the window's constructor so everything is initialized. See below the code.
public WIN_TreeView()
{
InitializeComponent();
this.LoadDirectories();
}
And that's it. We just finish the WPF TreeView file system explorer topic. Next topic is about the thing of loading the file system image into the TreeView.
Related topics:
Related topics:
This comment has been removed by the author.
ReplyDeleteCodes Directory: C Wpf Treeview File Explorer >>>>> Download Now
Delete>>>>> Download Full
Codes Directory: C Wpf Treeview File Explorer >>>>> Download LINK
>>>>> Download Now
Codes Directory: C Wpf Treeview File Explorer >>>>> Download Full
>>>>> Download LINK 4n
This is an awesome article... I've been away from coding for a little while so getting a well explained article like this speaks volume for me. I will read more of your topics.
ReplyDeleteOne more thing...do you have an article that shows how to display the files in a separate panel. Once I select the directory the files are located it will show in the right panel or list box. Also I only want CSV or XLS files to display in that panel.
ReplyDeleteCan we do run time add directory and it get refreshed on actual directory created? I should see directory added
ReplyDeleteThe dummy technique is nice, but I guess it will add the arrow to all the folders, even if it contains nothing. The Window Explorer add the arrow only if they are sub-folder.
ReplyDeleteThis is absolutely right. But it it up to you whether you consider the (onfocused) approach either.
DeleteI sincerely apologize for not being active here. I have moved my blogging to Medium and Twitter. Though, I tried my best to accommodate you on here.
ReplyDeleteHere is the link to the working project:
https://github.com/mikependon/Tutorials/tree/master/WPF/TreeViewFileExplorer
Moving forward, I will place everything in my Github account.
Thank you very much for writing this, Michael! Joe
ReplyDeleteNo problem Joe!
DeleteGreat and I have a dandy offer you: How Many Houses Has Hometown Renovated split level home kitchen remodel
ReplyDelete