Creating a Windows Explorer Application with Visual Studio 2012

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

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!

Hannes DuPreez
Hannes DuPreez
Ockert J. du Preez is a passionate coder and always willing to learn. He has written hundreds of developer articles over the years detailing his programming quests and adventures. He has written the following books: Visual Studio 2019 In-Depth (BpB Publications) JavaScript for Gurus (BpB Publications) He was the Technical Editor for Professional C++, 5th Edition (Wiley) He was a Microsoft Most Valuable Professional for .NET (2008–2017).

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read