Click here to Skip to main content
Click here to Skip to main content

C# TreeView with multiple selection

By , 18 Aug 2002
 

The .NET TreeView control has no built-in multiple selection. Let there be. This article about C# depicts a tree view control with multiple selection, derived from the base .NET TreeView control. It supports CTRL and SHIFT combinations.

How to use it

This control is a C# control. Once compiled, it becomes a managed .NET component and behaves much like good ol' ActiveX components for VB developers. To use it in your application, you have at least two options. The first is to simply reference the TreeViewMS project given in the source project, by clicking right on your current Windows Form application and choosing Add Reference, then browse to TreeViewMS.csproj. The screen capture below shows the steps:

Adding a reference to the new TreeView control to your code

You may also add the tree view control once for all in your Toolbox window, then simply drag&drop it onto your Form. In order to do this, show up the .NET Studio Toolbox window, then right click and choose Customize toolbox, then choose the .NET Framework components tab, and browse to the compiled control: TreeViewMS.dll, as in the capture below:

Adding the new tree view control to the Visual Studio .NET toolbox

Once you have added this tree view control in a form, you may start to use it like the base .NET TreeView control. The addition lies in a new exposed property, SelectedNodes, which returns the collection of selected TreeNode items. If we take the demo app, which adds selected items in the list view on the right, then code goes like this:

    private TreeViewMS.TreeViewMS treeViewMS1;
    private System.Windows.Forms.ListView listView1;
    ...

    // add selected items from treeview to the
    // listview on the right hand side
    foreach (TreeNode n in treeViewMS1.SelectedNodes)
    {
       listView1.Items.Add( n.Text, n.ImageIndex );
    }

SelectedNodes is also a read-write property. What follows is a sample code that forces the selection of given tree items:

    // valid item from the sample tree
    TreeNode n1 = treeViewMS1.Nodes[0].Nodes[0].Nodes[0]; 
    // valid item from the sample tree
    TreeNode n2 = treeViewMS1.Nodes[0].Nodes[0].Nodes[2]; 
    ArrayList coll = new ArrayList();
    coll.Add(n1);
    coll.Add(n2);
    treeViewMS1.SelectedNodes = coll;

Technical details

Of course, this control is derived from the base TreeView control:

public class TreeViewMS : System.Windows.Forms.TreeView
{
    // class members
    protected ArrayList     m_coll;
    protected TreeNode      m_lastNode, m_firstNode;

    ...

    /* API method */ public ArrayList SelectedNodes
    {
        get
        {
            return m_coll;
        }
        set
        {
            removePaintFromNodes();
            m_coll.Clear();
            m_coll = value;
            paintSelectedNodes();
        }
    }

}

What we need to control item selection is events such like BEFORESELECT and AFTERSELECT to get the current selected node. BEFORESELECT is only useful to undo a node selection, for instance when you select twice a node with CTRL down.

Because Microsoft has clearly figured out that TreeView s were likely to be derived, they have overridable methods BeforeSelect(...) and AfterSelect(...) that don't interfere with the events named the same. All what we need is override the 2 methods and not forget to call the base class in the implementation:

protected override void OnBeforeSelect(TreeViewCancelEventArgs e)
{
    // e.Node is the current node exposed by the base TreeView control
    base.OnBeforeSelect(e);

    bool bControl = (ModifierKeys==Keys.Control);
    bool bShift = (ModifierKeys==Keys.Shift);

    // selecting twice the node while pressing CTRL ?
    if (bControl && m_coll.Contains( e.Node ) )
    {
        // unselect it
        // (let framework know we don't want selection this time)
        e.Cancel = true;

        // update nodes
        removePaintFromNodes();
        m_coll.Remove( e.Node );
        paintSelectedNodes();
        return;
    }

    m_lastNode = e.Node;
    if (!bShift) m_firstNode = e.Node; // store begin of shift sequence
}

protected override void OnAfterSelect(TreeViewEventArgs e)
{
    // e.Node is the current node exposed by the base TreeView control

    base.OnAfterSelect(e);

    bool bControl = (ModifierKeys==Keys.Control);
    bool bShift = (ModifierKeys==Keys.Shift);

    if (bControl)
    {
        if ( !m_coll.Contains( e.Node ) ) // new node ?
        {
            m_coll.Add( e.Node );
        }
        else  // not new, remove it from the collection
        {
            removePaintFromNodes();
            m_coll.Remove( e.Node );
        }
        paintSelectedNodes();
    }
    else 
    {
        if (bShift)
        {
            Queue myQueue = new Queue();

            TreeNode uppernode = m_firstNode;
            TreeNode bottomnode = e.Node;

            // case 1 : begin and end nodes are parent
            bool bParent = isParent(m_firstNode, e.Node);
            if (!bParent)
            {
                bParent = isParent(bottomnode, uppernode);
                if (bParent) // swap nodes
                {
                    TreeNode t = uppernode;
                    uppernode = bottomnode;
                    bottomnode = t;
                }
            }
            if (bParent)
            {
                 TreeNode n = bottomnode;
                 while ( n != uppernode.Parent)
                 {
                     if ( !m_coll.Contains( n ) ) // new node ?
                         myQueue.Enqueue( n );

                      n = n.Parent;
                 }
            }
            // case 2 : nor the begin nor the
            // end node are descendant one another
            else
            {
                 // are they siblings ?                 

                 if ( (uppernode.Parent==null && bottomnode.Parent==null) 
                       || (uppernode.Parent!=null && 
                       uppernode.Parent.Nodes.Contains( bottomnode )) )
                 {
                      int nIndexUpper = uppernode.Index;
                      int nIndexBottom = bottomnode.Index;
                      if (nIndexBottom < nIndexUpper) // reversed?
                      {
                           TreeNode t = uppernode;
                           uppernode = bottomnode;
                           bottomnode = t;
                           nIndexUpper = uppernode.Index;
                           nIndexBottom = bottomnode.Index;
                      }

                      TreeNode n = uppernode;
                      while (nIndexUpper <= nIndexBottom)
                      {
                           if ( !m_coll.Contains( n ) ) // new node ?
                               myQueue.Enqueue( n );

                           n = n.NextNode;

                           nIndexUpper++;
                      } // end while

                  }
                  else
                  {
                      if ( !m_coll.Contains( uppernode ) ) 
                          myQueue.Enqueue( uppernode );
                      if ( !m_coll.Contains( bottomnode ) ) 
                          myQueue.Enqueue( bottomnode );
                  }

             }

             m_coll.AddRange( myQueue );

             paintSelectedNodes();
             // let us chain several SHIFTs if we like it
             m_firstNode = e.Node; 

         } // end if m_bShift
         else
         {
              // in the case of a simple click, just add this item
              if (m_coll!=null && m_coll.Count>0)
              {
                   removePaintFromNodes();
                   m_coll.Clear();
              }
              m_coll.Add( e.Node );
          }
     }
}


// Helpers
//
//


protected bool isParent(TreeNode parentNode, TreeNode childNode)
{
    if (parentNode==childNode)
        return true;

    TreeNode n = childNode;
    bool bFound = false;
    while (!bFound && n!=null)
    {
        n = n.Parent;
        bFound = (n == parentNode);
    }
    return bFound;
}


protected void paintSelectedNodes()
{
    foreach ( TreeNode n in m_coll )
    {
        n.BackColor = SystemColors.Highlight;
        n.ForeColor = SystemColors.HighlightText;
    }
}

protected void removePaintFromNodes()
{
    if (m_coll.Count==0) return;

    TreeNode n0 = (TreeNode) m_coll[0];
    Color back = n0.TreeView.BackColor;
    Color fore = n0.TreeView.ForeColor;

    foreach ( TreeNode n in m_coll )
    {
        n.BackColor = back;
        n.ForeColor = fore;
    }
}

As you may note, SelectedNodes returns a System.Collections.ArrayList instance of underlying TreeView items, not the more natural System.Windows.Forms.TreeNodeCollection instance. Why this? In fact, that's not up to developers, the TreeNodeCollection is deliberately provided by the .NET framework with a hidden constructor, hence it is not possible to reuse it. This has raised some concern on public newsgroups, but Microsoft people have not agreed to change this anytime soon.

Simpler reuse

What if you don't want to redistribute the TreeViewMS.dll library (I admit that separate libraries are always bottlenecks)? All you have to do is take the code for OnBeforeSelect() and OnAfterSelect() as described above and attach it to the standard TreeView events.

Thanks to David Sleeckx for the bug hunting.

History

  • August 8, 2002 - First version.
  • Updated August 18, 2002.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Stephane Rodriguez.
France France
Member
Addicted to reverse engineering. At work, I am developing business intelligence software in a team of smart people (independent software vendor).
 
