/********************************************************************************************* * 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 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. /// /// /// public class MultiSelectTreeView : System.Windows.Forms.TreeView { /// /// This is private member which caches the last treenode user clicked /// private TreeNode lastNode; private TreeNode ParentNode; private int SelectNodes_ImageId; /// /// This is private member stores the list of SelectedNodes /// private ArrayList selectedNodes; /// /// This is private member which caches the first treenode user clicked /// private TreeNode firstNode; /// /// The constructor which initialises the MultiSelectTreeView. /// public MultiSelectTreeView(int imgid) { SelectNodes_ImageId=imgid; selectedNodes = new ArrayList(); } /// /// The constructor which initialises the MultiSelectTreeView. /// [ 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 /// /// If the user has pressed "Control+A" keys then select all nodes. /// /// 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. } } /// /// This Function starts the multiple selection. /// /// 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; } /// /// This function ends the multi selection. Also adds and removes the node to /// the selectedNodes depending upon the keys pressed. /// /// 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 ); } } } /// /// Overriden OnLostFocus to mimic TreeView's behavior of de-selecting nodes. /// /// protected override void OnLostFocus(EventArgs e) { base.OnLostFocus (e); DeselectNodes(); } /// /// Overriden OnGotFocus to mimic TreeView's behavior of selecting nodes. /// /// protected override void OnGotFocus(EventArgs e) { base.OnGotFocus (e); SelectNodes(); } #endregion overrides /// /// Private function to check the parenting of the two nodes passed. /// /// /// /// 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; } /// /// 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. /// private void SelectNodes() { foreach ( TreeNode n in selectedNodes ) { n.BackColor = SystemColors.Highlight; n.ForeColor = SystemColors.HighlightText; } } /// /// 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. /// 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; } } /// /// This function selects all the Nodes in the MultiSelectTreeView.. /// private void SelectAllNodes(TreeNodeCollection nodes) { foreach (TreeNode n in this.Nodes) { selectedNodes.Add(n); if (n.Nodes.Count > 1) { SelectAllNodesInNode(n.Nodes, n); } } SelectNodes(); } /// /// Recursive function selects all the Nodes in the MultiSelectTreeView's Node /// 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; } } }