Click here to Skip to main content
Click here to Skip to main content

An "Explorer-Style" TreeView Control

By , 21 Feb 2006
 

ExplorerTreeView Demo

Introduction

This is a simple TreeView derived control that mimics the Windows Explorer interface. It provides no added functionality aside from the standard TreeView control methods, properties, and events, however it does provide a ShellItem class, which can be used to extend this basic example for your own needs, and it is also a good starting point for those wanting to get to grips with the system image list and Shell32 programming.

Background

Originally, I started researching this topic as I wanted to develop an FTP client in C# that would implement a local system explorer similar to that of the Windows Explorer, but it soon became apparent that it was not going to be an easy undertaking. Thanks to CodeProject and other code repositories, I came across some good examples of how to achieve what I wanted, however the best examples were written in VB.NET or Delphi, so I decided to write a simple C# control based on what I'd found.

Parts of the code are based on other CodeProject tutorials and code samples found elsewhere on the Internet. All of the code was written by myself but some of the concepts were "borrowed".

Helpful Hints

For each node in the TreeView, an associated ShellItem object is created and stored in the node's "Tag" property. The ShellItem object allows you to retrieve information for the shell folder represented by the node, such as whether the folder has any sub-folders. For example, in the TreeView's "OnBeforeCollapse" event, we can obtain the ShellItem object to perform a simple test...

ShellItem shNodeItem = (ShellItem)e.Node.Tag;
if (shNodeItem.HasSubFolder)
    // Do something...

We can also get the folder's IShellFolder interface from the ShellItem object using the ShellFolder property. Using this interface, we can do all sorts of nifty stuff, such as implementing shell context menus:

// Get the ShellItem for this node.
ShellItem shNodeItem = (ShellItem)e.Node.Tag;

// Create an array of PIDL's for obtaining
// the IContextMenu interface pointer.
IntPtr[] arrPidls = new IntPtr[0]; 
arrPidls[0] = shNodeItem.PIDL;

// Request the interface pointer.
uint uRetVal = 
  shNodeItem.ShellFolder.GetUIObjectOf(IntPtr.Zero, 
  1, arrPidls, ref IID_IContextMenu, 
  IntPtr.Zero, out pCtx);
if (uRetVal != 0)
    Marshal.ThrowExceptionForHR((int)uRetVal);

...

Marshal.ReleaseComObject(pCtx);

History

None yet.

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.

A list of licenses authors might use can be found here

About the Author

MrPJ
Software Developer
United Kingdom United Kingdom
Member
Software Engineering Undergraduate

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionLicensememberMember 896106528 May '12 - 23:01 
The article does not include any specified license, so, can you tell what license should apply?
Is it The Code Project Open License (CPOL)?
 
Thanks
Cornelia
GeneralHuh!? - Nodes.Count - Allways return 1memberMr.Jinky29 Jan '11 - 8:44 
Might just be me but... Why?
Clearly there are thousands of nodes, but the Nodes() Property of ExplorerTreeViewWnd allways return 1.
 
How do i get access to alle the nodes?
 
Thanks in advance.
-= Really cool sig goes here =-

GeneralRe: Huh!? - Nodes.Count - Allways return 1membercyntonix22 Mar '11 - 21:35 
You have to Expand() it first...
QuestionHow to prevent that + being added to empty folders?memberMr.Jinky27 Jan '11 - 22:26 
How do I prevent adding the + sign to folders that has no subfolders?
-= Really cool sig goes here =-

QuestionHow to get files in selected foldermemberDjibril7 Jul '10 - 23:42 
Hi control looks good...
How can I have the list of files contained in the selected folder?
Regards,
Where there is a WISH, there is a WILL.

AnswerRe: How to get files in selected folder [modified]memberJoakim Anandh25 Nov '10 - 5:54 
1)override the below method in ExplorerTreeViewWnd.cs file
 
protected override void OnAfterSelect(TreeViewEventArgs e)
{
 
if (e.Action != TreeViewAction.Unknown)
{
ShellItem shNodeItem = (ShellItem)e.Node.Tag;
string strPhysicalPath = shNodeItem.Path;
}
}
 

2)then implement delegate to get this strPhysicalPath...
 
Thumbs Up | :thumbsup:
modified on Thursday, November 25, 2010 12:10 PM

