,

This project introduces a Windows Explorer clone in an early state. It contains browsing through all files and folders on your computer, including virtual folders. It uses the same ContextMenus as Windows Explorer and includes drag and drop support.

I created this project with Visual Studio 2005 (.NET 2.0) and haven't tried it with .NET 1.1. I'm pretty sure it will work with .NET 1.1, but some changes need to be made. For example, I used the ToolBarMenuStrip which isn't available in 1.1. If anyone wants a 1.1 version and isn't able to convert it, I'm willing to convert it myself and provide the code.

Quite a lot of different new things in this update. The most important new feature is plug-ins. You can now add your own plugins to this application, which will be used to add columns to the details view and to add special views. More on this in the "Plug-ins" section. Another important update is an addition to this article which explains how to use this Control in your own application and how to use it's functions, see the "Using the Control" section for this update.

Furthermore there are some small updates, additions and bug fixes. See the "History" section for these updates.

This is quite a large improvement since the first version. It doesn't include a lot of new features, but has a lot of fixes and speed improvement. The most important fix is the memory leak fix, which was caused by the update thread. When I was solving this problem I also added another update method. This method uses the SHChangeNotifyRegister function to retrieve Shell notify messages. These messages are used to make some more updates, like changing icons, inserting media and renaming. So now when you insert a disc into your disc-drive the icon and text of the drive will change to the ones from the disc.

One new feature which needs some attention is the rename function. You can now rename items by selecting the rename item from their ContextMenu or by pressing F2. Be aware though that this will also change the extension of the file, but it will warn you if you do this. When renaming multiple items it will take the name you entered and add a number to it, different for any item, somewhat like Windows Explorer. I recommend trying the rename function on some test files to see exactly what is does, before using it on other files.

I was looking for something nice to program, when I got the idea of making my own Windows Explorer. I started this project with the idea of making an enhanced version of Windows Explorer with plug-in support. But before being able to enhance the Windows Explorer, you got to have a program which works like Windows Explorer. So I started searching the Internet for solutions.

While searching the Internet I found a lot of programming around Windows Explorer and Shell extensions. But none really had everything I needed and most programs where written in C++ while I really wanted one in C#. Finally I found the article I needed to start my program: An All VB.NET Explorer Tree Control with ImageList Management. Although it was written in VB I could get a really great deal of information from it and the largest part of this project relies on that article. So for more information or for a VB version see that article.

The only problem now was that I had never worked with the Windows Shell before. So first things first, I searched for articles explaining about how the Shell works and what you can do with it. Well, you can do a lot with it, too much to explain here. If you never worked with the Shell before or don't really know how the Shell works, I recommend this article: C# does Shell. This article also provides you with some links to MSDN articles. It takes some time to read them, but it definitely helped me a lot in making this program. You can also find a lot of info about all the Shell methods, structures and enumerations used in this program on MSDN.

After the base of the program was created I started implementing things like the Shell ContextMenu, drag/drop support and a Windows Explorer like ComboBox. I didn't really find a nice article on CodeProject for this, but a whole bunch where available on the Internet. I programmed everything with a wrapper around the Shell functions from the Windows API and was surprised how well it worked.

To use this control in your own program, add a reference to the dll to your project. After that you can add the Browser control to the toolbox and add it to your own project. I've created some properties which allow you to alter the behaviour and look of the control (at design time):

ShowNavigationBar

Shows or hides the navigation bar

ShowFolders

Shows or hides the folder TreeView

ShowFoldersButton

Shows or hides the button for the folder TreeView

StartUpDirectory

Enumeration indicating which directory will be opened at startup

StartUpDirectoryOther

String indicating which directory will be opened at startup (StartUpDirectory must be "Other")

ShellBrowser

Sets the ShellBrowser used for retrieving ShellItems, if set to null the Browser will create it's own

PluginWrapper

Sets the PluginWrapper used for retrieving plug-ins, if set to null the Browser will create it's own

SplitterDistance

Sets the distance of the Splitter between the TreeView and the ListView>/TD>

StartUpDirectory is an enumeration of special folders which are used to determine the startup location of the Browser. If you want to provide your own location, you must set this value to "Other" and provide your own location in the StartUpDirectoryOther property.

The ShellBrowser and PluginWrapper properties are used when you want to add more than one Browser control to your program. You can link those Browser controls by setting the ShellBrowser and PluginWrapper to the same object. This will make the program run a lot faster and more efficient than using a different ShellBrowser and PluginWrapper for the controls. In the demo project you'll see an example of how to add two Browser controls to a project.

There are also a few properties which you can only use at run-time:

ListViewMode

Sets the initial view of the ListView (can't be "Small Icons")

SelectedItem

Sets the current directory (ShellItem)

SelectedNode

Sets the current directory (TreeNode)

ShowFoldersButton

Shows or hides the button for the folder TreeView

Lastly, there are methods to programmatically do some actions for the Browser:

SelectPath

Sets the current directory

BrowserBack

Same as clicking the Back button of the Browser

BrowserForward

Same as clicking the Forward button of the Browser

BrowserUp

Same as clicking the Up button of the Browser

CreateNewFolder

Creates a new directory in the current directory, if possible

SelectPath takes 3 different Objects to set the current directory. Either the ShellItem of the folder to select, a string of the path to the directory (this can also be like "My Documents\My Music") or a value of the SpecialFolders enumeration.

These classes are quite simple and don't need a lot of explanation. To use my control in your project, you actually only need to use the Browser class. Just add this control to a form and all should be working. For more info, take a look at the comment in my code. Unfortunately at the moment my code doesn't have many comments, but I will try to add more shortly.

Represents a file system object, folder or file (this can be a virtual folder)

ShellImageList

Retrieves the Shell ImageList and makes them available for the Browser

PIDL

Build around a pointer to a PIDL-structure, which is used to identify a file system object

ShellAPI and ShellImageList are very much like the classes in Jim Parsells' project I mentioned earlier. They are similar to ShellDll and SystemImageListManager respectively. For more info about these classes first try his article. ShellItem comes from his CShItem class, but I've completely rewritten it, to match my needs. I'm not going to go through the detail of this class, but if many people really need more info about it, I might write an article about it.

The first thing you'll notice when trying to find a nice article about getting the Shell ContextMenu in your program, is that almost all articles are about making extensions to the menu and not about retrieving it for your own program. Luckily I found one blog that did explain this very thoroughly: How to host an IContextMenu. It was all in C++, so I had to translate it to C#. As this is an article existing of 11 parts, I'll try to explain everything here from a C# point of view. I'm going to assume you are familiar with the Shell namespace and pidls as it would take a lot of time to explain this here and this article is meant to cover the ContextMenu. So if you are not familiar with these terms, look for an article on those things first. The one I mentioned in the start of this article was all I needed (C# does Shell).

Before I explain the procedure for showing the ContextMenu, I'll give a short description for the interfaces we are going to use:

IShellFolder

Is used to manage folders, it is exposed by all Shell namespace folder objects

IContextMenu

Is called by the Shell to either create or merge a shortcut menu associated with a Shell object

IContextMenu2

Is used to either create or merge a shortcut menu associated with a certain object when the menu involves owner-drawn menu items

IContextMenu3

Is used to create or merge a shortcut menu associated with a certain object when the menu implementation needs to process the WM_MENUCHAR message

So know let's get started with the ContextMenu stuff. To retrieve the menu you want, you'll need a few things:

The IShellFolder interface from the parent directory

The pidls (relative to the parent) from the items you want to get the ContextMenu for

The IContextMenu from the same items

Not much has to be done to obtain the IShellFolder interface. The ShellItem class provides the IShellFolder for each directory, so you just have to get the ShellItem class for the parent directory and then you'll have the IShellFolder interface. The pidls can also be retrieved from the ShellItem class. In my control each TreeNode and ListViewItem has their own ShellItem in their Tag property, so it is also quite easy to get the pidls you need. After this has been done you have everything you to get the IContextMenu interface. The IShellFolder interface has a method which will provide a lot of different interfaces for its children, these interfaces include the IContextMenu. We need to make a call to the GetUIObjectOf method from the IShellFolder, like in the following example:

As you can see, you need an array of IntPtr. This array includes the pidls of the items for which to retrieve the IContextMenu. This can be any number, in our program this number depends on how many items are selected. With GetUIObjectOf you'll get a pointer to the IContextMenu and to obtain the real interface you need to use the Marshal class.

Now we need a ContextMenu and because we are calling only Windows API methods, all we need is a Handle to a ContextMenu. To make a new ContextMenu the Windows API way, we just need to call ShellAPI.CreatePopupMenu(), which will return a pointer to the new ContextMenu. You can now add all the menu items from the Shell ContextMenu by calling the QueryContextMenu method from the IContextMenu interface.

Now the contextMenu pointer points to the ContextMenu we need. After this call you can change the menu in any way you want. To change the menu you can use the API functions AppendMenu and InsertMenu from the ShellAPI class. After that it's time to show our menu to the user. We do this by calling ShellAPI.TrackPopupMenuEx. This method will wait for the user to select an item and will return the id of the selected item. This id is not just the index of the item in the list, but it's a special id. To execute the command that goes with the selected item we need a CMINVOKECOMMANDINFOEX structure. We can use this with the InvokeCommand method from the IContextMenu to execute the selected command. For more info about this structure see MSDN.

In the previous example the cmd variable is the selected index. All we need to do is cast this to a pointer and the Shell functions know what to do with it. As you can see I also included some code for ModfierKeys. As you might know, when you delete a file using Windows Explorer, there are two ways to do it: moving it to the recycle bin, or deleting it permanently. When you just press delete, the selected file will be moved into the recycle bin, but when you hold shift and press delete, the file will be deleted permanently. That is why you have to add the ModifierKeys to the structure.

Another thing to notice is that we add a POINT to the structure. This POINT represents the place on the screen where you pressed the right mouse button. Have you ever noticed that when you clicked Properties on the ContextMenu of Windows Explorer, that the Properties window will be shown on the point where you right clicked your mouse button? Well it does and to have the same effect in your program you will have to set this POINT.

When all of this worked I was really happy, but soon I found something strange. When you select the "Open With" or the "Send To" submenus, you don't see other menu items in it. As we also want this menus to work, we need to get some more interfaces. The IContextMenu has two child classes which are needed to get the menu's to work: IContextMenu2 and IContextMenu3. To get these interfaces we simply use the Marshal class like this:

These interfaces will draw the menus for us, but they need to know when to do this. For this we need to override the WndProc method and check the messages that are being send to it. When these messages are about creating, measuring or drawing the ContextMenu items we will call the HandleMenuMsg and HandleMenuMsg2 methods from the IContextMenu2 and IContextMenu3 interfaces respectively, these methods will do the rest of the necessary work.

As you can read on MSDN, the IContextMenu2 interface will process the WM_INITMENUPOPUP, WM_MEASUREITEM and WM_DRAWITEM messages and the IContextMenu3 interface will process the WM_MENUCHAR message. So if you encounter one of these messages while showing the ContextMenu call the HandleMenuMsg and HandleMenuMsg2 methods to handle the specific messages.

Once you implemented this, you will see that now the submenu's will also work the way they are supposed to.

This is the main idea to get the ContextMenus to work. My program also adds the Collapse and Expand MenuItems on a TreeNodeContextMenu, like Windows Explorer does. It will also raise an event for showing the ContextMenuItems help String when the item is being hovered over. Just check my code if you need to know how to do this.

Before I started implementing the drag/drop support to my program I read the following article by Jim Parsells: Adding Drag and Drop to an Explorer Tree Control and another one by Michael Dunn: How to Implement Drag and Drop Between Your Program and Explorer. These let me in the right way and also made it a bit more challenging for me. At the end of Jim Parsells article he mentions some problems when implementing it the way he did. I think I solved these problems, by implementing it without using the .Net drag/drop methods. So forget all the nice implementations of .Net, we are going to use the Windows API to get this to work.

Three new interfaces are needed for drag and drop support:

IDropTarget

Contains methods used in any application that can be a target for data during a drag/drop operation

IDropSource

Contains the methods for generating visual feedback to the end user and for canceling or completing the drag/drop operation

IDataObject

Is used by the Clipboard class and in drag/drop operations to store data from the dragged object

The nice thing of the Shell namespace is that it will do all the dirty work for you, the only problem is to figure out how to let the Shell do his job. Once you are working with the Shell and get more familiar with it however, this will get a lot easier and you will find solutions to your problem quite fast. Once I got the hang of the ContextMenu stuff, it was actually quite an easy job for me to implement drag/drop.

To register your program for drop operations you need a class which implements the IDropTarget interface. In my program the BrowserTVDropWrapper and the BrowserLVDropWrapper are both IDropTargets. Before we get the necessary events raised in our own classes we need to register them. You can do this by calling the ShellAPI.RegisterDragDrop method, this method takes two arguments. One argument being the handle of the control to register the drag operation for and the other being the IDropTarget to receive messages about the drag. You also need to revoke your registration once you program finishes using the ShellAPI.RevokeDragDrop method. Once you registered your IDropTarget, your class will receive 4 different messages which need some more attention.

The first message being DragEnter, which will be called when someone drags an object and enters you control. You will receive a pointer to the IDataObject being dragged, the current state of the modifier keys and mouse buttons, the location of the mouse pointer and a reference to an instance of the DragDropEffects enumeration. This is quite a lot of info, but we don't really need to use it all. The Shell provides us with an IDropTarget from specific Shell objects which will do all the work for us. The only thing we need to do is check which item is being dragged over, obtain the IDropTarget for that item and pass all the info to that interface. To get the IDropTarget from an item, we have to call the GetUIObjectOf method from the parent IShellFolder interface again (as with the IContextMenu interface). So the basic idea in code form looks like this:

When the DragEnter method has been called, the DragOver method will be called many times while the dragged item is over your control. This way you can give specific information on where the dragged item can be dropped and where it can't be. We can once again let the Shell do all the dirty work, just like with the DragEnter method.

Now there are two methods left, either the drag operation on your control will be canceled, or it will succeed and the item is dropped on your control. For when the operation is cancelled there is the DragLeave method. There is no additional info given, just a notice that the drag has ended on your control. Nothing much has to be done now, except for preparing your class to receive another drag operation.

If the drop succeeds the DragDrop method will be called providing you with pretty much the same info as the DragEnter and DragOver methods. The only difference being that some action has to be taken. Once again this action will be performed by the Windows Shell. When we call the DragDrop method from the IDropTarget which we retrieved earlier, the Shell does all the work for us. All the same notifications and process windows are shown like when you are using Windows Explorer.

Well that's pretty much it for the drop operations. In my classes a bit more has been done to give it all a nice look. This includes selecting the node over which you are dragging an object, and showing the nice ghost image from the content you are dragging which Windows Explorer shows. But these things aren't really necessary to get it all working.

Once the drop part is out of the way, it's time to implement the drag operations. This is the part where it's getting a bit different from the VB explorer. In Jim Parsell's explorer, he uses a special class to create IDataObjects for the items being dragged. He's doing it the .NET way by making use of .NET's IDataObject interface, but actually you do not really need to do anything with the IDataObject interface other than passing it on. That is, if you are doing it the Shell way, which is in my opinion a lot easier than the .Net way.

Because we are going to drag items using API methods, we are going to need an IDropSource interface. This interface will take care of any drawing or canceling while dragging your item. The BrowserTVDragWrapper and BrowserLVDragWrapper classes implement this interface, and they will make sure dragging will be supported.

The first thing we need is to get a notification when an item is being dragged. Both the TreeView and ListView have an event for this (ItemDrag), so we just register to it. Once a drag has been initialized we need to make a call to an API method, to register the wrapper as an IDropSource and to trigger the drag. The method to call is ShellAPI.DoDragDrop, it has two input arguments and one output. The two input arguments are the IDataObject from the item being dragged and an instance of the DragDropEffects enumeration telling the method which drag/drop effects are allowed. The output argument is also an instance of the DragDropEffects enumeration specifying which effect has been executed.

The DragDropEffects are easy to provide, but the IDataObject needs a bit more work. Fortunately we've already seen the procedure to get this interface twice. We can once again use the GetUIObjectOf method (this method is really very useful). Notice that when you are dragging multiple items the ItemDrag event will only be raised once, so you'll have to check which items are selected to get the right IDataObject.

Once the drag has been initialized, your IDropSource interface will receive two messages concerning the drag. The first one is QueryContinueDrag, which asks what to do with a certain situation, either perform the drop, cancel it, or continue dragging. You get some information to determine what to do. You'll get a bool whether the escape key has been pressed, if so the operation has to be cancelled. You also get the state of the modifier keys and the mouse buttons. This is where you have to check whether to continue the drag or perform the drop. If the mouse button which initialized the drag is still pressed, continue the drag, otherwise perform the drop. Then this is what the method is going to look like.

The other message your interface will receive is GiveFeedback. You will get the current DragDropEffect which applies to the dragged object. This message will allow you to change the Cursor to match this specific DragDropEffect. Because the normal Cursors are the ones we need, this method will only have one line in it. The Shell provides us with an option to just use the standard Cursors for drag/drop operations, which is exactly what we want. So the method will look like this.

Well, we're done. That was all you have to do for the drag operations. I haven't said anything about the browsing part of my control just because it would take too much time and make this article too long. If anyone really wants to know, I might write another article about this.

With the 1.3 update I added the option to add plug-ins to the program. These plug-ins are to gain extra information about files and folders. At this moment I have two different plug-ins and one is in the making. The first of the two is a plug-ins is a plug-in to retrieve extra columns for the details view of the ListView. Without plug-ins you only have the "Name" column which is just to little for a details view. With the demo project I have added a plug-in of this kind which addes the "size", "date created" and "date modified" columns. The second plug-in is a bit more advanced, it is a special view for the ListView. In the demo project I have added a demo plug-in of this kind which will add the "Image View" to the ListViews view options. If you select this view, you'll get to see a preview of images once you select them. See the following picture to get a better idea of what I mean.

To make your own plug-ins, you'll have to make a project with a reference to the FileBrowser.dll. Once you've done this there are two interface for the plug-ins I mentioned. One is the IColumnPlugin, the other is the IViewPlugin. You have to make a public class which implement one or even both of these interfaces. After you've done that build your project as a Class Library which will create a DLL-file. Add this dll-file to a folder named "plugins" in the folder where you start your program and start your program. Now your plug-in should be loaded and you can use it. For a demo project see the plug-in demo project you can download above.

Before you can build your own plug-in however, you obviously need to know what every method of both interfaces do and when they are called. So I'll give a short explanation of what they do. Both interfaces implement the basic IBrowserPlugin interface which I will explain first.

IBrowserPlugin

Name

The name of the plug-in.

Info

A short description of the plug-in

These properties aren't used at the moment, but I will use them later to list the loaded plug-ins and to allow the user to select which plug-in to use.

IColumnPlugin

ColumnNames

An array with the names of all the columns this plug-in provides

GetAlignment

Returns the HorizontalAlignment of a specific column

GetFolderInfo

Returns the information for a specific column for a folder

GetFileInfo

Returns the information for a specific column for a file

The GetFolderInfo and GetFileInfo methods are called when the current directory changes, they return the info which will be put in the columns for the plug-in. The plug-in will get two arguments when this method is called. Either an IDirInfoProvider if the item is a directory or IFileInfoProvider when the item is a file. These interface will provide a structure with info about the file or folder and for files it will also provide a Stream to that file. The second argument is the ShellItem of the specific item to provide the info for. With these two arguments the plug-in should retrieve the needed info and provide a string for the column. To get a better idea of the possibilities see the demo project.

IViewPlugin

ViewName

The name to show when selecting the ListView view options

ViewControl

The Control which will be showed when the view is selected

FolderSelected

Will be called when a folder is selected

FileSelected

Will be called when a file is selected

Reset

Will be called when a new directory is opened

The ViewControl can be about anything you like, so you can make quite a variety of view plug-ins. Just make sure the Control is initialized when the plug-ins constructor is called or you'll get cross-thread problems. The FolderSelected and FileSelected methods will be called when an item is selected and have the same arguments as the GetFolderInfo and GetFileInfo methods. In my demo plug-in I use the Stream I got from the IFileInfoProvider to read a picture and show it on the Control.

If you need any more info on the plug-ins please post a message below and I will try to answer as soon as possible. In the next update I hope to include a third plug-in with which you can add commands to the ContextMenu. For now you can experiment with these two.

While I was writing my program I used a lot of sources on the Internet, because there is just so much written for this subject, so I can't give you all the sites which contributed to my work, but I'll give you the main articles which are the ones I couldn't have done without.

There is definitely room for improvement in my program. The first thing I need to do is add much more comment to my code, because it almost hasn't got any, after that a few main things need to be polished:

Make a nice drag image when dragging from my control

Improve speed when browsing folders with many objects

Add more menu items to the standard ContextMenu of the ListView

Make a undo and redo option

And probably a lot more which I can't think of right now, any other ideas from readers would obviously be welcome as well

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Comments and Discussions

For all those folks who have very less space to include the TreeView + list view and need a compact one with Files and Folders as well, Then Put in your Email id . Ill mail you the source code. I hope the author helps me put my code up so that everyone can use it .

My modifications contains ( the Folders and Files in the single Tree )

I might be interested in your changes, if you'd send them to me. I've been using this control for over a year in an interface I wrote. I've always known about the app crash bug when changing a folder name, but just ignored it. Now I need to fix it and I'm having trouble getting this fixed. Have you figure this out?

For all those folks who have very less space to include the TreeView + list view and need a compact one with Files and Folders as well, Then Put in your Email id . Ill mail you the source code. I hope the author helps me put my code up so that everyone can use it .

Hello,
The control is different from component. In this project, BrowserTreeView and BorwserListView class are the controls, and that BrowserComboBox class is component. BrowserComboBox inherits from ToolStripComboBox. This hierachy inheritance below:
Component
ToolStripIem
ToolStripControlHost
ToolStripComboBox
BrowserComboBox

BrowserComboBox is put in ToolStripContainer. It's a componet, so can't display in Tool Box. You can rewrite the declaration of BrowserComboBox class from

internal class to public class. Generate solution. Then the BrowserComboBox presents in Tool Box. Remember rewrite to internal back affter that.

I want to have the browser to show only files with only ".txt" extension.
I am not getting how to use "Filter" property while using Treeview.
I also want to get preview of the selected text file in the same window itself.

Hi,
I have downloaded your source code.
I have used filebrowser control in one of my forms, i haven't written any code for the events of filebrowser control. I have just set StartupDirectoryOther to custom path and i have set ShowFolders Property to false.
Sometimes when i open my project, visual studio hangs.
Am using visual studio 2008 and my project is running on .net framework 3.5.

Could you please let me know if the source code and dll provided on the site is the updated source code and dll?

Hi, When I open a dialog that has the control (Browser) in it then VS 2010 crashes. The program also crashes when opened without vs 2010. I have attached files from the dump created by the crash. It appears that VS 2010 is crashing because of a heap corruption which must be caused by one of the interop calls. I am running Windows 7 (x64), Visual Studio 2010 and .NET Framework v4. I have also forwarded this issue to Microsoft to see if they can resolve this bug (https://connect.microsoft.com/VisualStudio/feedback/details/622103/visual-studio-2010-heap-corruption#details[^]). Please let me know if there is a workaround for this. Thanks!

to create your drag image when dragging from control is very simple:
1- create IDragSourceHelper, you create this by simply casting you IDropTargetHelper since both interfaces are implemented by the same object.
2- on item drag before calling DoDragDrop you create a SHDRAGIMAGE, and pass it to IDragSourceHelper.InitializeFromBitmap

note that "Show window content while draging" must be enabled to show the image.
also in SHDRAGIMAGE you should set crColorKey to ColorRef of the color to be transparent, set to -1 to keep bitmap transparency