Click here to Skip to main content
15,885,985 members
Articles / Programming Languages / C#

Themed Windows XP style Explorer Bar

Rate me:
Please Sign up or sign in to vote.
4.96/5 (471 votes)
6 Oct 200515 min read 1.9M   72.6K   1.1K  
A fully customizable Windows XP style Explorer Bar that supports Windows XP themes and animated expand/collapse with transparency.
/*
 * Copyright (c) 2004, Mathew Hall
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 *    - Redistributions of source code must retain the above copyright notice, 
 *      this list of conditions and the following disclaimer.
 * 
 *    - Redistributions in binary form must reproduce the above copyright notice, 
 *      this list of conditions and the following disclaimer in the documentation 
 *      and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 */


using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Design;
using System.Drawing.Imaging;
using System.Data;
using System.Globalization;
using System.Reflection;
using System.Windows.Forms;
using System.Windows.Forms.Design;


namespace XPExplorerBar
{
	#region Delegates

	/// <summary>
	/// 
	/// </summary>
	public delegate void ExpandoEventHandler(ExpandoEventArgs e);
	
	#endregion


	
	#region Expando

	/// <summary>
	/// Summary description for Expando.
	/// </summary>
	[ToolboxItem(true)]
	[DesignerAttribute(typeof(ExpandoDesigner))]
	public class Expando : Control, ISupportInitialize
	{
		#region EventHandlers
		
		/// <summary>
		/// Occurs when the value of the Collapsed property changes
		/// </summary>
		public event ExpandoEventHandler StateChanged;
		
		/// <summary>
		/// Occurs when the value of the TitleImage property changes
		/// </summary>
		public event ExpandoEventHandler TitleImageChanged;
		
		/// <summary>
		/// Occurs when the value of the SpecialGroup property changes
		/// </summary>
		public event ExpandoEventHandler SpecialGroupChanged;

		/// <summary>
		/// 
		/// </summary>
		public event ControlEventHandler ItemAdded;

		/// <summary>
		/// 
		/// </summary>
		public event ControlEventHandler ItemRemoved;

		#endregion	
		
		
		#region Class Data

		/// <summary>
		/// Required designer variable
		/// </summary>
		protected Container components = null;

		/// <summary>
		/// System settings for the Expando
		/// </summary>
		protected ExplorerBarInfo systemSettings;

		/// <summary>
		/// Is the Expando a special group
		/// </summary>
		protected bool specialGroup;

		/// <summary>
		/// The height of the Expando in its expanded state
		/// </summary>
		protected int expandedHeight;

		/// <summary>
		/// The image displayed on the left side of the titlebar
		/// </summary>
		protected Image titleImage;

		/// <summary>
		/// 
		/// </summary>
		protected Image watermark;

		/// <summary>
		/// The height of the header section 
		/// (includes titlebar and title image)
		/// </summary>
		protected int headerHeight;

		/// <summary>
		/// Is the Expando collapsed
		/// </summary>
		protected bool collapsed;

		/// <summary>
		/// The state of the titlebar
		/// </summary>
		protected FocusStates focusState;

		/// <summary>
		/// The height of the titlebar
		/// </summary>
		protected int titleBarHeight;

		/// <summary>
		/// Are we currently animating a fade
		/// </summary>
		protected bool animatingFade;

		/// <summary>
		/// Are we currently animating a slide
		/// </summary>
		protected bool animatingSlide;

		/// <summary>
		/// An image of the "client area" which is used 
		/// during a fade animation
		/// </summary>
		protected Image animationImage;

		/// <summary>
		/// The TaskPane the Expando belongs to
		/// </summary>
		protected TaskPane taskpane;

		/// <summary>
		/// Should the Expando layout its items itself
		/// </summary>
		protected bool autoLayout;

		/// <summary>
		/// The last known width of the Expando 
		/// (used while animating)
		/// </summary>
		protected int oldWidth;

		/// <summary>
		/// Are we initialising
		/// </summary>
		protected bool initialising = false;

		/// <summary>
		/// Internal list of items contained in the Expando
		/// </summary>
		protected ItemCollection itemList;

		/// <summary>
		/// Internal list of controls that have been hidden
		/// </summary>
		protected ArrayList hiddenControls;

		/// <summary>
		/// Are we able to perform any layout operations;
		/// </summary>
		protected bool layout;

		/// <summary>
		/// A panel we can move our controls onto when we are 
		/// animating from collapsed to expanded.
		/// </summary>
		internal AnimationPanel dummyPanel;

		/// <summary>
		/// Is the Expando allowed to collapse
		/// </summary>
		protected bool canCollapse;

		/// <summary>
		/// The height of the Expando at the end of its slide animation
		/// </summary>
		protected int slideEndHeight;

		#endregion


		#region Constructor
		
		/// <summary>
		/// Initializes a new instance of the Expando class with default settings
		/// </summary>
		public Expando() : base()
		{
			// This call is required by the Windows.Forms Form Designer.
			this.components = new System.ComponentModel.Container();

			// set control styles
			this.SetStyle(ControlStyles.DoubleBuffer, true);
			this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
			this.SetStyle(ControlStyles.UserPaint, true);
			this.SetStyle(ControlStyles.ResizeRedraw, true);
			this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
			this.SetStyle(ControlStyles.Selectable, true);
			this.TabStop = true;

			// get the system theme settings
			this.systemSettings = Util.GetSystemExplorerBarSettings();
			this.BackColor = this.systemSettings.Expando.NormalBackColor;

			// the height of the Expando in the expanded state
			this.expandedHeight = 100;

			// size
			this.Size = new Size(this.systemSettings.Header.BackImageWidth, this.expandedHeight);
			this.titleBarHeight = this.systemSettings.Header.BackImageHeight;
			this.headerHeight = this.titleBarHeight;
			this.oldWidth = this.Width;

			// start expanded
			this.collapsed = false;
			
			// not a special group
			this.specialGroup = false;

			// unfocused titlebar
			this.focusState = FocusStates.None;

			// no title image
			this.titleImage = null;
			this.watermark = null;

			// animation
			this.animatingFade = false;
			this.animatingSlide = false;
			this.animationImage = null;
			this.slideEndHeight = -1;

			// don't get the Expando to layout its items itself
			this.autoLayout = false;

			// don't know which TaskPane we belong to
			this.taskpane = null;

			// internal list of items
			this.itemList = new ItemCollection(this);
			this.hiddenControls = new ArrayList();

			// initialise the dummyPanel
			this.dummyPanel = new AnimationPanel();
			this.dummyPanel.Size = this.Size;
			this.dummyPanel.Location = new Point(-1000, 0);

			this.canCollapse = true;

			this.layout = true;

			this.Font = new Font(this.TitleFont.Name, 8.25f, FontStyle.Regular);
		}

		#endregion


		#region Methods

		#region Animation

		#region Fade Collapse/Expand

		/// <summary>
		/// Collapses the group without any animation.  
		/// </summary>
		public void Collapse()
		{
			if (!this.Enabled)
			{
				return;
			}
			
			this.Height = this.headerHeight;
		}


		/// <summary>
		/// Expands the group without any animation.  
		/// </summary>
		public void Expand()
		{
			if (!this.Enabled)
			{
				return;
			}
			
			this.Height = this.expandedHeight;
		}


		/// <summary>
		/// Gets the Expando ready to start its collapse/expand animation
		/// </summary>
		internal void StartFadeAnimation()
		{
			if (!this.Enabled)
			{
				return;
			}
			
			//
			this.animatingFade = true;

			//
			this.SuspendLayout();

			// get an image of the client area that we can
			// use for alpha-blending in our animation
			this.animationImage = this.GetFadeAnimationImage();

			// set each control invisible (otherwise they
			// appear to slide off the bottom of the group)
			foreach (Control control in this.Controls)
			{
				control.Visible = false;
			}

			// restart the layout engine
			this.ResumeLayout(false);
		}


		/// <summary>
		/// Updates the next "frame" of the animation
		/// </summary>
		internal void UpdateFadeAnimation(int animationStepNum, int numAnimationSteps)
		{
			if (!this.Enabled)
			{
				return;
			}
			
			// the percentage we need to adjust our height by
			// double step = (1 / (double) numAnimationSteps) * animationStepNum;
			// replacement by: Joel Holdsworth (joel@airwebreathe.org.uk)
			//                 Paolo Messina (ppescher@hotmail.com)
			//                 05/06/2004
			//                 v1.1
			double step = (1.0 - Math.Cos(Math.PI * (double) animationStepNum / (double) numAnimationSteps)) / 2.0;
			
			// set the height of the group
			if (this.collapsed)
			{
				this.Height = this.expandedHeight - (int) ((this.expandedHeight - this.headerHeight) * step);
			}
			else
			{
				this.Height = this.headerHeight + (int) ((this.expandedHeight - this.headerHeight) * step);
			}

			// draw the next frame
			this.Invalidate();
		}


		/// <summary>
		/// Gets the Expando to stop its animation
		/// </summary>
		internal void StopFadeAnimation()
		{
			if (!this.Enabled)
			{
				return;
			}
			
			//
			this.animatingFade = false;

			//
			this.SuspendLayout();

			// get rid of the image used for the animation
			this.animationImage.Dispose();
			this.animationImage = null;

			// set the final height of the group, depending on
			// whether we are collapsed or expanded
			if (this.collapsed)
			{
				this.Collapse();
			}
			else
			{
				this.Expand();
			}

			// set each control visible again
			foreach (Control control in this.Controls)
			{
				control.Visible = !this.hiddenControls.Contains(control);
			}

			//
			this.ResumeLayout(true);
		}


		/// <summary>
		/// Returns an image of the group's display area to be used
		/// in the fade animation
		/// </summary>
		/// <returns>The Image to use during the fade animation</returns>
		internal Image GetFadeAnimationImage()
		{
			if (this.Height == this.ExpandedHeight)
			{
				return this.GetExpandedImage();
			}
			else
			{
				return this.GetCollapsedImage();
			}
		}


		/// <summary>
		/// Gets the image to be used in the animation while the 
		/// Expando is in its expanded state
		/// </summary>
		/// <returns>The Image to use during the fade animation</returns>
		internal Image GetExpandedImage()
		{
			// create a new image to draw into
			Image image = new Bitmap(this.Width, this.Height);

			// get a graphics object we can draw into
			Graphics g = Graphics.FromImage(image);
			IntPtr hDC = g.GetHdc();

			// some flags to tell the control how to draw itself
			IntPtr flags = (IntPtr) (WmPrintFlags.PRF_CLIENT | WmPrintFlags.PRF_CHILDREN | WmPrintFlags.PRF_ERASEBKGND);
			
			// tell the control to draw itself
			NativeMethods.SendMessage(this.Handle, WindowMessageFlags.WM_PRINT, hDC, flags);

			// clean up resources
			g.ReleaseHdc(hDC);
			g.Dispose();

			// return the completed animation image
			return image;
		}


		/// <summary>
		/// Gets the image to be used in the animation while the 
		/// Expando is in its collapsed state
		/// </summary>
		/// <returns>The Image to use during the fade animation</returns>
		internal Image GetCollapsedImage()
		{
			// this is pretty nasty.  after much experimentation, 
			// this is the least preferred way to get the image as
			// it is a pain in the backside, but it stops any 
			// flickering and it gets xp themed controls to draw 
			// their borders properly.
			// we have to do this in two stages:
			//    1) pretend we're expanded and draw our background,
			//       borders and "client area" into a bitmap
			//    2) set the bitmap as our dummyPanel's background, 
			//       move all our controls onto the dummyPanel and 
			//       get the dummyPanel to print itself

			int width = this.Width;
			int height = this.ExpandedHeight;
			
			
			// create a new image to draw that is the same
			// size we would be if we were expanded
			Image backImage = new Bitmap(width, height);

			// get a graphics object we can draw into
			Graphics g = Graphics.FromImage(backImage);

			// draw our parents background
			this.PaintTransparentBackground(g, new Rectangle(0, 0, width, height));

			// don't need to draw the titlebar as it is ignored 
			// when we paint with the animation image, but we do 
			// need to draw the borders and "client area"

			// borders
			using (SolidBrush brush = new SolidBrush(this.BorderColor))
			{
				// top border
				g.FillRectangle(brush, 
					this.Border.Left, 
					this.HeaderHeight, 
					width - this.Border.Left - this.Border.Right, 
					this.Border.Top); 
				
				// left border
				g.FillRectangle(brush, 
					0, 
					this.HeaderHeight, 
					this.Border.Left, 
					height - this.HeaderHeight); 
				
				// right border
				g.FillRectangle(brush, 
					width - this.Border.Right, 
					this.HeaderHeight, 
					this.Border.Right, 
					height - this.HeaderHeight); 
				
				// bottom border
				g.FillRectangle(brush, 
					this.Border.Left, 
					height - this.Border.Bottom, 
					width - this.Border.Left - this.Border.Right, 
					this.Border.Bottom); 
			}

			// "client area"
			using (SolidBrush brush = new SolidBrush(this.BackColor))
			{
				g.FillRectangle(brush, 
					this.Border.Left, 
					this.HeaderHeight, 
					width - this.Border.Left - this.Border.Right,
					height - this.HeaderHeight - this.Border.Bottom - this.Border.Top);
			}

			// watermark
			if (this.Watermark != null)
			{
				// work out a rough location of where the watermark should go
				Rectangle rect = new Rectangle(0, 0, this.Watermark.Width, this.Watermark.Height);
				rect.X = width - this.Border.Right - this.Watermark.Width;
				rect.Y = height - this.Border.Bottom - this.Watermark.Height;

				// shrink the destination rect if necesary so that we
				// can see all of the image
				
				if (rect.X < 0)
				{
					rect.X = 0;
				}

				if (rect.Width > this.ClientRectangle.Width)
				{
					rect.Width = this.ClientRectangle.Width;
				}

				if (rect.Y < this.DisplayRectangle.Top)
				{
					rect.Y = this.DisplayRectangle.Top;
				}

				if (rect.Height > this.DisplayRectangle.Height)
				{
					rect.Height = this.DisplayRectangle.Height;
				}

				// draw the watermark
				g.DrawImage(this.Watermark, rect);
			}

			// cleanup resources;
			g.Dispose();

			// make sure the dummyPanel is the same size as our image
			// (we don't want any tiling of the image)
			this.dummyPanel.Size = new Size(width, height);
			this.dummyPanel.HeaderHeight = this.HeaderHeight;
			this.dummyPanel.Border = this.Border;
			
			// set the image as the dummyPanels background
			this.dummyPanel.BackImage = backImage;

			// move all our controls to the dummyPanel, and then add
			// the dummyPanel to us
			while (this.Controls.Count > 0)
			{
				Control control = this.Controls[0];

				this.Controls.RemoveAt(0);
				this.dummyPanel.Controls.Add(control);

				control.Visible = !this.hiddenControls.Contains(control);
			}
			this.Controls.Add(this.dummyPanel);


			// create a new image for the dummyPanel to draw itself into
			Image image = new Bitmap(width, height);

			// get a graphics object we can draw into
			g = Graphics.FromImage(image);
			IntPtr hDC = g.GetHdc();

			// some flags to tell the control how to draw itself
			IntPtr flags = (IntPtr) (WmPrintFlags.PRF_CLIENT | WmPrintFlags.PRF_CHILDREN);
			
			// tell the control to draw itself
			NativeMethods.SendMessage(this.dummyPanel.Handle, WindowMessageFlags.WM_PRINT, hDC, flags);

			// clean up resources
			g.ReleaseHdc(hDC);
			g.Dispose();

			this.Controls.Remove(this.dummyPanel);

			// get our controls back
			while (this.dummyPanel.Controls.Count > 0)
			{
				Control control = this.dummyPanel.Controls[0];

				control.Visible = false;
				
				this.dummyPanel.Controls.RemoveAt(0);
				this.Controls.Add(control);
			}

			// dispose of the background image
			this.dummyPanel.BackImage = null;
			backImage.Dispose();

			return image;
		}

		#endregion

		#region Slide Show/Hide

		/// <summary>
		/// Gets the Expando ready to start its show/hide animation
		/// </summary>
		internal void StartSlideAnimation()
		{
			if (!this.Enabled)
			{
				return;
			}
			
			this.animatingSlide = true;
			
			this.slideEndHeight = this.CalcHeightAndLayout();
		}


		/// <summary>
		/// Updates the next "frame" of the animation
		/// </summary>
		internal void UpdateSlideAnimation(int animationStepNum, int numAnimationSteps)
		{
			if (!this.Enabled)
			{
				return;
			}
			
			// the percentage we need to adjust our height by
			// double step = (1 / (double) numAnimationSteps) * animationStepNum;
			// replacement by: Joel Holdsworth (joel@airwebreathe.org.uk)
			//                 Paolo Messina (ppescher@hotmail.com)
			//                 05/06/2004
			//                 v1.1
			double step = (1.0 - Math.Cos(Math.PI * (double) animationStepNum / (double) numAnimationSteps)) / 2.0;
			
			// set the height of the group
			this.Height = this.expandedHeight + (int) ((this.slideEndHeight - this.expandedHeight) * step);

			// draw the next frame
			this.Invalidate();
		}


		/// <summary>
		/// Gets the Expando to stop its animation
		/// </summary>
		internal void StopSlideAnimation()
		{
			if (!this.Enabled)
			{
				return;
			}
			
			this.animatingSlide = false;

			// make sure we're the right height
			this.Height = this.slideEndHeight;
			this.slideEndHeight = -1;

			this.DoLayout();
		}

		#endregion

		#endregion

		#region Controls

		/// <summary>
		/// Hides the specified Control
		/// </summary>
		/// <param name="control">The Control to hide</param>
		public void HideControl(Control control)
		{
			this.HideControl(control, false);
		}


		/// <summary>
		/// Hides the specified Control
		/// </summary>
		/// <param name="control">The Control to hide</param>
		/// <param name="animate">Will any animation be performed</param>
		public void HideControl(Control control, bool animate)
		{
			this.HideControl(new Control[] {control}, animate);
		}


		/// <summary>
		/// Hides the Controls contained in the specified array
		/// </summary>
		/// <param name="controls">The array Controls to hide</param>
		public void HideControl(Control[] controls)
		{
			this.HideControl(controls, false);
		}


		/// <summary>
		/// Hides the Controls contained in the specified array
		/// </summary>
		/// <param name="controls">The array Controls to hide</param>
		/// <param name="animate">Will any animation be performed</param>
		public void HideControl(Control[] controls, bool animate)
		{
			// don't bother if we are animating or are collapsed
			// or are disabled
			if (this.Animating || this.Collapsed || !this.Enabled)
			{
				return;
			}
			
			this.SuspendLayout();
			
			// flag to check if we actually hid any controls
			bool anyHidden = false;
			
			foreach (Control control in controls)
			{
				// hide the control if we own it and it is not already hidden
				if (this.Controls.Contains(control) && !this.hiddenControls.Contains(control))
				{
					anyHidden = true;

					control.Visible = false;
					this.hiddenControls.Add(control);
				}
			}

			this.ResumeLayout(false);

			// if we didn't hide any, get out of here
			if (!anyHidden)
			{
				return;
			}

			// are we able to animate?
			if (!animate || !this.AutoLayout || this.TaskPane == null || !this.TaskPane.Animate)
			{
				// guess not
				this.DoLayout();
			}
			else
			{
				this.TaskPane.StartSlideAnimation(this);
			}
		}


		/// <summary>
		/// Shows the specified Control
		/// </summary>
		/// <param name="control">The Control to show</param>
		public void ShowControl(Control control)
		{
			this.ShowControl(control, false);
		}
		

		/// <summary>
		/// Shows the specified Control
		/// </summary>
		/// <param name="control">The Control to show</param>
		/// <param name="animate">Will any animation be performed</param>
		public void ShowControl(Control control, bool animate)
		{
			this.ShowControl(new Control[] {control}, animate);
		}


		/// <summary>
		/// Shows the Controls contained in the specified array
		/// </summary>
		/// <param name="controls">The array Controls to show</param>
		public void ShowControl(Control[] controls)
		{
			this.ShowControl(controls, false);
		}


		/// <summary>
		/// Shows the Controls contained in the specified array
		/// </summary>
		/// <param name="controls">The array Controls to show</param>
		/// <param name="animate">Will any animation be performed</param>
		public void ShowControl(Control[] controls, bool animate)
		{
			// don't bother if we are animating or are collapsed
			// or are disabled
			if (this.Animating || this.Collapsed || !this.Enabled)
			{
				return;
			}
			
			this.SuspendLayout();
			
			// flag to check if any controls were shown
			bool anyHidden = false;
			
			foreach (Control control in controls)
			{
				// show the control if we own it and it is not already shown
				if (this.Controls.Contains(control) && this.hiddenControls.Contains(control))
				{
					anyHidden = true;

					control.Visible = true;
					this.hiddenControls.Remove(control);
				}
			}

			this.ResumeLayout(false);

			// if we didn't show any, get out of here
			if (!anyHidden)
			{
				return;
			}

			// are we able to animate?
			if (!animate || !this.AutoLayout || this.TaskPane == null || !this.TaskPane.Animate)
			{
				// guess not
				this.DoLayout();
			}
			else
			{
				this.TaskPane.StartSlideAnimation(this);
			}
		}

		#endregion
		
		#region Invalidation

		/// <summary>
		/// Invalidates the titlebar area
		/// </summary>
		protected void InvalidateTitleBar()
		{
			this.Invalidate(new Rectangle(0, 0, this.Width, this.headerHeight), false);
		}

		#endregion

		#region ISupportInitialize Members

		/// <summary>
		/// Signals the object that initialization is starting
		/// </summary>
		public void BeginInit()
		{
			this.initialising = true;
		}


		/// <summary>
		/// Signals the object that initialization is complete
		/// </summary>
		public void EndInit()
		{
			this.initialising = false;

			this.DoLayout();
		}

		#endregion

		#region Layout

		/// <summary>
		/// Forces the control to apply layout logic to child controls, 
		/// and adjusts the height of the Expando if necessary
		/// </summary>
		public virtual void DoLayout()
		{
			// stop the layout engine
			this.SuspendLayout();

			// work out the height of the header section

			// is there an image to display on the titlebar
			if (this.titleImage != null)
			{
				// is the image bigger than the height of the titlebar
				if (this.titleImage.Height > this.titleBarHeight)
				{
					this.headerHeight = this.titleImage.Height;
				}
					// is the image smaller than the height of the titlebar
				else if (this.titleImage.Height < this.titleBarHeight)
				{
					this.headerHeight = this.titleBarHeight;
				}
					// is the image smaller than the current header height
				else if (this.titleImage.Height < this.headerHeight)
				{
					this.headerHeight = this.titleImage.Height;
				}
			}
			else
			{
				this.headerHeight = this.titleBarHeight;
			}

			// do we need to layout our items
			if (this.AutoLayout)
			{
				Control c;
				TaskItem ti;
				Point p;

				// work out how wide to make the controls, and where
				// the top of the first control should be
				int y = this.DisplayRectangle.Y + this.Padding.Top;
				int width = this.PseudoClientRect.Width - this.Padding.Left - this.Padding.Right;

				// for each control in our list...
				for (int i=0; i<this.itemList.Count; i++)
				{
					c = (Control) this.itemList[i];

					if (this.hiddenControls.Contains(c))
					{
						continue;
					}

					// set the starting point
					p = new Point(this.Padding.Left, y);

					// is the control a TaskItem?  if so, we may
					// need to take into account the margins
					if (c is TaskItem)
					{
						ti = (TaskItem) c;
						
						// only adjust the y co-ord if this isn't the first item 
						if (i > 0)
						{
							y += ti.Margin.Top;

							p.Y = y;
						}

						// adjust and set the width and height
						width -= ti.Margin.Left + ti.Margin.Right;

						ti.Width = width;
						ti.Height = ti.PreferredHeight;
					}					

					// set the location of the control
					c.Location = p;

					// update the next starting point.
					y += c.Height;

					// is the control a TaskItem?  if so, we may
					// need to take into account the bottom margin
					if (i < this.itemList.Count-1)
					{
						if (c is TaskItem)
						{
							ti = (TaskItem) c;
							
							y += ti.Margin.Bottom;
						}
						else
						{
							y += this.systemSettings.TaskLink.Margin.Bottom;
						}
					}
				}

				// workout where the bottom of the Expando should be
				y += this.Padding.Bottom + this.Border.Bottom;

				// adjust the ExpandedHeight if they're not the same
				if (y != this.ExpandedHeight)
				{
					this.ExpandedHeight = y;

					// if we're not collapsed then we had better change
					// our height as well
					if (!this.Collapsed)
					{
						this.Height = this.ExpandedHeight;

						// if we belong to a TaskPane then it needs to
						// re-layout its Expandos
						if (this.TaskPane != null)
						{
							this.TaskPane.DoLayout();
						}
					}
				}
			}

			// restart the layout engine
			this.ResumeLayout(true);
		}


		/// <summary>
		/// Calculates the height that the Expando would be if a 
		/// call to DoLayout() were made
		/// </summary>
		/// <returns>The height that the Expando would be if a 
		/// call to DoLayout() were made</returns>
		internal int CalcHeightAndLayout()
		{
			// stop the layout engine
			this.SuspendLayout();

			// work out the height of the header section

			// is there an image to display on the titlebar
			if (this.titleImage != null)
			{
				// is the image bigger than the height of the titlebar
				if (this.titleImage.Height > this.titleBarHeight)
				{
					this.headerHeight = this.titleImage.Height;
				}
					// is the image smaller than the height of the titlebar
				else if (this.titleImage.Height < this.titleBarHeight)
				{
					this.headerHeight = this.titleBarHeight;
				}
					// is the image smaller than the current header height
				else if (this.titleImage.Height < this.headerHeight)
				{
					this.headerHeight = this.titleImage.Height;
				}
			}
			else
			{
				this.headerHeight = this.titleBarHeight;
			}

			int y = -1;

			// do we need to layout our items
			if (this.AutoLayout)
			{
				Control c;
				TaskItem ti;
				Point p;

				// work out how wide to make the controls, and where
				// the top of the first control should be
				y = this.DisplayRectangle.Y + this.Padding.Top;
				int width = this.PseudoClientRect.Width - this.Padding.Left - this.Padding.Right;

				// for each control in our list...
				for (int i=0; i<this.itemList.Count; i++)
				{
					c = (Control) this.itemList[i];

					if (this.hiddenControls.Contains(c))
					{
						continue;
					}

					// set the starting point
					p = new Point(this.Padding.Left, y);

					// is the control a TaskItem?  if so, we may
					// need to take into account the margins
					if (c is TaskItem)
					{
						ti = (TaskItem) c;
						
						// only adjust the y co-ord if this isn't the first item 
						if (i > 0)
						{
							y += ti.Margin.Top;

							p.Y = y;
						}

						// adjust and set the width and height
						width -= ti.Margin.Left + ti.Margin.Right;

						ti.Width = width;
						ti.Height = ti.PreferredHeight;
					}					

					// set the location of the control
					c.Location = p;

					// update the next starting point.
					y += c.Height;

					// is the control a TaskItem?  if so, we may
					// need to take into account the bottom margin
					if (i < this.itemList.Count-1)
					{
						if (c is TaskItem)
						{
							ti = (TaskItem) c;
							
							y += ti.Margin.Bottom;
						}
						else
						{
							y += this.systemSettings.TaskLink.Margin.Bottom;
						}
					}
				}

				// workout where the bottom of the Expando should be
				y += this.Padding.Bottom + this.Border.Bottom;
			}

			// restart the layout engine
			this.ResumeLayout(true);

			return y;
		}


		/// <summary>
		/// Updates the layout of the Expandos items while in design mode, and 
		/// adds/removes itemss from the ControlCollection as necessary
		/// </summary>
		internal void UpdateItems()
		{
			if (this.Items.Count == this.Controls.Count)
			{
				// make sure the the items index in the ControlCollection 
				// are the same as in the ItemCollection (indexes in the 
				// ItemCollection may have changed due to the user moving 
				// them around in the editor)
				this.MatchControlCollToItemColl();				
				
				return;
			}

			// were any items added
			if (this.Items.Count > this.Controls.Count)
			{
				// add any extra items in the ItemCollection to the 
				// ControlCollection
				for (int i=0; i<this.Items.Count; i++)
				{
					if (!this.Controls.Contains(this.Items[i]))
					{
						this.OnItemAdded(new ControlEventArgs(this.Items[i]));
					}
				}
			}
			else
			{
				// items were removed
				int i = 0;
				Control control;

				// remove any extra items from the ControlCollection
				while (i < this.Controls.Count)
				{
					control = (Control) this.Controls[i];
					
					if (!this.Items.Contains(control))
					{
						this.OnItemRemoved(new ControlEventArgs(control));
					}
					else
					{
						i++;
					}
				}
			}

			this.Invalidate(true);
		}


		/// <summary>
		/// Make sure the controls index in the ControlCollection 
		/// are the same as in the ItemCollection (indexes in the 
		/// ItemCollection may have changed due to the user moving 
		/// them around in the editor or calling ItemCollection.Move())
		/// </summary>
		internal void MatchControlCollToItemColl()
		{
			this.SuspendLayout();
				
			for (int i=0; i<this.Items.Count; i++)
			{
				this.Controls.SetChildIndex(this.Items[i], i);
			}

			this.ResumeLayout(false);
				
			this.DoLayout();

			this.Invalidate(true);
		}

		#endregion

		#endregion


		#region Properties

		#region Alignment

		/// <summary>
		/// The alignment of the text in the title bar.
		/// </summary>
		protected ContentAlignment TitleAlignment
		{
			get
			{
				if (this.SpecialGroup)
				{
					return this.systemSettings.Header.SpecialAlignment;
				}
				
				return this.systemSettings.Header.NormalAlignment;
			}
		}

		#endregion

		#region Animation

		/// <summary>
		/// Gets whether the Expando is currently animating
		/// </summary>
		[Browsable(false)]
		public bool Animating
		{
			get
			{
				return (this.animatingFade || this.animatingSlide);
			}
		}


		/// <summary>
		/// Gets the Image used by the Expando while it is animating
		/// </summary>
		protected Image AnimationImage
		{
			get
			{
				return this.animationImage;
			}
		}

		#endregion

		#region Border

		/// <summary>
		/// The width of the border along each side of the Expando's pane.
		/// </summary>
		protected Border Border
		{
			get
			{
				if (this.SpecialGroup)
				{
					return this.systemSettings.Expando.SpecialBorder;
				}
				
				return this.systemSettings.Expando.NormalBorder;
			}
		}


		/// <summary>
		/// The color of the border along each side of the Expando's pane.
		/// </summary>
		protected Color BorderColor
		{
			get
			{
				if (this.SpecialGroup)
				{
					return this.systemSettings.Expando.SpecialBorderColor;
				}
				
				return this.systemSettings.Expando.NormalBorderColor;
			}
		}


		/// <summary>
		/// The width of the border along each side of the Expando's Title Bar.
		/// </summary>
		protected Border TitleBorder
		{
			get
			{
				if (this.SpecialGroup)
				{
					return this.systemSettings.Header.SpecialBorder;
				}
				
				return this.systemSettings.Header.NormalBorder;
			}
		}

		#endregion

		#region Color

		/// <summary>
		/// Gets the backgroubd color of the titlebar
		/// </summary>
		protected Color TitleBackColor
		{
			get
			{
				if (this.SpecialGroup)
				{
					if (this.systemSettings.Header.SpecialBackColor != Color.Transparent)
					{
						return this.systemSettings.Header.SpecialBackColor;
					}
					
					return this.systemSettings.Header.SpecialBorderColor;
				}

				if (this.systemSettings.Header.NormalBackColor != Color.Transparent)
				{
					return this.systemSettings.Header.NormalBackColor;
				}
					
				return this.systemSettings.Header.NormalBorderColor;
			}
		}

		#endregion

		#region Client Rectangle

		/// <summary>
		/// Returns a fake Client Rectangle.  
		/// The rectangle takes into account the size of the titlebar 
		/// and borders (these are actually parts of the real 
		/// ClientRectangle)
		/// </summary>
		protected Rectangle PseudoClientRect
		{
			get
			{
				return new Rectangle(this.Border.Left, 
					this.HeaderHeight + this.Border.Top,
					this.Width - this.Border.Left - this.Border.Right,
					this.Height - this.HeaderHeight - this.Border.Top - this.Border.Bottom);
			}
		}


		/// <summary>
		/// Returns the height of the fake client rectangle
		/// </summary>
		protected int PseudoClientHeight
		{	
			get
			{
				return this.Height - this.HeaderHeight - this.Border.Top - this.Border.Bottom;
			}
		}

		#endregion

		#region Display Rectangle

		/// <summary>
		/// Overrides DisplayRectangle so that docked controls
		/// don't cover the titlebar or borders
		/// </summary>
		[Browsable(false)]
		public override Rectangle DisplayRectangle
		{
			get
			{
				return new Rectangle(this.Border.Left, 
					this.HeaderHeight + this.Border.Top,
					this.Width - this.Border.Left - this.Border.Right,
					this.ExpandedHeight - this.HeaderHeight - this.Border.Top - this.Border.Bottom);
			}
		}


		/// <summary>
		/// Gets a rectangle that contains the titlebar area
		/// </summary>
		protected Rectangle TitleBarRectangle
		{
			get
			{
				return new Rectangle(0,
					this.HeaderHeight - this.TitleBarHeight,
					this.Width,
					this.TitleBarHeight);
			}
		}

		#endregion

		#region Fonts

		/// <summary>
		/// The color of the Title Bar's text.
		/// </summary>
		protected Color TitleForeColor
		{
			get
			{
				if (this.SpecialGroup)
				{
					return this.systemSettings.Header.SpecialTitleColor;
				}
				
				return this.systemSettings.Header.NormalTitleColor;
			}
		}


		/// <summary>
		/// The color of the Title Bar's text when highlighted.
		/// </summary>
		protected Color TitleHotForeColor
		{
			get
			{
				if (this.SpecialGroup)
				{
					return this.systemSettings.Header.SpecialTitleHotColor;
				}
				
				return this.systemSettings.Header.NormalTitleHotColor;
			}
		}


		/// <summary>
		/// Gets the current color of the Title Bar's text, depending 
		/// on the current state of the Expando
		/// </summary>
		protected Color TitleColor
		{
			get
			{
				if (this.FocusState == FocusStates.Mouse)
				{
					return this.TitleHotForeColor;
				}

				return this.TitleForeColor;
			}
		}


		/// <summary>
		/// The font used to render the Title Bar's text.
		/// </summary>
		protected Font TitleFont
		{
			get
			{
				return this.systemSettings.Header.TitleFont;
			}
		}

		#endregion		

		#region Images

		/// <summary>
		/// Gets the expand/collapse arrow image currently displayed 
		/// in the title bar, depending on the current state of the Expando
		/// </summary>
		protected Image ArrowImage
		{
			get
			{
				if (this.SpecialGroup)
				{
					if (this.collapsed)
					{
						if (this.FocusState == FocusStates.None)
						{
							return this.systemSettings.Header.SpecialArrowDown;
						}
						else
						{
							return this.systemSettings.Header.SpecialArrowDownHot;
						}
					}
					else
					{
						if (this.FocusState == FocusStates.None)
						{
							return this.systemSettings.Header.SpecialArrowUp;
						}
						else
						{
							return this.systemSettings.Header.SpecialArrowUpHot;
						}
					}
				}
				else
				{
					if (this.collapsed)
					{
						if (this.FocusState == FocusStates.None)
						{
							return this.systemSettings.Header.NormalArrowDown;
						}
						else
						{
							return this.systemSettings.Header.NormalArrowDownHot;
						}
					}
					else
					{
						if (this.FocusState == FocusStates.None)
						{
							return this.systemSettings.Header.NormalArrowUp;
						}
						else
						{
							return this.systemSettings.Header.NormalArrowUpHot;
						}
					}
				}
			}
		}


		/// <summary>
		/// Gets the width of the expand/collapse arrow image 
		/// currently displayed in the title bar
		/// </summary>
		protected int ArrowImageWidth
		{
			get
			{
				if (this.ArrowImage == null)
				{
					return 0;
				}

				return this.ArrowImage.Width;
			}
		}


		/// <summary>
		/// Gets the height of the expand/collapse arrow image 
		/// currently displayed in the title bar
		/// </summary>
		protected int ArrowImageHeight
		{
			get
			{
				if (this.ArrowImage == null)
				{
					return 0;
				}
			
				return this.ArrowImage.Height;
			}
		}


		/// <summary>
		/// The background image used for the Title Bar.
		/// </summary>
		protected Image TitleBackImage
		{
			get
			{
				if (this.SpecialGroup)
				{
					return this.systemSettings.Header.SpecialBackImage;
				}
				
				return this.systemSettings.Header.NormalBackImage;
			}
		}


		/// <summary>
		/// Gets the height of the background image used for the Title Bar.
		/// </summary>
		protected int TitleBackImageHeight
		{
			get
			{
				return this.systemSettings.Header.BackImageHeight;
			}
		}


		/// <summary>
		/// The image used on the left side of the Title Bar.
		/// </summary>
		[Bindable(true), 
		Category("Appearance"),
		DefaultValue(null),
		Description("The image used on the left side of the Title Bar.")]
		public Image TitleImage
		{
			get
			{
				return this.titleImage;
			}

			set
			{
				if (this.titleImage != value)
				{
					this.titleImage = value;

					this.DoLayout();

					this.Invalidate();

					OnTitleImageChanged(new ExpandoEventArgs(this));
				}
			}
		}


		/// <summary>
		/// The width of the image used on the left side of the Title Bar.
		/// </summary>
		protected int TitleImageWidth
		{
			get
			{
				if (this.TitleImage == null)
				{
					return 0;
				}
	
				return this.TitleImage.Width;
			}
		}


		/// <summary>
		/// The height of the image used on the left side of the Title Bar.
		/// </summary>
		protected int TitleImageHeight
		{
			get
			{
				if (this.TitleImage == null)
				{
					return 0;
				}
			
				return this.TitleImage.Height;
			}
		}


		/// <summary>
		/// 
		/// </summary>
		[Bindable(true), 
		Category("Appearance"),
		DefaultValue(null),
		Description("")]
		public Image Watermark
		{
			get
			{
				return this.watermark;
			}

			set
			{
				if (this.watermark != value)
				{
					this.watermark = value;

					this.Invalidate();
				}
			}
		}

		#endregion

		#region Items

		/// <summary>
		/// An Expando.ItemCollection representing the collection of 
		/// Controls contained within the Expando
		/// </summary>
		[Category("Behavior"),
		DefaultValue(null), 
		Description("The Controls contained in the Expando"), 
		DesignerSerializationVisibility(DesignerSerializationVisibility.Content), 
		Editor(typeof(ItemCollectionEditor), typeof(UITypeEditor))]
		public Expando.ItemCollection Items
		{
			get
			{
				return this.itemList;
			}
		}


		/// <summary>
		/// A Control.ControlCollection representing the collection of 
		/// controls contained within the control
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public new Control.ControlCollection Controls
		{
			get
			{
				return base.Controls;
			}
		}

		#endregion

		#region Layout

		/// <summary>
		/// Determines whether the Expando will automagically layout its items
		/// </summary>
		[Bindable(true),
		Category("Layout"),
		DefaultValue(false),
		Description("The AutoLayout property determines whether the Expando will automagically layout its items.")]
		public bool AutoLayout
		{
			get
			{
				return this.autoLayout;
			}

			set
			{
				this.autoLayout = value;

				if (this.autoLayout)
				{
					this.DoLayout();
				}
			}
		}

		
		/// <summary>
		/// The height of the Expando in its expanded state
		/// </summary>
		[Bindable(true),
		Category("Layout"),
		DefaultValue(100),
		Description("The height of the Expando in its expanded state.")]
		public int ExpandedHeight
		{
			get
			{
				return this.expandedHeight;
			}

			set
			{
				this.expandedHeight = value;

				if (!this.collapsed && !this.Animating)
				{
					this.Height = this.expandedHeight;

					if (this.taskpane != null)
					{
						this.taskpane.DoLayout();
					}
				}
			}
		}


		/// <summary>
		/// The height of the header section of the Expando
		/// </summary>
		protected int HeaderHeight
		{
			get
			{
				return this.headerHeight;
			}
		}


		/// <summary>
		/// The height of the titlebar
		/// </summary>
		protected int TitleBarHeight
		{
			get
			{
				return this.titleBarHeight;
			}
		}

		#endregion

		#region Padding

		/// <summary>
		/// The amount of space between the border and items along 
		/// each side of the ExplorerBarGroup.
		/// </summary>
		protected Padding Padding
		{
			get
			{
				if (this.SpecialGroup)
				{
					return this.systemSettings.Expando.SpecialPadding;
				}
				
				return this.systemSettings.Expando.NormalPadding;
			}
		}


		/// <summary>
		/// The amount of space between the border and items along 
		/// each side of the Title Bar.
		/// </summary>
		protected Padding TitlePadding
		{
			get
			{
				if (this.SpecialGroup)
				{
					return this.systemSettings.Header.SpecialPadding;
				}
				
				return this.systemSettings.Header.NormalPadding;
			}
		}

		#endregion

		#region Size

		/// <summary>
		/// Gets or sets the height and width of the control
		/// </summary>
		public new Size Size
		{
			get
			{
				return base.Size;
			}

			set
			{
				if (!this.Size.Equals(value))
				{
					if (!this.Animating)
					{
						this.Width = value.Width;
						this.ExpandedHeight = value.Height;
					}
				}
			}
		}

		#endregion

		#region Special Groups

		/// <summary>
		/// Specifies whether the Expando be rendered as a Special Group.
		/// </summary>
		[Bindable(true), 
		Category("Appearance"),
		DefaultValue(false),
		Description("The SpecialGroup property determines whether the Expando will be rendered as a SpecialGroup.")]
		public bool SpecialGroup
		{
			get
			{
				return this.specialGroup;
			}

			set
			{
				this.specialGroup = value;

				this.DoLayout();

				if (this.specialGroup)
				{
					this.BackColor = this.systemSettings.Expando.SpecialBackColor;
				}
				else
				{
					this.BackColor = this.systemSettings.Expando.NormalBackColor;
				}
				
				this.Invalidate();

				OnSpecialGroupChanged(new ExpandoEventArgs(this));
			}
		}

		#endregion

		#region State

		/// <summary>
		/// The Collapsed property determines whether the Expando is collapsed.
		/// </summary>
		[Bindable(true), 
		Category("Appearance"),
		DefaultValue(false),
		Description("The Collapsed property determines whether the Expando is collapsed.")]
		public bool Collapsed
		{
			get
			{
				return this.collapsed;
			}

			set
			{
				if (this.collapsed != value)
				{
					// don't bother if we're not enabled
					if (!this.Enabled)
					{
						return;
					}
					
					// if we're supposed to collapse, check if we can
					if (value && !this.CanCollapse)
					{
						// looks like we can't so time to bail
						return;
					}
					
					this.collapsed = value;

					// if we don't have a taskpane or the taskpane can't 
					// animate then we'd better expand/collapse by ourself
					if (this.TaskPane == null || !this.TaskPane.Animate)
					{
						if (this.collapsed)
						{
							this.Collapse();
						}
						else
						{
							this.Expand();
						}
					}

					// let everyone know we've collapsed/expanded
					this.OnStateChanged(new ExpandoEventArgs(this));
				}
			}
		}


		/// <summary>
		/// Specifies whether the title bar is in a highlighted state.
		/// </summary>
		[Browsable(false)]
		protected FocusStates FocusState
		{
			get
			{
				return this.focusState;
			}

			set
			{
				if (this.focusState != value)
				{
					this.focusState = value;

					this.InvalidateTitleBar();
				}
			}
		}


		/// <summary>
		/// Gets or sets whether the Expando is able to collapse
		/// </summary>
		[Bindable(true), 
		Category("Behavior"),
		DefaultValue(true),
		Description("The CanCollapse property determines whether the Expando is able to collapse.")]
		public bool CanCollapse
		{
			
			get
			{ 
				return this.canCollapse; 
			}
			
			set
			{ 
				this.canCollapse = value; 
			}
		}

		#endregion

		#region System Settings

		/// <summary>
		/// System settings for the Expando
		/// </summary>
		[Browsable(false)]
		public ExplorerBarInfo SystemSettings
		{
			set
			{
				// make sure we have a new value
				if (this.systemSettings != value)
				{
					// get rid of the old settings
					if (this.systemSettings != null)
					{
						this.systemSettings.Dispose();
						this.systemSettings = null;
					}

					// set the new settings
					this.systemSettings = value;
				}

				if (this.SpecialGroup)
				{
					this.BackColor = this.systemSettings.Expando.SpecialBackColor;
				}
				else
				{
					this.BackColor = this.systemSettings.Expando.NormalBackColor;
				}
				// update the system settings for each TaskItem
				for (int i=0; i<this.itemList.Count; i++)
				{
					Control control = (Control) this.itemList[i];

					if (control is TaskItem)
					{
						((TaskItem) control).SystemSettings = this.systemSettings;
					}
				}

				// if our parent is not an TaskPane then re-layout the 
				// Expando (don't need to do this if our parent is a 
				// TaskPane as it will tell us when to do it)
				if (this.TaskPane == null)
				{
					this.DoLayout();
				}
			}
		}

		#endregion
		
		#region TaskPane

		/// <summary>
		/// The TaskPane the Expando belongs to
		/// </summary>
		[Browsable(false)]
		public TaskPane TaskPane
		{
			get
			{
				return this.taskpane;
			}

			set
			{
				this.taskpane = value;

				if (value != null)
				{
					this.SystemSettings = this.TaskPane.SystemSettings;
				}
			}
		}

		#endregion

		#region Text

		/// <summary>
		/// Gets or sets the text displayed on the titlebar
		/// </summary>
		public override string Text
		{
			get
			{
				return base.Text;
			}

			set
			{
				base.Text = value;

				this.InvalidateTitleBar();
			}
		}

		#endregion

		#region Visible

		/// <summary>
		/// Gets or sets a value indicating whether the Expando is displayed
		/// </summary>
		public new bool Visible
		{
			get
			{
				return base.Visible;
			}

			set
			{
				if (base.Visible != value)
				{
					base.Visible = value;

					if (this.TaskPane != null)
					{
						this.TaskPane.DoLayout();
					}
				}
			}
		}


		/// <summary>
		/// Gets or sets a value indicating whether the control can 
		/// respond to user interaction
		/// </summary>
		public new bool Enabled
		{
			get
			{
				return base.Enabled;
			}

			set
			{
				if (base.Enabled != value && !this.Animating)
				{
					base.Enabled = value;
				}
			}
		}
	
		#endregion

		#endregion


		#region Events

		#region Controls

		/// <summary>
		/// Raises the ControlAdded event
		/// </summary>
		/// <param name="e">A ControlEventArgs that contains the event data</param>
		protected override void OnControlAdded(ControlEventArgs e)
		{
			// don't do anything if we are animating
			// (as we're probably the ones who added the control)
			if (this.Animating)
			{
				return;
			}
			
			base.OnControlAdded(e);
			
			// check if the expando was added by dragging it onto 
			// the taskpane instead of being added to the expando 
			// collection during design time
			if (this.DesignMode && !this.Items.Contains(e.Control))
			{
				this.Items.Add(e.Control);
			}
		}


		/// <summary>
		/// Raises the ControlRemoved event
		/// </summary>
		/// <param name="e">A ControlEventArgs that contains the event data</param>
		protected override void OnControlRemoved(ControlEventArgs e)
		{
			// don't do anything if we are animating 
			// (as we're probably the ones who removed the control)
			if (this.Animating)
			{
				return;
			}
			
			base.OnControlRemoved(e);

			// remove the control from the itemList
			if (this.Items.Contains(e.Control))
			{
				this.Items.Remove(e.Control);
			}

			// update the layout of the controls
			this.DoLayout();
		}

		#endregion

		#region Expando

		/// <summary>
		/// Raises the StateChanged event
		/// </summary>
		/// <param name="e">An ExpandoEventArgs that contains the event data</param>
		protected virtual void OnStateChanged(ExpandoEventArgs e)
		{
			if (StateChanged != null)
			{
				StateChanged(e);
			}
		}


		/// <summary>
		/// Raises the TitleImageChanged event
		/// </summary>
		/// <param name="e">An ExpandoEventArgs that contains the event data</param>
		protected virtual void OnTitleImageChanged(ExpandoEventArgs e)
		{
			if (TitleImageChanged != null)
			{
				TitleImageChanged(e);
			}
		}


		/// <summary>
		/// Raises the SpecialGroupChanged event
		/// </summary>
		/// <param name="e">An ExpandoEventArgs that contains the event data</param>
		protected virtual void OnSpecialGroupChanged(ExpandoEventArgs e)
		{
			if (SpecialGroupChanged != null)
			{
				SpecialGroupChanged(e);
			}
		}

		#endregion

		#region Items

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnItemAdded(ControlEventArgs e)
		{
			// add the expando to the ControlCollection if it hasn't already
			if (!this.Controls.Contains(e.Control))
			{
				this.Controls.Add(e.Control);
			}

			// check if the control is a TaskItem
			if (e.Control is TaskItem)
			{
				TaskItem item = (TaskItem) e.Control;
			
				// set anchor styles
				item.Anchor = (AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right);
			
				// tell the TaskItem who's its daddy...
				item.Expando = this;
				item.SystemSettings = this.systemSettings;
			}

			// update the layout of the controls
			this.DoLayout();

			//
			if (ItemAdded != null)
			{
				ItemAdded(this, e);
			}
		}


		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnItemRemoved(ControlEventArgs e)
		{
			// remove the control from the ControlCollection if it hasn't already
			if (this.Controls.Contains(e.Control))
			{
				this.Controls.Remove(e.Control);
			}

			// update the layout of the controls
			this.DoLayout();

			//
			if (ItemRemoved != null)
			{
				ItemRemoved(this, e);
			}
		}

		#endregion

		#region Mouse

		/// <summary>
		/// Raises the MouseUp event
		/// </summary>
		/// <param name="e">A MouseEventArgs that contains the event data</param>
		protected override void OnMouseUp(MouseEventArgs e)
		{
			base.OnMouseUp(e);

			// was it the left mouse button
			if (e.Button == MouseButtons.Left)
			{
				// was it in the titlebar area
				if (e.Y < this.HeaderHeight && e.Y > (this.HeaderHeight - this.TitleBarHeight))
				{
					// make sure that our taskPane (if we have one) is not animating
					if (this.TaskPane == null || !this.TaskPane.IsAnimating)
					{
						// collapse/expand the group
						this.Collapsed = !this.Collapsed;
					}
				}
			}
		}


		/// <summary>
		/// Raises the MouseDown event
		/// </summary>
		/// <param name="e">A MouseEventArgs that contains the event data</param>
		protected override void OnMouseDown(MouseEventArgs e)
		{
			base.OnMouseDown(e);

			// we're not doing anything here yet...
			// but we might later :)
		}


		/// <summary>
		/// Raises the MouseMove event
		/// </summary>
		/// <param name="e">A MouseEventArgs that contains the event data</param>
		protected override void OnMouseMove(MouseEventArgs e)
		{
			base.OnMouseMove(e);

			// check if the mouse is moving in the titlebar area
			if (e.Y < this.HeaderHeight && e.Y > (this.HeaderHeight - this.TitleBarHeight))
			{
				// change the cursor to a hand and highlight the titlebar
				this.Cursor = Cursors.Hand;
				this.FocusState = FocusStates.Mouse;
			}
			else
			{
				// reset the titlebar highlight and cursor if they haven't already
				this.Cursor = Cursors.Default;
				this.FocusState = FocusStates.None;
			}
		}


		/// <summary>
		/// Raises the MouseLeave event
		/// </summary>
		/// <param name="e">An EventArgs that contains the event data</param>
		protected override void OnMouseLeave(EventArgs e)
		{
			base.OnMouseLeave(e);

			// reset the titlebar highlight if it hasn't already
			this.FocusState = FocusStates.None;
		}

		#endregion

		#region Paint

		/// <summary>
		/// Raises the PaintBackground event
		/// </summary>
		/// <param name="e">A PaintEventArgs that contains the event data</param>
		protected override void OnPaintBackground(PaintEventArgs e)
		{
			// if we have a transparent backgroubd color, get windows
			// to paint our background for us
			if (this.BackColor == Color.Transparent)
			{
				base.OnPaintBackground(e);
			}
				// we have a solid background color, but the titlebar back image
				// might have treansparent bits, so instead we draw our own 
				// transparent background (rather than getting windows to draw 
				// a solid background)
			else
			{
				this.PaintTransparentBackground(e.Graphics, this.ClientRectangle);
			}
		}


		/// <summary>
		/// Raises the Paint event
		/// </summary>
		/// <param name="e">A PaintEventArgs that contains the event data</param>
		protected override void OnPaint(PaintEventArgs e)
		{
			// paint the titlebar
			PaintTitleBar(e.Graphics);

			// only paint the border and "display rect" if we are not collapsed
			if (this.Height != this.headerHeight)
			{
				PaintBorder(e.Graphics);

				PaintDisplayRect(e.Graphics);
			}
		}


		/// <summary>
		/// Simulates a transparent background by getting the Expandos parent 
		/// to paint its background and foreground into the specified Graphics 
		/// at the specified location
		/// </summary>
		/// <param name="g">The Graphics used to paint the background</param>
		/// <param name="rect">The Rectangle that represents the rectangle 
		/// in which to paint</param>
		protected void PaintTransparentBackground(Graphics g, Rectangle clipRect)
		{
			// check if we have a parent
			if (this.Parent != null)
			{
				// convert the clipRects coordinates from ours to our parents
				clipRect.Offset(this.Location);

				PaintEventArgs e = new PaintEventArgs(g, clipRect);

				// save the graphics state so that if anything goes wrong 
				// we're not fubar
				GraphicsState state = g.Save();

				try
				{
					// move the graphics object so that we are drawing in 
					// the correct place
					g.TranslateTransform((float) -this.Location.X, (float) -this.Location.Y);
					
					// draw the parents background and foreground
					this.InvokePaintBackground(this.Parent, e);
					this.InvokePaint(this.Parent, e);

					return;
				}
				finally
				{
					// reset everything back to where they were before
					g.Restore(state);
					clipRect.Offset(-this.Location.X, -this.Location.Y);
				}
			}

			// we don't have a parent, so fill the rect with
			// the default control color
			g.FillRectangle(SystemBrushes.Control, clipRect);
		}


		/// <summary>
		/// Paints the "Display Rectangle".  This is the dockable
		/// area of the control (ie non-titlebar/border area).  This is
		/// also the same as the PseudoClientRect.
		/// </summary>
		/// <param name="g">The Graphics used to paint the DisplayRectangle</param>
		protected void PaintDisplayRect(Graphics g)
		{
			// are we animating a fade
			if (this.animatingFade && this.AnimationImage != null)
			{
				// calculate the transparency value for the animation image
				float alpha = (((float) (this.Height - this.HeaderHeight)) / ((float) (this.ExpandedHeight - this.HeaderHeight)));
				
				float[][] ptsArray = {new float[] {1, 0, 0, 0, 0},
										 new float[] {0, 1, 0, 0, 0},
										 new float[] {0, 0, 1, 0, 0},
										 new float[] {0, 0, 0, alpha, 0}, 
										 new float[] {0, 0, 0, 0, 1}}; 

				ColorMatrix colorMatrix = new ColorMatrix(ptsArray);
				ImageAttributes imageAttributes = new ImageAttributes();
				imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

				// work out how far up the animation image we need to start
				int y = this.AnimationImage.Height - this.PseudoClientHeight - this.Border.Bottom;

				// draw the image
				g.DrawImage(this.AnimationImage,
					new Rectangle(0, this.HeaderHeight, this.Width, this.Height - this.HeaderHeight),
					0,
					y,
					this.AnimationImage.Width, 
					this.AnimationImage.Height - y,
					GraphicsUnit.Pixel,
					imageAttributes);
			}
				// are we animating a slide
			else if (this.animatingSlide)
			{
				// just paint the area with a solid brush
				using (SolidBrush brush = new SolidBrush(this.BackColor))
				{
					g.FillRectangle(brush, 
						this.Border.Left, 
						this.HeaderHeight + this.Border.Top, 
						this.Width - this.Border.Left - this.Border.Right,
						this.Height - this.HeaderHeight - this.Border.Top - this.Border.Bottom);
				}
			}
			else
			{
				// just paint the area with a solid brush
				using (SolidBrush brush = new SolidBrush(this.BackColor))
				{
					g.FillRectangle(brush, this.DisplayRectangle);
				}

				if (this.Watermark != null)
				{
					// work out a rough location of where the watermark should go
					Rectangle rect = new Rectangle(0, 0, this.Watermark.Width, this.Watermark.Height);
					rect.X = this.PseudoClientRect.Right - this.Watermark.Width;
					rect.Y = this.DisplayRectangle.Bottom - this.Watermark.Height;

					// shrink the destination rect if necesary so that we
					// can see all of the image
				
					if (rect.X < 0)
					{
						rect.X = 0;
					}

					if (rect.Width > this.ClientRectangle.Width)
					{
						rect.Width = this.ClientRectangle.Width;
					}

					if (rect.Y < this.DisplayRectangle.Top)
					{
						rect.Y = this.DisplayRectangle.Top;
					}

					if (rect.Height > this.DisplayRectangle.Height)
					{
						rect.Height = this.DisplayRectangle.Height;
					}

					// draw the watermark
					g.DrawImage(this.Watermark, rect);
				}
			}
		}


		/// <summary>
		/// Paints the title bar
		/// </summary>
		/// <param name="g">The Graphics used to paint the titlebar</param>
		protected void PaintTitleBar(Graphics g)
		{
			// fix: draw grayscale titlebar when disabled
			//      Brad Jones (brad@bradjones.com)
			//      20/08/2004
			//      v1.21
			
			int y = 0;
			
			// work out where the top of the titleBar actually is
			if (this.HeaderHeight > this.TitleBarHeight)
			{
				y = this.HeaderHeight - this.TitleBarHeight;
			}

			// draw the title background image if we have one
			if (this.TitleBackImage != null)
			{
				if (this.Enabled)
				{
					g.DrawImage(this.TitleBackImage, 0, y, this.Width, this.TitleBarHeight);
				}
				else
				{
					// first stretch the background image for ControlPaint.
					using (Image image = new Bitmap(this.TitleBackImage, this.Width, this.TitleBarHeight))
					{
						ControlPaint.DrawImageDisabled(g, image, 0, y, this.TitleBackColor);
					}
				}
			}
			else
			{
				Color c = this.TitleBackColor;
				
				using (SolidBrush brush = new SolidBrush(this.TitleBackColor))
				{
					g.FillRectangle(brush, 0, y, this.Width, this.TitleBarHeight);
				}
			}

			// draw the titlebar image if we have one
			if (this.TitleImage != null)
			{
				if (this.Enabled)
				{
					g.DrawImage(this.TitleImage, 0, 0);
				}
				else
				{
					ControlPaint.DrawImageDisabled(g, TitleImage, 0, 0, this.TitleBackColor);
				}
			}

			// get which collapse/expand arrow we should draw
			Image arrowImage = this.ArrowImage;

			// get the titlebar's border and padding
			Border border = this.TitleBorder;
			Padding padding = this.TitlePadding;

			// draw the arrow if we have one
			if (arrowImage != null)
			{
				// work out where to position the arrow
				int x = this.Width - arrowImage.Width - border.Right - padding.Right;
				y += border.Top + padding.Top;

				// draw it...
				if (this.Enabled)
				{
					g.DrawImage(arrowImage, x, y);
				}
				else
				{
					ControlPaint.DrawImageDisabled(g, arrowImage, x, y, this.TitleBackColor);
				}
			}

			// check if we have any text to draw in the titlebar
			if (this.Text.Length > 0)
			{
				// a rectangle that will contain our text
				Rectangle rect = new Rectangle();
				
				// work out the x coordinate
				if (this.TitleImage == null)
				{
					rect.X = border.Left + padding.Left;
				}
				else
				{
					rect.X = this.TitleImage.Width + border.Left;
				}

				// work out the y coordinate
				ContentAlignment alignment = this.TitleAlignment;

				switch (alignment)
				{
					case ContentAlignment.MiddleLeft:
					case ContentAlignment.MiddleCenter:
					case ContentAlignment.MiddleRight:	rect.Y = ((this.HeaderHeight - this.TitleFont.Height) / 2) + ((this.HeaderHeight - this.TitleBarHeight) / 2) + border.Top + padding.Top;
						break;

					case ContentAlignment.TopLeft:
					case ContentAlignment.TopCenter:
					case ContentAlignment.TopRight:		rect.Y = (this.HeaderHeight - this.TitleBarHeight) + border.Top + padding.Top;
						break;

					case ContentAlignment.BottomLeft:
					case ContentAlignment.BottomCenter:
					case ContentAlignment.BottomRight:	rect.Y = this.HeaderHeight - this.TitleFont.Height;
						break;
				}

				// the height of the rectangle
				rect.Height = this.TitleFont.Height;

				// make sure the text stays inside the header
				if (rect.Bottom > this.HeaderHeight)
				{
					rect.Y -= rect.Bottom - this.HeaderHeight;
				}
					
				// work out how wide the rectangle should be
				if (arrowImage != null)
				{
					rect.Width = this.Width - arrowImage.Width - border.Right - padding.Right - rect.X;
				}
				else
				{
					rect.Width = this.Width - border.Right - padding.Right - rect.X;
				}

				// don't wrap the string, and use an ellipsis if
				// the string is too big to fit the rectangle
				StringFormat sf = new StringFormat();
				sf.FormatFlags = StringFormatFlags.NoWrap;
				sf.Trimming = StringTrimming.EllipsisCharacter;

				// should the string be aligned to the left/center/right
				switch (alignment)
				{
					case ContentAlignment.MiddleLeft:
					case ContentAlignment.TopLeft:
					case ContentAlignment.BottomLeft:	sf.Alignment = StringAlignment.Near;
						break;

					case ContentAlignment.MiddleCenter:
					case ContentAlignment.TopCenter:
					case ContentAlignment.BottomCenter:	sf.Alignment = StringAlignment.Center;
						break;

					case ContentAlignment.MiddleRight:
					case ContentAlignment.TopRight:
					case ContentAlignment.BottomRight:	sf.Alignment = StringAlignment.Far;
						break;
				}

				// draw the text
				using (SolidBrush brush = new SolidBrush(this.TitleColor))
				{
					//g.DrawString(this.Text, this.TitleFont, brush, rect, sf);
					if (this.Enabled)
					{
						g.DrawString(this.Text, this.TitleFont, brush, rect, sf);
					}
					else
					{
						ControlPaint.DrawStringDisabled(g, this.Text, this.TitleFont, SystemColors.ControlLightLight, rect, sf);
					}
				}
			}
		}


		/// <summary>
		/// Paints the borders
		/// </summary>
		/// <param name="g">The Graphics used to paint the border</param>
		protected void PaintBorder(Graphics g)
		{
			// get the current border and border colors
			Border border = this.Border;
			Color c = this.BorderColor;

			// check if we are currently animating a fade
			if (this.animatingFade)
			{
				// calculate the alpha value for the color
				int alpha = (int) (255 * (((float) (this.Height - this.HeaderHeight)) / ((float) (this.ExpandedHeight - this.HeaderHeight))));

				// make sure it doesn't go past 0 or 255
				if (alpha < 0)
				{
					alpha = 0;
				}
				else if (alpha > 255)
				{
					alpha = 255;
				}

				// update the color with the alpha value
				c = Color.FromArgb(alpha, c.R, c.G, c.B);
			}
			
			// draw the borders
			using (SolidBrush brush = new SolidBrush(c))
			{
				g.FillRectangle(brush, border.Left, this.HeaderHeight, this.Width-border.Left-border.Right, border.Top); // top border
				g.FillRectangle(brush, 0, this.HeaderHeight, border.Left, this.Height-this.HeaderHeight); // left border
				g.FillRectangle(brush, this.Width-border.Right, this.HeaderHeight, border.Right, this.Height-this.HeaderHeight); // right border
				g.FillRectangle(brush, border.Left, this.Height-border.Bottom, this.Width-border.Left-border.Right, border.Bottom); // bottom border
			}
		}

		#endregion

		#region Size

		/// <summary>
		/// Raises the SizeChanged event
		/// </summary>
		/// <param name="e">An EventArgs that contains the event data</param>
		protected override void OnSizeChanged(EventArgs e)
		{
			base.OnSizeChanged(e);

			// if we are currently animating and the width of the
			// group has changed (eg. due to a scrollbar on the 
			// TaskPane appearing/disappearing), get a new image 
			// to use for the animation. (if we were to continue to 
			// use the old image it would be shrunk or stretched making 
			// the animation look wierd)
			if (this.Animating && this.Width != this.oldWidth)
			{
				this.oldWidth = this.Width;
				
				if (this.AnimationImage != null)
				{
					// get the new animationImage
					this.animationImage = this.GetFadeAnimationImage();
				}
			}
				// check if the width has changed.  if it has re-layout
				// the group so that the TaskItems can resize themselves
				// if neccessary
			else if (this.Width != this.oldWidth)
			{
				this.oldWidth = this.Width;
				
				this.DoLayout();
			}
		}

		#endregion

		#endregion


		#region AnimationPanel

		/// <summary>
		/// An extremely stripped down version of an Expando that an 
		/// Expando can use instead of itself to get an image of its 
		/// "client area" and child controls
		/// </summary>
		internal class AnimationPanel : Panel
		{
			#region Class Data

			/// <summary>
			/// The height of the header section 
			/// (includes titlebar and title image)
			/// </summary>
			protected int headerHeight;
 
			/// <summary>
			/// The border around the "client area"
			/// </summary>
			protected Border border;

			/// <summary>
			/// The background image displayed in the control
			/// </summary>
			protected Image backImage;

			#endregion


			#region Constructor

			/// <summary>
			/// Initializes a new instance of the AnimationPanel class with default settings
			/// </summary>
			public AnimationPanel() : base()
			{
				this.headerHeight = 0;
				this.border = new Border();
				this.backImage = null;
			}

			#endregion


			#region Properties

			/// <summary>
			/// Overrides AutoScroll to disable scrolling
			/// </summary>
			public new bool AutoScroll
			{
				get
				{
					return false;
				}

				set
				{

				}
			}


			/// <summary>
			/// Gets or sets the height of the header section of the Expando
			/// </summary>
			public int HeaderHeight
			{
				get
				{
					return this.headerHeight;
				}

				set
				{
					this.headerHeight = value;
				}
			}


			/// <summary>
			/// Gets or sets the border around the "client area"
			/// </summary>
			public Border Border
			{
				get
				{
					return this.border;
				}

				set
				{
					this.border = value;
				}
			}


			/// <summary>
			/// Gets or sets the background image displayed in the control
			/// </summary>
			public Image BackImage
			{
				get
				{
					return this.backImage;
				}

				set
				{
					this.backImage = value;
				}
			}


			/// <summary>
			/// Overrides DisplayRectangle so that docked controls
			/// don't cover the titlebar or borders
			/// </summary>
			public override Rectangle DisplayRectangle
			{
				get
				{
					return new Rectangle(this.Border.Left, 
						this.HeaderHeight + this.Border.Top,
						this.Width - this.Border.Left - this.Border.Right,
						this.Height - this.HeaderHeight - this.Border.Top - this.Border.Bottom);
				}
			}

			#endregion


			#region Events

			/// <summary>
			/// Raises the Paint event
			/// </summary>
			/// <param name="e">A PaintEventArgs that contains the event data</param>
			protected override void OnPaint(PaintEventArgs e)
			{
				base.OnPaint(e);

				if (this.BackImage != null)
				{
					e.Graphics.DrawImageUnscaled(this.BackImage, 0, 0);
				}
			}

			#endregion
		}

		#endregion


		#region ItemCollection

		/// <summary>
		/// Represents a collection of Control objects
		/// </summary>
		public class ItemCollection : CollectionBase
		{
			#region Class Data

			/// <summary>
			/// The Expando that owns this ControlCollection
			/// </summary>
			private Expando owner;

			#endregion


			#region Constructor

			/// <summary>
			/// Initializes a new instance of the Expando.ItemCollection class
			/// </summary>
			/// <param name="owner">An Expando representing the expando that owns 
			/// the Control collection</param>
			public ItemCollection(Expando owner) : base()
			{
				if (owner == null)
				{
					throw new ArgumentNullException("owner");
				}
				
				this.owner = owner;
			}

			#endregion


			#region Methods

			/// <summary>
			/// Adds the specified control to the control collection
			/// </summary>
			/// <param name="value">The Control to add to the control collection</param>
			public void Add(Control value)
			{
				if (value == null)
				{
					throw new ArgumentNullException("value");
				}

				this.List.Add(value);
				this.owner.Controls.Add(value);

				this.owner.OnItemAdded(new ControlEventArgs(value));
			}