GeneralRe: How to get files in selected foldermemberStehtimSchilf23 Jan '11 - 12:50 
Hi
 
can anyone give me the source how I have to implement this delegate to get strPhysicalPath?
 
And how can I achieve that when ever the user selects another node, the physical path is written into a textbox on the same form as the ExplorerTreeView control is?
 
This would be surly a great example for how to use delegates and events.
 
thx
SiS
GeneralRe: How to get files in selected foldermemberjunli27 Jan '11 - 16:07 
Hi,
How to implement the delegate?
code please.
 
Thank you
AnswerRe: How to get files in selected folder [modified]memberJoakim Anandh27 Jan '11 - 22:59 
ExplorerTreeView.cs
===
 
namespace WilsonProgramming
{
#region Declaration
 
// Declare delegate
public delegate void SelectedIndexChangedEventHandler(string physicalPath);
 
#endregion Declaration
 
public partial class ExplorerTreeView : UserControl
{
#region Declaration
 
// Create delegate reference
public event SelectedIndexChangedEventHandler SelectedIndexChanged;

string imgPath = string.Empty;
public string strPhysicalPath
{
get { return imgPath; }
set
{
imgPath = value;
OnSelectedIndexChanged(imgPath);
}
}
 
#endregion Declaration
 
private void OnSelectedIndexChanged(string strPhysicalPath)
{
if (SelectedIndexChanged != null)
SelectedIndexChanged(strPhysicalPath);
}
 
public ExplorerTreeView()
{
InitializeComponent();
 
// Set the TreeView image list to the system image list.
SystemImageList.SetTVImageList(treeWnd.Handle);
LoadRootNodes();
 
// Point the reference to the SelectedIndexChangedEventHandler method
treeWnd.SelectedIndexChanged += new SelectedIndexChangedEventHandler(treeWnd_SelectedIndexChanged);
 
}
 
void treeWnd_SelectedIndexChanged(string physicalPath)
{
strPhysicalPath = physicalPath;
}
 
/// <summary>
/// Loads the root TreeView nodes.
/// </summary>
private void LoadRootNodes()
{
// Create the root shell item.
ShellItem m_shDesktop = new ShellItem();
 
// Create the root node.
TreeNode tvwRoot = new TreeNode();
tvwRoot.Text = m_shDesktop.DisplayName;
tvwRoot.ImageIndex = m_shDesktop.IconIndex;
tvwRoot.SelectedImageIndex = m_shDesktop.IconIndex;
tvwRoot.Tag = m_shDesktop;
 
// Now we need to add any children to the root node.
ArrayList arrChildren = m_shDesktop.GetSubFolders();
foreach (ShellItem shChild in arrChildren)
{
TreeNode tvwChild = new TreeNode();
tvwChild.Text = shChild.DisplayName;
tvwChild.ImageIndex = shChild.IconIndex;
tvwChild.SelectedImageIndex = shChild.IconIndex;
tvwChild.Tag = shChild;
 
// If this is a folder item and has children then add a place holder node.
if (shChild.IsFolder && shChild.HasSubFolder)
tvwChild.Nodes.Add("PH");
tvwRoot.Nodes.Add(tvwChild);
}
 
// Add the root node to the tree.
treeWnd.Nodes.Clear();
treeWnd.Nodes.Add(tvwRoot);
tvwRoot.Expand();
 
strPhysicalPath = treeWnd.strPhysicalPath;
 
}
}
}
 
