Click here to Skip to main content
Click here to Skip to main content
Articles » Languages » C# » General » Downloads
 
Add your own
alternative version
Go to top

100% Reflective Class Diagram Creation Tool

, 14 Jun 2011
100% Reflective Class Diagram Creation Tool
autodiagramer.zip
AutoDiagramer
AutoDiagramer
AutoDiagramer.csproj.user
AutoDiagramer_TemporaryKey.pfx
bin
Debug
AutoDiagramer.application
AutoDiagramer.exe
AutoDiagramer.exe.manifest
AutoDiagramer.pdb
AutoDiagramer.publish
AutoDiagramer.application
AutoDiagramer_1_7_0_0
AutoDiagramer.exe.deploy
AutoDiagramer.exe.manifest
np.ico.deploy
setup.exe
AutoDiagramer.vshost.application
AutoDiagramer.vshost.exe
AutoDiagramer.vshost.exe.manifest
Controls
Forms
Helpers
images
about16.png
AboutImg.png
BannerImg.png
Collapse9.png
Constructors16.png
Draw16.png
edging.png
Events16.png
Exit16.png
Expand9.png
Fields16.png
help16.gif
HideTree.png
HideTree16.png
Interfaces16.png
MainCollapse13.png
MainExpand13.png
Method16.png
open16.png
print16.png
Prop16.png
Prop33.png
save16.png
SaveImg.png
Setting16.png
SettingImg.png
ShowTree.png
ShowTree16.png
Thumbs.db
TreeCode.png
TreeLeaf.png
TreeRoot.png
View16.png
np.ico
obj
Debug
AutoDiagramer.application
AutoDiagramer.csproj.GenerateResource.Cache
AutoDiagramer.exe
AutoDiagramer.exe.manifest
AutoDiagramer.frmAbout.resources
AutoDiagramer.frmMain.resources
AutoDiagramer.frmSave.resources
AutoDiagramer.frmSettings.resources
AutoDiagramer.frmZoom.resources
AutoDiagramer.InputBoxDialog.resources
AutoDiagramer.pdb
AutoDiagramer.Properties.Resources.resources
AutoDiagramer.Resource1.resources
AutoDiagramer.ucExpander.resources
ClassDiagFromAssembly.csproj.GenerateResource.Cache
Refactor
TempPE
Properties.Resources.Designer.cs.dll
Resource1.Designer.cs.dll
Printing
Properties
Settings.settings
Thumbs.db
autodiagramerdll.zip
AutoDiagramer.dll
autodiagrammer.zip
AutoDiagrammer
AutoDiagramerReflectorAddin
AutoDiagramerReflectorAddin.csproj.user
AutoDiagramer_TemporaryKey.pfx
bin
Debug
AutoDiagramer.dll
AutoDiagramer.pdb
Controls
Forms
Helpers
images
about16.png
AboutImg.png
BannerImg.png
Collapse9.png
Constructors16.png
Draw16.png
edging.png
Events16.png
Exit16.png
Expand9.png
Fields16.png
help16.gif
HideTree.png
HideTree16.png
Interfaces16.png
MainCollapse13.png
MainExpand13.png
Method16.png
open16.png
print16.png
Prop16.png
Prop33.png
save16.png
SaveImg.png
Setting16.png
SettingImg.png
ShowTree.png
ShowTree16.png
Thumbs.db
TreeCode.png
TreeLeaf.png
TreeRoot.png
View16.png
np.ico
Printing
Properties
Settings.settings
Thumbs.db
AutoDiagramerStandAlone
AutoDiagramerStandAlone.csproj.user
AutoDiagramer_TemporaryKey.pfx
bin
Debug
AutoDiagramer.application
AutoDiagramer.exe
AutoDiagramer.exe.manifest
AutoDiagramer.pdb
AutoDiagramer.publish
AutoDiagramer.application
AutoDiagramer_1_12_0_0
AutoDiagramer.exe.deploy
AutoDiagramer.exe.manifest
np.ico.deploy
setup.exe
AutoDiagramer.vshost.application
AutoDiagramer.vshost.exe
AutoDiagramer.vshost.exe.manifest
Controls
Forms
Helpers
images
about16.png
AboutImg.png
BannerImg.png
Collapse9.png
Constructors16.png
Draw16.png
edging.png
Events16.png
Exit16.png
Expand9.png
Fields16.png
help16.gif
HideTree.png
HideTree16.png
Interfaces16.png
MainCollapse13.png
MainExpand13.png
Method16.png
open16.png
print16.png
Prop16.png
Prop33.png
save16.png
SaveImg.png
Setting16.png
SettingImg.png
ShowTree.png
ShowTree16.png
Thumbs.db
TreeCode.png
TreeLeaf.png
TreeRoot.png
View16.png
np.ico
Printing
Properties
Settings.settings
Thumbs.db
autodiagrammer2.zip
AutoDiagramer
AutoDiagramer.csproj.user
AutoDiagramer_TemporaryKey.pfx
bin
Debug
AutoDiagramer.application
AutoDiagramer.exe
AutoDiagramer.exe.manifest
AutoDiagramer.pdb
AutoDiagramer.publish
AutoDiagramer.application
AutoDiagramer_1_9_0_0
AutoDiagramer.exe.deploy
AutoDiagramer.exe.manifest
np.ico.deploy
Reflector.exe.deploy
setup.exe
AutoDiagramer.vshost.exe
Reflector.cfg
Reflector.exe
Controls
Forms
Helpers
images
about16.png
AboutImg.png
BannerImg.png
Collapse9.png
Constructors16.png
Draw16.png
edging.png
Events16.png
Exit16.png
Expand9.png
Fields16.png
help16.gif
HideTree.png
HideTree16.png
Interfaces16.png
MainCollapse13.png
MainExpand13.png
Method16.png
open16.png
print16.png
Prop16.png
Prop33.png
save16.png
SaveImg.png
Setting16.png
SettingImg.png
ShowTree.png
ShowTree16.png
Thumbs.db
TreeCode.png
TreeLeaf.png
TreeRoot.png
View16.png
np.ico
obj
Printing
Properties
Settings.settings
Thumbs.db
Reflector AddIn DLL
AutoDiagramer.dll
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 ucDrawableClass 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 ucDrawableClass : 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 ucExpander _ucConstructors=null;
        private ucExpander _ucFields = null;
        private ucExpander _ucProperties = null;
        private ucExpander _ucMethods = null;
        private ucExpander _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="ucExpander">ucExpander </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 ucDrawableClass(Type t)
        {
            InitializeComponent();
            //set to flickerless style
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw |
                    ControlStyles.DoubleBuffer | ControlStyles.UserPaint, true);

            _type_to_Draw = t;
            _Name = t.Name;
            _rowFont = new Font("Microsoft Sans Serif", 8.25F);
            _titleFont = new Font("Microsoft Sans Serif", 8.25F, FontStyle.Bold);
            this.BorderStyle = BorderStyle.None;
            this.Width = 5;
            this.Height = 5;

            try
            {
                //begin analysis
                AnalyseType();

                #region Create ucExpander 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 
                        ucExpander("Constructors", ucExpander.PossibleTypes.Constructors);
                    _ucConstructors.RowContent = _Constructors;
                    _ucConstructors.PanelStateChanged += 
                        new ucExpander.PanelStateChangedEventHandler(_ucExpander_PanelStateChanged);
                }
                //create Fields drawing items
                if (_Fields.Count > 0)
                {
                    _ucFields = new
                        ucExpander("Fields", ucExpander.PossibleTypes.Fields);
                    _ucFields.RowContent = _Fields;
                    _ucFields.PanelStateChanged +=
                        new ucExpander.PanelStateChangedEventHandler(_ucExpander_PanelStateChanged);

                }
                //create Properties drawing items
                if (_Properties.Count > 0)
                {
                    _ucProperties = new
                        ucExpander("Properties", ucExpander.PossibleTypes.Properties);
                    _ucProperties.RowContent = _Properties;
                    _ucProperties.PanelStateChanged +=
                        new ucExpander.PanelStateChangedEventHandler(_ucExpander_PanelStateChanged);
                }
                //create Methods drawing items
                if (_Methods.Count > 0)
                {
                    _ucMethods = new
                        ucExpander("Methods", ucExpander.PossibleTypes.Methods);
                    _ucMethods.RowContent = _Methods;
                    _ucMethods.PanelStateChanged +=
                        new ucExpander.PanelStateChangedEventHandler(_ucExpander_PanelStateChanged);
                }
                //create Events drawing items
                if (_Events.Count > 0)
                {
                    _ucEvents = new
                        ucExpander("Events", ucExpander.PossibleTypes.Events);
                    _ucEvents.RowContent = _Events;
                    _ucEvents.PanelStateChanged +=
                        new ucExpander.PanelStateChangedEventHandler(_ucExpander_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);

            }
            catch (Exception ex)
            {
                Program.ErrorBox(ex.Message);
            }
        }

        /// <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 ucExpander control parameter provided.
        /// In the case of showing, the height parameter is used for
        /// the height of the ucExpander
        /// </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, ucExpander 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 ucExpander controls changes state. Will also
        /// change the current state of this control and will raise the 
        /// OnSizeChanged() event
        /// </summary>
        /// <param name="sender">The current ucExpander control</param>
        /// <param name="args"></param>
        private void _ucExpander_PanelStateChanged(object sender, ExpanderSizeEventArgs args)
        {

            RegenerateControl();
            if (args.CurrentState== ucExpander.States.Collapsed)
            {
                this.CurrentState = States.PartiallyCollasped;
            }
            else if (args.CurrentState== ucExpander.States.Expanded)
            {
                this.CurrentState = States.Expanded;
            }

            // If anyone has subscribed, notify them
            OnSizeChanged(this, new SizeEventArgs(this));
        }
        #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
        /// <summary>
        /// Raised when the panel changes size (collaped/expanded)
        /// </summary>
        /// <param name="sender"><see cref="ucDrawableClass">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 ucExpander 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 ucExpander 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 ucExpander 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 ucExpander 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 ucExpander 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 ucExpander 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 ucExpander 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"))
                {
                    //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(_type_to_Draw.BaseType.Name, _rowFont, b_Black,
                        new PointF(arrowStartXPos + 13, arrowStartYPos - _rowFont.Height / 2));
                }
                   
            }

            #endregion
        }

        /// <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 ex)
            {
                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
        }
        #endregion
    }
    #endregion
    #region SizeEventArgs CLASS
    /// <summary>
    /// provides the events args for the <see cref="ucDrawableClass">
    /// ucDrawableClass </see>SizeChanged event
    /// </summary>
    public class SizeEventArgs : System.EventArgs
    {
        #region Instance Fields
        //instance fields
        private ucDrawableClass ucd;
        #endregion
        #region Ctor
        /// <summary>
        /// Constructs a new SizeEventArgs using the params provided
        /// </summary>
        /// <param name="ucd">A <see cref="ucDrawableClass">ucDrawableClass </see>
        /// ucDrawableClass</param>object which raised the event
        public SizeEventArgs(ucDrawableClass ucd)
        {
            this.ucd = ucd;
        }
        #endregion
        #region Public Properties
        /// <summary>
        /// gets the CurrentState collapsed / expanded
        /// </summary>
        public ucDrawableClass.States CurrentState
        {
            get { return this.ucd.CurrentState; }
        }
        #endregion
    }
    #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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Sacha Barber
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)
 
- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence
 
Both of these at Sussex University UK.
 
Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 14 Jun 2011
Article Copyright 2007 by Sacha Barber
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid