/*
* 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.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Data;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace XPExplorerBar
{
#region TaskPane
/// <summary>
/// A ScrollableControl that can contain Expandos
/// </summary>
[ToolboxItem(true),
DesignerAttribute(typeof(TaskPaneDesigner))]
public class TaskPane : ScrollableControl, ISupportInitialize
{
#region Event Handlers
/// <summary>
/// Occurs when an Expando is added to the TaskPane
/// </summary>
public event ExpandoEventHandler ExpandoAdded;
/// <summary>
/// Occurs when an Expando is removed from the TaskPane
/// </summary>
public event ExpandoEventHandler ExpandoRemoved;
#endregion
#region Class Data
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
/// <summary>
/// Internal list of Expandos contained in the TaskPane
/// </summary>
private TaskPane.ExpandoCollection expandoCollection;
/// <summary>
/// System defined settings for the TaskBar
/// </summary>
private ExplorerBarInfo systemSettings;
/// <summary>
/// Specifies whether the TaskPane is able to perform animations
/// </summary>
private bool animate;
/// <summary>
/// Internal list of AnimationHelpers that aid in the animation
/// of Expandos
/// </summary>
private ArrayList animationHelpers;
/// <summary>
/// Specifies whether the TaskPane is currently initialising
/// </summary>
private bool initialising;
/// <summary>
/// Specifies whether the TaskPane and its children should render
/// themselves using a theme similar to the Windows XP Classic theme
/// </summary>
private bool classicTheme;
/// <summary>
/// Specifies whether the TaskPane and its children should render
/// themselves using a non-official Windows XP theme
/// </summary>
private bool customTheme;
/// <summary>
/// A Rectangle that specifies the size and location of the watermark
/// </summary>
private Rectangle watermarkRect;
/// <summary>
/// Specifies whether the scrollbars have changed
/// </summary>
internal bool scrollChanged = false;
/// <summary>
/// Specifies whether the TaskPane is currently performing a
/// layout operation
/// </summary>
internal bool layout = false;
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the TaskPane class with default settings
/// </summary>
public TaskPane()
{
// This call is required by the Windows.Forms Form Designer.
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.expandoCollection = new TaskPane.ExpandoCollection(this);
// get the system theme settings
this.systemSettings = ThemeManager.GetSystemExplorerBarSettings();
this.BackColor = this.systemSettings.TaskPane.GradientStartColor;
this.BackgroundImage = this.BackImage;
this.classicTheme = false;
this.customTheme = false;
// size
int width = (this.systemSettings.TaskPane.Padding.Left +
this.systemSettings.TaskPane.Padding.Right +
this.systemSettings.Header.BackImageWidth);
int height = width;
this.Size = new Size(width, height);
// setup sutoscrolling
this.AutoScroll = false;
this.AutoScrollMargin = new Size(this.systemSettings.TaskPane.Padding.Right,
this.systemSettings.TaskPane.Padding.Bottom);
// don't use animation
this.animate = false;
this.animationHelpers = new ArrayList();
this.initialising = false;
}
#endregion
#region Methods
#region Animation
/// <summary>
/// Starts a fade animation for the specified Expando
/// </summary>
/// <param name="expando">The Expando that should perform the
/// fade animation</param>
public void StartFadeAnimation(Expando expando)
{
AnimationHelper animationHelper = new AnimationHelper(this, expando, AnimationHelper.FadeAnimation);
this.animationHelpers.Add(animationHelper);
animationHelper.StartAnimation();
}
/// <summary>
/// Starts a slide animation for the specified Expando
/// </summary>
/// <param name="expando">The Expando that should perform the
/// slide animation</param>
public void StartSlideAnimation(Expando expando)
{
AnimationHelper animationHelper = new AnimationHelper(this, expando, AnimationHelper.SlideAnimation);
this.animationHelpers.Add(animationHelper);
animationHelper.StartAnimation();
}
/// <summary>
/// Notifies the TaskPane that the specified AnimationHelper
/// has completed its animation
/// </summary>
/// <param name="animationHelper">The AnimationHelper that
/// has completed its animation</param>
public void AnimationStopped(AnimationHelper animationHelper)
{
this.animationHelpers.Remove(animationHelper);
animationHelper = null;
this.Invalidate(true);
}
#endregion
#region Appearance
/// <summary>
/// Forces the TaskPane and all it's Expandos to use a theme
/// equivalent to Windows XPs classic theme
/// </summary>
public void UseClassicTheme()
{
this.classicTheme = true;
this.customTheme = false;
ExplorerBarInfo settings = ThemeManager.GetSystemExplorerBarSettings();
settings.UseClassicTheme();
this.systemSettings.Dispose();
this.systemSettings = null;
this.SystemSettings = settings;
}
/// <summary>
/// Forces the TaskPane and all it's Expandos to use the
/// specified theme
/// </summary>
/// <param name="stylePath">The path to the custom
/// shellstyle.dll to use</param>
public void UseCustomTheme(string stylePath)
{
this.customTheme = true;
this.classicTheme = false;
ExplorerBarInfo settings = ThemeManager.GetSystemExplorerBarSettings(stylePath);
this.systemSettings.Dispose();
this.systemSettings = null;
this.SystemSettings = settings;
}
/// <summary>
/// Forces the TaskPane and all it's Expandos to use the
/// current system theme
/// </summary>
public void UseDefaultTheme()
{
this.customTheme = false;
this.classicTheme = false;
ExplorerBarInfo settings = ThemeManager.GetSystemExplorerBarSettings();
this.systemSettings.Dispose();
this.systemSettings = null;
this.SystemSettings = settings;
}
#endregion
#region Dispose
/// <summary>
/// Releases the unmanaged resources used by the TaskPane and
/// optionally releases the managed resources
/// </summary>
/// <param name="disposing">True to release both managed and unmanaged
/// resources; false to release only unmanaged resources</param>
protected override void Dispose( bool disposing )
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
if (this.systemSettings != null)
{
this.systemSettings.Dispose();
}
}
base.Dispose(disposing);
}
#endregion
#region Expandos
/// <summary>
/// Collaspes all the Expandos contained in the TaskPane
/// </summary>
// suggested by: PaleyX (jmpalethorpe@tiscali.co.uk)
// 03/06/2004
// v1.1
public void CollapseAll()
{
foreach (Expando expando in this.Expandos)
{
expando.Collapsed = true;
}
}
/// <summary>
/// Expands all the Expandos contained in the TaskPane
/// </summary>
// suggested by: PaleyX (jmpalethorpe@tiscali.co.uk)
// 03/06/2004
// v1.1
public void ExpandAll()
{
foreach (Expando expando in this.Expandos)
{
expando.Collapsed = false;
}
}
/// <summary>
/// Collaspes all the Expandos contained in the TaskPane,
/// except for the specified Expando which is expanded
/// </summary>
/// <param name="expando">The Expando that is to be expanded</param>
// suggested by: PaleyX (jmpalethorpe@tiscali.co.uk)
// 03/06/2004
// v1.1
public void CollapseAllButOne(Expando expando)
{
foreach (Expando e in this.Expandos)
{
if (e != expando)
{
e.Collapsed = true;
}
else
{
expando.Collapsed = false;
}
}
}
/// <summary>
/// Invalidates the non-client areas of all the Expandos
/// contained in the TaskPane
/// </summary>
internal void InvalidateAllExpandoFrames()
{
foreach (Expando expando in this.Expandos)
{
expando.InvalidateFrame();
}
}
#endregion
#region ISupportInitialize Members
/// <summary>
/// Signals the TaskPane that initialization is starting
/// </summary>
public void BeginInit()
{
this.initialising = true;
}
/// <summary>
/// Signals the TaskPane that initialization is complete
/// </summary>
public void EndInit()
{
this.initialising = false;
this.DoLayout();
foreach (Expando expando in this.Expandos)
{
expando.RecalcFrame(true);
}
}
/// <summary>
/// Gets whether the TaskPane is currently initialising
/// </summary>
[Browsable(false)]
public bool Initialising
{
get
{
return this.initialising;
}
}
#endregion
#region Layout
/// <summary>
/// Forces the TaskPane to apply layout logic to child Expandos,
/// and adjusts the Size and Location of the Expandos if necessary
/// </summary>
public void DoLayout()
{
if (this.layout)
{
return;
}
this.layout = true;
// stop the layout engine
this.SuspendLayout();
Expando e;
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.ClientSize.Width - this.Padding.Left - this.Padding.Right;
// for each control in our list...
for (int i=0; i<this.Expandos.Count; i++)
{
e = this.Expandos[i];
// go to the next expando if this one is invisible
if (!e.Visible)
{
continue;
}
p = new Point(this.Padding.Left, y);
// set the width and location of the control
e.Location = p;
e.Width = width;
// update the next starting point
if (this.scrollChanged && e.AutoLayout)
{
e.DoLayout();
}
y += e.Height + this.Padding.Bottom;
}
// restart the layout engine
this.ResumeLayout(false);
this.layout = false;
}
/// <summary>
/// Updates the layout of the Expandos while in design mode, and
/// adds/removes Expandos from the ControlCollection as necessary
/// </summary>
internal void UpdateExpandos()
{
if (this.Expandos.Count == this.Controls.Count)
{
// make sure the the expandos index in the ControlCollection
// are the same as in the ExpandoCollection (indexes in the
// ExpandoCollection may have changed due to the user moving
// them around in the editor)
this.MatchControlCollToExpandoColl();
return;
}
// were any expandos added
if (this.Expandos.Count > this.Controls.Count)
{
// add any extra expandos in the ExpandoCollection to the
// ControlCollection
for (int i=0; i<this.Expandos.Count; i++)
{
if (!this.Controls.Contains(this.Expandos[i]))
{
this.OnExpandoAdded(new ExpandoEventArgs(this.Expandos[i]));
}
}
}
else
{
// expandos were removed
int i = 0;
Expando expando;
// remove any extra expandos from the ControlCollection
while (i < this.Controls.Count)
{
expando = (Expando) this.Controls[i];
if (!this.Expandos.Contains(expando))
{
this.OnExpandoRemoved(new ExpandoEventArgs(expando));
}
else
{
i++;
}
}
}
}
/// <summary>
/// Make sure the the expandos index in the ControlCollection
/// are the same as in the ExpandoCollection (indexes in the
/// ExpandoCollection may have changed due to the user moving
/// them around in the editor or calling ExpandoCollection.Move())
/// </summary>
internal void MatchControlCollToExpandoColl()
{
this.SuspendLayout();
for (int i=0; i<this.Expandos.Count; i++)
{
this.Controls.SetChildIndex(this.Expandos[i], i);
}
this.ResumeLayout(false);
this.DoLayout();
this.Invalidate(true);
}
#endregion
#endregion
#region Properties
#region Animation
/// <summary>
/// Gets or sets whether the TaskPane should animate Expando's
/// when they collapse or expand.
/// </summary>
[Bindable(true),
Category("Appearance"),
DefaultValue(false),
Description("Specifies whether the TaskPane should animate Expando's when they collapse or expand.")]
public bool Animate
{
get
{
return this.animate;
}
set
{
this.animate = value;
}
}
/// <summary>
/// Determines whether the TaskPane is currently animating
/// an Expando
/// </summary>
[Browsable(false)]
public bool IsAnimating
{
get
{
return this.animationHelpers.Count > 0;
}
}
#endregion
#region Colors
/// <summary>
/// Gets the first color of the TaskPane's background gradient fill.
/// </summary>
[Browsable(false)]
public Color GradientStartColor
{
get
{
return this.systemSettings.TaskPane.GradientStartColor;
}
}
/// <summary>
/// Gets the second color of the TaskPane's background gradient fill.
/// </summary>
[Browsable(false)]
public Color GradientEndColor
{
get
{
return this.systemSettings.TaskPane.GradientEndColor;
}
}
/// <summary>
/// Gets the direction of the TaskPane's background gradient fill.
/// </summary>
[Browsable(false)]
public LinearGradientMode GradientDirection
{
get
{
return this.systemSettings.TaskPane.GradientDirection;
}
}
#endregion
#region Expandos
/// <summary>
/// A TaskPane.ExpandoCollection representing the collection of
/// Expandos contained within the TaskPane
/// </summary>
[Category("Behavior"),
DefaultValue(null),
Description("The Expandos contained in the TaskPane"),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
Editor(typeof(ExpandoCollectionEditor), typeof(UITypeEditor))]
public TaskPane.ExpandoCollection Expandos
{
get
{
return this.expandoCollection;
}
}
/// <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 Images
/// <summary>
/// Gets the Image used as the TaskPane's background
/// </summary>
protected Image BackImage
{
get
{
return this.systemSettings.TaskPane.BackImage;
}
}
/// <summary>
/// Gets how the TaskPane's background Image is to be drawn
/// </summary>
protected ImageStretchMode StretchMode
{
get
{
return this.systemSettings.TaskPane.StretchMode;
}
}
/// <summary>
/// Gets the Image that is used as a watermark in the TaskPane's
/// client area
/// </summary>
protected Image Watermark
{
get
{
return this.systemSettings.TaskPane.Watermark;
}
}
/// <summary>
/// Gets the alignment of the TaskPane's watermark
/// </summary>
protected ContentAlignment WatermarkAlignment
{
get
{
return this.systemSettings.TaskPane.WatermarkAlignment;
}
}
#endregion
#region Padding
/// <summary>
/// Gets the amount of space between the border and the
/// Expando's along each side of the TaskPane.
/// </summary>
protected Padding Padding
{
get
{
return this.systemSettings.TaskPane.Padding;
}
}
#endregion
#region SystemSettings
/// <summary>
/// Gets or sets the system defined settings for the TaskPane
/// </summary>
internal ExplorerBarInfo SystemSettings
{
get
{
return this.systemSettings;
}
set
{
// ignore null values
if (value == null)
{
return;
}
if (this.systemSettings != value)
{
this.SuspendLayout();
if (this.systemSettings != null)
{
this.systemSettings.Dispose();
this.systemSettings = null;
}
this.watermarkRect = Rectangle.Empty;
this.systemSettings = value;
this.BackColor = this.systemSettings.TaskPane.GradientStartColor;
this.BackgroundImage = this.BackImage;
foreach (Expando expando in this.Expandos)
{
expando.SystemSettings = this.systemSettings;
expando.DoLayout();
}
this.DoLayout();
this.ResumeLayout(true);
this.Invalidate(true);
}
}
}
#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)
{
base.OnControlAdded(e);
// make sure the control is an Expando
if ((e.Control as Expando) == null)
{
// remove the control
this.Controls.Remove(e.Control);
// throw a hissy fit
throw new InvalidCastException("Only Expando's can be added to the TaskPane");
}
// add the expando to the ExpandoCollection if necessary
if (!this.Expandos.Contains((Expando) e.Control))
{
this.Expandos.Add((Expando) 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)
{
base.OnControlRemoved (e);
// remove the control from the itemList
if (this.Expandos.Contains(e.Control))
{
this.Expandos.Remove((Expando) e.Control);
}
// update the layout of the controls
this.DoLayout();
}
#endregion
#region Expandos
/// <summary>
/// Event handler for the Expando StateChanged event
/// </summary>
/// <param name="sender">The object that fired the event</param>
/// <param name="e">An ExpandoEventArgs that contains the event data</param>
private void expando_StateChanged(object sender, ExpandoEventArgs e)
{
OnExpandoStateChanged(e);
}
/// <summary>
/// Occurs when the value of an Expandos Collapsed property changes
/// </summary>
/// <param name="e">An ExpandoEventArgs that contains the event data</param>
protected virtual void OnExpandoStateChanged(ExpandoEventArgs e)
{
// check if we need to animate
// if we are in design mode, don't animate as the gripper thingys
// that are shown around the outside of the expando are incorrect
// once the animation has finished. instead, go straight to
// being collapsed/expanded
if (this.DesignMode)
{
if (e.Expando.Collapsed)
{
e.Expando.Collapse();
}
else
{
e.Expando.Expand();
}
// re-align the expandos
this.DoLayout();
}
// start the animation if we are able to animate and we aren't
// initialising the taskpane
else if (this.Animate && !this.initialising)
{
this.StartFadeAnimation(e.Expando);
}
else
{
// can't animate, so collapse/expand the expando
if (e.Collapsed)
{
e.Expando.Collapse();
}
else
{
e.Expando.Expand();
}
this.DoLayout();
this.Invalidate(true);
}
}
/// <summary>
/// Raises the ExpandoAdded event
/// </summary>
/// <param name="e">An ExpandoEventArgs that contains the event data</param>
protected virtual void OnExpandoAdded(ExpandoEventArgs e)
{
// add the expando to the ControlCollection if it hasn't already
if (!this.Controls.Contains(e.Expando))
{
this.Controls.Add(e.Expando);
}
// set anchor styles
e.Expando.Anchor = (AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right);
// tell the Expando who's its daddy...
e.Expando.TaskPane = this;
e.Expando.SystemSettings = this.systemSettings;
// listen for collapse/expand events
e.Expando.StateChanged += new ExpandoEventHandler(this.expando_StateChanged);
// update the layout of the controls
this.DoLayout();
//
if (ExpandoAdded != null)
{
ExpandoAdded(this, e);
}
}
/// <summary>
/// Raises the ExpandoRemoved event
/// </summary>
/// <param name="e">An ExpandoEventArgs that contains the event data</param>
protected virtual void OnExpandoRemoved(ExpandoEventArgs e)
{
// remove the control from the ControlCollection if it hasn't already
if (this.Controls.Contains(e.Expando))
{
this.Controls.Remove(e.Expando);
}
// remove the StateChanged listener
e.Expando.StateChanged -= new ExpandoEventHandler(this.expando_StateChanged);
// update the layout of the controls
this.DoLayout();
//
if (ExpandoRemoved != null)
{
ExpandoRemoved(this, e);
}
}
#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)
{
// paint background
if (this.BackImage != null)
{
//base.OnPaintBackground(e);
WrapMode wrap = WrapMode.Clamp;
if ((this.StretchMode == ImageStretchMode.Tile) || (this.StretchMode == ImageStretchMode.Horizontal))
{
wrap = WrapMode.Tile;
}
using (TextureBrush brush = new TextureBrush(this.BackImage, wrap))
{
e.Graphics.FillRectangle(brush, this.DisplayRectangle);
}
}
else
{
if (this.GradientStartColor != this.GradientEndColor)
{
using (LinearGradientBrush brush = new LinearGradientBrush(this.DisplayRectangle,
this.GradientStartColor,
this.GradientEndColor,
this.GradientDirection))
{
e.Graphics.FillRectangle(brush, this.DisplayRectangle);
}
}
else
{
using (SolidBrush brush = new SolidBrush(this.GradientStartColor))
{
e.Graphics.FillRectangle(brush, this.ClientRectangle);
}
}
}
// draw the watermark if we have one
if (this.Watermark != null)
{
Rectangle rect = new Rectangle(0, 0, this.Watermark.Width, this.Watermark.Height);
// work out a rough location of where the watermark should go
switch (this.WatermarkAlignment)
{
case ContentAlignment.BottomCenter:
case ContentAlignment.BottomLeft:
case ContentAlignment.BottomRight:
{
rect.Y = this.DisplayRectangle.Bottom - this.Watermark.Height;
break;
}
case ContentAlignment.MiddleCenter:
case ContentAlignment.MiddleLeft:
case ContentAlignment.MiddleRight:
{
rect.Y = this.DisplayRectangle.Top + ((this.DisplayRectangle.Height - this.Watermark.Height) / 2);
break;
}
}
switch (this.WatermarkAlignment)
{
case ContentAlignment.BottomRight:
case ContentAlignment.MiddleRight:
case ContentAlignment.TopRight:
{
rect.X = this.ClientRectangle.Right - this.Watermark.Width;
break;
}
case ContentAlignment.BottomCenter:
case ContentAlignment.MiddleCenter:
case ContentAlignment.TopCenter:
{
rect.X = this.ClientRectangle.Left + ((this.ClientRectangle.Width - this.Watermark.Width) / 2);
break;
}
}
// 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
e.Graphics.DrawImage(this.Watermark, rect);
}
}
#endregion
#region Size
/// <summary>
/// Raises the Layout event
/// </summary>
/// <param name="e">A LayoutEventArgs that contains the event data</param>
protected override void OnLayout(LayoutEventArgs levent)
{
bool hScroll = this.HScroll;
bool vScroll = this.VScroll;
this.SuspendLayout();
base.OnLayout(levent);
if (hScroll != this.HScroll || vScroll != this.VScroll)
{
this.scrollChanged = true;
this.DoLayout();
this.scrollChanged = false;
this.Invalidate(this.ClientRectangle, true);
}
this.ResumeLayout(true);
}
#endregion
#region System Colors
/// <summary>
/// Raises the SystemColorsChanged event
/// </summary>
/// <param name="e">An EventArgs that contains the event data</param>
protected override void OnSystemColorsChanged(EventArgs e)
{
base.OnSystemColorsChanged(e);
// don't go any further if we are explicitly using
// the classic or a custom theme
if (this.classicTheme || this.customTheme)
{
return;
}
this.SuspendLayout();
// get rid of the current system theme info
this.systemSettings.Dispose();
this.systemSettings = null;
// get a new system theme info for the new theme
this.systemSettings = ThemeManager.GetSystemExplorerBarSettings();
this.BackgroundImage = this.BackImage;
// update the system settings for each expando
foreach (Control control in this.Controls)
{
if (control is Expando)
{
Expando expando = (Expando) control;
expando.SystemSettings = this.systemSettings;
}
}
// update the layout of the controls
this.DoLayout();
}
#endregion
#endregion
#region ExpandoCollection
/// <summary>
/// Represents a collection of Expando objects
/// </summary>
public class ExpandoCollection : CollectionBase
{
#region Class Data
/// <summary>
/// The TaskPane that owns this ExpandoCollection
/// </summary>
private TaskPane owner;
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the TaskPane.ExpandoCollection class
/// </summary>
/// <param name="owner">A TaskPane representing the taskpane that owns
/// the Expando collection</param>
public ExpandoCollection(TaskPane owner) : base()
{
if (owner == null)
{
throw new ArgumentNullException("owner");
}
this.owner = owner;
}
#endregion
#region Methods
/// <summary>
/// Adds the specified expando to the expando collection
/// </summary>
/// <param name="value">The Expando to add to the expando collection</param>
public void Add(Expando value)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
this.List.Add(value);
this.owner.Controls.Add(value);
this.owner.OnExpandoAdded(new ExpandoEventArgs(value));
}
/// <summary>
/// Adds an array of expando objects to the collection
/// </summary>
/// <param name="expandos">An array of Expando objects to add
/// to the collection</param>
public void AddRange(Expando[] expandos)
{
if (expandos == null)
{
throw new ArgumentNullException("expandos");
}
for (int i=0; i<expandos.Length; i++)
{
this.Add(expandos[i]);
}
}
/// <summary>
/// Removes all expandos from the collection
/// </summary>
public new void Clear()
{
while (this.Count > 0)
{
this.RemoveAt(0);
}
}
/// <summary>
/// Determines whether the specified expando is a member of the
/// collection
/// </summary>
/// <param name="expandos">The Expando to locate in the collection</param>
/// <returns>true if the Expando is a member of the collection;
/// otherwise, false</returns>
public bool Contains(Expando expando)
{
if (expando == null)
{
throw new ArgumentNullException("expando");
}
return (this.IndexOf(expando) != -1);
}
/// <summary>
/// Determines whether the specified control is a member of the
/// collection
/// </summary>
/// <param name="control">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 is Expando))
{
return false;
}
return this.Contains((Expando) control);
}
/// <summary>
/// Retrieves the index of the specified expando in the expando
/// collection
/// </summary>
/// <param name="expando">The Expando to locate in the collection</param>
/// <returns>A zero-based index value that represents the position
/// of the specified Expando in the TaskPane.ExpandoCollection</returns>
public int IndexOf(Expando expando)
{
if (expando == null)
{
throw new ArgumentNullException("expando");
}
for (int i=0; i<this.Count; i++)
{
if (this[i] == expando)
{
return i;
}
}
return -1;
}
/// <summary>
/// Removes the specified expando from the expando collection
/// </summary>
/// <param name="value">The Expando to remove from the
/// TaskPane.ExpandoCollection</param>
public void Remove(Expando value)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
this.List.Remove(value);
this.owner.Controls.Remove(value);
this.owner.OnExpandoRemoved(new ExpandoEventArgs(value));
}
/// <summary>
/// Removes an expando from the expando collection at the
/// specified indexed location
/// </summary>
/// <param name="index">The index value of the Expando to
/// remove</param>
public new void RemoveAt(int index)
{
this.Remove(this[index]);
}
/// <summary>
/// Moves the specified expando to the specified indexed location
/// in the expando collection
/// </summary>
/// <param name="expando">The expando to be moved</param>
/// <param name="index">The indexed location in the expando collection
/// that the specified expando will be moved to</param>
public void Move(Expando 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 expando to the top of the expando collection
/// </summary>
/// <param name="value">The expando to be moved</param>
public void MoveToTop(Expando value)
{
this.Move(value, 0);
}
/// <summary>
/// Moves the specified expando to the bottom of the expando collection
/// </summary>
/// <param name="value">The expando to be moved</param>
public void MoveToBottom(Expando value)
{
this.Move(value, this.Count);
}
#endregion
#region Properties
/// <summary>
/// The Expando located at the specified index location within
/// the expando collection
/// </summary>
/// <param name="index">The index of the expando to retrieve
/// from the expando collection</param>
public virtual Expando this[int index]
{
get
{
return this.List[index] as Expando;
}
}
#endregion
}
#endregion
#region ExpandoCollectionEditor
/// <summary>
///
/// </summary>
internal class ExpandoCollectionEditor : CollectionEditor
{
/// <summary>
/// Initializes a new instance of the CollectionEditor class
/// using the specified collection type
/// </summary>
/// <param name="type"></param>
public ExpandoCollectionEditor(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)
{
TaskPane originalControl = (TaskPane) context.Instance;
object returnObject = base.EditValue(context, isp, value);
originalControl.UpdateExpandos();
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)
{
object expando = base.CreateInstance(itemType);
((Expando) expando).Name = "expando";
return expando;
}
}
#endregion
}
#endregion
#region TaskPaneDesigner
/// <summary>
/// A custom designer used by TaskPanes to remove unwanted
/// properties from the Property window in the designer
/// </summary>
public class TaskPaneDesigner : ScrollableControlDesigner
{
/// <summary>
/// Initializes a new instance of the TaskPaneDesigner class
/// </summary>
public TaskPaneDesigner() : base()
{
}
/// <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(System.Collections.IDictionary properties)
{
base.PreFilterProperties(properties);
properties.Remove("BackColor");
properties.Remove("BackgroundImage");
properties.Remove("Cursor");
properties.Remove("ForeColor");
}
}
#endregion
#region AnimationHelper
/// <summary>
/// A class that helps Expandos animate
/// </summary>
public class AnimationHelper
{
#region Class Data
/// <summary>
/// Specifes that a fade animation is to be performed
/// </summary>
public static int FadeAnimation = 1;
/// <summary>
/// Specifes that a slide animation is to be performed
/// </summary>
public static int SlideAnimation = 2;
/// <summary>
/// The type of animation to perform
/// </summary>
private int animationType;
/// <summary>
/// the TaskPane the helper belongs to
/// </summary>
private TaskPane taskpane;
/// <summary>
/// The Expando to animate
/// </summary>
private Expando expando;
/// <summary>
/// The current frame in animation
/// </summary>
private int animationStepNum;
/// <summary>
/// The number of frames in the animation
/// </summary>
private int numAnimationSteps;
/// <summary>
/// The amount of time each frame is shown (in milliseconds)
/// </summary>
private int animationFrameInterval;
/// <summary>
/// Specifies whether an animation is being performed
/// </summary>
private bool animating;
/// <summary>
/// A timer that notifies the helper when the next frame is due
/// </summary>
private Timer animationTimer;
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the AnimationHelper class with the specified settings
/// </summary>
/// <param name="taskpane">The TaskPane the AnimationHelper belongs to</param>
/// <param name="expando">The Expando to be animated</param>
/// <param name="animationType">The type of animation to perform</param>
public AnimationHelper(TaskPane taskpane, Expando expando, int animationType)
{
this.taskpane = taskpane;
this.expando = expando;
this.animationType = animationType;
this.animating = false;
this.numAnimationSteps = 20;
this.animationFrameInterval = 10;
// I know that this isn't the best way to do this, but I
// haven't quite worked out how to do it with threads so
// this will have to do for the moment
this.animationTimer = new Timer();
this.animationTimer.Tick += new EventHandler(this.animationTimer_Tick);
this.animationTimer.Interval = this.animationFrameInterval;
}
#endregion
#region Methods
/// <summary>
/// Starts the Expando collapse/expand animation
/// </summary>
public void StartAnimation()
{
// don't bother going any further if we are already animating
if (this.Animating)
{
return;
}
this.animationStepNum = 0;
// tell the expando to get ready to animate
if (this.AnimationType == FadeAnimation)
{
this.expando.StartFadeAnimation();
}
else
{
this.expando.StartSlideAnimation();
}
// start the animation timer
this.animationTimer.Start();
}
/// <summary>
/// Updates the animation for the Expando
/// </summary>
protected void PerformAnimation()
{
// if we have more animation steps to perform
if (this.animationStepNum < this.numAnimationSteps)
{
// update the animation step number
this.animationStepNum++;
// tell the animating expando to update the animation
if (this.AnimationType == FadeAnimation)
{
this.expando.UpdateFadeAnimation(this.animationStepNum, this.numAnimationSteps);
}
else
{
this.expando.UpdateSlideAnimation(this.animationStepNum, this.numAnimationSteps);
}
// rearrange the groups
this.taskpane.DoLayout();
}
else
{
// stop the animation
this.animationTimer.Stop();
if (this.AnimationType == FadeAnimation)
{
this.expando.StopFadeAnimation();
}
else
{
this.expando.StopSlideAnimation();
}
// let the TaskPane know that the animation has finished
// so it can do some cleaning up (like getting rid of us)
this.taskpane.AnimationStopped(this);
}
}
#endregion
#region Properties
/// <summary>
/// Gets the TaskPane that the AnimationHelper belongs to
/// </summary>
public TaskPane TaskPane
{
get
{
return this.taskpane;
}
}
/// <summary>
/// Gets the Expando that is te be animated
/// </summary>
public Expando Expando
{
get
{
return this.expando;
}
}
/// <summary>
/// Gets or sets the number of steps that are needed for the Expando
/// to get from fully expanded to fully collapsed, or visa versa
/// </summary>
public int NumAnimationSteps
{
get
{
return this.numAnimationSteps;
}
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("value", "NumAnimationSteps must be at least 0");
}
// only change this if we are not currently animating
// (if we are animating, changing this could cause things
// to screw up big time)
if (!this.animating)
{
this.numAnimationSteps = value;
}
}
}
/// <summary>
/// Gets or sets the number of milliseconds that each "frame"
/// of the animation stays on the screen
/// </summary>
public int AnimationFrameInterval
{
get
{
return this.animationFrameInterval;
}
set
{
this.animationFrameInterval = value;
}
}
/// <summary>
/// Gets whether the Expando is currently animating
/// </summary>
public bool Animating
{
get
{
return this.animating;
}
}
/// <summary>
/// Gets the type of animation to perform
/// </summary>
public int AnimationType
{
get
{
return this.animationType;
}
}
#endregion
#region Events
/// <summary>
/// Event handler for the animation timer tick event
/// </summary>
/// <param name="sender">The object that fired the event</param>
/// <param name="e">An EventArgs that contains the event data</param>
private void animationTimer_Tick(object sender, EventArgs e)
{
// do the next bit of the aniation
this.PerformAnimation();
}
#endregion
}
#endregion
}