2)ExplorerTreeViewWnd.cs
=====
namespace WilsonProgramming
{
 
class ExplorerTreeViewWnd : TreeView
{
 
#region Declaration
 
public event SelectedIndexChangedEventHandler SelectedIndexChanged;
 
string imgPath = string.Empty;
public string strPhysicalPath
{
get { return imgPath; }
set { imgPath = value; }
}
 
#endregion Declaration
 
protected override void OnBeforeExpand(TreeViewCancelEventArgs e)
{
// Remove the placeholder node.
e.Node.Nodes.Clear();
 
// We stored the ShellItem object in the node's Tag property - hah!
ShellItem shNode = (ShellItem)e.Node.Tag;
ArrayList arrSub = shNode.GetSubFolders();
foreach (ShellItem shChild in arrSub)
{
TreeNode tvwChild = new TreeNode();
tvwChild.Text = shChild.DisplayName;
tvwChild.ImageIndex = shChild.IconIndex;
tvwChild.SelectedImageIndex = shChild.IconIndex;
tvwChild.Tag = shChild;
 
// If this is a folder item and has children then add a place holder node.
if (shChild.IsFolder && shChild.HasSubFolder)
tvwChild.Nodes.Add("PH");
e.Node.Nodes.Add(tvwChild);
}
 
base.OnBeforeExpand(e);
}
 
protected override void OnAfterSelect(TreeViewEventArgs e)
{
 
// Confirm that the user initiated the selection.
// This prevents the first node from expanding when it is
// automatically selected during the initialization of
// the TreeView control.
if (e.Action != TreeViewAction.Unknown)
{
if (e.Node.IsExpanded)
{
e.Node.Collapse();
}
else
{
e.Node.Expand();
}
 
ShellItem shNodeItem = (ShellItem)e.Node.Tag;
OnSelectedIndexChanged(shNodeItem.Path);
//MessageBox.Show(shNodeItem.Path);
}
}
 
private void OnSelectedIndexChanged(string strPhysicalPath)
{
if (SelectedIndexChanged != null)
SelectedIndexChanged(strPhysicalPath);
}
 
}
}
 
3)frmDemo.cs
============
public partial class frmDemo : Form
{
public frmDemo()
{
InitializeComponent();
 
// Invoke the delegate
 
this.treeExplorer.SelectedIndexChanged += new WilsonProgramming.SelectedIndexChangedEventHandler(treeExplorer_SelectedIndexChanged);
 
}
 
void treeExplorer_SelectedIndexChanged(string physicalPath)
{
MessageBox.Show("physicalPath : " + physicalPath);
 
}
 
}

modified on Friday, January 28, 2011 6:07 AM

AnswerRe: How to get files in selected foldermemberMalcolm J Stewart18 May '11 - 3:48 
I've been looking into how to have this list the files contained in a folder, and I'm clearly not completely grasping how this works.
 
To get non-folders into the treeview, I changed ...
 
ShellItem.cs
{
   .
   .
   .
    public ArrayList GetSubFolders()
    {
        .
        .
        .
        uint hRes = ShellFolder.EnumObjects(IntPtr.Zero, ShellAPI.SHCONTF.SHCONTF_FOLDERS, out pEnum);
        .
        .
        .
    }
}
 
... to ...
 
ShellItem.cs
{
   .
   .
   .
    public ArrayList GetSubFolders()
    {
        .
        .
        .
        uint hRes = ShellFolder.EnumObjects(IntPtr.Zero, ShellAPI.SHCONTF.SHCONTF_FOLDERS | ShellAPI.SHCONTF.SHCONTF_NONFOLDERS, out pEnum);
        .
        .
        .
    }
}
 
This falls over in ...
 
ShellItem.cs
{
   .
   .
   .
    public ShellItem(ShellAPI.IShellFolder shDesktop, IntPtr pIDL, ShellItem shParent)
    {
        .
        .
        .        
    }
}
 
... as the call to ...
 
shDesktop.GetAttributesOf(1, out m_pIDL, out uFlags);
 
... returns uFlags as equal to "ShellAPI.SFGAOF.SFGAO_FOLDER | ShellAPI.SFGAOF.SFGAO_HASSUBFOLDER" for files (as well as for folders that have no sub-folders), so the subsequent function ...
   if (IsFolder)
      {
         uint hRes = shParent.m_shShellFolder.BindToObject(pIDL, IntPtr.Zero, ref ShellAPI.IID_IShellFolder, out m_shShellFolder);
         if (hRes != 0)
             Marshal.ThrowExceptionForHR((int)hRes);
      }
 
... throws an exception as the IsFolder and HasSubFolder booleans have been set to true.
 
This incorrect setting of the flags also causes the "+" icon to turn up in the tree for folders that have no subfolders (look in ExplorerTreeViewWnd.cs-protected override void OnBeforeExpand(TreeViewCancelEventArgs e){...} where it acts on these values being "True" to add a "placeholder" child)
 
Anyway, some pointers as to why the call to
shDesktop.GetAttributesOf(1, out m_pIDL, out uFlags)
is not returning something like the "ShellAPI.SFGAOF.SFGAO_FILESYSTEM" flag would be much appreciated.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 21 Feb 2006
Article Copyright 2006 by MrPJ
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid