Click here to Skip to main content
15,896,606 members
Articles / Programming Languages / XML

Enhanced BrowseForFolder Styled TreeView

Rate me:
Please Sign up or sign in to vote.
4.86/5 (85 votes)
27 May 2013Apache5 min read 582.9K   16.8K   269  
Supports Explorer, SingleChecked and RecursiveChecked mode (checkboxes). Lets you specify the displayed drive types, etc...
// Copyright � 2004 by Christoph Richner. All rights are reserved.
// 
// If you like this code then feel free to go ahead and use it.
// The only thing I ask is that you don't remove or alter my copyright notice.
//
// Your use of this software is entirely at your own risk. I make no claims or
// warrantees about the reliability or fitness of this code for any particular purpose.
// If you make changes or additions to this code please mark your code as being yours.
// 
// website http://raccoom.sytes.net, email microweb@bluewin.ch, msn chrisdarebell@msn.com
 
using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.IO;
using System.Drawing.Design;
using System.ComponentModel;
using System.Windows.Forms.ComponentModel;
using System.Windows.Forms.Design;
using System.ComponentModel.Design;

namespace Raccoom.Windows.Forms
{
	/// <summary>
	/// TreeViewFolderBrowser works a bite like FolderBrowserDialog but was designed to let the user choose many directories by <c>Chechboxes</c>.	
	/// <seealso cref="DriveTypes"/><seealso cref="CheckboxBehaviorMode"/><seealso cref="TreeNodePath"/>
	/// </summary>	
	/// <remarks>
	/// Because this class delegates the drive, folder and ImageList specific task's to a <see cref="ITreeViewFolderBrowserDataProvider"/> instance, this class needs a wired <see cref="ITreeViewFolderBrowserDataProvider"/> instance before you can call any method wich fill's the tree view.
	/// </remarks>
	[Designer(typeof(TreeViewFolderBrowserDesigner))]
	[System.Drawing.ToolboxBitmap(typeof(System.Windows.Forms.TreeView))]
	[DefaultProperty("CheckboxBehaviorMode"), DefaultEvent("SelectedDirectoriesChanged")]
	public class TreeViewFolderBrowser : TreeView
	{
		#region fields
		/// <summary>
		/// Fired if a directory was selected or deselected.
		/// </summary>
		public event SelectedDirectoriesChangedDelegate SelectedDirectoriesChanged;
		/// <summary>
		/// Fired if a data provider has changed
		/// </summary>
		public EventHandler DataSourceChanged;		
		/// <summary>
		/// Fired if a CheckboxBehaviorMode has changed
		/// </summary>
		public EventHandler CheckboxBehaviorModeChanged;
		/// <summary>
		/// Fired if a drive types has changed
		/// </summary>
		public EventHandler DriveTypesChanged;
		/// <summary>holds the path list</summary>
		protected System.Collections.Specialized.StringCollection folderList_;		
		/// <summary>flag used to suppress CheckItem Event</summary>
		private int _supressCheck;		
		/// <summary>font used to mark nodes which contains checked sub nodes</summary>
		protected Font boldFont_;
		/// <summary>current working mode</summary>
		private CheckboxBehaviorMode _checkboxBehavior;			
		/// <summary>designer</summary>
		private System.ComponentModel.IContainer components;		
		/// <summary>designer</summary>
		private Environment.SpecialFolder _specialFolderRootFolder;		
		/// <summary>Specify which drive types are displayed.</summary>
		private DriveTypes _driveTypes; 
		/// <summary>data provider which is responsible to manage this instance</summary>
		private ITreeViewFolderBrowserDataProvider _dataProvider=null;
		/// <summary>data provider helper instance</summary>
		private TreeViewFolderBrowserHelper _helper = null;
		#endregion
		
		#region constructors
		/// <summary>
		/// Required designer variable.
		/// </summary>
		public TreeViewFolderBrowser()
		{
			InitializeComponent();	
			// initalize a new helper instance for this tree view.
			_helper = new TreeViewFolderBrowserHelper(this);
			//
			this.ContextMenu = new ContextMenu();
			this.ContextMenu.Popup +=new EventHandler(OnContextMenu_Popup);
			//
			this._driveTypes = DriveTypes.NoRootDirectory | DriveTypes.RemovableDisk | DriveTypes.LocalDisk | DriveTypes.NetworkDrive | DriveTypes.CompactDisc | DriveTypes.RAMDisk;
			this.RootFolder = Environment.SpecialFolder.MyComputer;
			this.CheckboxBehaviorMode = CheckboxBehaviorMode.SingleChecked;
			// init bold font
			boldFont_ = new Font(this.Font, FontStyle.Bold);				
		}
		#endregion
		
		#region public interface
		/// <summary>
		/// Gets or sets <see cref="ITreeViewFolderBrowserDataProvider"/> which is responsible to fill this <c>TreeViewFolderBrowser</c> instance.
		/// </summary>
		[Browsable(true), Category("FolderBrowser"),Description("DataSource specifies the DataProvider which is responsible to fill this instance")]
		[DefaultValue(Environment.SpecialFolder.MyComputer)]
		[System.ComponentModel.TypeConverter(typeof(System.ComponentModel.ExpandableObjectConverter))]
		public ITreeViewFolderBrowserDataProvider DataSource
		{
			get
			{
				return _dataProvider;
			}
			set
			{
				bool changed = !object.Equals(_dataProvider, value);
				_dataProvider = value;
				//
				if(_dataProvider!=null)
				{
					base.ImageList = _dataProvider.ImageList;					
				}
				else
				{
					base.ImageList = null;
				}
				//
				if(changed)	OnDataSourceChanged(EventArgs.Empty);
			}
		}			
		/// <summary>
		/// Gets or sets a value indicating whether check boxes are displayed next to the tree nodes in the tree view control.
		/// </summary>
		new public bool CheckBoxes
		{
			get
			{
				return base.CheckBoxes;
			}
//			set
//			{
//				base.CheckBoxes = value;				
//			}
		}
		/// <summary>
		/// Gets or sets the root folder where the browsing starts from.
		/// </summary>
		[Browsable(true), Category("FolderBrowser"),Description("Only the specified folder and any subfolders that are beneath it will appear in the dialog box and be selectable.")]
		[DefaultValue(Environment.SpecialFolder.MyComputer)]
		public Environment.SpecialFolder RootFolder
		{
			get
			{
				return _specialFolderRootFolder;
			}
			set
			{
				_specialFolderRootFolder = value;				
			}
		}
		/// <summary>
		/// List contains the path for all checked items.
		/// </summary>
		[Browsable(false)]
		public virtual System.Collections.Specialized.StringCollection SelectedDirectories
		{
			get
			{
				if(folderList_==null)
				{
					folderList_= new System.Collections.Specialized.StringCollection();
				}				
				return folderList_;
			}
			set
			{
				folderList_ = value;
			}
		}
		/// <summary>
		/// Specify how the tree view handles checkboxes and associated events.
		/// </summary>
		[Browsable(true), Category("FolderBrowser"),Description("Specify how the tree view handles checkboxes and associated events"), DefaultValue(CheckboxBehaviorMode.SingleChecked)]
		public virtual CheckboxBehaviorMode CheckboxBehaviorMode
		{
			get
			{
				return _checkboxBehavior;
			}
			set
			{
				_checkboxBehavior = value;
				//
				OnCheckboxBehaviorModeChanged(EventArgs.Empty);
			}
		}		
		/// <summary>
		/// Specify which drive types are displayed.
		/// </summary>
		[Browsable(true), Category("FolderBrowser"),Description("Specify which drive types are displayed"),DefaultValue(DriveTypes.All)]
		public virtual DriveTypes DriveTypes
		{
			get
			{
				return _driveTypes;
			}
			set
			{
				_driveTypes = value;
				//
				OnDriveTypesChanged(EventArgs.Empty);
			}
		}
		/// <summary>
		/// Clears the TreeView and popluates the root level.
		/// </summary>
		/// <param name="specialFolder">The <c> SpecialFolder</c> which should be selected after population. </param>
		public virtual void Populate(Environment.SpecialFolder specialFolder)
		{
			Populate(Environment.GetFolderPath(specialFolder));
		}
		/// <summary>
		/// Clears the TreeView and popluates the root level.
		/// </summary>
		public virtual void Populate()
		{
			Populate(null);
		}
		/// <summary>
		/// Clears the TreeView and popluates the root level.
		/// </summary>
		/// <param name="selectedFolderPath">The path of the folder that should be selected after population.</param>
		public virtual void Populate(string selectedFolderPath)
		{			
			// clear out the old values
			this.BeginUpdate();
			this.Nodes.Clear();	
			Cursor.Current = Cursors.WaitCursor;
			try
			{
				base.CheckBoxes = (this._checkboxBehavior != CheckboxBehaviorMode.None);
				_dataProvider.RequestRoot(_helper);
			} 
			catch(Exception e)
			{
				throw e;
			}
			finally
			{
				this.EndUpdate();
				Cursor.Current = Cursors.Default;
			}
			// open selected folder
			if((selectedFolderPath!=null) && (selectedFolderPath.Length > 0))
			{
				this.ShowFolder(selectedFolderPath);
			}
		}
		/// <summary>
		/// Focus the specified folder and scroll it in to view.
		/// </summary>
		/// <param name="directoryPath">The path which should be focused</param>
		public void ShowFolder(string directoryPath)
		{
			if ((directoryPath==null) || (directoryPath=="") || (directoryPath== string.Empty)) return;			
			// start search at root node
			TreeNodeCollection nodeCol = _dataProvider.RequestDriveCollection(_helper);
			//
			if(!System.IO.Directory.Exists(directoryPath) || nodeCol==null) return;
			//
			System.IO.DirectoryInfo dirInfo = new System.IO.DirectoryInfo(directoryPath);
			// get path tokens
			ArrayList dirs = new ArrayList();
			dirs.Add(dirInfo.FullName);
			//
			while(dirInfo.Parent!=null)
			{
				dirs.Add(dirInfo.Parent.FullName);
				//
				dirInfo = dirInfo.Parent;
			}
			// try to expand all path tokens
			Cursor.Current = Cursors.WaitCursor;
			this.BeginUpdate();			
			// load on demand was not fired till now
			if(nodeCol.Count==1 && ((TreeNodePath)nodeCol[0]).Path == null)
			{
				nodeCol[0].Parent.Expand();
			}
			try
			{
				//			
				for(int i= dirs.Count-1;i>=0;i--)
				{				 
					foreach(TreeNodePath n in nodeCol)
					{
						if (n.Path.ToLower().CompareTo(dirs[i].ToString().ToLower())==0)
						{
							nodeCol = n.Nodes;
							if(i==0)
							{
								n.EnsureVisible();
								this.SelectedNode = n;
							} 
							else
							{
								n.Expand();
							}
							break;
						}
					}
				}
			} 
			catch(Exception e)
			{
				throw e;
			}
			finally
			{
				this.EndUpdate();
				Cursor.Current = Cursors.Default;
			}
		}		
		#endregion
		
		#region internal interface	
		private void OnContextMenu_Popup(object sender, EventArgs e)
		{
            this.OnContextMenuPopup(e);
		}
		protected virtual void OnContextMenuPopup(EventArgs e)
		{
			if(_dataProvider==null) return;
			//
			ContextMenu.MenuItems.Clear();
			//			
			TreeNodePath node = _helper.TreeView.GetNodeAt(_helper.TreeView.PointToClient(Cursor.Position)) as TreeNodePath;
			if(node==null) return;
			//
			_dataProvider.QueryContextMenuItems(_helper, node);
		}

		/// <summary>
		/// True to supress OnBeforeCheck Execution, otherwise false to allow it.
		/// </summary>
		/// <param name="supressEvent"></param>
		protected internal virtual void SupressCheckEvent(bool supressEvent)
		{
			this._supressCheck += (supressEvent)? +1 : -1;
		}
		/// <summary>
		/// Indicates if OnBeforeCheck is permitted to call code
		/// </summary>
		protected bool IsCheckEventSupressed
		{
			get
			{
				return this._supressCheck != 0;
			}
		}
		/// <summary>
		/// Gets the root <c>TreeNodeCollection</c> depended on current RootFolder.
		/// </summary>
		/// <returns></returns>
		protected virtual TreeNodeCollection GetRootCollection()
		{
			switch(RootFolder)
			{
				case Environment.SpecialFolder.Desktop:
					return this.Nodes[0].Nodes[1].Nodes;					
				default:
					return this.Nodes;
			}			
		}		
		/// <summary>
		/// Populates the Directory structure for a given path.
		/// </summary>
		/// <param name="parent"></param>
		protected virtual void GetSubDirs( TreeNodePath parent, TreeViewCancelEventArgs e)
		{
			if(parent.Path==null) return;
			// everything ok, here we go
			this.BeginUpdate();
			try
			{
				parent.RemoveDummyNode();
				// if we have not scanned this folder before
				if ( parent.Nodes.Count == 0)
				{					
					_dataProvider.RequestSubDirs(_helper, parent,e);
				}
			}
			catch( Exception ex )
			{				
				throw ex;
			}
			finally
			{
				this.EndUpdate();
			}
		}
		/// <summary>
		/// Toggle the check flag for tree nodes, works recursive
		/// </summary>
		/// <param name="parent"></param>
		/// <param name="check"></param>
		protected virtual void CheckNodesRec(TreeNode parent,bool check)
		{	
			foreach(TreeNode n in parent.Nodes)
			{
				n.Checked = check;
				//
				CheckNodesRec(n,check);
			}
		}
		/// <summary>
		/// Add or removes the nodes recursive to or from the folderList_.
		/// </summary>
		/// <param name="parent"></param>
		/// <param name="add"></param>
		protected virtual void ExchangeFoldersRec(TreeNodePath parent,bool add)
		{				
			foreach(TreeNodePath n in parent.Nodes)
			{				
				if (n.Path!=null)
				{
					ExchangePath( n.Path, add);
					MarkNode(parent);
				}
				//
				ExchangeFoldersRec(n,add);
			}
		}
		/// <summary>
		/// Add or removes path from the folderList_.
		/// </summary>
		/// <param name="path"></param>
		/// <param name="add"></param>
		protected virtual void ExchangePath(string path, bool add)
		{
			
			if (add)
			{
				if(!folderList_.Contains(path))
				{
					folderList_.Add(path);
					// notfiy add
					OnSelectedDirectoriesChanged(new SelectedDirectoriesChangedEventArgs(path, System.Windows.Forms.CheckState.Checked));
				}
			} 
			else
			{
				if (folderList_.Contains(path))
				{
					folderList_.Remove(path);
					// notfiy remove
					OnSelectedDirectoriesChanged(new SelectedDirectoriesChangedEventArgs(path, System.Windows.Forms.CheckState.Unchecked));
				}
			}
		}
		/// <summary>
		/// Set the text bold if there is a child node checked.
		/// </summary>
		/// <param name="node"></param>
		protected internal virtual void MarkNode(TreeNodePath node)
		{
			if(this._checkboxBehavior == CheckboxBehaviorMode.None) return;
			//
			if(node==null) return;
			// no path selected, no node could be marked
			if (folderList_.Count == 0)
			{
				try
				{
					if((node.NodeFont !=null) && (node.NodeFont.Bold))
					{
						node.NodeFont = this.Font;
					}
				} 
				catch{}
				return;
			}
			// there are a few paths, so we have to check each against our node path
			string path = node.Path;
			//
			bool isBold = false;
			foreach(string s in folderList_)
			{
				// if path is equal, return
				if (s.Equals(path)) continue;
				// if path is substring, mark node bold, otherwise normal font is used
				if (s.IndexOf(path) != -1)
				{
					isBold = true;
					break;
				}
				else
				{
					isBold = false;					
				}

			}
			//
			if (isBold)
			{
				node.NodeFont = boldFont_;
			}
			else
			{
				node.NodeFont = this.Font;
			}
		}
		/// <summary>
		/// Set the text bold for each parent node if there is a child node checked.
		/// </summary>
		/// <param name="parent"></param>
		protected virtual void MarkNodesRec(TreeNodePath parent)
		{
			if(this._checkboxBehavior == CheckboxBehaviorMode.None) return;
			//
			if (parent == null) return;
			//
			MarkNode(parent);
			if(parent.Parent !=null)
			{
				MarkNodesRec(parent.Parent as TreeNodePath);
			}
		}
		#endregion
		
		#region events	
		/// <summary>
		/// Raises the CheckboxBehaviorModeChanged event.
		/// </summary>
		/// <param name="e">An EventArgs that contains the event data.</param>
		protected virtual void OnCheckboxBehaviorModeChanged(EventArgs e)
		{
			if(CheckboxBehaviorModeChanged!=null) CheckboxBehaviorModeChanged(this, e);
		}
		/// <summary>
		/// Raises the DriveTypesChanged event.
		/// </summary>
		/// <param name="e">An EventArgs that contains the event data.</param>
		protected virtual void OnDriveTypesChanged(EventArgs e)
		{
			if(DriveTypesChanged!=null) DriveTypesChanged(this, e);
		}
		/// <summary>
		/// Raises the DataSourceChanged event.
		/// </summary>
		/// <param name="e">An EventArgs that contains the event data.</param>
		protected virtual void OnDataSourceChanged(EventArgs e)
		{
			if(DataSourceChanged!=null) DataSourceChanged(this, e);
		}
		/// <summary>
		/// Used for drives like floppy, cd - rom ect. where it can be that no valid medium is inserted.
		/// in this case the click on the + will remove the +, after double click there's a new + to give the user
		/// the chance to browse this device after inserting a valid medium.
		/// </summary>		
		protected override void OnDoubleClick(EventArgs e)
		{
			if (this.SelectedNode==null) return;
			//
			TreeNodePath node = this.SelectedNode as TreeNodePath;
			if(node==null) return;
			//
			if ((node.Nodes.Count > 0) ||(node.Path.Length>3)) return;
			//
			node.AddDummyNode();
			//
			base.OnDoubleClick (e);
		}
		/// <summary>
		/// Fired before check action occurs, manages the folderList_.
		/// </summary>		
		protected override void OnBeforeCheck(TreeViewCancelEventArgs e)
		{
			// check suppress flag
			if (IsCheckEventSupressed)
			{
				base.OnBeforeCheck(e);
				return;
			}
			// get current action		
			bool check = !e.Node.Checked;
			// is it allowed to check item ?
			if ((this.CheckboxBehaviorMode==CheckboxBehaviorMode.RecursiveChecked) & (!check) & (e.Node.Parent!=null) && (e.Node.Parent.Checked))
			{
				e.Cancel = true;
				base.OnBeforeCheck(e);
				return;
			}
			// set supress flag
			SupressCheckEvent(true);			
			// stop drawing tree content
			this.BeginUpdate();	
			// set cursor
			Cursor.Current = Cursors.WaitCursor;			
			//
			try
			{
				// add or remove path
				ExchangePath(((TreeNodePath)e.Node).Path,check);				
				// handle recursive behaviour
				if(this.CheckboxBehaviorMode==CheckboxBehaviorMode.RecursiveChecked)
				{	
					// remove all childs from folderList_
					ExchangeFoldersRec(e.Node as TreeNodePath,false);
					// check child nodes to reflect parent check state
					CheckNodesRec(e.Node, check);
				}
				// update marked nodes fonts
				MarkNodesRec(e.Node.Parent as TreeNodePath);				
			}
			catch(Exception ex)
			{
				// ups, exception ?
				Console.WriteLine(ex.Message);
			}
			finally
			{
				// reset supress flag
				SupressCheckEvent(false);
				// let the tree redraw his content
				this.EndUpdate();
				// reset the cursor
				Cursor.Current = Cursors.Default;				
			}
			//
			base.OnBeforeCheck (e);
		}
		/// <summary>
		/// Fired before node expands, used to fill next level in directory structure.
		/// </summary>		
		protected override void OnBeforeExpand(TreeViewCancelEventArgs e)
		{
			TreeNodePath node = e.Node as TreeNodePath;
			//
			Cursor.Current = Cursors.WaitCursor;
			try
			{
				GetSubDirs(node,e);				
			}
			catch(System.Exception ex)
			{
				MessageBox.Show(ex.Message, System.Reflection.MethodBase.GetCurrentMethod().Name);
				e.Cancel = true;
			}
			finally
			{
				Cursor.Current = Cursors.Default;
			}			
			//
			base.OnBeforeExpand (e);
		}
		/// <summary>
		/// Raises the SelectedDirectoriesChanged event.<seealso cref="SelectedDirectoriesChangedDelegate"/>
		/// </summary>
		protected virtual void OnSelectedDirectoriesChanged(SelectedDirectoriesChangedEventArgs e)
		{
			if(this.SelectedDirectoriesChanged!=null) SelectedDirectoriesChanged(this, e);
		}
		#endregion

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{				
			// 
			// TreeViewFolderBrowser
			// 
			this.HideSelection = false;

		}
		#endregion
	}
	/// <summary>
	/// <c>ITreeViewFolderBrowserDataProvider</c> is used by a <see cref="TreeViewFolderBrowser"/> instance and is responsible for
	/// <list> 
	/// <item>retrieve the computer drives and directories</item>
	/// <item>Imagelist which is used to assign images to the nodes created by this instance.</item>  
	/// <item>ContextMenu</item>
	/// </list>
	/// <see cref="TreeViewFolderBrowser"/> calls the interface method's and provide a <see cref="TreeViewFolderBrowserHelper"/> instance which let you create node's and give you access to the underlying <see cref="TreeViewFolderBrowser"/> instance.
	/// </summary>
	public interface ITreeViewFolderBrowserDataProvider : IDisposable
	{	
		/// <summary>
		/// Gets the ImageList that contains the Image objects used by the tree nodes.
		/// </summary>
		ImageList ImageList{get;}
		/// <summary>
		/// Fired before the context menu popup.
		/// </summary>
		/// <param name="helper">The helper instance which provides method's and properties related to create and get nodes.</param>
		/// <param name="node">The node on which the context menu was requested.</param>
		void QueryContextMenuItems(TreeViewFolderBrowserHelper helper, TreeNodePath node);
		/// <summary>
		/// Fill the root level.
		/// </summary>
		/// <param name="helper">The helper instance which provides method's and properties related to create and get nodes.</param>
		void RequestRoot(TreeViewFolderBrowserHelper helper);
		/// <summary>
		/// Fill the Directory structure for a given path.
		/// </summary>
		/// <param name="helper">The helper instance which provides method's and properties related to create and get nodes.</param>
		/// <param name="parent">The expanding node.</param>
		void RequestSubDirs(TreeViewFolderBrowserHelper helper, TreeNodePath parent, TreeViewCancelEventArgs e);
		/// <summary>
		/// Gets the tree node collection which holds the drive node's. The requested collection is than used to search a specific node.
		/// </summary>
		TreeNodeCollection RequestDriveCollection(TreeViewFolderBrowserHelper helper);
	}
	/// <summary>
	/// <c>TreeViewFolderBrowserHelper</c> is like a bridge between <see cref="ITreeViewFolderBrowserDataProvider"/> and <see cref="TreeViewFolderBrowser"/>
	/// and provides access to the needed informations to fill the tree view.
	/// </summary>
	public class TreeViewFolderBrowserHelper
	{
		#region fields
		/// <summary>the managed tree view instance</summary>
		private TreeViewFolderBrowser _treeView;
		#endregion

		#region constructors
		/// <summary>
		/// Initialize a new instance of TreeViewFolderBrowserHelper for the specified TreeViewFolderBrowser instance.
		/// </summary>
		/// <param name="treeView"></param>
		internal TreeViewFolderBrowserHelper(TreeViewFolderBrowser treeView)
		{
			_treeView = treeView;
		}
		#endregion

		#region public interface
		/// <summary>
		/// Gets the underlying <see cref="TreeViewFolderBrowser"/> instance.
		/// </summary>
		public TreeViewFolderBrowser TreeView
		{
			get
			{
				return _treeView;
			}
		}
		/// <summary>
		/// Creates a tree node and add it to the <c>TreeNodeCollection</c>.
		/// </summary>
		/// <param name="text">The text displayed in the label of the tree node.</param>
		/// <param name="path">The path the node represents.</param>
		/// <param name="addDummyNode">True to add + sign, otherwise no + sign appears.</param>
		/// <param name="forceChecked">True to check node in each case, otherwise false to allow normal check against selected paths.</param>
		/// <param name="isSpecialFolder">Specifies if this node is a special folder. Special folders do not request data from the attached data provider.</param>
		/// <returns></returns>
		public virtual TreeNodePath CreateTreeNode(string text, string path, bool addDummyNode,bool forceChecked, bool isSpecialFolder)
		{			
			TreeNodePath newNode = new TreeNodePath(text, isSpecialFolder);								
			// path
			newNode.Path = path;
			//						
			try
			{
				_treeView.SupressCheckEvent(true);
				//
				if(forceChecked)
				{
					newNode.Checked = true;
				} 
				else
				{
					newNode.Checked = _treeView.SelectedDirectories.Contains(path);
				}
				_treeView.MarkNode(newNode);
			}
			catch (System.Exception e)
			{
				System.Diagnostics.Debug.WriteLine(e.Message,_treeView.Name);
			}
			finally
			{
				_treeView.SupressCheckEvent(false);
			}
			//
			if(addDummyNode)
			{
				// add dummy node, otherwise there is no + sign
				newNode.AddDummyNode();	
			}
			//
			return newNode;
		}
		#endregion
	}	
	/// <summary>
	/// A simple designer class for the <see cref="TreeViewFolderBrowser"/> control to remove 
	/// unwanted properties at design time.
	/// </summary>
	public class TreeViewFolderBrowserDesigner : System.Windows.Forms.Design.ControlDesigner
	{
		/// <summary>
		/// Allows a designer to change or remove items from the set of properties that it exposes through a TypeDescriptor. 
		/// </summary>
		/// <param name="properties">The properties for the class of the component.</param>
		protected override void PreFilterProperties(System.Collections.IDictionary properties)
		{
			properties.Remove("CheckBoxes");			
			properties.Remove("ImageList");
			properties.Remove("SelectedImageIndex");
			properties.Remove("ImageIndex");
			properties.Remove("ContextMenu");
		}
	}
	/// <summary>
	/// Extends the <c>TreeNode</c> type with a path property. This node type is used by <see cref="TreeViewFolderBrowser"/>
	/// </summary>
	public class TreeNodePath : System.Windows.Forms.TreeNode
	{
		#region fields
		/// <summary>Specifiy that this node instance represent a special folder.</summary>
		private readonly bool _isSpecialFolder;
		/// <summary>
		/// File or directory path information
		/// </summary>
		private string _path;
		#endregion fiels

		#region constructor
		/// <summary>
		/// Initializes a new instance of the TreeNodePath class.
		/// </summary>
		public TreeNodePath()
		{
		}
		/// <summary>
		/// Initializes a new instance of the TreeNodePath class.
		/// </summary>
		/// <param name="text">The label Text of the new tree node. </param>
		public TreeNodePath(string text, bool isSpecialFolder) : base(text)
		{
			this._isSpecialFolder = isSpecialFolder;
		}		
		/// <summary>
		/// Initializes a new instance of the TreeNodePath class.
		/// </summary>
		/// <param name="text">The label Text of the new tree node. </param>
		/// <param name="imageIndex">The index value of Image to display when the tree node is unselected.</param>
		/// <param name="selectedImageIndex">The index value of Image to display when the tree node is selected.</param>
		public TreeNodePath(string text, bool isSpecialFolder, int imageIndex,	int selectedImageIndex) : base(text, imageIndex, selectedImageIndex)
		{
			_isSpecialFolder = isSpecialFolder;
		}
		#endregion

		#region public interface
		/// <summary>
		/// Adds a dummy node to the parent node
		/// </summary>		
		public virtual void AddDummyNode()
		{
			Nodes.Add(new TreeNodePath("@@Dummy@@",false));
		}
		/// <summary>
		/// Removes the dummy node from the parent node.
		/// </summary>		
		public virtual void RemoveDummyNode()
		{
			if ((Nodes.Count == 1 ) & (Nodes[0].Text == "@@Dummy@@"))
			{
				Nodes[0].Remove();
			}
		}
		/// <summary>
		/// Gets or sets this node as a special folder node.
		/// </summary>
		/// <remarks>
		/// SpecialFolder's are folder's which are defined by <see cref="Environment.SpecialFolder"/> enum.
		/// </remarks>
		public bool IsSpecialFolder
		{
			get
			{				
				return _isSpecialFolder;
			}
		}
		/// <summary>
		/// Gets or sets the file or directory path information
		/// </summary>			
		public string Path
		{
			get
			{					
				return _path;
			}
			set
			{
				_path = value;
			}
		}
		#endregion
	}	
	/// <summary>
	/// Occurs when a directory checkstate has changed
	/// </summary>
	/// <remarks>
	/// The SelectedDirectoriesChangedDelegate event passes an SelectedDirectoriesChangedEventArgs object to its event handler. 
	/// </remarks>
	public delegate void SelectedDirectoriesChangedDelegate(object sender, SelectedDirectoriesChangedEventArgs e);
	/// <summary>
	/// Provides data for the SelectedDirectoriesChangedDelegate event of a TreeViewFolderBrowser control.
	/// </summary>
	public class SelectedDirectoriesChangedEventArgs
	{
		#region fields
		/// <summary>File path</summary>
		private readonly string _path;
		/// <summary>Checkstate</summary>
		private readonly System.Windows.Forms.CheckState _checkState;
		#endregion

		#region constructors
		/// <summary>Initalize a new instance of SelectedDirectoriesChangedEventArgs</summary>
		public SelectedDirectoriesChangedEventArgs(string path,System.Windows.Forms.CheckState checkState)
		{
			_path = path;
			_checkState = checkState;
		}
		#endregion

		/// <summary>Gets the path which was modified</summary>
		public string Path
		{
			get{return _path;}
		}
		/// <summary>Gets the check state for the path</summary>
		public System.Windows.Forms.CheckState CheckState
		{
			get{return _checkState;}
		}
	}
	/// <summary>
	/// Indicating whether check boxes are displayed next to the tree nodes in the tree view control and how the tree view handle related events.
	/// </summary>
	public enum CheckboxBehaviorMode
	{
		/// <summary>
		/// No check boxes are displayed next to the tree nodes in the tree view control.
		/// </summary>
		None,
		/// <summary>
		/// Check boxes are displayed next to the tree nodes in the tree view control. The user can check directories.
		/// </summary>
		SingleChecked,
		/// <summary>
		/// Check boxes are displayed next to the tree nodes in the tree view control. The user can check directories, the subdirectories are checked recursive.
		/// </summary>
		RecursiveChecked
	}
	/// <summary>
	/// Defines the DriveTypes used for Win32_LogicalDisk<seealso cref="TreeViewFolderBrowser"/>.This enumeration can be treated as a bit field, that is, a set of flags.
	/// </summary>
	[Flags]
	[Editor(typeof(Raccoom.Windows.Forms.Design.FlagsEditor), typeof(UITypeEditor))]
	public enum DriveTypes
	{
		
		/// <summary>All drive types</summary>
		All = NoRootDirectory | RemovableDisk | LocalDisk | NetworkDrive | CompactDisc | RAMDisk,
		/// <summary>
		/// NoRootDirectory
		/// </summary>
		NoRootDirectory = 0x0001,
		/// <summary>
		/// Drive has removable media. This includes all floppy drives and many other varieties of storage devices.
		/// </summary>
		RemovableDisk = 0x0002,
		/// <summary>
		/// Drive has fixed (nonremovable) media. This includes all hard drives, including hard drives that are removable.
		/// </summary>
		LocalDisk = 0x0004,
		/// <summary>
		/// Network drives. This includes drives shared anywhere on a network.
		/// </summary>
		NetworkDrive = 0x0008,
		/// <summary>
		/// Drive is a CD-ROM. No distinction is made between read-only and read/write CD-ROM drives.
		/// </summary>
		CompactDisc= 0x0020,
		/// <summary>
		/// Drive is a block of Random Access Memory (RAM) on the local computer that behaves like a disk drive.
		/// </summary>
		RAMDisk= 0x0040
	}
	/// <summary>
	/// Defines the DriveTypes used for Win32_LogicalDisk<seealso cref="TreeViewFolderBrowser"/>.This enumeration can a<b>not</b> be treated as a bit field
	/// </summary>
	public enum Win32_LogicalDiskDriveTypes
	{
		/// <summary>
		/// NoRootDirectory
		/// </summary>
		NoRootDirectory = 1,
		/// <summary>
		/// Drive has removable media. This includes all floppy drives and many other varieties of storage devices.
		/// </summary>
		RemovableDisk,
		/// <summary>
		/// Drive has fixed (nonremovable) media. This includes all hard drives, including hard drives that are removable.
		/// </summary>
		LocalDisk,
		/// <summary>
		/// Network drives. This includes drives shared anywhere on a network.
		/// </summary>
		NetworkDrive,
		/// <summary>
		/// Drive is a CD-ROM. No distinction is made between read-only and read/write CD-ROM drives.
		/// </summary>
		CompactDisc,
		/// <summary>
		/// Drive is a block of Random Access Memory (RAM) on the local computer that behaves like a disk drive.
		/// </summary>
		RAMDisk
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0


Written By
Software Developer (Senior)
Switzerland Switzerland
My interest is in the future because I am going to spend the rest of my life there. (Charles Kettering)

Biography

  • 1996 - 1998 PC Board PPL, HTML, DHTML, Javascript and ASP
  • 1999 - 2001 coding Centura against Sql Database (SqlBase,MSSQL,Oracle)
  • 2002 - 2004 C# Windows Forms
  • 2005 - 2006 C# ASP.NET, Windows Forms
  • 2006 - 2009 C#, WCF, WF, WPF
  • 2010 - 2012 C#, Dynamics CRM, Sharepoint, Silverlight
  • 2013 - 2013 C#, WCF DS (OData), WF, WPF
  • 2014 - 2016 C#, Azure PaaS, Identity, OWIN, OData, Web Api
  • 2017 - now C#, aspnet.core, IdentityServer4, TypeScript & Angular @ Azure IaaS or PaaS

Interests

  • family & friends
  • chilaxing ,)
  • coding

Comments and Discussions