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;
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| }
 |