Need a fast Excel generation component? Try xlsgen.
 

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionHow to find thus the selected node are Parent or Childmemberarventh30 May '12 - 2:17 
How to find thus the selected node are Parent or Child
 
i show a message box when the user select the Parent Node
QuestionLicense ClarificationmemberMember 181268929 Dec '11 - 11:34 
Stephane,
 
I realise that this article doesn't mention any license restrictions so maybe I should assume that the code is free to use in any context, but before I include it in any commercial code could you please assure me this is the case? I'd be really gratefully if you could do that for me.
 
Thanks,
 
Lloyd Maddock (dev manager)
GeneralMy vote of 4memberArmando de la Torre22 Oct '11 - 12:10 
Usefull
GeneralCheck for null in SelectedNodes settermemberMember 429184417 Apr '10 - 2:11 
The setter of SelectedNodes is dangerous since it permits setting SelectedNodes = null causing almost every method to crash. The setter should prohibit setting to null or create an empty collection in this case.
GeneralremovePaintFromNodes and a check on nullmemberzazoef16 Nov '09 - 8:32 
First : Great work this treeview, works fine and easy.
 
Second : Can you make removePaintFromNodes more robust by adding one if statement.
 
Like :
 
protected void removePaintFromNodes()
{
if (m_coll.Count==0) return;
 
TreeNode n0 = (TreeNode) m_coll[0];
if (n0 != null)
{
Color back = n0.TreeView.BackColor;
Color fore = n0.TreeView.ForeColor;
 
foreach (TreeNode n in m_coll)
{
n.BackColor = back;
n.ForeColor = fore;
}
}
}
 
At this moment the procedure crashes on blind clicking and selecting nodes.
 
Thanks a lot.
GeneralRe: removePaintFromNodes and a check on nullmemberlandete8523 Mar '12 - 5:03 
I agree with you.
 
First of all, great work Mr. Rodriguez.
 
And second, I still got some problems with this function, it looks to be solved with an additional condition on this if statement:
 
protected void removePaintFromNodes()
{
if (m_coll.Count == 0) return;
 
TreeNode n0 = (TreeNode)m_coll[0];
if ((n0 != null) && (n0.TreeView != null))
{
Color back = n0.TreeView.BackColor;
Color fore = n0.TreeView.ForeColor;
 
foreach (TreeNode n in m_coll)
{
n.BackColor = back;
n.ForeColor = fore;
}
}
}
Thank you very much.
GeneralBug when deleting nodes from the tree: invalid nodes in m_collmemberMember 26063281 Sep '09 - 5:06 
In my application I re-organize the tree structure by (among other thigs) deleting nodes using TreeNode.Nodes.Remove().
 
This leads to invalid nodes in m_coll since it is not updated when a node is deleted.
As a result you get a Null Reference Exception in removePaintFromNodes() where n0.TreeView is null when the node is no longer in the tree. In addition SelectedNodes will return TreeNodes no longer in the tree.
 
Since I could not find an Event (may someone can help here ?!) fired when the tree itself changes I implemented a cleanup function
 
protected void checkSelectedNodes() {
ArrayList toBeRemoved = new ArrayList();
foreach ( object o in m_coll ) {
if ((o as TreeNode).TreeView != this) {
toBeRemoved.Add(o);
}
}
foreach (object o in toBeRemoved) {
m_coll.Remove(o);
}
}
 
which I call in 2 places:
 
protected override void OnBeforeSelect(TreeViewCancelEventArgs e)
{
base.OnBeforeSelect(e);
 
checkSelectedNodes();
...
}
 
and
 
public ArrayList SelectedNodes
{
get
{
checkSelectedNodes();
return m_coll;
}
...
}
GeneralBug: Don't use TreeViewMS.SelectedNodes.Clear()memberMember 39983554 Jun '09 - 4:00 
It took me a while to figure out what the problem is exactly.
 
This code works:
TreeViewMs.SelectedNodes = new ArrayList();
... but this code doesn't:
TreeViewMs.SelectedNodes.Clear();
 
It's because Clear() doesn't trigger the set-accessor of the SelectedNodes property and therefore it doesn't draw the selection properly.
 
Maybe someone has a solution for this. Smile | :)
Generala bug related to the shift-clickmemberaerishan19 Sep '08 - 8:08 
Hello,
 
if user does a shift-click directly when there is no firstnode set, the application will crush.
i think in OnAfterSelect(), we should test if _firstNode is null, e.g.
replace
if (bShift)
by
if (bShift && _firstNode != null)
 
Thank you.
Questioncustomized TreeNode classmembersanjeevmedhi7 Aug '08 - 0:41 
Hi all,
 
I have a treeview in which i add node from TreeNode class and set treeview.checkboxes = false.
In that treeview now i want to add another node with checkbox(that node is from a customized TreeNode class) . Can anybody help me to design the customized TreeNode class.
 

Thanks
Generalcolor mismatch when disabling the treeview.memberMember 258632628 May '08 - 6:59 
When disableing the TreeView, the nodes which were selected once are not rendered correctly (they don't change their color into gray. It keep foreground color.)
 
So.. following should be changed.
 
protected void removePaintFromNodes()
{
if (m_coll.Count==0) return;

TreeNode n0 = (TreeNode) m_coll[0];
Color back = Color.AntiqueWhite;
Color fore = Color.Azure;
if (n0.TreeView != null)
{
 
back = Color.Empty; // This should be Empty
fore = Color.Empty; // This should be Empty
}
foreach (TreeNode n in m_coll)
{
n.BackColor = back;
n.ForeColor = fore;
}

 
}
 
Anyway, Thanks for the useful TreeViewMS.
QuestionhimemberMember 459203229 Feb '08 - 2:29 
How to Disable mouse click event when parent node is selected
 
Plz help me in this regards...........
QuestionHeelo StepehenmemberMember 459203228 Feb '08 - 23:51 
If i dont want to select the Child Nodes if parent node is selected if eventhough ctrl are shift if pressed what should i do,
i have included this dll as it is, if i select parent node child nodes will not get selected and viceversa needs to be implemented
 
Can u help me for this?
 
thanks in advance....
GeneralHelp with non-dll implementationmemberHayden Devlin13 Aug '07 - 6:03 
Hello,
 
I am quite new to C# programming so there are quite a few basic things I just don't know how to do, so I apologise if this is a really simple thing to do, but I want to implement this TreeViewMS technology without using the .dll, because I am not allowed to use .dll's in my project. At the bottom of the page it says just add the code, but I don't understand exactly what to do for that. Where should that code be added? I assume that you dont't mean (and can not anyway) add the code to the windows code, so I figure I have to make some kind of extended / inherited class with extended methods in, but can anyone offer me some help as to how to do that, as I am not sure. Is this how it needs to be done anyway? If so what exactly do you need to inherit/extend. I would appreciate any help anyone can offer as soon as possible because my project is quite urgent,
 
Thanks in advance,
 
Hayden Devlin
GeneralRe: Help with non-dll implementationmemberMember 459203214 Feb '08 - 22:11 
Plz help me i am new to C#
 
Have u used the above code without DLL if u used plz help me How u do that?
Generalanother small bugmemberalacevic28 Aug '06 - 16:40 
registering to receive an AfterSelection event,
and getting a list of selected nodes within the handler
returns selections without the current one because the
base handler is called before updating the selected collection.
to fix the issue, move "base.OnAfterSelect(e);" to bottom of method
protected override void OnAfterSelect(TreeViewEventArgs e).
 
someone should rewrite this control, it'd be very useful to have a good implementation of this.
 
these smileys are ridiculous Dead | X|
 
-A. Lacevic

GeneralRe: another small bug [modified]memberWilliam H Gates III3 Jul '08 - 20:41 
There are for sure better implementations out there, but all commercial Frown | :(
I was looking and willing to pay for a multiselect treeview which is derived from the .net TreeView that works in XP and Vista (with Vista theme) and only one came up (after a long search - this site somehow isn't even listed in google). I came across it in a directory listing.
 
If anyone's interested, here it is (commercial and don't know how to purchase it!!!):
http://www.zachsaw.co.cc/?pg=advanced_treeview[^]
 
EDIT: The author contacted me. Apparently, the page that I had posted was not supposed to be public yet. He has given me the correct link to his advanced tree view. Srory bout that!
 
modified on Saturday, July 12, 2008 11:19 AM

QuestionVarious colors in the treenode text?memberSaez31 May '06 - 7:56 
Would be possible for the treenode text have two colors?
 
For example a black name and then a grey number.
 
Thanks!
GeneralReload TreeviewmemberPaul_Fels21 May '06 - 2:20 
I cought an exception on reloading (re-populate) the treeview. The solution was to deselect the nodes before clearing, reload the treeview and select (the first) node again. When cleared, m_coll = null, so adapt this in the TreeViewMS code.
 
Reload the treeview:
// Reload the treeview to update for changes/additions     
// First set the selectedNode(s) to null as they are cleared
     treeViewMS1.SelectedNode = null;    
     treeViewMS1.SelectedNodes = null;
              
// Clear the treeview
     treeViewMS1.Nodes.Clear();
 
[Methode to reload the treeview]
 
// Set the selected node and nodes to the first node
// this is required for the treeview to work properly
     TreeNode selectedNode = treeViewMS1.Nodes[0];
     ArrayList coll = new ArrayList();
     coll.Add(selectedNode);
     treeViewMS1.SelectedNodes = coll;
     treeViewMS1.SelectedNode = selectedNode;                   
 
Change the code in the TreeViewMS solution:
 
public ArrayList SelectedNodes
{
     get
     {
          return m_coll;
     }
     set
     {
          removePaintFromNodes();
          if (m_coll != null)
          {
               m_coll.Clear();
          }
          m_coll = value;
          paintSelectedNodes();
     }
}
 
protected void paintSelectedNodes()
{
     if (m_coll == null) return;              
     if (m_coll.Count==0) return;
              
     foreach ( TreeNode n in m_coll )
     {
          n.BackColor = SystemColors.Highlight;
          n.ForeColor = SystemColors.HighlightText;
     }
}
 
protected void removePaintFromNodes()
{              
     if (m_coll == null)     return;              
     if (m_coll.Count==0) return;
 
     TreeNode n0 = (TreeNode) m_coll[0];
     Color back = this.BackColor;
     Color fore = this.ForeColor;
 
     foreach ( TreeNode n in m_coll )
     {
          n.BackColor = back;
          n.ForeColor = fore;
     }
 
}

GeneralRe: Reload TreeviewmemberStephane Rodriguez.21 May '06 - 20:29 

Thanks. In fact, the whole treeview multiple selection thing must be rewritten because there is no need to maintain a separate node collection : it is possible to derive a TreeNode class and add the appropriate properties to it.

GeneralNullException!memberMohanad!4 May '05 - 11:01 
protected void removePaintFromNodes()
{
if (m_coll.Count==0) return;
 
TreeNode n0 = (TreeNode) m_coll[0];
Color back = n0.TreeView.BackColor;
Color fore = n0.TreeView.ForeColor;
 
foreach ( TreeNode n in m_coll )
{
n.BackColor = back;
n.ForeColor = fore;
}
}
 
should be replaced by :
protected void removePaintFromNodes()
{
if (m_coll.Count==0) return;
 
TreeNode n0 = (TreeNode) m_coll[0];
Color back = this.BackColor;
Color fore = this.ForeColor;
 
foreach ( TreeNode n in m_coll )
{
n.BackColor = back;
n.ForeColor = fore;
}
}

 
Mohanad

GeneralRe: NullException!memberdkerr00724 Sep '08 - 13:21 
you rule, this fixed my problem!!! Thanks
GeneralNodes font and background colors aren't savedmemberArtem Kliatchkine1 Apr '05 - 2:31 
This tree doesn't save font and background colors of it's nodes. If I add a node with color other than default one, select it, then clear the selection, the node's color is reverted back to the default one. I solved this problem by saving and restoring colors before and after selection. I changed several methods, it will be too long to paste it here. I can e-mail the changed file if anyone is interested.
 
One more small note:
I think that this code is not safe
 
public ArrayList SelectedNodes
{
get
{
return m_coll;
}
}
 
It returns a reference to an internal class structure. A user could easily change it, breaking a selection. In my version I changed it to
 
return m_coll.Clone();

QuestionHow to select the node by coding instead of Clicking the mouse?memberThanh Pham9 Dec '04 - 13:36 
I used to use treeview in vb6 and this tv.Nodes(i).Selected = True is how I select the node by coding.
But in c# I don't know how. Can some one help me.
Thanks in advance.
AnswerRe: How to select the node by coding instead of Clicking the mouse?memberStephane Rodriguez.14 Dec '04 - 9:50 

With the regular .NET treeview, it's something like TreeView.SelectedNode = node; where node is a TreeNode instance.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 19 Aug 2002
Article Copyright 2002 by Stephane Rodriguez.
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid