Multiple selection in a TreeView

The code demonstrates an example how multiple selection can be implemented in C# using TreeView control.

treeview multiselection

Download Source Code

Initialization

Font boldFont;
Font normalFont;
List selectedNodes;
TreeNode previousNode;

public Form1()
{
    selectedNodes = new List();
    boldFont = new Font("Arial", 10, FontStyle.Bold);
    normalFont = new Font("Arial", 10, FontStyle.Regular);
    InitializeComponent();
}

Build a tree of My Documents folders

private void Form1_Shown(object sender, EventArgs e)
{
    String directory = Environment.GetFolderPath(
        Environment.SpecialFolder.MyDocuments);
    BuildTree(directory, null);
}

private void BuildTree(String directory, TreeNode node)
{
    try
    {
        String[] subdirectories = Directory.GetDirectories(directory);
        foreach (String subdirectory in subdirectories)
        {
            String name = Path.GetFileName(subdirectory);
            TreeNode subnode = (node == null) ?
                treeView1.Nodes.Add(name) : node.Nodes.Add(name);
            subnode.NodeFont = boldFont;
            subnode.Text = subnode.Text; // google: 94354 treenode
            BuildTree(subdirectory, subnode);
        }
        String[] files = Directory.GetFiles(directory);
        foreach (String file in files)
        {
            String name = Path.GetFileName(file);
            TreeNode subnode = (node == null) ?
                treeView1.Nodes.Add(name) : node.Nodes.Add(name);
            subnode.NodeFont = normalFont;
            subnode.Text = subnode.Text; // google: 94354 treenode
        }
    }
    catch (Exception)
    {
         // Ignore exception, e.g. when access to a folder denied
    }
}

Handle node selection

private void treeView1_BeforeSelect(object sender, TreeViewCancelEventArgs e)
{
    // cancel selection, the selection will be handled in MouseDown
    e.Cancel = true;
}

private void treeView1_MouseDown(object sender, MouseEventArgs e)
{
    TreeNode currentNode = treeView1.GetNodeAt(e.Location);
    if (currentNode == null) return;
    currentNode.BackColor = treeView1.BackColor;
    currentNode.ForeColor = treeView1.ForeColor;

    bool control = (ModifierKeys == Keys.Control);
    bool shift = (ModifierKeys == Keys.Shift);

    if (control)
    {

        // the node clicked with control button pressed:
        // invert selection of the current node
        List addedNodes = new List();
        List removedNodes = new List();
        if (!selectedNodes.Contains(currentNode))
        {
            addedNodes.Add(currentNode);
            previousNode = currentNode;
        }
        else
        {
            removedNodes.Add(currentNode);
        }
        changeSelection(addedNodes, removedNodes);
    }
    else if (shift && previousNode != null)
    {
        if (currentNode.Parent == previousNode.Parent)
        {
            // the node clicked with shift button pressed:
            // if current node and previously selected node
            // belongs to the same parent,
            // select range of nodes between these two
            List addedNodes = new List();
            List removedNodes = new List();
            bool selection = false;
            bool selectionEnd = false;

            TreeNodeCollection nodes = null;
            if (previousNode.Parent == null)
            {
                nodes = treeView1.Nodes;
            }
            else
            {
                nodes = previousNode.Parent.Nodes;
            }
            foreach (TreeNode n in nodes)
            {
                if (n == currentNode || n == previousNode)
                {
                    if (selection)
                    {
                        selectionEnd = true;
                    }
                    if (!selection)
                    {
                        selection = true;
                    }
                }
                if (selection && !selectedNodes.Contains(n))
                {
                    addedNodes.Add(n);
                }
                if (selectionEnd)
                {
                    break;
                }
            }

            if (addedNodes.Count > 0)
            {
                changeSelection(addedNodes, removedNodes);
            }
        }
    }
    else
    {
        if (!currentNode.NodeFont.Bold)
        {
            // single click:
            // remove all selected nodes
            // and add current node
            List addedNodes = new List();
            List removedNodes = new List();
            removedNodes.AddRange(selectedNodes);
            if (removedNodes.Contains(currentNode))
            {
                removedNodes.Remove(currentNode);
            }
            else
            {
                addedNodes.Add(currentNode);
            }
            changeSelection(addedNodes, removedNodes);
            previousNode = currentNode;
        }
    }
}

protected void changeSelection(List addedNodes, List removedNodes)
{
    foreach (TreeNode n in addedNodes)
    {
        if (!n.NodeFont.Bold)
        {
            n.BackColor = SystemColors.Highlight;
            n.ForeColor = SystemColors.HighlightText;
            selectedNodes.Add(n);
        }
    }
    foreach (TreeNode n in removedNodes)
    {
        n.BackColor = treeView1.BackColor;
        n.ForeColor = treeView1.ForeColor;
        selectedNodes.Remove(n);
    }
}