440 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			440 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
/*********************************************************************************************
 | 
						|
 * Copyright 2005 - Volian Enterprises, Inc. All rights reserved.
 | 
						|
 * Volian Enterprises - Proprietary Information - DO NOT COPY OR DISTRIBUTE
 | 
						|
 * ------------------------------------------------------------------------------
 | 
						|
 * $Workfile: TreeViewMultiSelect.cs $     $Revision: 1 $
 | 
						|
 * $Author: Kathy $   $Date: 3/08/05 1:45p $
 | 
						|
 *
 | 
						|
 * $History: TreeViewMultiSelect.cs $
 | 
						|
 * 
 | 
						|
 * *****************  Version 1  *****************
 | 
						|
 * User: Kathy        Date: 3/08/05    Time: 1:45p
 | 
						|
 * Created in $/LibSource/GUI_Utils
 | 
						|
 * Approval
 | 
						|
 * 
 | 
						|
 * *****************  Version 1  *****************
 | 
						|
 * User: Kathy        Date: 3/08/05    Time: 1:40p
 | 
						|
 * Created in $/LibSource/GUI_Utils/GUI_Utils
 | 
						|
 * 
 | 
						|
 * *****************  Version 1  *****************
 | 
						|
 * User: Kathy        Date: 3/08/05    Time: 1:33p
 | 
						|
 * Created in $/LibSource/GUI_Utils/GUI_Utils
 | 
						|
 * 
 | 
						|
 * *****************  Version 1  *****************
 | 
						|
 * User: Kathy        Date: 3/08/05    Time: 1:32p
 | 
						|
 * Created in $/LibSource/GUI_Utils/GUI_Utils
 | 
						|
 * 
 | 
						|
 * *****************  Version 1  *****************
 | 
						|
 * User: Kathy        Date: 3/08/05    Time: 1:30p
 | 
						|
 * Created in $/GUI_Utils/GUI_Utils
 | 
						|
 *********************************************************************************************/
 | 
						|
using System;
 | 
						|
using System.Collections;
 | 
						|
using System.ComponentModel;
 | 
						|
using System.Drawing;
 | 
						|
using System.Data;
 | 
						|
using System.Windows.Forms;
 | 
						|
 | 
						|
namespace MultiSelectTreeView
 | 
						|
{
 | 
						|
	/// <summary>
 | 
						|
	/// Summary description for MultiSelectTreeView.
 | 
						|
	/// The MultiSelectTreeView inherits from System.Windows.Forms.TreeView to 
 | 
						|
	/// allow user to select multiple nodes.
 | 
						|
	/// The underlying comctl32 TreeView doesn't support multiple selection.
 | 
						|
	/// Hence this MultiSelectTreeView listens for the BeforeSelect && AfterSelect
 | 
						|
	/// events to dynamically change the BackColor of the individual treenodes to
 | 
						|
	/// denote selection. 
 | 
						|
	/// It then adds the TreeNode to the internal arraylist of currently
 | 
						|
	/// selectedNodes after validation checks.
 | 
						|
	/// 
 | 
						|
	/// The MultiSelectTreeView supports
 | 
						|
	///		1) Select + Control will add the current node to list of SelectedNodes
 | 
						|
	///		2) Select + Shift  will add the current node and all the nodes between the two 
 | 
						|
	///			(if the start node and end node is at the same level)
 | 
						|
	///		3) Control + A when the MultiSelectTreeView has focus will select all Nodes.
 | 
						|
	///		
 | 
						|
	/// 
 | 
						|
	/// </summary>
 | 
						|
	public class MultiSelectTreeView : System.Windows.Forms.TreeView
 | 
						|
	{
 | 
						|
		/// <summary>
 | 
						|
		///  This is private member which caches the last treenode user clicked
 | 
						|
		/// </summary>
 | 
						|
		private TreeNode lastNode;
 | 
						|
		private TreeNode ParentNode;
 | 
						|
		private int SelectNodes_ImageId;
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		///  This is private member stores the list of SelectedNodes
 | 
						|
		/// </summary>
 | 
						|
		private	ArrayList selectedNodes;
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		///  This is private member which caches the first treenode user clicked
 | 
						|
		/// </summary>
 | 
						|
		private TreeNode firstNode;
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// The constructor which initialises the MultiSelectTreeView.
 | 
						|
		/// </summary>
 | 
						|
		public MultiSelectTreeView(int imgid)
 | 
						|
		{
 | 
						|
			SelectNodes_ImageId=imgid;
 | 
						|
			selectedNodes = new ArrayList();
 | 
						|
		}
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// The constructor which initialises the MultiSelectTreeView.
 | 
						|
		/// </summary>
 | 
						|
		[
 | 
						|
		Category("Selection"),
 | 
						|
		Description("Gets or sets the selected nodes as ArrayList")
 | 
						|
		]
 | 
						|
		public ArrayList SelectedNodes
 | 
						|
		{
 | 
						|
			get
 | 
						|
			{
 | 
						|
				return selectedNodes;
 | 
						|
			}
 | 
						|
			set
 | 
						|
			{
 | 
						|
				DeselectNodes();
 | 
						|
				selectedNodes.Clear();
 | 
						|
				selectedNodes = value;
 | 
						|
				SelectNodes();
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		#region overrides
 | 
						|
		/// <summary>
 | 
						|
		///		If the user has pressed "Control+A" keys then select all nodes.
 | 
						|
		/// </summary>
 | 
						|
		/// <param name="e"></param>
 | 
						|
		protected override void OnKeyUp(KeyEventArgs e)
 | 
						|
		{
 | 
						|
			base.OnKeyDown (e);
 | 
						|
			bool Pressed = (e.Control && ((e.KeyData & Keys.A) == Keys.A));
 | 
						|
			if (Pressed)
 | 
						|
			{
 | 
						|
			//	SelectAllNodes(this.Nodes);		//we won't allow selection of all.
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		///		This Function starts the multiple selection.
 | 
						|
		/// </summary>
 | 
						|
		/// <param name="e"></param>
 | 
						|
		protected override void OnBeforeSelect(TreeViewCancelEventArgs e)
 | 
						|
		{
 | 
						|
			base.OnBeforeSelect(e);
 | 
						|
 | 
						|
			//if (e.Node.SelectedImageIndex!=SelectNodes_ImageId)return;
 | 
						|
			//if (selectedNodes.Count>1 && e.Node.Parent != ParentNode) return;
 | 
						|
			//ParentNode=e.Node.Parent;
 | 
						|
 | 
						|
			//Check for the current keys press..	
 | 
						|
			bool isControlPressed = (ModifierKeys==Keys.Control);
 | 
						|
			bool isShiftPressed = (ModifierKeys==Keys.Shift);
 | 
						|
			bool isRightMouse = (Control.MouseButtons==MouseButtons.Right);
 | 
						|
 | 
						|
			if (isRightMouse)return;
 | 
						|
 | 
						|
			//If control is pressed and the selectedNodes contains current Node
 | 
						|
			//Deselect that node...
 | 
						|
			//Remove from the selectedNodes Collection...
 | 
						|
			if (isControlPressed && selectedNodes.Contains(e.Node))
 | 
						|
			{
 | 
						|
				DeselectNodes();
 | 
						|
				selectedNodes.Remove( e.Node );
 | 
						|
				SelectNodes();
 | 
						|
				//MultiSelectTreeView has handled this event ....
 | 
						|
				//Windows.Forms.TreeView should eat this event.
 | 
						|
				e.Cancel = true;
 | 
						|
				return;
 | 
						|
			}
 | 
						|
			
 | 
						|
 | 
						|
			//else (if Shift key is pressed)
 | 
						|
			//Start the multiselection ...
 | 
						|
            //Since Shift is pressed we would "SELECT" 
 | 
						|
			///all nodes from first node - to last node
 | 
						|
			lastNode = e.Node;
 | 
						|
 | 
						|
			//If Shift not pressed...
 | 
						|
			//Remember this Node to be the Start Node .. in case user presses Shift to 
 | 
						|
			//select multiple nodes.
 | 
						|
			if (!isShiftPressed&&!isControlPressed&&(e.Node.ImageIndex==this.SelectNodes_ImageId)) 
 | 
						|
				firstNode = e.Node;
 | 
						|
		}
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		///		This function ends the multi selection. Also adds and removes the node to
 | 
						|
		///		the selectedNodes depending upon the keys pressed.
 | 
						|
		/// </summary>
 | 
						|
		/// <param name="e"></param>
 | 
						|
		protected override void OnAfterSelect(TreeViewEventArgs e)
 | 
						|
		{
 | 
						|
			base.OnAfterSelect(e);
 | 
						|
			//if (e.Node.SelectedImageIndex!=SelectNodes_ImageId)return;
 | 
						|
			//if (selectedNodes.Count>1 && e.Node.Parent != ParentNode) return;
 | 
						|
			ParentNode=e.Node.Parent;
 | 
						|
			//Check for the current keys press..
 | 
						|
			bool isControlPressed = (ModifierKeys==Keys.Control);
 | 
						|
			bool isShiftPressed = (ModifierKeys==Keys.Shift);
 | 
						|
			bool isRightMouse = (Control.MouseButtons==MouseButtons.Right);
 | 
						|
 | 
						|
			if (isRightMouse)return;
 | 
						|
			if (isControlPressed)
 | 
						|
			{
 | 
						|
				if ((!selectedNodes.Contains(e.Node ))&& (e.Node.SelectedImageIndex==SelectNodes_ImageId) && (selectedNodes.Count==0 || e.Node.Parent == ParentNode)) 
 | 
						|
				{
 | 
						|
					//This is a new Node, so add it to the list.
 | 
						|
					selectedNodes.Add(e.Node);
 | 
						|
					ParentNode = e.Node.Parent;
 | 
						|
					SelectNodes();
 | 
						|
				}
 | 
						|
				else  if (selectedNodes.Contains(e.Node))
 | 
						|
				{
 | 
						|
					//If control is pressed and the selectedNodes contains current Node
 | 
						|
					//Deselect that node...
 | 
						|
					//Remove from the selectedNodes Collection...
 | 
						|
					DeselectNodes();
 | 
						|
					selectedNodes.Remove( e.Node );
 | 
						|
					if (selectedNodes.Count==0)ParentNode=null;
 | 
						|
					SelectNodes();
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					//If control is pressed and this is not at the level of multi
 | 
						|
					// select, clear selectetdNodes.
 | 
						|
					if (selectedNodes!=null && selectedNodes.Count>0)
 | 
						|
					{
 | 
						|
						DeselectNodes();
 | 
						|
						selectedNodes.Clear();
 | 
						|
						ParentNode=null;
 | 
						|
					}
 | 
						|
					selectedNodes.Add( e.Node );
 | 
						|
				}
 | 
						|
			}
 | 
						|
			else 
 | 
						|
			{
 | 
						|
				// SHIFT is pressed
 | 
						|
 | 
						|
				if (isShiftPressed && e.Node.SelectedImageIndex==SelectNodes_ImageId)
 | 
						|
				{
 | 
						|
					if (firstNode==null)
 | 
						|
						firstNode=e.Node;
 | 
						|
					else
 | 
						|
					{
 | 
						|
						//Start Looking for the start and end nodes to select all the nodes between them.				
 | 
						|
						TreeNode uppernode = firstNode;
 | 
						|
						TreeNode bottomnode = e.Node;
 | 
						|
						//Check Parenting Upper ---> Bottom
 | 
						|
						//Is Upper Node parent (direct or indirect) of Bottom Node
 | 
						|
						bool bParent = CheckIfParent(uppernode, bottomnode); 
 | 
						|
						if (!bParent)
 | 
						|
						{
 | 
						|
							//Check Parenting the other way round
 | 
						|
							bParent = CheckIfParent(bottomnode, uppernode);
 | 
						|
							if (bParent) // SWAPPING
 | 
						|
							{
 | 
						|
								TreeNode temp = uppernode;
 | 
						|
								uppernode = bottomnode;
 | 
						|
								bottomnode = temp;
 | 
						|
							}
 | 
						|
						}
 | 
						|
						if (bParent)
 | 
						|
						{
 | 
						|
							TreeNode n = bottomnode;
 | 
						|
							while ( n != uppernode.Parent)
 | 
						|
							{
 | 
						|
								if ( !selectedNodes.Contains( n ) ) 
 | 
						|
									selectedNodes.Add( n );
 | 
						|
								n = n.Parent;
 | 
						|
							}
 | 
						|
						}
 | 
						|
							// Parenting Fails ... but check if the NODES are on the same LEVEL.
 | 
						|
						else
 | 
						|
						{
 | 
						|
							if ( (uppernode.Parent==null && bottomnode.Parent==null) || (uppernode.Parent!=null && uppernode.Parent.Nodes.Contains( bottomnode )) ) // are they siblings ?
 | 
						|
							{
 | 
						|
								int nIndexUpper = uppernode.Index;
 | 
						|
								int nIndexBottom = bottomnode.Index;
 | 
						|
								//Need to SWAP if the order is reversed...
 | 
						|
								if (nIndexBottom < nIndexUpper) 
 | 
						|
								{
 | 
						|
									TreeNode temp = uppernode;
 | 
						|
									uppernode = bottomnode;
 | 
						|
									bottomnode = temp;
 | 
						|
									nIndexUpper = uppernode.Index;
 | 
						|
									nIndexBottom = bottomnode.Index;
 | 
						|
								}
 | 
						|
 | 
						|
								TreeNode n = uppernode;
 | 
						|
								selectedNodes.Clear();
 | 
						|
								while (nIndexUpper < nIndexBottom)
 | 
						|
								{
 | 
						|
									//Add all the nodes if nodes not present in the current
 | 
						|
									//SelectedNodes list...
 | 
						|
 | 
						|
									if (!selectedNodes.Contains( n )) 
 | 
						|
									{
 | 
						|
										selectedNodes.Add(n);
 | 
						|
										SelectAllNodesInNode(n.Nodes, n);
 | 
						|
									}
 | 
						|
									n = n.NextNode;
 | 
						|
									nIndexUpper++;
 | 
						|
								}
 | 
						|
								//Add the Last Node.
 | 
						|
								selectedNodes.Add(n);
 | 
						|
							}
 | 
						|
							else
 | 
						|
							{
 | 
						|
								if ( !selectedNodes.Contains( uppernode ) ) selectedNodes.Add( uppernode );
 | 
						|
								if ( !selectedNodes.Contains( bottomnode ) )selectedNodes.Add( bottomnode );
 | 
						|
							}
 | 
						|
						}
 | 
						|
						ParentNode = e.Node.Parent;
 | 
						|
						SelectNodes();
 | 
						|
						//Reset the firstNode counter for subsequent "SHIFT" keys.
 | 
						|
						firstNode = e.Node; 
 | 
						|
					}
 | 
						|
				} 
 | 
						|
				else
 | 
						|
				{
 | 
						|
					// If Normal selection then add this to SelectedNodes Collection.
 | 
						|
					if (selectedNodes!=null && selectedNodes.Count>0)
 | 
						|
					{
 | 
						|
						DeselectNodes();
 | 
						|
						selectedNodes.Clear();
 | 
						|
					}
 | 
						|
					selectedNodes.Add( e.Node );
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		///		Overriden OnLostFocus to mimic TreeView's behavior of de-selecting nodes.
 | 
						|
		/// </summary>
 | 
						|
		/// <param name="e"></param>
 | 
						|
		protected override void OnLostFocus(EventArgs e)
 | 
						|
		{
 | 
						|
			base.OnLostFocus (e);
 | 
						|
			DeselectNodes();
 | 
						|
		}
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		///		Overriden OnGotFocus to mimic TreeView's behavior of selecting nodes.
 | 
						|
		/// </summary>
 | 
						|
		/// <param name="e"></param>
 | 
						|
		protected override void OnGotFocus(EventArgs e)
 | 
						|
		{
 | 
						|
			base.OnGotFocus (e);
 | 
						|
			SelectNodes();
 | 
						|
		}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
		#endregion overrides
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		///		Private function to check the parenting of the two nodes passed.
 | 
						|
		/// </summary>
 | 
						|
		/// <param name="parentNode"></param>
 | 
						|
		/// <param name="childNode"></param>
 | 
						|
		/// <returns></returns>
 | 
						|
		private bool CheckIfParent(TreeNode parentNode, TreeNode childNode)
 | 
						|
		{
 | 
						|
			if (parentNode == childNode)
 | 
						|
				return true;
 | 
						|
 | 
						|
			TreeNode node = childNode;
 | 
						|
			bool parentFound = false;
 | 
						|
			while (!parentFound && node != null)
 | 
						|
			{
 | 
						|
				node = node.Parent;
 | 
						|
				parentFound = (node == parentNode);
 | 
						|
			}
 | 
						|
			return parentFound;
 | 
						|
		}
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		///		This function provides the user feedback that the node is selected
 | 
						|
		///		Basically the BackColor and the ForeColor is changed for all
 | 
						|
		///		the nodes in the selectedNodes collection.
 | 
						|
		/// </summary>
 | 
						|
		private void SelectNodes()
 | 
						|
		{
 | 
						|
			foreach ( TreeNode n in selectedNodes )
 | 
						|
			{
 | 
						|
				n.BackColor = SystemColors.Highlight;
 | 
						|
				n.ForeColor = SystemColors.HighlightText;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		///		This function provides the user feedback that the node is de-selected
 | 
						|
		///		Basically the BackColor and the ForeColor is changed for all
 | 
						|
		///		the nodes in the selectedNodes collection.
 | 
						|
		/// </summary>
 | 
						|
		private void DeselectNodes()
 | 
						|
		{
 | 
						|
			if (selectedNodes.Count==0) return;
 | 
						|
 | 
						|
			TreeNode node = (TreeNode) selectedNodes[0];
 | 
						|
			if (node.TreeView==null) return;		// on dispose, at end of program
 | 
						|
 | 
						|
			Color backColor = node.TreeView.BackColor;
 | 
						|
			Color foreColor= node.TreeView.ForeColor;
 | 
						|
 | 
						|
			foreach ( TreeNode n in selectedNodes )
 | 
						|
			{
 | 
						|
				n.BackColor = backColor;
 | 
						|
				n.ForeColor = foreColor;
 | 
						|
			}
 | 
						|
 | 
						|
		}
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		///		This function selects all the Nodes in the MultiSelectTreeView..
 | 
						|
		/// </summary>
 | 
						|
		private void SelectAllNodes(TreeNodeCollection nodes)
 | 
						|
		{
 | 
						|
			foreach (TreeNode n in this.Nodes)
 | 
						|
			{
 | 
						|
				selectedNodes.Add(n);
 | 
						|
				if (n.Nodes.Count > 1)
 | 
						|
				{
 | 
						|
					SelectAllNodesInNode(n.Nodes, n);
 | 
						|
				}
 | 
						|
									
 | 
						|
			}
 | 
						|
			SelectNodes();
 | 
						|
		}
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		///		Recursive function selects all the Nodes in the MultiSelectTreeView's Node
 | 
						|
		/// </summary>
 | 
						|
		private void SelectAllNodesInNode(TreeNodeCollection nodes, TreeNode node)
 | 
						|
		{
 | 
						|
			foreach (TreeNode n in node.Nodes)
 | 
						|
			{
 | 
						|
				selectedNodes.Add(n);
 | 
						|
				if (n.Nodes.Count > 1)
 | 
						|
				{
 | 
						|
					SelectAllNodesInNode(n.Nodes, n);
 | 
						|
				}
 | 
						|
			}
 | 
						|
			SelectNodes();
 | 
						|
		}
 | 
						|
 | 
						|
		public bool MultiSelectActive()
 | 
						|
		{
 | 
						|
			if (selectedNodes.Count>1)return true;
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
 | 
						|
	}
 | 
						|
}
 |