Click here to Skip to main content
15,895,841 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 108.2K   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;

namespace BinaryComponents.Utility.Console
{
	public static class Argument
	{
		#region NameAttribute

		public sealed class NameAttribute : Attribute
		{
			public NameAttribute( string value )
			{
				if( value == null )
				{
					throw new ArgumentNullException( "value" );
				}

				_value = value;
			}

			public string Value
			{
				get
				{
					return _value;
				}
			}

			private string _value;
		}

		#endregion
		#region HelpAttribute

		public sealed class HelpAttribute : Attribute
		{
			public HelpAttribute( string value )
			{
				if( value == null )
				{
					throw new ArgumentNullException( "value" );
				}

				_value = value;
			}

			public string Value
			{
				get
				{
					return _value;
				}
			}

			private string _value;
		}

		#endregion
		#region DefaultAttribute

		public sealed class DefaultAttribute : Attribute
		{
			public DefaultAttribute( string value )
			{
				if( value == null )
				{
					throw new ArgumentNullException( "value" );
				}

				_value = value;
			}

			public string Value
			{
				get
				{
					return _value;
				}
			}

			private string _value;
		}

		#endregion
		#region MandatoryAttribute

		public sealed class MandatoryAttribute : Attribute
		{
			public MandatoryAttribute()
			{
				_value = true;
			}

			public MandatoryAttribute( bool value )
			{
				_value = value;
			}

			public bool Value
			{
				get
				{
					return _value;
				}
			}

			private bool _value;
		}

		#endregion
	}

