Click here to Skip to main content
15,892,005 members
Articles / Multimedia / GDI+

Customizable Tree Control with Animation Support

Rate me:
Please Sign up or sign in to vote.
4.72/5 (17 votes)
8 Apr 2008CPOL4 min read 108K   23   115  
A tree control implementation, allowing complete customization and animation support
/////////////////////////////////////////////////////////////////////////////
//
// (c) 2007 BinaryComponents Ltd.  All Rights Reserved.
//
// http://www.binarycomponents.com/
//
/////////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace BinaryComponents.Utility.Net
{
	/// <summary>
	/// Used to prioritise network calls
	/// </summary>
	public static class AppNetOps
	{
		public enum Priority{ Low = 0, Medium, High };

		/// <summary>
		/// Creates a disposable instance object that represents some space in
		/// time that your going to do some internet activity. Note if the current
		/// threads IsBackground property is true then this context will be given
		/// a low priority, otherwise it will be set by default to high.
		/// </summary>
		/// <returns></returns>
		public static InstanceContext StartInstanceContext()
		{
			return new InstanceContext( Thread.CurrentThread.IsBackground ? Priority.Low : Priority.High );
		}


		/// <summary>
		/// Creates a disposable instance object that represents some space in
		/// time that your going to do some internet activity. 
		/// </summary>
		/// <param name="priority">What priority to give this context</param>
		/// <returns></returns>
		public static InstanceContext StartInstanceContext( Priority priority )
		{
				return new InstanceContext( priority );
		}

		/// <summary>
		/// Returns true if no contexts are greater than this (in different threads).
		/// Note that if somewhere in your thread you have a higher priority context
		/// then your priority will be elevated to it.
		/// </summary>
		/// <param name="priority"></param>
		/// <returns></returns>
		public static bool CanContinue( Priority priority )
		{
			return CanContinue( Thread.CurrentThread, priority );
		}

		#region Class Instance
		public sealed class InstanceContext: IDisposable
		{
#if DEBUG
			private System.Diagnostics.StackTrace _stackTrace = new System.Diagnostics.StackTrace();
#endif
			internal InstanceContext( Priority priority )
			{
				_conceptionThread = Thread.CurrentThread;
				_priority = priority;
				AddInstanceToList( this );
			}

			/// <summary>
			/// Waits until all other contexts in other threads are lower or equal priority
			/// </summary>
			public void SpinWaitUntilAllowed()
			{
				while( !CanContinue )
				{
					Thread.Sleep( 100 );
				}
			}

			/// <summary>
			/// Returns true if all other contexts in other threads are lower or equal priority
			/// otherwise false.
			/// </summary>
			public bool CanContinue
			{
				get
				{
					CheckThreadState();
					if( !_priority.HasValue )
					{
						throw new InvalidOperationException( "Object has already been disposed" );
					}

					return AppNetOps.CanContinue( _priority.Value );
				}
			}
			public void Dispose()
			{
				CheckThreadState();

				if( _priority.HasValue )
				{
					RemoveInstanceToList( this );
					_priority = null;
				}
			}

			private void CheckThreadState()
			{
				if( _conceptionThread != Thread.CurrentThread )
				{
					throw new InvalidOperationException( "This object can only be used in the thread it was created" );
				}
			}

			internal Thread ConceptionThread
			{
				get
				{
					return _conceptionThread;
				}
			}
			internal Priority Priority
			{
				get
				{
					if( !_priority.HasValue )
					{
						throw new InvalidOperationException( "Object has already been disposed" );
					}
					return _priority.Value;
				}
			}

			private Priority ?_priority;
			private Thread _conceptionThread;
		}
		#endregion

		private static bool CanContinue( Thread thread, Priority priority )
		{
			lock( _instancesByThread )
			{
				//
				// Bump up priority if there is a higher one for this thread.
				List<InstanceContext> threadList;
				if( _instancesByThread.TryGetValue( thread, out threadList ) )
				{
					for( int i = 0; i < threadList.Count; i++ )
					{
						if( (int)threadList[i].Priority > (int)priority )
						{
							priority = threadList[i].Priority;
						}
					}
				}

				//
				// Check to see if anything is higher priortiy over us.
				foreach( KeyValuePair<Thread, List<InstanceContext>> kv in _instancesByThread )
				{
					for( int i = 0; i < kv.Value.Count; i++ )
					{
						if( thread != kv.Key && (int)kv.Value[i].Priority > (int)priority )
						{
							//System.Diagnostics.Debug.WriteLine( string.Format( "'{0}' Can't continue", Thread.CurrentThread.Name ) );
							return false;
						}
					}
				}
			}
			//System.Diagnostics.Debug.WriteLine( string.Format( "'{0}' Can continue", Thread.CurrentThread.Name ) );
			return true;
		}

		private static void AddInstanceToList( InstanceContext instance )
		{
			lock( _instancesByThread )
			{
				List<InstanceContext> threadList;
				if( !_instancesByThread.TryGetValue( instance.ConceptionThread, out threadList ) )
				{
					threadList = new List<InstanceContext>();
					_instancesByThread[instance.ConceptionThread] = threadList;
				}
				threadList.Add( instance );
			}
		}
		private static void RemoveInstanceToList( InstanceContext instance )
		{
			lock( _instancesByThread )
			{
				List<InstanceContext> threadList = _instancesByThread[instance.ConceptionThread];

				bool removed = threadList.Remove( instance );

				if( !removed )
				{
					throw new Exception( "Should have deleted instance" );
				}

				if( threadList.Count == 0 )
				{
					_instancesByThread.Remove( instance.ConceptionThread );
				}
			}
		}
		private static Dictionary<Thread, List<InstanceContext>> _instancesByThread = new Dictionary<Thread,List<InstanceContext>>();
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Web Developer
United Kingdom United Kingdom
I'm currently working for a small start-up company, BinaryComponents Ltd, producing the FeedGhost RSS reader.

FeedGhost RSS Reader:
http://www.feedghost.com

Bespoke Software Development
http://www.binarycomponents.com

Comments and Discussions