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