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

Multiselect Treeview Implementation

By , 20 Sep 2007
 
Screenshot - app.gif

Introduction

Why doesn't .NET have a multiselect treeview? There are so many uses for one and turning on checkboxes in the treeview is a pretty lousy alternative. I tried some third party treeviews and I think what turned me off the most is that the object model is different than the .NET treeview I'm used to working with. All I wanted is the standard .NET treeview with a SelectedNodes property as well as a SelectedNode property. After a quick search on The Code Project, I found Jean Allisat's implementation here. I wasn't satisfied though because some things didn't behave correctly. For example, you click on a node and then as you Ctrl+Click on a second node, the first node loses its highlighting until the click operation is completed. Strange. So it looks a little bit choppy, but it works. I started with Jean's implementation and took it to the next level to try and clean up the UI behaviour a bit.

Using the Code

The "choppy" problem I was having with the original implementation of the multiselect treeview was that we were letting the treeview select and highlight the selected node, while in the overridden events, we would deal with manually highlighting other selected nodes. The conclusion I came to was to do all of the highlighting myself and not fight with the treeview. So the first thing we need to do is cripple the treeview so that it can NEVER have a SelectedNode. We do this by overriding the OnMouseDown, OnBeforeSelect & OnAfterSelect events and setting base.SelectedNode to null as well as setting e.Cancel in some of the events to stop them from processing. We also hide the treeview's SelectedNode property (with the new keyword) and reimplement our own version.

Now that we have a treeview that is crippled, we can implement new logic for selecting node(s). When you click on a node it becomes the SelectedNode and it is highlighted. If you were not holding down a ModifierKey, then we can clear the previous selection. If you were holding down the Ctrl ModifierKey, then we decide whether to add this node to the selection or remove it if it was already in the selection. If you were holding down the Shift ModifierKey, then we have to select all the nodes from the current SelectedNode to this one. All of this logic resides in the SelectNode() helper function.

One gotcha here. All of the treeview's KeyDown messages are processed off of the SelectedNode and since there never is a SelectedNode (we've crippled it...) then you can't use the keyboard to navigate/edit the tree. Well, that's no good... So we have to trap the OnKeyDown event and handle Left, Right, Up, Down, Home, End, Page Up, Page Down, and any alpha-numeric character. Each of these key commands can have different behaviours if the Ctrl or Shift ModifierKey are pressed, and possibly different behaviours if a branch is expanded or not.

Selected Node(s) Properties

#region Selected Node(s) Properties

public MultiSelectTreeview()
{
    m_SelectedNodes = new List<TreeNode>();
    base.SelectedNode = null;
}

private List<TreeNode> m_SelectedNodes = null;
public List<TreeNode> SelectedNodes
{
    get
    {
        return m_SelectedNodes;
    }
    set
    {
        ClearSelectedNodes();
        if( value != null )
        {
            foreach( TreeNode node in value )
            {
                ToggleNode( node, true );
            }
        }
    }
}

// Note we use the new keyword to Hide the native treeview's 
// SelectedNode property.
private TreeNode m_SelectedNode;
public new TreeNode SelectedNode
{
    get
    {
        return m_SelectedNode;
    }
    set
    {
        ClearSelectedNodes();
        if( value != null )
        {
            SelectNode( value );
        }
    }
}

#endregion  

Overridden Events

#region Overridden Events

protected override void OnGotFocus( EventArgs e )
{
    // Make sure at least one node has a selection
    // this way we can tab to the ctrl and use the
    // keyboard to select nodes
    try
    {
        if( m_SelectedNode == null && this.TopNode != null )
        {
            ToggleNode( this.TopNode, true );
        }

        base.OnGotFocus( e );
    }
    catch( Exception ex )
    {
        HandleException( ex );
    }
}

protected override void OnMouseDown( MouseEventArgs e )
{
    // If the user clicks on a node that was not
    // previously selected, select it now.
    try
    {
        base.SelectedNode = null;

        TreeNode node = this.GetNodeAt( e.Location );
        if( node != null )
        {
            //Allow user to click on image
            int leftBound = node.Bounds.X; // - 20; 
            // Give a little extra room
            int rightBound = node.Bounds.Right + 10; 
            if( e.Location.X > leftBound && e.Location.X < rightBound )
            {
                if( ModifierKeys == 
                    Keys.None && ( m_SelectedNodes.Contains( node ) ) )
                {
                    // Potential Drag Operation
                    // Let Mouse Up do select
                }
                else
                {
                    SelectNode( node );
                }
            }
        }

        base.OnMouseDown( e );
    }
    catch( Exception ex )
    {
        HandleException( ex );
    }
}

protected override void OnMouseUp( MouseEventArgs e )
{
    // If you clicked on a node that WAS previously
    // selected then, reselect it now. This will clear
    // any other selected nodes. e.g. A B C D are selected
    // the user clicks on B, now A C & D are no longer selected.
    try
    {
        // Check to see if a node was clicked on
        TreeNode node = this.GetNodeAt( e.Location );
        if( node != null )
        {
            if( ModifierKeys == Keys.None && m_SelectedNodes.Contains( node ) )
            {
                // Allow user to click on image
                int leftBound = node.Bounds.X; // - 20; 
                // Give a little extra room
                int rightBound = node.Bounds.Right + 10; 
                if( e.Location.X > leftBound && e.Location.X < rightBound )
                {
                    SelectNode( node );
                }
            }
        }

        base.OnMouseUp( e );
    }
    catch( Exception ex )
    {
        HandleException( ex );
    }
}

protected override void OnItemDrag( ItemDragEventArgs e )
{
    // If the user drags a node and the node being dragged is NOT
    // selected, then clear the active selection, select the
    // node being dragged and drag it. Otherwise if the node being
    // dragged is selected, drag the entire selection.
    try
    {
        TreeNode node = e.Item as TreeNode;

        if( node != null )
        {
            if( !m_SelectedNodes.Contains( node ) )
            {
                SelectSingleNode( node );
                ToggleNode( node, true );
            }
        }

        base.OnItemDrag( e );
    }
    catch( Exception ex )
    {
        HandleException( ex );
    }
}

protected override void OnBeforeSelect( TreeViewCancelEventArgs e )
{
    // Never allow base.SelectedNode to be set!
    try
    {
        base.SelectedNode = null;
        e.Cancel = true;

        base.OnBeforeSelect( e );
    }
    catch( Exception ex )
    {
        HandleException( ex );
    }
}

protected override void OnAfterSelect( TreeViewEventArgs e )
{
    // Never allow base.SelectedNode to be set!
    try
    {
        base.OnAfterSelect( e );
        base.SelectedNode = null;
    }
    catch( Exception ex )
    {
        HandleException( ex );
    }
}