			/// <summary>
			/// Adds an array of control objects to the collection
			/// </summary>
			/// <param name="expandos">An array of Control objects to add 
			/// to the collection</param>
			public void AddRange(Control[] controls)
			{
				if (controls == null)
				{
					throw new ArgumentNullException("controls");
				}

				for (int i=0; i<controls.Length; i++)
				{
					this.Add(controls[i]);
				}
			}
			
			
			/// <summary>
			/// Removes all controls from the collection
			/// </summary>
			public new void Clear()
			{
				while (this.Count > 0)
				{
					this.RemoveAt(0);
				}
			}


			/// <summary>
			/// Determines whether the specified control is a member of the 
			/// collection
			/// </summary>
			/// <param name="expandos">The Control to locate in the collection</param>
			/// <returns>true if the Control is a member of the collection; 
			/// otherwise, false</returns>
			public bool Contains(Control control)
			{
				if (control == null)
				{
					throw new ArgumentNullException("control");
				}

				return (this.IndexOf(control) != -1);
			}


			/// <summary>
			/// Retrieves the index of the specified control in the control 
			/// collection
			/// </summary>
			/// <param name="expando">The Control to locate in the collection</param>
			/// <returns>A zero-based index value that represents the position 
			/// of the specified Control in the Expando.ItemCollection</returns>
			public int IndexOf(Control control)
			{
				if (control == null)
				{
					throw new ArgumentNullException("control");
				}
				
				for (int i=0; i<this.Count; i++)
				{
					if (this[i] == control)
					{
						return i;
					}
				}

				return -1;
			}


			/// <summary>
			/// Removes the specified control from the control collection
			/// </summary>
			/// <param name="value">The Control to remove from the 
			/// Expando.ItemCollection</param>
			public void Remove(Control value)
			{
				if (value == null)
				{
					throw new ArgumentNullException("value");
				}

				this.List.Remove(value);
				this.owner.Controls.Remove(value);

				this.owner.OnItemRemoved(new ControlEventArgs(value));
			}

			
			/// <summary>
			/// Removes a control from the control collection at the 
			/// specified indexed location
			/// </summary>
			/// <param name="index">The index value of the Control to 
			/// remove</param>
			public new void RemoveAt(int index)
			{
				this.Remove(this[index]);
			}


			/// <summary>
			/// Moves the specified control to the specified indexed location 
			/// in the control collection
			/// </summary>
			/// <param name="expando">The control to be moved</param>
			/// <param name="index">The indexed location in the control collection 
			/// that the specified control will be moved to</param>
			public void Move(Control value, int index)
			{
				if (value == null)
				{
					throw new ArgumentNullException("value");
				}

				// make sure the index is within range
				if (index < 0)
				{
					index = 0;
				}
				else if (index > this.Count)
				{
					index = this.Count;
				}

				// don't go any further if the expando is already 
				// in the desired position or we don't contain it
				if (!this.Contains(value) || this.IndexOf(value) == index)
				{
					return;
				}

				this.List.Remove(value);

				// if the index we're supposed to move the expando to
				// is now greater to the number of expandos contained, 
				// add it to the end of the list, otherwise insert it at 
				// the specified index
				if (index > this.Count)
				{
					this.List.Add(value);
				}
				else
				{
					this.List.Insert(index, value);
				}

				// re-layout the controls
				//this.owner.MatchControlCollToExpandoColl();
			}


			/// <summary>
			/// Moves the specified control to the top of the control collection
			/// </summary>
			/// <param name="value">The control to be moved</param>
			public void MoveToTop(Control value)
			{
				this.Move(value, 0);
			}


			/// <summary>
			/// Moves the specified control to the bottom of the control collection
			/// </summary>
			/// <param name="value">The control to be moved</param>
			public void MoveToBottom(Control value)
			{
				this.Move(value, this.Count);
			}

			#endregion


			#region Properties

			/// <summary>
			/// The Control located at the specified index location within 
			/// the control collection
			/// </summary>
			/// <param name="index">The index of the control to retrieve 
			/// from the control collection</param>
			public virtual Control this[int index]
			{
				get
				{
					return this.List[index] as Control;
				}
			}

			#endregion
		}

		#endregion
	
	
		#region ItemCollectionEditor

		/// <summary>
		/// 
		/// </summary>
		internal class ItemCollectionEditor : CollectionEditor
		{
			/// <summary>
			/// Initializes a new instance of the CollectionEditor class 
			/// using the specified collection type
			/// </summary>
			/// <param name="type"></param>
			public ItemCollectionEditor(Type type) : base(type)
			{
			
			}


			/// <summary>
			/// Edits the value of the specified object using the specified 
			/// service provider and context
			/// </summary>
			/// <param name="context">An ITypeDescriptorContext that can be 
			/// used to gain additional context information</param>
			/// <param name="isp">A service provider object through which 
			/// editing services can be obtained</param>
			/// <param name="value">The object to edit the value of</param>
			/// <returns>The new value of the object. If the value of the 
			/// object has not changed, this should return the same object 
			/// it was passed</returns>
			public override object EditValue(ITypeDescriptorContext context, IServiceProvider isp, object value)
			{
				Expando originalControl = (Expando) context.Instance;

				object returnObject = base.EditValue(context, isp, value);

				originalControl.UpdateItems();

				return returnObject;
			}


			/// <summary>
			/// Creates a new instance of the specified collection item type
			/// </summary>
			/// <param name="itemType">The type of item to create</param>
			/// <returns>A new instance of the specified object</returns>
			protected override object CreateInstance(Type itemType)
			{
				// ignore the type of item we're supposed to create 
				// and assume the user wants to create a taskitem
				object item = base.CreateInstance(typeof(TaskItem));
			
				((TaskItem) item).Name = "taskitem";
			
				return item;
			}
		}

		#endregion
	}

	#endregion



	#region ExpandoEventArgs

	/// <summary>
	/// Summary description for ExpandoEventArgs.
	/// </summary>
	public class ExpandoEventArgs : EventArgs
	{
		#region Class Data

		/// <summary>
		/// The Expando thet generated the event
		/// </summary>
		private Expando expando;

		#endregion


		#region Constructor

		/// <summary>
		/// Initializes a new instance of the ExpandoEventArgs class with default settings
		/// </summary>
		public ExpandoEventArgs()
		{
			expando = null;
		}


		/// <summary>
		/// Initializes a new instance of the ExpandoEventArgs class with specific Expando
		/// </summary>
		/// <param name="expando"></param>
		public ExpandoEventArgs(Expando expando)
		{
			this.expando = expando;
		}

		#endregion


		#region Properties

		/// <summary>
		/// Gets the Expando that generated the event
		/// </summary>
		public Expando Expando
		{
			get
			{
				return this.expando;
			}
		}


		/// <summary>
		/// Gets whether the Expando is collapsed
		/// </summary>
		public bool Collapsed
		{
			get
			{
				return this.expando.Collapsed;
			}
		}

		#endregion
	}

	#endregion



	#region ExpandoConverter

	/// <summary>
	/// 
	/// </summary>
	internal class ExpandoConverter : TypeConverter
	{
		/// <summary>
		/// Returns whether this converter can convert the object to the 
		/// specified type, using the specified context
		/// </summary>
		/// <param name="context">An ITypeDescriptorContext that provides a 
		/// format context</param>
		/// <param name="destinationType">A Type that represents the type 
		/// you want to convert to</param>
		/// <returns>true if this converter can perform the conversion; o
		/// therwise, false</returns>
		public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
		{
			if (destinationType == typeof(InstanceDescriptor))
			{
				return true;
			}

			return base.CanConvertTo(context, destinationType);
		}

		/// <summary>
		/// Converts the given value object to the specified type, using 
		/// the specified context and culture information
		/// </summary>
		/// <param name="context">An ITypeDescriptorContext that provides 
		/// a format context</param>
		/// <param name="culture">A CultureInfo object. If a null reference 
		/// is passed, the current culture is assumed</param>
		/// <param name="value">The Object to convert</param>
		/// <param name="destinationType">The Type to convert the value 
		/// parameter to</param>
		/// <returns>An Object that represents the converted value</returns>
		public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
		{
			if (destinationType == typeof(InstanceDescriptor) && value is Expando)
			{
				ConstructorInfo ci = typeof(Expando).GetConstructor(new Type[] {});

				if (ci != null)
				{
					return new InstanceDescriptor(ci, null, false);
				}
			}

			return base.ConvertTo(context, culture, value, destinationType);
		}
	}

	#endregion



	#region ExpandoDesigner

	/// <summary>
	/// Summary description for ExpandoDesigner.
	/// </summary>
	public class ExpandoDesigner : ParentControlDesigner
	{
		/// <summary>
		/// Initializes a new instance of the ExpandoDesigner class
		/// </summary>
		public ExpandoDesigner()
		{
			
		}


		/// <summary>
		/// Adjusts the set of properties the component exposes through 
		/// a TypeDescriptor
		/// </summary>
		/// <param name="properties">An IDictionary containing the properties 
		/// for the class of the component</param>
		protected override void PreFilterProperties(IDictionary properties)
		{
			base.PreFilterProperties(properties);

			//properties.Remove("AutoScroll");
			//properties.Remove("AutoScrollMargin");
			//properties.Remove("AutoScrollMinSize");
			properties.Remove("BackColor");
			properties.Remove("BackgroundImage");
			properties.Remove("BorderStyle");
			properties.Remove("Cursor");
			properties.Remove("BackgroundImage");
		}
	}

	#endregion



	#region FocusStates

	/// <summary>
	/// Summary description for FocusState.
	/// </summary>
	public enum FocusStates
	{
		/// <summary>
		/// Normal state
		/// </summary>
		None = 0,	
		
		/// <summary>
		/// The mouse is over the title bar
		/// </summary>
		Mouse = 1,	
		
		/// <summary>
		/// Gained focus via the keyboard
		/// </summary>
		Keyboard = 2
	}

	#endregion
}

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 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


Written By
Web Developer
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions