using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
namespace AutoDiagramer
{
#region DrawableClass CLASS
/// <summary>
/// Provides the analysis and rendering of everything that is required
/// to completely reverse engineer a single Type, and to draw this as
/// a class on a class diagram.
/// </summary>
public partial class DrawableClass : UserControl
{
#region Instance fields
//private instance fields
private string _Name = "";
private List<string> _Constructors = new List<string>();
private List<string> _Fields = new List<string>();
private List<string> _Properties = new List<string>();
private List<string> _Interfaces = new List<string>();
private List<string> _Methods = new List<string>();
private List<string> _Events = new List<string>();
private List<string> _Associations = new List<string>();
private Type _type_to_Draw;
private Label[] _lbls;
private Font _rowFont;
private Font _titleFont;
private int _interfaces_X_End_Pos = 0;
private int _interfaces_Y_End_Pos = 0;
private Expander _ucConstructors=null;
private Expander _ucFields = null;
private Expander _ucProperties = null;
private Expander _ucMethods = null;
private Expander _ucEvents = null;
private PictureBox _picMainExpander = null;
private int _expanderSize = 13;
private int _genericSpaceSizeH = 6;
private int _genericSpaceSizeV = 10;
private int _EndSpacerSize = 40;
private int _interfaceDrawingLineStart = 0;
private int _interfaceDrawingLineEnd = 0;
private int _MainBodyRenderStart = 0;
private int _MainBodyBorderWidth = 2;
private Color _ClassStartColor = Color.LightGray;
private Color _ClassEndColor = Color.White;
private Color _ClassBorderColor = Color.SteelBlue;
private States _currentState = States.Expanded;
private int _ConstructorsOldHeight = 0;
private int _FieldsOldHeight = 0;
private int _PropertiesOldHeight = 0;
private int _MethodsOldHeight = 0;
private int _EventsOldHeight = 0;
private bool _latchedIn = false;
private int _ContainerRow = 0;
private int _ContainerColumn = 0;
private int _FullExpandedHeight = 0;
//public instance fields
public enum States { Collapsed, PartiallyCollasped, Expanded };
public delegate void SizeStateChangedEventHandler(object sender, SizeEventArgs args);
public event SizeStateChangedEventHandler SizeChanged;
#endregion
#region Ctor
/// <summary>
/// Stores the type provided as an internal field and
/// calls the AnalyseType() internal method. And also
/// creates several <see cref="Expander">Expander </see>
/// controls to diplay the individual Properties,Events,Fields,
/// Constructors,Methods details. Finally an overall
/// collapse / expand image is created
/// </summary>
/// <param name="t">The Type to analyze</param>
public DrawableClass(Type t)
{
this.InitializeComponent();
//set to flickerless style
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw | ControlStyles.DoubleBuffer | ControlStyles.UserPaint, true);
this._type_to_Draw = t;
this._Name = t.Name;
this._rowFont = new Font("Microsoft Sans Serif", 8.25F);
this._titleFont = new Font("Microsoft Sans Serif", 8.25F, FontStyle.Bold);
this.BorderStyle = BorderStyle.None;
this.Width = 5;
this.Height = 5;
try
{
//begin analysis
AnalyseType();
commonUICodeSetup();
}
catch (Exception ex)
{
Program.ErrorBox(ex.Message);
}
}
#endregion
#region Public Properties
/// <summary>
/// gets / sets the border Color to use
/// </summary>
[Category("Appearance")]
[DefaultValue(typeof(Color))]
[Browsable(true)]
public Color ClassBorderColor
{
get { return _ClassBorderColor; }
set { _ClassBorderColor = value; }
}
/// <summary>
/// gets / sets the start Color to use
/// </summary>
[Category("Appearance")]
[DefaultValue(typeof(Color))]
[Browsable(true)]
public Color ClassStartColor
{
get { return _ClassStartColor; }
set { _ClassStartColor = value; }
}
/// <summary>
/// gets / sets the end Color to use
/// </summary>
[Category("Appearance")]
[DefaultValue(typeof(Color))]
[Browsable(true)]
public Color ClassEndColor
{
get { return _ClassEndColor; }
set { _ClassEndColor = value; }
}
/// <summary>
/// gets / sets the title Font to use
/// </summary>
[Browsable(true)]
public Font TitleFont
{
get { return _titleFont; }
set { _titleFont = value; }
}
/// <summary>
/// gets / sets the row Font to use
/// </summary>
[Browsable(true)]
public Font RowFont
{
get { return _rowFont; }
set { _rowFont = value; }
}
/// <summary>
/// gets the controls Name to use
/// NOTE : Hides Control.Name inherited property
/// </summary>
[Browsable(true)]
public new string Name
{
get { return _Name; }
}
/// <summary>
/// gets / sets the fully expanded height for this control
/// </summary>
[Browsable(false)]
public int FullExpandedHeight
{
get { return _FullExpandedHeight; }
set { _FullExpandedHeight = value; }
}
/// <summary>
/// gets / sets the container row number.
/// These controls are placed in a container grid control,
/// using row / col positions. This is the container grid
/// control row position in the grid, which
/// helps for drawing association lines etc etc
/// </summary>
[Browsable(false)]
public int ContainerRow
{
get { return _ContainerRow; }
set { _ContainerRow = value; }
}
/// <summary>
/// gets / sets the container column number.
/// These controls are placed in a container grid control,
/// using row / col positions. This is the container grid
/// control column position in the grid, which
/// helps for drawing association lines etc etc
/// </summary>
[Browsable(false)]
public int ContainerColumn
{
get { return _ContainerColumn; }
set { _ContainerColumn = value; }
}
/// <summary>
/// gets / sets the list of Constructors for the Type this
/// control is being used to paint
/// </summary>
[Browsable(false)]
public List<string> Constructors
{
get { return _Constructors; }
}
/// <summary>
/// gets / sets the list of Fields for the Type this
/// control is being used to paint
/// </summary>
[Browsable(false)]
public List<string> Fields
{
get { return _Fields; }
}
/// <summary>
/// gets / sets the list of Properties for the Type this
/// control is being used to paint
/// </summary>
[Browsable(false)]
public List<string> Properties
{
get { return _Properties; }
}
/// <summary>
/// gets / sets the list of Interfaces for the Type this
/// control is being used to paint
/// </summary>
[Browsable(false)]
public List<string> Interfaces
{
get { return _Interfaces; }
}
/// <summary>
/// gets / sets the list of Methods for the Type this
/// control is being used to paint
/// </summary>
[Browsable(false)]
public List<string> Methods
{
get { return _Methods; }
}
/// <summary>
/// gets / sets the list of Events for the Type this
/// control is being used to paint
/// </summary>
[Browsable(false)]
public new List<string> Events
{
get { return _Events; }
}
/// <summary>
/// gets / sets the list of Associations for the Type this
/// control is being used to paint
/// </summary>
[Browsable(false)]
public List<string> Associations
{
get { return _Associations; }
set { _Associations= value; }
}
/// <summary>
/// gets the current state expanded / collapsed
/// </summary>
[Browsable(false)]
public States CurrentState
{
get { return this._currentState; }
set { _currentState = value; }
}
#endregion
#region Private Methods
private void commonUICodeSetup()
{
#region Create Expander controls for Methods, Properties etc etc
//create interface drawing items
if (_Interfaces.Count > 0)
{
showInterfaces();
}
//create Constructor drawing items
if (_Constructors.Count > 0)
{
_ucConstructors = new
Expander("Constructors", Expander.PossibleTypes.Constructors);
_ucConstructors.RowContent = _Constructors;
_ucConstructors.PanelStateChanged +=
new Expander.PanelStateChangedEventHandler(_Expander_PanelStateChanged);
}
//create Fields drawing items
if (_Fields.Count > 0)
{
_ucFields = new
Expander("Fields", Expander.PossibleTypes.Fields);
_ucFields.RowContent = _Fields;
_ucFields.PanelStateChanged +=
new Expander.PanelStateChangedEventHandler(_Expander_PanelStateChanged);
}
//create Properties drawing items
if (_Properties.Count > 0)
{
_ucProperties = new
Expander("Properties", Expander.PossibleTypes.Properties);
_ucProperties.RowContent = _Properties;
_ucProperties.PanelStateChanged +=
new Expander.PanelStateChangedEventHandler(_Expander_PanelStateChanged);
}
//create Methods drawing items
if (_Methods.Count > 0)
{
_ucMethods = new
Expander("Methods", Expander.PossibleTypes.Methods);
_ucMethods.RowContent = _Methods;
_ucMethods.PanelStateChanged +=
new Expander.PanelStateChangedEventHandler(_Expander_PanelStateChanged);
}
//create Events drawing items
if (_Events.Count > 0)
{
_ucEvents = new
Expander("Events", Expander.PossibleTypes.Events);
_ucEvents.RowContent = _Events;
_ucEvents.PanelStateChanged +=
new Expander.PanelStateChangedEventHandler(_Expander_PanelStateChanged);
}
#endregion
//main expander picture box
_picMainExpander = new PictureBox();
_picMainExpander.Name = "picExpander";
_picMainExpander.Image = global::AutoDiagramer.Resource1.MainCollapse13;
_picMainExpander.Width = _expanderSize;
_picMainExpander.Height = _expanderSize;
_picMainExpander.Click += new EventHandler(_picMainExpander_Click);
//have to wait to add the expander picture box, as its loction is determined
//by RegenerateControl(), so call that 1st
RegenerateControl();
ChangeStateOfControl();
//then add the expander picture box
this.Controls.Add(_picMainExpander);
}
/// <summary>
/// Determines which state should be the new overall state
/// </summary>
/// <param name="sender">the _picMainExpander</param>
/// <param name="e">the events args</param>
private void _picMainExpander_Click(object sender, EventArgs e)
{
ChangeStateOfControl();
}
/// <summary>
/// Changes the current state of the control (expanded / collapsed)
/// </summary>
private void ChangeStateOfControl()
{
if (_currentState == States.Expanded)
{
_currentState = States.Collapsed;
}
else if (_currentState == States.Collapsed || _currentState == States.PartiallyCollasped)
{
_currentState = States.Expanded;
}
switch (_currentState)
{
case States.Expanded:
case States.PartiallyCollasped:
this._picMainExpander.Image = global::AutoDiagramer.Resource1.MainCollapse13;
showExpanders(true);
RegenerateControl();
break;
case States.Collapsed:
this._picMainExpander.Image = global::AutoDiagramer.Resource1.MainExpand13;
showExpanders(false);
RegenerateControl();
break;
}
// If anyone has subscribed, notify them
OnSizeChanged(this, new SizeEventArgs(this));
}
/// <summary>
/// Shows or hides the Expander control parameter provided.
/// In the case of showing, the height parameter is used for
/// the height of the Expander
/// </summary>
/// <param name="visible">true to show the expander</param>
/// <param name="expander">the expander to show/hide</param>
/// <param name="old_expanderHeight">the old expander controls height</param>
private void setExpanderState(bool visible, Expander expander, int old_expanderHeight)
{
if (expander != null)
{
if (!visible)
{
expander.Height = 0;
}
else
{
expander.Height = old_expanderHeight;
}
}
}
/// <summary>
/// if the visible parameter is true show ucExander controls
/// </summary>
/// <param name="visible">true means shown ucExander controls</param>
private void showExpanders(bool visible)
{
//create Constructor height
setExpanderState(visible, _ucConstructors, _ConstructorsOldHeight);
//create Fields height
setExpanderState(visible, _ucFields, _FieldsOldHeight);
//create Properties height
setExpanderState(visible, _ucProperties, _PropertiesOldHeight);
//create Methods height
setExpanderState(visible, _ucMethods, _MethodsOldHeight);
//create Events height
setExpanderState(visible, _ucEvents, _EventsOldHeight);
}
/// <summary>
/// Raised when one of the Expander controls changes state. Will also
/// change the current state of this control and will raise the
/// OnSizeChanged() event
/// </summary>
/// <param name="sender">The current Expander control</param>
/// <param name="args"></param>
private void _Expander_PanelStateChanged(object sender, ExpanderSizeEventArgs args)
{
RegenerateControl();
if (args.CurrentState == Expander.States.Collapsed)
{
this.CurrentState = States.PartiallyCollasped;
}
else if (args.CurrentState == Expander.States.Expanded)
{
this.CurrentState = States.Expanded;
}
// If anyone has subscribed, notify them
OnSizeChanged(this, new SizeEventArgs(this));
}
/// <summary>
/// Raised when the panel changes size (collaped/expanded)
/// </summary>
/// <param name="sender"><see cref="DrawableClass">this</see></param>
/// <param name="e">the event args</param>
private void OnSizeChanged(object sender, SizeEventArgs args)
{
// Check if there are any Subscribers
if (SizeChanged != null)
{
// Call the Event
SizeChanged(sender, args);
}
}
/// <summary>
/// Creates the interfaces for this Type and draws them on this controls
/// client area
/// </summary>
private void showInterfaces()
{
//Label array ToolBar hold interface labels
_lbls = new Label[_Interfaces.Count];
//start location and size
int yLoc = 0;
_interfaces_X_End_Pos = this.Height;
_interfaces_Y_End_Pos = this.Width;
//create a new row for each row that has been provided in the _Interfaces list
//will a Label for each row seen
for (int i = 0; i < _lbls.Length; i++)
{
//add the relevant Labels
_lbls[i] = new Label();
_lbls[i].AutoSize = true;
_lbls[i].Location = new System.Drawing.Point(0, yLoc);
_lbls[i].Font = _rowFont;
_lbls[i].TextAlign = ContentAlignment.MiddleLeft;
_lbls[i].Name = "label" + i;
_lbls[i].Text = _Interfaces[i].ToString();
this.Controls.Add(_lbls[i]);
//work out next Locations, and correct sizes for overall control
yLoc += _lbls[i].Height;
if (_lbls[i].Width > _interfaces_X_End_Pos)
_interfaces_X_End_Pos = _lbls[i].Width;
_interfaces_Y_End_Pos += _lbls[i].Height;
}
}
/// <summary>
/// Calls the getMaxWidth() and getMaxHeight() methods
/// and causes the control to redraw
/// </summary>
public void RegenerateControl()
{
this.Width = getMaxWidth();
this.Height = getMaxHeight();
this.Invalidate();
Application.DoEvents();
}
/// <summary>
/// Measures all contenst to see how height this control needs to be
/// to fit its contents
/// </summary>
/// <returns>An int which is the highest height this control needs to be
/// to fit its contents</returns>
private int getMaxHeight()
{
int MaxHeight = 0;
Graphics g = this.CreateGraphics();
//is interface drawing area the widest renderable area
if (_interfaces_Y_End_Pos > MaxHeight)
{
_interfaceDrawingLineStart = _interfaces_Y_End_Pos + 15;
_interfaceDrawingLineEnd = _interfaces_Y_End_Pos + 30;
MaxHeight = _interfaceDrawingLineEnd;
_MainBodyRenderStart = _interfaceDrawingLineEnd;
}
//work out where the main expander picture box should be
int xExanderPos = this.Width - (_genericSpaceSizeH + _expanderSize);
int yExanderPos = MaxHeight + _genericSpaceSizeH;
_picMainExpander.Location = new Point(xExanderPos, yExanderPos);
#region Look at all Expander hieghts
//space from top to expander + expander size + space to base type +
//base type text + space to start of Expander controls
MaxHeight += _genericSpaceSizeV + _expanderSize + _genericSpaceSizeV + _genericSpaceSizeV;
this.SuspendLayout();
int borderOffset = (int)_MainBodyBorderWidth/2;
//see if there is a Constructor Expander control to show
if (_ucConstructors != null)
{
_ucConstructors.Location = new Point(borderOffset, MaxHeight);
_ucConstructors.Width = this.Width - _MainBodyBorderWidth;
this.Controls.Add(_ucConstructors);
MaxHeight += _ucConstructors.Height;
}
//see if there is a Fields Expander control to show
if (_ucFields != null)
{
_ucFields.Location = new Point(borderOffset, MaxHeight);
_ucFields.Width = this.Width - _MainBodyBorderWidth;
this.Controls.Add(_ucFields);
MaxHeight += _ucFields.Height;
}
//see if there is a Properties Expander control to show
if (_ucProperties != null)
{
_ucProperties.Location = new Point(borderOffset, MaxHeight);
_ucProperties.Width = this.Width - _MainBodyBorderWidth;
this.Controls.Add(_ucProperties);
MaxHeight += _ucProperties.Height;
}
//see if there is a Methods Expander control to show
if (_ucMethods != null)
{
_ucMethods.Location = new Point(borderOffset, MaxHeight);
_ucMethods.Width = this.Width - _MainBodyBorderWidth;
this.Controls.Add(_ucMethods);
MaxHeight += _ucMethods.Height;
}
//see if there is a Events Expander control to show
if (_ucEvents != null)
{
_ucEvents.Location = new Point(borderOffset, MaxHeight);
_ucEvents.Width = this.Width - _MainBodyBorderWidth;
this.Controls.Add(_ucEvents);
MaxHeight += _ucEvents.Height;
}
MaxHeight += _genericSpaceSizeV;
this.ResumeLayout();
#endregion
#region Old heights (for re-eapansion)
//keep a copy of all the old hieghts for when we need to expand the whole class
//again
if (! _latchedIn)
{
_FullExpandedHeight = MaxHeight;
if (_ucConstructors != null)
_ConstructorsOldHeight = _ucConstructors.Height;
if (_ucProperties != null)
_PropertiesOldHeight = _ucProperties.Height;
if (_ucFields != null)
_FieldsOldHeight = _ucFields.Height;
if (_ucMethods != null)
_MethodsOldHeight = _ucMethods.Height;
if (_ucEvents != null)
_EventsOldHeight = _ucEvents.Height;
_latchedIn = true;
}
#endregion
//return the MaxWidth
return MaxHeight;
}
/// <summary>
/// Measures all contenst to see how wide this control needs to be
/// to fit its contents
/// </summary>
/// <returns>An int which is the widest width this control needs to be
/// to fit its contents</returns>
private int getMaxWidth()
{
int MaxWidth = 0;
Graphics g = this.CreateGraphics();
//is interface drawing area the widest renderable area
if (_interfaces_X_End_Pos > MaxWidth)
{
MaxWidth = _interfaces_X_End_Pos;
}
//work out title width
int overallClassTitleAreaWidth = (int)
//left space before title
_genericSpaceSizeH +
//title text
(int)((SizeF)g.MeasureString(this.Name, _titleFont)).Width +
//space after title before expander PictureBox
_genericSpaceSizeH +
//expander PictureBox
_expanderSize +
//space after expander PictureBox
_genericSpaceSizeH;
//is interface drawing area the widest renderable area
if (overallClassTitleAreaWidth > MaxWidth)
{
MaxWidth = overallClassTitleAreaWidth;
}
//Is there a baseclass to render
if (_type_to_Draw.BaseType != null)
{
int baseTypeWidth = _EndSpacerSize +
(int)((SizeF)g.MeasureString(_type_to_Draw.BaseType.Name, _rowFont)).Width + _EndSpacerSize;
if (baseTypeWidth > MaxWidth)
{
MaxWidth = baseTypeWidth;
}
}
#region Look at all Expander widths
//compare it to the constructor expander controls width
if (_ucConstructors != null)
{
//is interface drawing area the widest renderable area
if (_ucConstructors.Width > MaxWidth)
{
MaxWidth = _ucConstructors.Width;
}
}
//compare it to the Fields expander controls width
if (_ucFields != null)
{
//is interface drawing area the widest renderable area
if (_ucFields.Width > MaxWidth)
{
MaxWidth = _ucFields.Width;
}
}
//compare it to the Properties expander controls width
if (_ucProperties != null)
{
//is interface drawing area the widest renderable area
if (_ucProperties.Width > MaxWidth)
{
MaxWidth = _ucProperties.Width;
}
}
//compare it to the Methods expander controls width
if (_ucMethods != null)
{
//is interface drawing area the widest renderable area
if (_ucMethods.Width > MaxWidth)
{
MaxWidth = _ucMethods.Width;
}
}
//compare it to the Events expander controls width
if (_ucEvents != null)
{
//is interface drawing area the widest renderable area
if (_ucEvents.Width > MaxWidth)
{
MaxWidth = _ucEvents.Width;
}
}
#endregion
//return the MaxWidth
return MaxWidth + _MainBodyBorderWidth;
}
/// <summary>
/// Paint the control, basically does a nice
/// rounded corner rectangle with custom fill
/// </summary>
/// <param name="e">the event args</param>
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
SolidBrush b_Black = new SolidBrush(Color.Black);
SolidBrush b_White = new SolidBrush(Color.White);
SolidBrush b_Border = new SolidBrush(_ClassBorderColor);
#region Draw interface bubble
//draw the interface bubble if required
if (_Interfaces.Count > 0)
{
int ellipse_Y_Pos = _interfaces_Y_End_Pos + 5;
g.FillEllipse(b_White, 10, ellipse_Y_Pos, 10, 10);
Pen p = new Pen(b_Black);
g.DrawEllipse(p, 10, ellipse_Y_Pos, 10, 10);
g.DrawLine(p, new Point(15, _interfaceDrawingLineStart),
new Point(15, _interfaceDrawingLineEnd));
}
#endregion
#region Draw main class body
//draw the main class body area
g.SmoothingMode = SmoothingMode.AntiAlias;
int borderOffset = (int)_MainBodyBorderWidth / 2;
Rectangle rBounds = new Rectangle(0, _MainBodyRenderStart, this.Width - borderOffset,
(this.Height - _MainBodyRenderStart) - borderOffset);
const int diameter = 20;
int radius = diameter / 2;
// Create a GraphicsPath with curved top corners
GraphicsPath path = this.GetRoundedRect(rBounds, radius);
//draw background
LinearGradientBrush linBrush = new LinearGradientBrush(
rBounds, _ClassStartColor, _ClassEndColor, LinearGradientMode.Horizontal);
g.FillPath(linBrush, path);
//draw outline
g.DrawPath(new Pen(b_Border, 2F), path);
//draw the title string
g.DrawString(this.Name, this.TitleFont, b_Black,
new PointF(_genericSpaceSizeH*2, _interfaceDrawingLineEnd + _genericSpaceSizeV));
//Is there a baseclass to render
if (_type_to_Draw.BaseType != null)
{
//Dont include object inheritence rendering
if (! _type_to_Draw.BaseType.Name.ToLower().Equals("object"))
{
DrawInheritenceArrow(g,path,_type_to_Draw.BaseType.Name);
}
}
#endregion
}
private void DrawInheritenceArrow(Graphics g, GraphicsPath path, String baseName)
{
SolidBrush b_Black = new SolidBrush(Color.Black);
SolidBrush b_White = new SolidBrush(Color.White);
SolidBrush b_Border = new SolidBrush(_ClassBorderColor);
//draw inheritence arrow
Pen p = new Pen(b_Black);
int arrowStartXPos = _genericSpaceSizeH * 2;
int arrowStartYPos = _interfaceDrawingLineEnd + _genericSpaceSizeV + TitleFont.Height + 8;
g.DrawLine(p, new Point(arrowStartXPos, arrowStartYPos),
new Point(arrowStartXPos + 5, arrowStartYPos));
path = new GraphicsPath();
path.AddLine(new Point(arrowStartXPos + 5, arrowStartYPos - 4),
new Point(arrowStartXPos + 5, arrowStartYPos + 4));
path.AddLine(new Point(arrowStartXPos + 5, arrowStartYPos - 4),
new Point(arrowStartXPos + 9, arrowStartYPos));
path.AddLine(new Point(arrowStartXPos + 9, arrowStartYPos),
new Point(arrowStartXPos + 5, arrowStartYPos + 4));
g.FillPath(b_Black, path);
//draw the base type name
g.DrawString(baseName, _rowFont, b_Black,
new PointF(arrowStartXPos + 13, arrowStartYPos - _rowFont.Height / 2));
}
/// <summary>
/// Takes a rectangle, and returns a GraphicsPath which is a round cornered
/// rectangle which is same size as base rectangle
/// </summary>
/// <param name="baseRect">the base rectangle (straight edged client area)</param>
/// <param name="radius">a radius to use for corners</param>
/// <returns>a GraphicsPath which is a round cornered rectangle which is same size
/// as base rectangle</returns>
private GraphicsPath GetRoundedRect(RectangleF baseRect,
float radius)
{
// if corner radius is less than or equal to zero,
// return the original rectangle
if (radius <= 0.0F)
{
GraphicsPath mPath = new GraphicsPath();
mPath.AddRectangle(baseRect);
mPath.CloseFigure();
return mPath;
}
// if the corner radius is greater than or equal to
// half the width, or height (whichever is shorter)
// then return a capsule instead of a lozenge
if (radius >= (Math.Min(baseRect.Width, baseRect.Height)) / 2.0)
return GetCapsule(baseRect);
// create the arc for the rectangle sides and declare
// a graphics path object for the drawing
float diameter = radius * 2.0F;
SizeF sizeF = new SizeF(diameter, diameter);
RectangleF arc = new RectangleF(baseRect.Location, sizeF);
GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath();
// top left arc
path.AddArc(arc, 180, 90);
// top right arc
arc.X = baseRect.Right - diameter;
path.AddArc(arc, 270, 90);
// bottom right arc
arc.Y = baseRect.Bottom - diameter;
path.AddArc(arc, 0, 90);
// bottom left arc
arc.X = baseRect.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
return path;
}
/// <summary>
/// Takes a rectangle, and returns a GraphicsPath which is a round cornered
/// rectangle which is same size as base rectangle
/// </summary>
/// <param name="baseRect">the base rectangle (straight edged client area)</param>
/// <returns>a GraphicsPath which is a round cornered rectangle which is same size
/// as base rectangle</returns>
private GraphicsPath GetCapsule(RectangleF baseRect)
{
float diameter;
RectangleF arc;
GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath();
try
{
if (baseRect.Width > baseRect.Height)
{
// return horizontal capsule
diameter = baseRect.Height;
SizeF sizeF = new SizeF(diameter, diameter);
arc = new RectangleF(baseRect.Location, sizeF);
path.AddArc(arc, 90, 180);
arc.X = baseRect.Right - diameter;
path.AddArc(arc, 270, 180);
}
else if (baseRect.Width < baseRect.Height)
{
// return vertical capsule
diameter = baseRect.Width;
SizeF sizeF = new SizeF(diameter, diameter);
arc = new RectangleF(baseRect.Location, sizeF);
path.AddArc(arc, 180, 180);
arc.Y = baseRect.Bottom - diameter;
path.AddArc(arc, 0, 180);
}
else
{
// return circle
path.AddEllipse(baseRect);
}
}
catch (Exception)
{
path.AddEllipse(baseRect);
}
finally
{
path.CloseFigure();
}
return path;
}
/// <summary>
/// Takes an input string and lower cases it and trims
/// it. Taking all chars after a "." (if one exists",
/// then returns it
/// </summary>
/// <param name="sIn">the input string</param>
/// <returns>the input string lower cased and trimmed</returns>
private string LowerAndTrim(string sIn)
{
string s = sIn;
s = s.ToLower();
if (s.IndexOf(".") > 0)
{
s = s.Substring(s.LastIndexOf(".") + 1);
}
return s;
}
/// <summary>
/// Returs a string which is the name of the type in its full
/// format. If its not a generic type, then the name of the
/// t input parameter is simply returned, if however it is
/// a generic method say a List of ints then the appropraite string
/// will be retrurned
/// </summary>
/// <param name="t">The Type to check for generics</param>
/// <returns></returns>
private string getGenericsForType(Type t)
{
string name ="";
if (!t.GetType().IsGenericType)
{
//see if there is a ' char, which there is for
//generic types
int idx = t.Name.IndexOfAny(new char[] {'`','\''});
if (idx >= 0)
{
name=t.Name.Substring(0,idx);
//get the generic arguments
Type[] genTypes =t.GetGenericArguments();
//and build the list of types for the result string
if (genTypes.Length == 1)
{
//name+="<" + genTypes[0].Name + ">";
name+="<" + getGenericsForType(genTypes[0]) + ">";
}
else
{
name+="<";
foreach(Type gt in genTypes)
{
name+= getGenericsForType(gt) + ", ";
}
if (name.LastIndexOf(",") > 0)
{
name = name.Substring(0, name.LastIndexOf(","));
}
name+=">";
}
}
else
{
name=t.Name;
}
return name;
}
else
{
return t.Name;
}
}
/// <summary>
/// Analyses the current Type (which was supplied on construction)
/// and creates lists for its Constructors, Fields, Properties,
/// Interfaces, Methods, Events to provide to these lists to
/// <see cref="ucExpander">ucExpander </see>controls
/// </summary>
private void AnalyseType()
{
// lists for containing get and set methods
List<MethodInfo> propGetters = new List<MethodInfo>();
List<MethodInfo> propSetters = new List<MethodInfo>();
#region Constructors
//do constructors
foreach (ConstructorInfo ci in _type_to_Draw.GetConstructors(Program.RequiredBindings))
{
if (_type_to_Draw == ci.DeclaringType)
{
string cDetail = _type_to_Draw.Name + "( ";
string pDetail="";
//add all the constructor param types to the associations List, so that
//the association lines for this class can be obtained, and
//possibly drawn on the container
ParameterInfo[] pif = ci.GetParameters();
foreach (ParameterInfo p in pif)
{
string pName=getGenericsForType(p.ParameterType);
pName = LowerAndTrim(pName);
if (!_Associations.Contains(pName))
{
_Associations.Add(pName);
}
pDetail = pName + " " + p.Name + ", ";
cDetail += pDetail;
}
if (cDetail.LastIndexOf(",") > 0)
{
cDetail = cDetail.Substring(0, cDetail.LastIndexOf(","));
}
cDetail += ")";
//do we want long or short field constructor displayed
if (Program._FullConstructorDescribe)
{
//_Constructors.Add(ci.ToString().Replace(".ctor", ""));
_Constructors.Add(cDetail);
}
else
_Constructors.Add(_type_to_Draw.Name + "( )");
}
}
#endregion
#region Fields
//do fields
foreach (FieldInfo fi in _type_to_Draw.GetFields(Program.RequiredBindings))
{
if (_type_to_Draw == fi.DeclaringType)
{
//add all the field types to the associations List, so that
//the association lines for this class can be obtained, and
//possibly drawn on the container
string fName=getGenericsForType(fi.FieldType);
fName = LowerAndTrim(fName);
if (!_Associations.Contains(fName))
{
_Associations.Add(fName);
}
//do we want long or short field description displayed
if (Program._IncludeFieldType)
_Fields.Add(fName + " " + fi.Name);
else
_Fields.Add(fi.Name);
}
}
#endregion
#region Properties
//do properties
foreach (PropertyInfo pi in _type_to_Draw.GetProperties(Program.RequiredBindings))
{
if (_type_to_Draw == pi.DeclaringType)
{
// add read method if exists
if (pi.CanRead) { propGetters.Add(pi.GetGetMethod(true)); }
// add write method if exists
if (pi.CanWrite) { propSetters.Add(pi.GetSetMethod(true)); }
string pName=getGenericsForType(pi.PropertyType);
//add all the property types to the associations List, so that
//the association lines for this class can be obtained, and
//possibly drawn on the container
pName = LowerAndTrim(pName);
if (!_Associations.Contains(pName))
{
_Associations.Add(pName);
}
//do we want long or short property description displayed
if (Program._IncludePropValues)
_Properties.Add(pName + " " + pi.Name);
else
_Properties.Add(pi.Name);
}
}
#endregion
#region Interfaces
//do interfaces
if (Program._IncludeInterfaces)
{
Type[] tiArray = _type_to_Draw.GetInterfaces();
foreach (Type ii in tiArray)
{
_Interfaces.Add(ii.Name.ToString());
}
}
#endregion
#region Methods
//do methods
foreach (MethodInfo mi in _type_to_Draw.GetMethods(Program.RequiredBindings))
{
if (_type_to_Draw == mi.DeclaringType)
{
string mDetail = mi.Name + "( ";
string pDetail="";
//do we want to display method arguments, if we do create the
//appopraiate string
if (Program._IncludeMethodArgs)
{
ParameterInfo[] pif = mi.GetParameters();
foreach (ParameterInfo p in pif)
{
//add all the parameter types to the associations List, so that
//the association lines for this class can be obtained, and
//possibly drawn on the container
string pName=getGenericsForType(p.ParameterType);
pName = LowerAndTrim(pName);
if (!_Associations.Contains(pName))
{
_Associations.Add(pName);
}
pDetail = pName + " " + p.Name + ", ";
mDetail += pDetail;
}
if (mDetail.LastIndexOf(",") > 0)
{
mDetail = mDetail.Substring(0, mDetail.LastIndexOf(","));
}
}
mDetail += " )";
//add the return type to the associations List, so that
//the association lines for this class can be obtained, and
//possibly drawn on the container
string rName=getGenericsForType(mi.ReturnType);
//dont want to include void as an association type
if (!string.IsNullOrEmpty(rName))
{
rName=getGenericsForType(mi.ReturnType);
rName = LowerAndTrim(rName);
if (!_Associations.Contains(rName))
{
_Associations.Add(rName);
}
//do we want to display method return types
if (Program._IncludeMethodReturnType)
mDetail += " : " + rName;
}
else
{
//do we want to display method return types
if (Program._IncludeMethodReturnType)
mDetail += " : void";
}
//work out whether this is a normal method, in which case add it
//or if its a property get/set method, should it be added
if (!Program._ShowPropGetters && propGetters.Contains(mi)) { /* hidden get method */ }
else if (!Program._ShowPropSetters && propSetters.Contains(mi)) { /* hidden set method */ }
else {
_Methods.Add(mDetail);
}
}
}
#endregion
#region Events
//do events
foreach (EventInfo ei in _type_to_Draw.GetEvents(Program.RequiredBindings))
{
if (_type_to_Draw == ei.DeclaringType)
{
//add all the event types to the associations List, so that
//the association lines for this class can be obtained, and
//possibly drawn on the container
string eName=getGenericsForType(ei.EventHandlerType);
eName = LowerAndTrim(eName);
if (!_Associations.Contains(eName))
{
_Associations.Add(eName);
}
//do we want long or short event description displayed
if (Program._IncludeEventType)
_Events.Add(eName + " " + ei.Name);
else
_Events.Add(ei.Name);
}
}
#endregion
}
/// <summary>
/// Constructs a tooltip for this classes known associations
/// </summary>
/// <param name="sender">DrawableClass</param>
/// <param name="e">the event args</param>
private void DrawableClass_MouseHover(object sender, EventArgs e)
{
if (_Associations.Count > 0)
{
string assClasses="ASSOCIATIONS\r\n\r\n";
foreach(string s in _Associations)
{
assClasses+=s +"\r\n";
}
toolTip1.Show(assClasses,this,5000);
}
}
#endregion
}
#endregion
#region SizeEventArgs CLASS
/// <summary>
/// provides the events args for the <see cref="DrawableClass">
/// DrawableClass </see>SizeChanged event
/// </summary>
public class SizeEventArgs : System.EventArgs
{
#region Instance Fields
//instance fields
private DrawableClass ucd;
#endregion
#region Ctor
/// <summary>
/// Constructs a new SizeEventArgs using the params provided
/// </summary>
/// <param name="ucd">A <see cref="DrawableClass">DrawableClass </see>
/// DrawableClass</param>object which raised the event
public SizeEventArgs(DrawableClass ucd)
{
this.ucd = ucd;
}
#endregion
#region Public Properties
/// <summary>
/// gets the CurrentState collapsed / expanded
/// </summary>
public DrawableClass.States CurrentState
{
get { return this.ucd.CurrentState; }
}
#endregion
}
#endregion
}