Introduction
As you may know, I have always been very curious about how things work. Being a programming teacher also helps because I get a lot of questions from students, and it is always a joy; OK, more like a challenge to work out a simple project based on their questions.
Today, I will cover one of the most popular questions in my classroom, as well as on the forums. The topic of today is how to create an application similar to Windows Explorer. Just a note, this program used to be an FAQ on CodeGuru forums, but it is over six years old already and it is indeed time for a proper facelift and proper explanation on the logic involved.
Design
Open Visual Studio 2012 and create a new Windows Forms Application in either C# or VB.NET. Put the following controls ( with their Properties ) on your Form:
| Control | Property | Value |
| Form | Name | frmWinExplore |
| TreeView | Name | trvFolders |
| ContextMenuStrip | cmClipboardOp | |
| ListView | Name | lvFiles |
| ContextMenuStrip | cmClipboardOp | |
| ComboBox | Name | cboView |
| Items | Large Icon Details Small Icon List Tile |
|
| Button | Name | btnExit |
| Text | Exit | |
| ImageList | Name | iIconList |
| ContextMenuStrip | Name | cmClipboardOp |
| Copy | ||
| Paste |
Code
The most logical place to start is with getting all the folders / directories.
Folders
Because we are working with the file system here, we need to include our System.IO namespace:
VB.NET
Imports System.IO 'File Operations
C#
using System.IO; //File Operations
Now, we need the “magic” code to get all system folders – let us add it now:
VB.NET
Private Sub AddAllFolders(ByVal TNode As TreeNode, ByVal FolderPath As String)
Try
For Each FolderNode As String In Directory.GetDirectories(FolderPath) 'Load All Sub Folders
Dim SubFolderNode As TreeNode = TNode.Nodes.Add(FolderNode.Substring(FolderNode.LastIndexOf(""c) + 1)) 'Add Each Sub Folder Name
SubFolderNode.Tag = FolderNode 'Set Tag For Each Sub Folder
SubFolderNode.Nodes.Add("Loading...")
Next
Catch ex As Exception
MessageBox.Show(ex.Message) 'Something Went Wrong
End Try
End Sub
C#
private void AddAllFolders(TreeNode TNode, string FolderPath)
{
try
{
foreach (string FolderNode in Directory.GetDirectories(FolderPath)) //Load All Sub Folders
{
TreeNode SubFolderNode = TNode.Nodes.Add(FolderNode.Substring(FolderNode.LastIndexOf('\') + 1)); //Add Each Sub Folder Name
SubFolderNode.Tag = FolderNode; //Set Tag For Each Sub Folder
SubFolderNode.Nodes.Add("Loading...");
}
}
catch (Exception ex) //Something Went Wrong
{
MessageBox.Show(ex.Message);
}
}
This sub obtains all the sub folders inside the supplied path. It then iterates through them and adds it to the TreeView. very important here, is that we add a Tag for each of the subfolders. This tag will actually represent each sub folder name, which we could use to obtain all the files inside the particular folder.
We now need to make use of the TreeView’s BeforeExpand method to get the subfolders of the selected folder inside the treeview:
VB.NET
Private Sub trvFolders_BeforeExpand(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewCancelEventArgs) Handles trvFolders.BeforeExpand
If e.Node.Nodes.Count = 1 AndAlso e.Node.Nodes(0).Text = "Loading..." Then
e.Node.Nodes.Clear() 'Clear All Items
AddAllFolders(e.Node, CStr(e.Node.Tag)) 'Add All Folders
End If
End Sub
C#
private void trvFolders_BeforeExpand(object sender, TreeViewCancelEventArgs e)
{
if (e.Node.Nodes.Count == 1 && e.Node.Nodes[0].Text == "Loading...")
{
e.Node.Nodes.Clear(); //Clear All Items
AddAllFolders(e.Node, Convert.ToString(e.Node.Tag)); //Add All Folders
}
}
We clear all the previous nodes, and add all the subfolders of the selected TreeView folder via the AddAllFolders sub. The final piece of the puzzle for our folders is to add the initial starting node for our TreeView. This should be the drive you want to browse through. Add the next inside Form_Load:
VB.NET
Private Sub frmWinExplore_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
trvFolders.Sort() 'Sort Alphabetically
Dim Tnode As TreeNode = trvFolders.Nodes.Add("(Drive C:)") 'Add Main Node
AddAllFolders(Tnode, "C:") 'Add All Folders
End Sub
C#
private void frmWinExplore_Load(object sender, EventArgs e)
{
trvFolders.Sort(); //Sort Alphabetically
TreeNode Tnode = trvFolders.Nodes.Add("(Drive C:)"); //Add Main Node
AddAllFolders(Tnode, "C:\"); //Add All Folders
}
We Sort the TreeView alphabetically and add the main node to it. In this case it is Drive C:. We then call the AddAllFolders sub to add all the folders found on the C: drive.
If you were to run your project now, you’d be able to load all folders and their subfolders into the Treeview. Now, we need to obtain all the files inside the folders.
Files
Because we need a list of the files present in the selected folder, the most obvious place to get them is inside the TreeView’s AfterSelect method. This event happens when a selection was made in the TreeView. Let us add it now:
VB.NET
Private Sub trvFolders_AfterSelect(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewEventArgs) Handles trvFolders.AfterSelect
Dim FileExtension As String 'Stores File Extension
Dim SubItemIndex As Integer 'Sub Item Counter
Dim DateMod As String 'Stores Date Modified Of File
lvFiles.Items.Clear() 'Clear Existing Items
If trvFolders.SelectedNode.Nodes.Count = 1 AndAlso trvFolders.SelectedNode.Nodes(0).Text = "Loading..." Then
trvFolders.SelectedNode.Nodes.Clear() 'Reset
AddAllFolders(trvFolders.SelectedNode, CStr(trvFolders.SelectedNode.Tag))
End If
Dim folder As String = CStr(trvFolders.SelectedNode.Tag) 'Folder Name
If Not folder Is Nothing AndAlso Directory.Exists(folder) Then
Try
For Each file As String In Directory.GetFiles(folder) 'Get Files In Folder
FileExtension = Path.GetExtension(file) 'Get File Extension(s)
DateMod = IO.File.GetLastWriteTime(file).ToString() 'Get Date Modified For Each File
AddImages(file) 'Add Icons
lvFiles.Items.Add(file.Substring(file.LastIndexOf(""c) + 1), file.ToString()) 'Add Files & File Properties To ListView
lvFiles.Items(SubItemIndex).SubItems.Add(FileExtension.ToString() & " File")
lvFiles.Items(SubItemIndex).SubItems.Add(DateMod.ToString())
SubItemIndex += 1
Next
Catch ex As Exception 'Something Went Wrong
MessageBox.Show(ex.Message)
End Try
End If
End Sub
C#
private void trvFolders_AfterSelect(object sender, TreeViewEventArgs e)
{
string FileExtension = null; //Stores File Extension
int SubItemIndex = 0; //Sub Item Counter
string DateMod = null; //Stores Date Modified Of File
lvFiles.Items.Clear(); //Clear Existing Items
if (trvFolders.SelectedNode.Nodes.Count == 1 && trvFolders.SelectedNode.Nodes[0].Text == "Loading...")
{
trvFolders.SelectedNode.Nodes.Clear(); //Reset
AddAllFolders(trvFolders.SelectedNode, Convert.ToString(trvFolders.SelectedNode.Tag));
}
string folder = Convert.ToString(trvFolders.SelectedNode.Tag); //Folder Name
if ((folder != null) && System.IO.Directory.Exists(folder))
{
try
{
foreach (string file in System.IO.Directory.GetFiles(folder)) //Get Files In Folder
{
FileExtension = System.IO.Path.GetExtension(file); //Get File Extension(s)
DateMod = System.IO.File.GetLastWriteTime(file).ToString(); //Get Date Modified For Each File
AddImages(file); //Add File Icons
lvFiles.Items.Add(file.Substring(file.LastIndexOf('\') + 1), file.ToString()); //Add Files & File Properties To ListView
lvFiles.Items[SubItemIndex].SubItems.Add(FileExtension.ToString() + " File");
lvFiles.Items[SubItemIndex].SubItems.Add(DateMod.ToString());
SubItemIndex += 1;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message); //Something Went Wrong
}
}
}
We obtain the folder name, and ensure that it does in fact exist. It may sound silly to mention because with the AddAllFolders sub we added all the folders that exist, but you never know. A folder could have been deleted whilst looking for the files. A folder could have become corrupted. A folder could have moved.
We then get each file’s extension to identify the type of file it is. We will used the associated icons for each file. If you do not know what File Association is, have a look here. We get the Date Modified property for each file and add all of this info to our ListView.
You might have noticed that we made reference to a procedure called AddImages. We will add that shortly.
Before we proceed with the file icons, we have one little modification we need to do for our Form_Load event. Add the next few lines into Form_Load:
VB.NET
lvFiles.View = View.Details 'Set View of ListView
' Add ListView Columns With Specified Width
lvFiles.Columns.Add("File Name", 150, HorizontalAlignment.Left)
lvFiles.Columns.Add("File Type", 80, HorizontalAlignment.Left)
lvFiles.Columns.Add("Date Modified", 150, HorizontalAlignment.Left)
C#
lvFiles.View = View.Details; //Set ListView View Option
//Add ListView Columns With Specified Width
lvFiles.Columns.Add("File Name", 150, HorizontalAlignment.Left);
lvFiles.Columns.Add("File Type", 80, HorizontalAlignment.Left);
lvFiles.Columns.Add("Date Modified", 150, HorizontalAlignment.Left);
This creates the necessary columns for our ListView.
File Icons
To be able to get each associated file’s icon, we need to make use of the SHGetFileInfo API function. This API obtains all info about files such as which icons are associated with it, to name only one. If you do not know what APIs are, look here, or file association look here.
The first thing to do is to add the appropriate namespace so that we can use APIs properly:
VB.NET
Imports System.Runtime.InteropServices 'APIs
C#
using System.Runtime.InteropServices; //APIs
Now we must create the API:
VB.NET
Private Structure SHFILEINFO 'Contains information about a file object
Public hIcon As IntPtr 'Icon
Public iIcon As Integer 'Icondex
Public dwAttributes As Integer 'SFGAO_ flags
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> _
Public szDisplayName As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=80)> _
Public szTypeName As String
End Structure
Private Declare Auto Function SHGetFileInfo Lib "shell32.dll" _
(ByVal pszPath As String, _
ByVal dwFileAttributes As Integer, _
ByRef psfi As SHFILEINFO, _
ByVal cbFileInfo As Integer, _
ByVal uFlags As Integer) As IntPtr 'Retrieves information about an object in the file system, such as a file, folder, directory, or drive root
Private Const SHGFI_ICON = &H100 'Icon
Private Const SHGFI_SMALLICON = &H1 'Small Icon
Private Const SHGFI_LARGEICON = &H0 ' Large icon
Private Const MAX_PATH = 260 'Path to Icon
C#
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SHFILEINFO //Contains information about a file object
{
public IntPtr hIcon; //Icon
public int iIcon; //Icondex
public uint dwAttributes; //SFGAO_ flags
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
}
[DllImport("shell32.dll")] //Retrieves information about an object in the file system, such as a file, folder, directory, or drive root
public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);
private const uint SHGFI_ICON = 0x100; //Icon
private const uint SHGFI_SMALLICON = 0x1; //Small Icon
private const uint SHGFI_LARGEICON = 0x0; // Large icon
private const int MAX_PATH = 260; //Path to Icon
Let us now add the AddImages procedure to link each file with its associated icon:
VB.NET
Private Sub AddImages(ByVal strFileName As String)
Try
Dim shInfo As SHFILEINFO 'Create File Info Object
shInfo = New SHFILEINFO() 'Instantiate File Info Object
shInfo.szDisplayName = New String(vbNullChar, MAX_PATH) 'Get Display Name
shInfo.szTypeName = New String(vbNullChar, 80) 'Get File Type
Dim hIcon As IntPtr 'Get File Type Icon Based On File Association
hIcon = SHGetFileInfo(strFileName, 0, shInfo, Marshal.SizeOf(shInfo), SHGFI_ICON Or SHGFI_SMALLICON)
Dim MyIcon As Drawing.Bitmap 'Create icon
MyIcon = Drawing.Icon.FromHandle(shInfo.hIcon).ToBitmap 'Set Icon
iIconList.Images.Add(strFileName.ToString(), MyIcon) 'Add To ListView FileNames
Catch ex As Exception
MessageBox.Show(ex.Message & ex.Source)
End Try
End Sub
C#
private void AddImages(string strFileName)
{
char NullChar = Convert.ToChar(0); //NULL Character, C# Doesn't Have vbNullChar Constant Built In
SHFILEINFO shInfo = default(SHFILEINFO); //Create File Info Object
shInfo = new SHFILEINFO(); //Instantiate File Info Object
shInfo.szDisplayName = new string(NullChar, MAX_PATH); //Get Display Name
shInfo.szTypeName = new string(NullChar, 80); //Get File Type
IntPtr hIcon = default(IntPtr); //Get File Type Icon Based On File Association
hIcon = SHGetFileInfo(strFileName, 0, ref shInfo, (uint)Marshal.SizeOf(shInfo), SHGFI_ICON | SHGFI_SMALLICON);
System.Drawing.Bitmap MyIcon = null; //Create Icon
MyIcon = System.Drawing.Icon.FromHandle(shInfo.hIcon).ToBitmap(); //Set Icon
ilFileIcons.Images.Add(strFileName.ToString(), MyIcon); //Add To ListView FileNames
}
Copying and Pasting
Add the following code behind your Copy menustrip item’s code:
VB.NET
Private Sub CopyToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CopyToolStripMenuItem.Click
Dim lvItem As ListViewItem 'ListView item
Dim lvSel As ListView.SelectedListViewItemCollection = Me.lvFiles.SelectedItems 'ListViewItems
Dim strFileName As String 'File Name
For Each lvItem In lvSel 'Loop Through Select File Names In ListView
strFileName = trvFolders.SelectedNode.Tag & "" & lvItem.Text 'Get Selected File Name
Dim clpDataObj As New DataObject() 'Create New Data Object
Dim cbClipBoardFile(0) As String 'Break File Apart Into A String Array
cbClipBoardFile(0) = strFileName
clpDataObj.SetData(DataFormats.FileDrop, True, cbClipBoardFile) 'Put String Array Onto ClipBoard
Clipboard.SetDataObject(clpDataObj)
MessageBox.Show("File Copied To Clipboard") 'Inform User
Next
End Sub
C#
private void copyToolStripMenuItem_Click(object sender, EventArgs e)
{
ListView.SelectedListViewItemCollection lvSel = this.lvFiles.SelectedItems; //ListViewItems
string strFileName = null; //File Name
foreach ( ListViewItem lvItem in lvSel) //Loop Through All ListView Items
{
strFileName = trvFolders.SelectedNode.Tag + "\" + lvItem.Text; //Get Selected Filename
DataObject clpDataObj = new DataObject(); //Create New DataObject
string[] cbClipBoardFile = new string[1]; //Break File Apart Into A String Array
cbClipBoardFile[0] = strFileName;
clpDataObj.SetData(DataFormats.FileDrop, true, cbClipBoardFile); //Put String Array Onto ClipBoard
Clipboard.SetDataObject(clpDataObj);
MessageBox.Show("File Copied To Clipboard"); //Inform The User
}
}
We obtain the selected ListView item and concatenate it with the folder name (which is stored in that particular node’s tag). This gives us the complete file name and location. We break the file apart into a string array and place this string array onto the Clipboard. The file is now copied onto the clipboard.
Add the code to paste the file:
VB.NET
Private Sub PasteToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PasteToolStripMenuItem.Click
Dim idClipboardData As IDataObject = Clipboard.GetDataObject() 'Get Data Present on ClipBoard
If idClipboardData.GetDataPresent(DataFormats.FileDrop) Then
Dim strClipFile As String() = DirectCast(idClipboardData.GetData(DataFormats.FileDrop), String()) 'Convert String Array Back Into File
Dim i As Integer
For i = 0 To strClipFile.Length - 1
'If File Exists, Rename COpied File
If File.Exists(trvFolders.SelectedNode.Tag & "" & Path.GetFileName(strClipFile(i))) Then
File.Move(trvFolders.SelectedNode.Tag & "" & Path.GetFileName(strClipFile(i)), trvFolders.SelectedNode.Tag & "temp")
End If
'Copy File From ClipbBoard
File.Copy(strClipFile(i), trvFolders.SelectedNode.Tag & "" & Path.GetFileName(strClipFile(i)))
Next
MessageBox.Show("File Pasted From Clipboard") 'Inform User
End If
End Sub
C#
private void pasteToolStripMenuItem_Click(object sender, EventArgs e)
{
IDataObject idClipboardData = Clipboard.GetDataObject(); //Get Data Present on ClipBoard
if (idClipboardData.GetDataPresent(DataFormats.FileDrop))
{
string[] strClipFile = (string[])idClipboardData.GetData(DataFormats.FileDrop); //Convert String Array Back Into File
int i = 0;
for (i = 0; i <= strClipFile.Length - 1; i++)
{
//If File Exists, Rename COpied File
if (File.Exists(trvFolders.SelectedNode.Tag + "\" + Path.GetFileName(strClipFile[i])))
{
File.Move(trvFolders.SelectedNode.Tag + "\" + Path.GetFileName(strClipFile[i]), trvFolders.SelectedNode.Tag + "temp");
}
//Copy File From ClipbBoard
File.Copy(strClipFile[i], trvFolders.SelectedNode.Tag + "\" + Path.GetFileName(strClipFile[i]));
}
MessageBox.Show("File Pasted From Clipboard"); //Inform User
}
}
We identify the data on the clipboard. If the data resembles a file we then convert the string array back into a file. We attempt to paste it in the folder we selected in the TreeView. If the file already exists it will still copy it but change its name. If the file doesn’t exist yet, it will paste it in there.
Finishing up
All that is left to do is to program our View Combobox to be able to change Views and add code to our Exit button. Let us do that now:
VB.NET
Private Sub cboView_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cboView.SelectedIndexChanged
Dim strSelView = CType(cboView.SelectedItem, String) 'Change ListView ViewMode
Select Case strSelView
Case "Large Icon"
lvFiles.View = View.LargeIcon
Case "Details"
lvFiles.View = View.Details
Case "Small Icon"
lvFiles.View = View.SmallIcon
Case "List"
lvFiles.View = View.List
Case "Tile"
lvFiles.View = View.Tile
End Select
End Sub
Private Sub btnExit_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExit.Click
Application.Exit() 'Exit
End Sub
C#
private void cboView_SelectedIndexChanged(object sender, EventArgs e)
{
string strSelView = Convert.ToString(cboView.SelectedItem); //Change ListView ViewMode
switch (strSelView)
{
case "Large Icon":
lvFiles.View = View.LargeIcon;
break;
case "Details":
lvFiles.View = View.Details;
break;
case "Small Icon":
lvFiles.View = View.SmallIcon;
break;
case "List":
lvFiles.View = View.List;
break;
case "Tile":
lvFiles.View = View.Tile;
break;
}
}
private void btnExit_Click(object sender, EventArgs e)
{
Application.Exit(); //Exit
}
I am attaching both projects below, just in case you did something wrong.
Conclusion
That wasn’t too complicated, was it? Thanks for reading my article and I hope you have benefited from it. Until next time, Cheers!