	public class ArgumentReader<T>
		where T : new()
	{

		public ArgumentReader( string[] args )
		{
			if( args == null )
			{
				throw new ArgumentNullException( "args" );
			}

			_args = args;

			System.Reflection.MemberInfo[] members = typeof( T ).FindMembers
				( System.Reflection.MemberTypes.Field, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance, null, null );

			_allowedSwitches = new Dictionary<string, ArgumentDetails>();

			Type[] allowedFieldTypes = new Type[]
			{
				typeof( bool ),
				typeof( string ),
				typeof( List<string> )
			};

			foreach( System.Reflection.FieldInfo fieldInfo in members )
			{
				string name = fieldInfo.Name;
				ArgumentDetails argumentDetails = new ArgumentDetails();

				argumentDetails.FieldInfo = fieldInfo;

				Argument.NameAttribute nameAttribute = GetAttribute<Argument.NameAttribute>( fieldInfo );
				Argument.HelpAttribute helpAttribute = GetAttribute<Argument.HelpAttribute>( fieldInfo );
				Argument.DefaultAttribute defaultAttribute = GetAttribute<Argument.DefaultAttribute>( fieldInfo );
				Argument.MandatoryAttribute mandatoryAttribute = GetAttribute<Argument.MandatoryAttribute>( fieldInfo );

				if( nameAttribute != null )
				{
					name = nameAttribute.Value;
				}
				if( helpAttribute != null )
				{
					argumentDetails.Help = helpAttribute.Value;
				}
				if( defaultAttribute != null )
				{
					argumentDetails.DefaultValue = defaultAttribute.Value;
				}
				if( mandatoryAttribute != null )
				{
					argumentDetails.Mandatory = mandatoryAttribute.Value;
				}

				if( fieldInfo.FieldType == typeof( bool ) )
				{
					argumentDetails.NeedsArgument = false;
				}
				else
				{
					argumentDetails.NeedsArgument = true;
				}

				if( argumentDetails.DefaultValue != null && argumentDetails.Mandatory )
				{
					throw new ArgumentException( string.Format( "The argument '{0}' cannot be mandatory and have a default value.", name ) );
				}
				if( _allowedSwitches.ContainsKey( name ) )
				{
					throw new ArgumentException( string.Format( "Argument '{0}' defined multiple times in '{1}'.",
						name, typeof( T ).Name ) );
				}
				if( Array.IndexOf( allowedFieldTypes, fieldInfo.FieldType ) < 0 )
				{
					throw new ArgumentException( string.Format( "Field '{0}' must be of type bool, string, or List<string>.", name ) );
				}

				_allowedSwitches[name] = argumentDetails;
			}
		}

		public T ReadArguments()
		{
			T target = new T();
			return ReadArguments( target ) ? target : default( T );
		}

		public bool ReadArguments( T target )
		{
			int argumentPos = 0;

			foreach( KeyValuePair<string, ArgumentDetails> kvp in _allowedSwitches )
			{
				System.Reflection.FieldInfo fieldInfo = kvp.Value.FieldInfo;

				if( fieldInfo.FieldType.Equals( typeof( List<string> ) ) )
				{
					if( fieldInfo.GetValue( target ) == null )
					{
						fieldInfo.SetValue( target, new List<string>() );
					}
				}
			}

			while( argumentPos < _args.Length )
			{
				string name = _args[argumentPos];

				if( name == "/?" )
				{
					DisplayHelp( HelpString );

					return false;
				}

				if( name[0] == '/' )
				{
					name = name.Substring( 1 );

					if( _allowedSwitches.ContainsKey( name ) )
					{
						ArgumentDetails memberDetails = _allowedSwitches[name];

						if( memberDetails.Found )
						{
							HandleError( target, string.Format( "Argument '{0}' already defined.", name ) );
						}

						++argumentPos;

						if( memberDetails.NeedsArgument )
						{
							if( argumentPos >= _args.Length )
							{
								HandleError( target, string.Format( "Argument '{0}' is not followed by a value.", name ) );
							}

							if( memberDetails.FieldInfo.FieldType.Equals( typeof( List<string> ) ) )
							{
								List<string> list = (List<string>) memberDetails.FieldInfo.GetValue( target );

								list.Add( _args[argumentPos] );
							}
							else if( memberDetails.FieldInfo.FieldType.Equals( typeof( string ) ) )
							{
								memberDetails.FieldInfo.SetValue( target, _args[argumentPos] );
							}

							++argumentPos;
						}
						else
						{
							memberDetails.FieldInfo.SetValue( target, true );
						}

						memberDetails.Found = true;
					}
					else
					{
						++argumentPos;
						HandleUnknownArgument( target, name );
					}
				}
				else
				{
					++argumentPos;
					HandleUnknownArgument( target, name );
				}
			}

			foreach( KeyValuePair<string, ArgumentDetails> kvp in _allowedSwitches )
			{
				if( kvp.Value.Mandatory )
				{
					System.Reflection.FieldInfo fieldInfo = kvp.Value.FieldInfo;

					if( fieldInfo.FieldType.Equals( typeof( List<string> ) ) )
					{
						List<string> list = (List<string>) fieldInfo.GetValue( target );

						if( list.Count == 0 )
						{
							HandleError( target, string.Format( "You must specify at least one '{0}' argument.", kvp.Key ) );
						}
					}
					else if( fieldInfo.FieldType.Equals( typeof( string ) ) )
					{
						if( fieldInfo.GetValue( target ) == null )
						{
							HandleError( target, string.Format( "You must specify the '{0}' argument.", kvp.Key ) );
						}
					}
				}
			}

			return true;
		}

		public string HelpString
		{
			get
			{
				string help = "Allowed arguments:" +Environment.NewLine + Environment.NewLine;

				foreach( KeyValuePair<string, ArgumentDetails> kvp in _allowedSwitches )
				{
					string defaultText = string.Empty;

					if( kvp.Value.DefaultValue != null )
					{
						defaultText = string.Format( " (default '{0}')", kvp.Value.DefaultValue );
					}

					help += string.Format( "/{0}\t{1}{2}{3}", kvp.Key, kvp.Value.Help, defaultText, Environment.NewLine );
				}

				return help;
			}
		}

		public virtual void DisplayHelp( string help )
		{
			System.Console.WriteLine( help );
		}

		public virtual void HandleError( T returnValue, string error )
		{
			throw new ArgumentException( string.Format( "{0}{1}{1}{2}", error, Environment.NewLine, HelpString ) );
		}

		public virtual void HandleUnknownArgument( T returnValue, string value )
		{
			HandleError( returnValue, string.Format( "Unknown argument '{0}'", value ) );
		}

		private TAttribute GetAttribute<TAttribute>( System.Reflection.MemberInfo memberInfo )
			where TAttribute : Attribute
		{
			TAttribute value = null;

			foreach( TAttribute attr in memberInfo.GetCustomAttributes( typeof( TAttribute ), false ) )
			{
				if( value != null )
				{
					throw new InvalidOperationException( string.Format("Attribute '{0}' found multiple times on type '{1}'.",
						typeof(TAttribute).Name, typeof(T).Name) );
				}

				value = attr;
			}

			return value;
		}

		private sealed class ArgumentDetails
		{
			internal System.Reflection.FieldInfo FieldInfo;
			internal string Help = string.Empty;
			internal string DefaultValue = null;
			internal bool Mandatory = false;
			internal bool Found;
			internal bool NeedsArgument;
		}

		private string[] _args;
		private Dictionary<string, ArgumentDetails> _allowedSwitches;
	}
}

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