protected override void OnKeyDown( KeyEventArgs e )
{
    // Handle all possible key strokes for the control.
    // including navigation, selection, etc.

    base.OnKeyDown( e );

    if( e.KeyCode == Keys.ShiftKey ) return;

    //this.BeginUpdate();
    bool bShift = ( ModifierKeys == Keys.Shift );

    try
    {
        // Nothing is selected in the tree, this isn't a good state
        // select the top node
        if( m_SelectedNode == null && this.TopNode != null )
        {
            ToggleNode( this.TopNode, true );
        }

        // Nothing is still selected in the tree, 
        // this isn't a good state, leave.
        if( m_SelectedNode == null ) return;

        if( e.KeyCode == Keys.Left )
        {
            if( m_SelectedNode.IsExpanded && m_SelectedNode.Nodes.Count > 0 )
            {
                // Collapse an expanded node that has children
                m_SelectedNode.Collapse();
            }
            else if( m_SelectedNode.Parent != null )
            {
                // Node is already collapsed, try to select its parent.
                SelectSingleNode( m_SelectedNode.Parent );
            }
        }
        else if( e.KeyCode == Keys.Right )
        {
            if( !m_SelectedNode.IsExpanded )
            {
                // Expand a collapsed node's children
                m_SelectedNode.Expand();
            }
            else
            {
                // Node was already expanded, select the first child
                SelectSingleNode( m_SelectedNode.FirstNode );
            }
        }
        else if( e.KeyCode == Keys.Up )
        {
            // Select the previous node
            if( m_SelectedNode.PrevVisibleNode != null )
            {
                SelectNode( m_SelectedNode.PrevVisibleNode );
            }
        }
        else if( e.KeyCode == Keys.Down )
        {
            // Select the next node
            if( m_SelectedNode.NextVisibleNode != null )
            {
                SelectNode( m_SelectedNode.NextVisibleNode );
            }
        }
        else if( e.KeyCode == Keys.Home )
        {
            if( bShift )
            {
                if( m_SelectedNode.Parent == null )
                {
                    // Select all of the root nodes up to this point
                    if( this.Nodes.Count > 0 )
                    {
                        SelectNode( this.Nodes[0] );
                    }
                }
                else
                {
                    // Select all of the nodes up to this point under 
                    // this nodes parent
                    SelectNode( m_SelectedNode.Parent.FirstNode );
                }
            }
            else
            {
                // Select this first node in the tree
                if( this.Nodes.Count > 0 )
                {
                    SelectSingleNode( this.Nodes[0] );
                }
            }
        }
        else if( e.KeyCode == Keys.End )
        {
            if( bShift )
            {
                if( m_SelectedNode.Parent == null )
                {
                    // Select the last ROOT node in the tree
                    if( this.Nodes.Count > 0 )
                    {
                        SelectNode( this.Nodes[this.Nodes.Count - 1] );
                    }
                }
                else
                {
                    // Select the last node in this branch
                    SelectNode( m_SelectedNode.Parent.LastNode );
                }
            }
            else
            {
                if( this.Nodes.Count > 0 )
                {
                    // Select the last node visible node in the tree.
                    // Don't expand branches incase the tree is virtual
                    TreeNode ndLast = this.Nodes[0].LastNode;
                    while( ndLast.IsExpanded && ( ndLast.LastNode != null ) )
                    {
                        ndLast = ndLast.LastNode;
                    }
                    SelectSingleNode( ndLast );
                }
            }
        }
        else if( e.KeyCode == Keys.PageUp )
        {
            // Select the highest node in the display
            int nCount = this.VisibleCount;
            TreeNode ndCurrent = m_SelectedNode;
            while( ( nCount ) > 0 && ( ndCurrent.PrevVisibleNode != null ) )
            {
                ndCurrent = ndCurrent.PrevVisibleNode;
                nCount--;
            }
            SelectSingleNode( ndCurrent );
        }
        else if( e.KeyCode == Keys.PageDown )
        {
            // Select the lowest node in the display
            int nCount = this.VisibleCount;
            TreeNode ndCurrent = m_SelectedNode;
            while( ( nCount ) > 0 && ( ndCurrent.NextVisibleNode != null ) )
            {
                ndCurrent = ndCurrent.NextVisibleNode;
                nCount--;
            }
            SelectSingleNode( ndCurrent );
        }
        else
        {
            // Assume this is a search character a-z, A-Z, 0-9, etc.
            // Select the first node after the current node that
            // starts with this character
            string sSearch = ( (char) e.KeyValue ).ToString();

            TreeNode ndCurrent = m_SelectedNode;
            while( ( ndCurrent.NextVisibleNode != null ) )
            {
                ndCurrent = ndCurrent.NextVisibleNode;
                if( ndCurrent.Text.StartsWith( sSearch ) )
                {
                    SelectSingleNode( ndCurrent );
                    break;
                }
            }
        }
    }
    catch( Exception ex )
    {
        HandleException( ex );
    }
    finally
    {
        this.EndUpdate();
    }
}

#endregion

Helper Functions

#region Helper Methods

private void SelectNode( TreeNode node )
{
    try
    {
        this.BeginUpdate();

        if( m_SelectedNode == null || ModifierKeys == Keys.Control )
        {
            // Ctrl+Click selects an unselected node, 
            // or unselects a selected node.
            bool bIsSelected = m_SelectedNodes.Contains( node );
            ToggleNode( node, !bIsSelected );
        }
        else if( ModifierKeys == Keys.Shift )
        {
            // Shift+Click selects nodes between the selected node and here.
            TreeNode ndStart = m_SelectedNode;
            TreeNode ndEnd = node;

            if( ndStart.Parent == ndEnd.Parent )
            {
                // Selected node and clicked node have same parent, easy case.
                if( ndStart.Index < ndEnd.Index )
                {
                    // If the selected node is beneath 
                    // the clicked node walk down
                    // selecting each Visible node until we reach the end.
                    while( ndStart != ndEnd )
                    {
                        ndStart = ndStart.NextVisibleNode;
                        if( ndStart == null ) break;
                        ToggleNode( ndStart, true );
                    }
                }
                else if( ndStart.Index == ndEnd.Index )
                {
                    // Clicked same node, do nothing
                }
                else
                {
                    // If the selected node is above the clicked node walk up
                    // selecting each Visible node until we reach the end.
                    while( ndStart != ndEnd )
                    {
                        ndStart = ndStart.PrevVisibleNode;
                        if( ndStart == null ) break;
                        ToggleNode( ndStart, true );
                    }
                }
            }
            else
            {
                // Selected node and clicked node have same parent, hard case.
                // We need to find a common parent to determine if we need
                // to walk down selecting, or walk up selecting.

                TreeNode ndStartP = ndStart;
                TreeNode ndEndP = ndEnd;
                int startDepth = Math.Min( ndStartP.Level, ndEndP.Level );

                // Bring lower node up to common depth
                while( ndStartP.Level > startDepth )
                {
                    ndStartP = ndStartP.Parent;
                }

                // Bring lower node up to common depth
                while( ndEndP.Level > startDepth )
                {
                    ndEndP = ndEndP.Parent;
                }

                // Walk up the tree until we find the common parent
                while( ndStartP.Parent != ndEndP.Parent )
                {
                    ndStartP = ndStartP.Parent;
                    ndEndP = ndEndP.Parent;
                }

                // Select the node
                if( ndStartP.Index < ndEndP.Index )
                {
                    // If the selected node is beneath 
                    // the clicked node walk down
                    // selecting each Visible node until we reach the end.
                    while( ndStart != ndEnd )
                    {
                        ndStart = ndStart.NextVisibleNode;
                        if( ndStart == null ) break;
                        ToggleNode( ndStart, true );
                    }
                }
                else if( ndStartP.Index == ndEndP.Index )
                {
                    if( ndStart.Level < ndEnd.Level )
                    {
                        while( ndStart != ndEnd )
                        {
                            ndStart = ndStart.NextVisibleNode;
                            if( ndStart == null ) break;
                            ToggleNode( ndStart, true );
                        }
                    }
                    else
                    {
                        while( ndStart != ndEnd )
                        {
                            ndStart = ndStart.PrevVisibleNode;
                            if( ndStart == null ) break;
                            ToggleNode( ndStart, true );
                        }
                    }
                }
                else
                {
                    // If the selected node is above 
                    // the clicked node walk up
                    // selecting each Visible node until we reach the end.
                    while( ndStart != ndEnd )
                    {
                        ndStart = ndStart.PrevVisibleNode;
                        if( ndStart == null ) break;
                        ToggleNode( ndStart, true );
                    }
                }
            }
        }
        else
        {
            // Just clicked a node, select it
            SelectSingleNode( node );
        }

        OnAfterSelect( new TreeViewEventArgs( m_SelectedNode ) );
    }
    finally
    {
        this.EndUpdate();
    }
}

private void ClearSelectedNodes()
{
    try
    {
        foreach( TreeNode node in m_SelectedNodes )
        {
            node.BackColor = this.BackColor;
            node.ForeColor = this.ForeColor;
        }
    }
    finally
    {
        m_SelectedNodes.Clear();
        m_SelectedNode = null;
    }
}

private void SelectSingleNode( TreeNode node )
{
    if( node == null )
    {
        return;
    }

    ClearSelectedNodes();
    ToggleNode( node, true );
    node.EnsureVisible();
}

private void ToggleNode( TreeNode node, bool bSelectNode )
{
    if( bSelectNode )
    {
        m_SelectedNode = node;
        if( !m_SelectedNodes.Contains( node ) )
        {
            m_SelectedNodes.Add( node );
        }
        node.BackColor = SystemColors.Highlight;
        node.ForeColor = SystemColors.HighlightText;
    }
    else
    {
        m_SelectedNodes.Remove( node );
        node.BackColor = this.BackColor;
        node.ForeColor = this.ForeColor;
    }
}

private void HandleException( Exception ex )
{
    // Perform some error handling here.
    // We don't want to bubble errors to the CLR.
    MessageBox.Show( ex.Message );
}

#endregion

Points of Interest

Check out my other post: Virtual Treeview Implementation

About the Author

Andrew D. Weiss
Software Engineer
Check out my blog: More-On C#

Screenshot - me.gif

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Andrew D. Weiss
Software Developer (Senior) BrightStar Partners
United States United States
Member
No Biography provided

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   
SuggestionMultiple AfterSelect callsmemberdsweaver3 Apr '13 - 11:25 
The AfterSelect event is fired twice when the user clicks on a node due to it being called from each of OnMouseDown and OnMouseUp. Subsequently clicking on a node also causes AfterSelect. This behaviour is not seen in the standard TreeView. While this behaviour could be easily be handled in client code, I think they should be addressed in the control code so as to adhere to the standard control.
 
The solution can be implemented by modifying the conditional in OnMouseUp to read:
if (ModifierKeys == Keys.None && m_SelectedNodes.Contains(node) && m_SelectedNodes.Count > 1)

SuggestionPerformance improvmentmemberMr Scotty4 Mar '13 - 2:14 
Hi,
 
First a big Thumbs Up | :thumbsup: thumbs up for your control extension, it is simple to replace an existing TreeView and have multiple select functionality.
I needed the expand and collapse all functionality like a tree view ( * and - keys) but it was simple to add it to the OnKeyDown handler.
 
On a certain point i noticed that it took some half of a second to select a tree node Confused | :confused: .
I digged a little and this is what i found.
 
When you have a large tree (i tested it with a node with 500 child nodes having 500 child nodes each).
In the SelectNode method the call to EndUpdate takes approx 1500 mSecs. It is called from the MouseDown and MouseUp handler so things become real slow.
When you simply make a the calls BeginUpdate followed by EndUpdate you see the 1500 mSec.
 
The solution is very simple, remove the BeginUpdate at the start and remove the EndUpdate at the end.
The selection is then again blazing fast Cool | :cool:
GeneralMy vote of 5memberChris Robert Mead4 Jul '12 - 7:17 
Was able to replace my treeview with your class and it worked straight away... very nice.
GeneralMy vote of 5memberrangdebasanti2 Mar '12 - 1:00 
Very good
BugWhen cloning a node to another mutlitreeview, the second treeviews elements stay highlighed.memberTheDiddy21 Nov '11 - 14:58 
Not sure why but the gui is correct on the first treeview but when i try to clone multiple nodes and add them to a second on the same form the second treeview has all the nodes highlighted and control doesnt not behave correctly.
BugDrawNode = OwnerDrawText does not propwerly workmemberIgnacio Hernandez-Ros8 Nov '11 - 11:51 
Hello,
 
I'm block trying to make this to work after migrating from a regular TreeView to this new extension. In the original code, TreeView calls a method for drawing the node text. The code simply worked fine. In the new code the selected node(s) are never properly painted. They are always a white line. Don't know how to move forward on this Frown | :( img src="/script/Forums/Images/smiley_confused.gif" align="top" alt="Confused | :confused:" />
GeneralWould like to use CANCEL property in OnBeforeNodeSelectmembertheitmueller25 Jan '11 - 5:57 
hello,
first of all thanks for this work - made my life a lot easier. Big Grin | :-D
I am trying to prevent a user from selecting Level-1 nodes a level -0 node is selected and vice-verse.
I implemented some code for this and it should work .. but doesn't, the control ignores e.Cancel =true in the event.
 
Does anybody have a clue how I could get around that issue?
 
Thank's in advance,
Thomas
GeneralRe: Would like to use CANCEL property in OnBeforeNodeSelectmemberMember 802192029 Jun '11 - 8:43 
Since the code is not actually using the built-in code for treeview to select its nodes, trying to cancel a select through the normal means won't work.   You'll have to put a check into its OnMouseUp, where it implements its own selection functionality, to not select the node if a certain selection is not allowed.
GeneralThanks!!memberzulbaric19 May '10 - 6:23 
Many thanks go to the guy who made this. It is just what I was looking for!!
GeneralAfterSelect called before BeforeSelectmemberRob Johansen2 Sep '09 - 7:01 
By calling OnAfterSelect at the end of the SelectNode method, the progam using this will recieve on AfterSelect BEFORE it recieves the BeforeSelect, and then it will recieve a second AfterSelect. So far this seems to be preventing me from cancelling a node selection if, for instance, I only want level 0 nodes to be selectable in a multi-select. I tried to add a call to OnBeforeSelect at the top of SelectNode at which time I discovered the Focus issue (I had a couple break points on). Even with the added OnBeforeSelect, the cancel does not seem to make it back to SelectNode. Still working on it.
Generalcrippling the base.SelectedNode causes extra Selects from the base's OnGotFocusmemberRob Johansen2 Sep '09 - 5:51 
The standard TreeView's default behavior is to select the first node if there are no nodes selected when it gets focus. By crippling the base.SelectedNode by setting it to null all the time, you end up re-generating the OnBeforeSelect & OnAfterSelect events every time the control gets focus. This can be as simple as the app itself gaining focus. If you have a break-point in the select chain of events, you can end up with an end-less recursion loop as focus shifts back & forth from the VS IDE to the application.
 
So, what is the main purpose of crippling the base.SelectedNode? What happens if we leave it uncrippled?
GeneralSelect All (Ctrl-A) Not SupportedmemberKeith Watson26 Aug '09 - 16:57 
Select all (CTRL-A) is not supported. Add the code in bold to the OnKeyDown() function.
 
else if (e.Control && (e.KeyCode == Keys.A))
{
	// Select All (CTRL-A), selects all top level nodes
	this.ClearSelectedNodes();
	this.CollapseAll();
	TreeNode ndCurrent = this.TopNode;
	while (ndCurrent != null)
	{
		ToggleNode(ndCurrent, true);
		ndCurrent = ndCurrent.NextNode;
	}
}
else
{
	// Assume this is a search character a-z, A-Z, 0-9, etc.

GeneralRe: Select All (Ctrl-A) Not SupportedmemberZasky5 Mar '13 - 22:19 
Thanks for this. Although it may have worked for you because your tree is shorter than its container, it will not work when the top of the tree is scrolled off the top of the screen. That's because TopNode returns the first visible node, rather than the first node in the tree, so the hidden part of the tree is not selected.
 
I prefer this code:
                this.ClearSelectedNodes();
                TreeNode ndCurrent = this.Nodes[0];
                while (ndCurrent != null)
                {
	            ToggleNode(ndCurrent, true);
	            ndCurrent = ndCurrent.NextNode;
                }
which selects all top level nodes. I left out the CollapseAll() because I don't think users expect the tree structure to change on selection (and it isn't necessary), but opinions may differ on that.
GeneralOnKeyDown ignored ShiftKey but not ControlKeymemberKeith Watson1 Aug '09 - 18:24 
If you press a shift key by itself, OnKeyDown correctly ignores it. But if you press a control key by itself, OnKeyDown runs through the code. This is OK unless you have added a call to OnAfterSelect at the bottom of the function because most keys which change the selection do not call that function.
 
In OnKeyDown, I made the following changes in bold to my version:
// Check if modifier keys are being pressed or held by themself
if (e.KeyCode == Keys.ShiftKey) return;
if (e.KeyCode == Keys.ControlKey) return;

GeneralHideSelection property ignoredmemberKeith Watson31 Jul '09 - 8:50 
From the Microsoft documentation for TreeView.HideSelection Property:
 
"When this property is set to false, selected nodes in the TreeView control remain highlighted in a different color than the current selection color when the TreeView control loses focus."
 
This behavior is ignored by this class.
GeneralWhy so many threads on problems?memberKeith Watson31 Jul '09 - 8:57 
I have an app which monitors tree selection. It really needed multiple selection, but when I changed over to this class I started noticing behavior differences. So I built one version of the app with TreeView and another with this class and ran them side-by-side to look for differences. I'm documenting the ones I find.
GeneralRe: HideSelection property ignoredmemberKeith Watson31 Jul '09 - 14:32 
In my copy, I added always change selected nodes to highlight color in OnGotFocus. Added function OnLeaveFocus which checks HideSelection, and if it is enabled then dim the selected nodes. (Pretty simple change.)
GeneralCode for Re: HideSelection property ignoredmemberKeith Watson26 Aug '09 - 17:09 
Add the following bold code to OnGotFocus()
 
// Handle if HideSelection property is in use.
// Always redraw as highlighted when we gain focus
HighlightSelection();
 

base.OnGotFocus(e);
Also add this function
protected override void OnLostFocus(EventArgs e)
{
	try
	{
		// Handle if HideSelection property is in use.
		if (this.HideSelection)
		{
			DimSelection();
		}
 
		base.OnLostFocus(e);
	}
	catch (Exception ex)
	{
		HandleException(ex);
	}
}

GeneralRe: Code for Re: HideSelection property ignoredmemberRob Johansen1 Sep '09 - 11:37 
All your code additions have been great. However, you did not include your code for DimSelection() or HighlightSelection(). I can try to write my own, but it would be great to see what you did.
GeneralRe: Code for Re: HideSelection property ignoredmemberKeith Watson10 Oct '09 - 19:21 
They are pretty simple and use the color values from ToggleNode().
 
private void HighlightSelection()
{
	foreach (TreeNode node in SelectedNodes)
	{
		node.BackColor = SystemColors.Highlight;
		node.ForeColor = SystemColors.HighlightText;
	}
}
 
private void DimSelection()
{
	foreach (TreeNode node in SelectedNodes)
	{
		node.BackColor = SystemColors.Control;
		node.ForeColor = this.ForeColor;
	}
}

GeneralEnd key behavior incorrectmemberKeith Watson31 Jul '09 - 8:21 
In TreeView, the Home and End keys respectively cause selection to go to the highest entry and lowest entries in the entire tree. In this class the End key only goes to the last node withing the top outermost node!
 
modified on Friday, July 31, 2009 3:11 PM

GeneralRe: End key behavior incorrectmemberKeith Watson31 Jul '09 - 14:41 
Turns out to be a simple fix. In the function OnKeyDown, inside of the block of code for
else if( e.KeyCode == Keys.End )
change this line
TreeNode ndLast = this.Nodes[0].LastNode;
to this
TreeNode ndLast = this.Nodes[this.Nodes.Count - 1].LastNode;

GeneralOnKeyDown does not fire OnAfterSelect in most casesmemberKeith Watson31 Jul '09 - 8:17 
OnKeyDown handles key navigation/selection. But when selection changes the AfterSelect event is not fired in most cases. Of all the keys supported, only Up and Down call AfterSelect.
 
modified on Friday, July 31, 2009 2:24 PM

GeneralOnGotFocus does not fire OnAfterSelectmemberKeith Watson30 Jul '09 - 20:31 
The OnGotFocus function will select the top node if nothing is already selected. So the selection changes but the OnAfterSelect event is not fired. Add the following bold line of code to OnGotFocus:
if( m_SelectedNode == null && this.TopNode != null )
{
	ToggleNode( this.TopNode, true );
	OnAfterSelect(new TreeViewEventArgs(m_SelectedNode));
}

QuestionWhat is SelectedNode?memberKeith Watson30 Jul '09 - 20:24 
It is not documented, but after running the code it appears that SelectedNode is the last node that was selected in multiple selection. Is this correct?

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 20 Sep 2007
Article Copyright 2007 by Andrew D. Weiss
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid