Click here to Skip to main content
5,788,212 members and growing! (19,916 online)
Email Password   helpLost your password?
Desktop Development » Miscellaneous » General     Intermediate License: The Code Project Open License (CPOL)

Virtual Treeview Implementation

By Andrew D. Weiss

A simple implementation of a virtual treeview which loads nodes synchrously or asynchrously.
C# 1.0, C# 2.0, C#, Windows, .NET, .NET 3.0, .NET 1.0, .NET 1.1, .NET 2.0VS.NET2002, VS.NET2003, VS2005, Visual Studio, Dev

Posted: 18 Sep 2007
Updated: 18 Sep 2007
Views: 13,547
Bookmarked: 27 times
Note: This is an unedited reader contribution
Announcements
Loading...



Search    
Advanced Search
Sitemap
11 votes for this Article.
Popularity: 4.17 Rating: 4.00 out of 5
1 vote, 9.1%
1
0 votes, 0.0%
2
0 votes, 0.0%
3
4 votes, 36.4%
4
6 votes, 54.5%
5
Note: This is an unedited contribution. If this article is inappropriate, needs attention or copies someone else's work without reference then please Report This Article
Screenshot - app.gif

Introduction

This is my first my first blog, so I picked a simple topic to talk about: Implementing the simplest virtual treeview possible.

Large data and large metadata are problems I run into on every project I work on, providing search capabilities makes browsing large amounts of information possible, but somewhere you're going to have a Treeview, a Listview or a Grid that is going to need to present some unknown amount of information. How you handle this unknown is what makes your application scalable, or come to grinding halt.

Making your UI controls "Virtual" is also a important to keep your UI thread from locking up. Don't load massive amounts of data that will never be viewed or needed. Virtual (in this context) simply means you only load the amount of data that is being displayed to the user, but give visual cues that there is more data. The .NET Listview nativly implements a virtual view so listview items can be loaded on demand, also most popular 3rd party grids have some sort of virtual support, but the .Net Treeview control doesn't...

Using the code

Synchronous Virtual Treeview

The logic for implementing a synchronous virtual treeview is extremly simple, but doesn't come without its flaws.
  1. Load your root nodes
  2. Add a child node to each root node with the name VIRT. This causes the treeview to put a plus sign next to each node.
  3. Catch the onBeforeExpand event and if the VIRT node is a child of the node being expanded then replace it with the real children.
  4. Add a VIRT node to each of the newly added children. (unless you know for sure it's a leaf node)
private void treeVirt1_BeforeExpand( object sender, TreeViewCancelEventArgs e )
{
   // If the node being expanded contains a virtual node then

   // we need to load this node's children on demand. If it doesn't

   // contain a virtual node then we already did it, so do nothing.


   if( e.Node.Nodes.ContainsKey( VIRTUALNODE ) )
   {
      try
      {
         // Do some work to load data.

         // Note this may take a while and could

         // be annoying to your user.

         // See asynchronous version below.


         // Clear out all of the children

         e.Node.Nodes.Clear();

         // Load the new children into the treeview.

         string[] arrChildren = new string[] { "Grapes", "Apples", "Tomatoes", "Kiwi" };
         foreach( string sChild in arrChildren )
         {
            // Be sure to add virtual nodes to new items that "may"

            // have children.  If you know for sure that your item is

            // a leaf node, then there's no need to add the virtual node.

            TreeNode tNode = e.Node.Nodes.Add( sChild );
            AddVirtualNode( tNode );
         }
      }
      catch
      {
         // Error occured, reset to a known state

         e.Node.Nodes.Clear();
         AddVirtualNode( e.Node );
      }
   }
}

This algorithm is very simple to implement and defers load time of data when/if needed. This implementation is good for simple applications where all the data is local and loading a branch of data is quick. However if you need to make a server call to load a branch, that could take... forever... And since you're doing the work on the UI thread you'll lock up your entire application. It's trivial to extend this algorithm to use a background worker thread to load your data asynchrounously.

Asynchronous Virtual Treeview

The logic for implementing an asynchronous virtual treeview is slightly different. Instead of catching the OnBeforeExpand event we catch the OnAfterExpand, we then use a BackgroundWorker thread so we can load the data (time consuming operation) on a thread other than the UI thread. We can't touch UI objects from other threads, but that's the beauty of the BackgroundWorker thread, its callback event fires on the UI thread. So when the RunWorkerCompleted event fires you can take the data you loaded on the other thread and create TreeNodes out of it. When the RunWorkerCompleted event runs you'll need to know what TreeNode initiated the request (this way we know who to add the new nodes to) so we'll send it "along for the ride" on the BackgroundWorker thread, but remember DO NOT use the TreeNode on the BackgroundWorker thread's DoWork function. You can't use UI controls on threads other than the thread that created them. (That's actually not true, you can use BeginInvoke, maybe that will be my next blog).
  1. Load your root nodes
  2. Add a child node to each root node with the name VIRT. This causes the treeview to put a plus sign next to each node.
  3. Catch the onAfterExpand event
  4. Create a BackgroundWorker thread and pass in the TreeNode and any other needed information to the DoWork function
  5. Perform the timeconsuming operation in the DoWork function on the background thread
  6. Return the original TreeNode as well as the results from the time consuming operation
  7. Get the TreeNode and data from the server in the RunWorkerCompleted event
  8. Remove the VIRT node and replace it with new TreeNodes
  9. Add a VIRT node to each of the newly added children. (unless you know for sure it's a leaf node)
#region Asynchronous Treeview

private void treeVirt2_AfterExpand( object sender, TreeViewEventArgs e )
{
   if( e.Node.Nodes.ContainsKey( VIRTUALNODE ) )
   {
      BackgroundWorker bw = new BackgroundWorker();
      bw.DoWork += new DoWorkEventHandler( bw_DoWork );
      bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler( bw_RunWorkerCompleted );

      object[] oArgs = new object[] { e.Node, "Some information..." };            
      bw.RunWorkerAsync( oArgs );
   }
}

private void bw_DoWork( object sender, DoWorkEventArgs e )
{
   object[] oArgs = e.Argument as object[];
   TreeNode tNodeParent = oArgs[0] as TreeNode;
   string sInfo = oArgs[1].ToString();

   // Note you can't use tNodeParent in here because

   // we're not on the the UI thread (see Invoke).  We've only

   // passed it in so we can round trip it to the 

   // bw_RunWorkerCompleted event.

   
   // Use sInfo argument to load the data

   Random r = new Random();
   Thread.Sleep( r.Next( 500, 2500 ) );
   string[] arrChildren = new string[] { "Grapes", "Apples", "Tomatoes", "Kiwi" };

   // Return the Parent Tree Node and the list of children to the

   // UI thread.

   e.Result = new object[] { tNodeParent, arrChildren };
}

private void bw_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e )
{
   // Get the Parent Tree Node and the list of children

   // from the Background Worker Thread

   object[] oResult = e.Result as object[];
   TreeNode tNodeParent = oResult[0] as TreeNode;
   string[] arrChildren = oResult[1] as string[];

   tNodeParent.Nodes.Clear();

   foreach( string sChild in arrChildren )
   {
      TreeNode tNode = tNodeParent.Nodes.Add( sChild );
      AddVirtualNode( tNode );
   }
}

#endregion

Helper Functions

// Add Virtual Node function used above. Simply  

// adds a "Loading..." node with the special "VIRT" 

// name to the parent node being passed in.


private const string VIRTUALNODE = "VIRT";

private void AddVirtualNode( TreeNode tNode )
{
   TreeNode tVirt = new TreeNode();
   tVirt.Text = "Loading...";
   tVirt.Name = VIRTUALNODE;
   tVirt.ForeColor = Color.Blue;
   tVirt.NodeFont = new Font( "Microsoft Sans Serif", 8.25F, FontStyle.Underline);
   tNode.Nodes.Add( tVirt );
}

Points of Interest

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

Screenshot - me.gif

-asdf

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



Occupation: Software Developer (Senior)
Company: BrightStar Partners
Location: United States United States

Other popular Miscellaneous articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 1 of 1 (Total in Forum: 1) (Refresh)FirstPrevNext
GeneralBest Job ! Thanks :)memberigetorix7:52 24 Nov '07  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 18 Sep 2007
Editor:
Copyright 2007 by Andrew D. Weiss
Everything else Copyright © CodeProject, 1999-2009
Web18 | Advertise on the Code Project