Click here to Skip to main content
15,881,204 members
Articles / Multimedia / GDI+

100% Reflective Class Diagram Creation Tool

Rate me:
Please Sign up or sign in to vote.
4.98/5 (498 votes)
14 Jun 2011CPOL28 min read 1.8M   39.6K   1.2K  
100% Reflective Class Diagram Creation Tool
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;



namespace AutoDiagramer
{
    #region frmMain CLASS
    /// <summary>
    /// This class provides a form to allow the user to select and input Dll/Exe
    /// to analyse to create a 100% reflected class diagram. If the file picked
    /// is not a valid CLR type the user will be alerted. If the Type is a valid 
    /// CLR type the user may choose to save the diagram produced, or tailor the
    /// diagramming using the <see cref="frmSettings">frmSettings</see> form
    /// </summary>
    public partial class frmMain : Form
    {
        #region Instance Fields
        //instance fields
        private TreeNode _tvnRoot;
        private TreeNode _tvnCurrent;
        private String _CurrFile="";
        private NameSpaces _nspaces;
        private FileInfo _currFile = null;
        private bool _TVShown=true;
        private int _oldTVWidth=0;
        private BitmapPrintPageEventHandler _BitmapPrintPageEventHandler;
        #endregion
        #region Ctor
        /// <summary>
        /// Constructs a new frmMain object
        /// </summary>
        public frmMain()
        {
            InitializeComponent();
            _oldTVWidth = pnlTreeContents.Width;
            Program._isStandAloneApp=true;



        }
        #endregion
        #region Private Methods
        /// <summary>
        /// calls the Exit() method
        /// </summary>
        /// <param name="sender">mnuExit</param>
        /// <param name="e">the event args</param>
        private void mnuExit_Click(object sender, EventArgs e)
        {
            Exit();
        }

        /// <summary>
        /// calls the Exit() method
        /// </summary>
        /// <param name="sender">btnExit</param>
        /// <param name="e">the event args</param>
        private void btnExit_Click(object sender, EventArgs e)
        {
            Exit();
        }

        /// <summary>
        /// If the user confirms that they wish to exit, the application
        /// is exited
        /// </summary>
        private void Exit()
        {
            if (Program.YesNoBox("Are you sure you want to exit") == DialogResult.Yes)
            {
                Application.Exit();
            }
        }

        /// <summary>
        /// calls the OpenFile() method
        /// </summary>
        /// <param name="sender">mnuOpen</param>
        /// <param name="e">the event args</param>
        private void mnuOpen_Click(object sender, EventArgs e)
        {
            OpenFile();
        }

        /// <summary>
        /// calls the OpenFile() method
        /// </summary>
        /// <param name="sender">btnOpenFile</param>
        /// <param name="e">the event args</param>
        private void btnOpenFile_Click(object sender, EventArgs e)
        {
            OpenFile();
        }

        /// <summary>
        /// prompts the user for a file (Dll or Exe). The selected file
        /// is then checked to see if its a valid CLR type, and if it is
        /// and not a System namspace assembly, an analysis process is started
        /// to reflectively draw a class diagram of the assembly
        /// </summary>
        private void OpenFile()
        {
            //initialise openFileDialog1
            openFileDialog1.InitialDirectory = @"C:\";
            openFileDialog1.Filter = "Assemblies (*.dll)|*.dll|Executables (*.exe)|*.exe";
            openFileDialog1.FileName = "";

            //if result was DialogResult.OK and not empty, extract the filename
            if (openFileDialog1.ShowDialog(this) == DialogResult.OK)
            {
                if (!openFileDialog1.FileName.Equals(string.Empty))
                {
                    _currFile = new FileInfo(openFileDialog1.FileName);
                    openFileDialog1.Dispose();
                    GC.Collect();
                    Application.DoEvents();
                    //process the file
                    processFile();
                }
            }
        }

        /// <summary>
        /// Processes the input file
        /// </summary>
        private void processFile()
        {
            if (_currFile != null)
            {
                //check its a valid CLR type
                if (DotNetObject.isDotNetAssembly(_currFile.FullName))
                {
                    //load the assembly and check it not System namespace
                    //we dont want to be here all year
                    Assembly ass = Assembly.LoadFrom(_currFile.FullName);
                    if (ass.FullName.StartsWith("System"))
                    {
                        Program.ErrorBox("System namespace assemblies not allowed");
                    }
                    //valid assembly that not a System one, so proceed
                    #region valid CLR type, non-system namespace
                    else
                    {
                        //set UI state correctly
                        CreateClassPanel();
                        _CurrFile = _currFile.Name;
                        mnuSave.Enabled = false;
                        btnSaveDiagram.Enabled = false;
                        mnuDraw.Enabled = false;
                        btnDrawDiagram.Enabled = false;
                        mnuZoom.Enabled = false;
                        btnZoom.Enabled = false;
                        mnuPrint.Enabled = false;
                        btnPrint.Enabled = false;
                        lblBannerText1.ForeColor = Color.CornflowerBlue;
                        lblBannerText1.Text = "Sucessfully opened file";
                        lblBannerText2.Text = "Current file : " + _currFile.Name;
                        lblStatus.Text = "Current file : " + _currFile.Name;
                        pnlFlowClasses.Controls.Clear();
                        tvNameSpaces.Visible = false;
                        tvNameSpaces.Enabled = false;
                        pnlFlowClasses.Visible = false;
                        SetColor(Color.White);
                        //add root node
                        tvNameSpaces.Nodes.Clear();
                        _tvnRoot = new TreeNode(_currFile.Name, 0, 0);
                        tvNameSpaces.Nodes.Add(_tvnRoot);
                        _nspaces = AutoDiagramer.NameSpaces.Instance;
                        _nspaces.Clear();
                        pnlShowHide.Visible=false;
                        //update UI
                        Refresh();
                        Application.DoEvents();
                        //create a new BackgroundWorker thread to do the scanning
                        BackgroundWorker bgw = new BackgroundWorker();
                        bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
                        bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
                        bgw.RunWorkerAsync(ass);
                    }
                    #endregion
                }
                //not a valid CLR type, so alert user
                else
                {
                    SetColor(Color.White);
                    tvNameSpaces.Nodes.Clear();
                    lblBannerText1.ForeColor = Color.Red;
                    lblBannerText1.Text = "Invalid file";
                    lblBannerText2.Text = "Current file : " + _currFile.Name +
                        " is not a valid CLR file. Please choose another file";
                    Program.ErrorBox("The file [" + _currFile.Name +
                                     "], is NOT a valid CLR type.\r\n\r\n" +
                                     "Please choose another file");
                }
            }
            else
            {
                Program.ErrorBox("you will need to open a file for the settings to be applied");               
            }
        }



        /// <summary>
        /// calls the About() method
        /// </summary>
        /// <param name="sender">mnuAboutApp</param>
        /// <param name="e">the event args</param>
        private void mnuAboutApp_Click(object sender, EventArgs e)
        {
            About();
        }

        /// <summary>
        /// calls the About() method
        /// </summary>
        /// <param name="sender">btnAbout</param>
        /// <param name="e">the event args</param>
        private void btnAbout_Click(object sender, EventArgs e)
        {
            About();
        }

        /// <summary>
        /// Shows a new <see cref="frmAbout">about form</see> 
        /// </summary>
        private void About()
        {
            frmAbout fAbout = new frmAbout();
            fAbout.ShowDialog(this);
        }

        /// <summary>
        /// calls the Settings() method
        /// </summary>
        /// <param name="sender">mnuUserSettings</param>
        /// <param name="e">the event args</param>
        private void mnuUserSettings_Click(object sender, EventArgs e)
        {
            Settings();
        }

        /// <summary>
        /// calls the Settings() method
        /// </summary>
        /// <param name="sender">btnSettings</param>
        /// <param name="e">the event args</param>
        private void btnSettings_Click(object sender, EventArgs e)
        {
            Settings();
        }

        /// <summary>
        /// Shows a new <see cref="frmSettings">settings form</see> 
        /// </summary>
        private void Settings()
        {
            frmSettings fSettings = new frmSettings();
            if (fSettings.ShowDialog(this) == DialogResult.OK)
            {
                processFile();
            }
        }

        /// <summary>
        /// calls the SaveDiagram() method
        /// </summary>
        /// <param name="sender">mnuSave</param>
        /// <param name="e">the event args</param>
        private void mnuSave_Click(object sender, EventArgs e)
        {
            SaveDiagram();
        }

        /// <summary>
        /// calls the SaveDiagram() method
        /// </summary>
        /// <param name="sender">btnSaveDiagram</param>
        /// <param name="e">the event args</param>
        private void btnSaveDiagram_Click(object sender, EventArgs e)
        {
            SaveDiagram();
        }

        /// <summary>
        /// Shows a new <see cref="frmSave">save form</see> 
        /// and uses the user entered parameters to call the
        /// SaveTheDiagram(string filename, ImageFormat imgFormat) method
        /// </summary>
        private void SaveDiagram()
        {
            //create new save form
            frmSave fSave = new frmSave();
            if (fSave.ShowDialog(this) == DialogResult.OK)
            {
                //get the values from the save form
                string filename = @fSave.SaveFile;

                if (Directory.Exists(@fSave.SaveDirectory))
                {
                    ImageFormat imgformat = fSave.RequiredImageFormat;
                    fSave.Dispose();
                    //GC.Collect();
                    Application.DoEvents();

                    //call the SaveTheDiagram method
                    if (SaveTheDiagram(filename, imgformat))
                    {
                        RenderPanel();
                        pnlFlowClasses.Visible = true;
                        this.Refresh();
                        Cursor.Current = Cursors.Default;
                        Program.InfoBox("Successfully saved the file \r\n" + filename);
                    }
                    else
                    {
                        Program.InfoBox("There was a problem saving the file \r\n" + filename);
                    }
                }
                else 
                {
                    Program.InfoBox("The save directory chosen \r\n" + fSave.SaveDirectory + "\r\n"
                                    + "does not exist. Please retry");
                }
            }
        }

        /// <summary>
        /// Saves the entire contents of the pnlFlowClasses to an image, specified
        /// by the input parameters
        /// </summary>
        /// <param name="filename">the filename to save the diagram to</param>
        /// <param name="imgFormat">the image format to save the diagram as</param>
        /// <returns></returns>
        private bool SaveTheDiagram(string filename, ImageFormat imgFormat)
        {
            Cursor.Current = Cursors.WaitCursor;
            int bmpSrcWidth = pnlFlowClasses.MaxSize.Width;
            int bmpSrcHeight = pnlFlowClasses.MaxSize.Height;
            //create a new ClassDrawerContainerPanel (which will not be shown on the form)
            ClassDrawerContainerPanel pnl = new ClassDrawerContainerPanel();
            pnlFlowClasses.SuspendLayout();
            Rectangle newBounds = new Rectangle(0, 0, bmpSrcWidth, bmpSrcHeight);
            pnl.Height = bmpSrcHeight;
            pnl.Width = bmpSrcWidth;
            pnl.Bounds = newBounds;
            pnl.BackColor = Color.White;
            pnl.SetBounds(0, 0, bmpSrcWidth, bmpSrcHeight);
            pnl.ClassesToDraw = pnlFlowClasses.ClassesToDraw;
            pnl.LayoutControls();

            Bitmap SrcBmp=null;
            Bitmap bmpNew = null;
            Graphics gfx = null;

            //save the image, however if we are saving the image to the save file name
            //the Bitmap object maintains a lock on the pyhsical file, so we need to use
            //another dummy Bitmap to hold the original image, so that the lock creates
            //by the original image saving process can be released, then we can save
            //the dummy Bitmap contents back to the orginal image and conduct the save.

            //This is a well documented feature, see the following resources
            //http://blog.vishalon.net/
            //http://support.microsoft.com/?id=814675
            try
            {
                SrcBmp = new Bitmap(bmpSrcWidth, bmpSrcHeight);
                pnl.DrawToBitmap(SrcBmp, newBounds);
                bmpNew = new Bitmap(SrcBmp.Width, SrcBmp.Height);
                gfx = Graphics.FromImage(bmpNew);
                gfx.DrawImage(SrcBmp, new Rectangle(0, 0, bmpNew.Width, bmpNew.Height),
                    0, 0, SrcBmp.Width, SrcBmp.Height, GraphicsUnit.Pixel);
                // As original SrcBmp keeps lock on file, we need to copy the original image
                // to a second image, and then release the lock on the image file. Of course, 
                // image from the initial image file is now existing on the new Graphics object
                // which points to the second image, ready to copy back to the orinal image
                SrcBmp.Dispose();
                SrcBmp = bmpNew;
                gfx.Dispose();
                //do the save (now that the original lock has been dealt with)
                SrcBmp.Save(filename, imgFormat);
                SrcBmp.Dispose();
                pnlFlowClasses.ResumeLayout();
                return true;
            }
            catch (Exception)
            {
                if (SrcBmp != null) { SrcBmp.Dispose(); }
                if (bmpNew != null) { bmpNew.Dispose(); }
                if (gfx != null) { gfx.Dispose(); }
                GC.Collect();
                return false;
            }

        }

        /// <summary>
        /// Initialise the GUI components initial values
        /// </summary>
        /// <param name="sender">frmMain</param>
        /// <param name="e">the event args</param>
        private void frmMain_Load(object sender, EventArgs e)
        {
            lblBannerText1.Text = "Awaiting Dll / Exe";
            lblBannerText2.Text = "Please open a Dll / Exe, using either the menu or toolbar";
            lblStatus.Text = "Please open a Dll / Exe, using either the menu or toolbar";
            pbStatus.Visible = false;
        }

        /// <summary>
        /// Set the GUI controls to the specified Color
        /// </summary>
        /// <param name="clr">The Color to set the GUI controls to</param>
        private void SetColor(Color clr)
        {
            splitContainer1.BackColor = clr;
        }


        /// <summary>
        /// The background worker thread has finished analysing the current
        /// Type the user selected. So update the gui to the finished state
        /// </summary>
        /// <param name="sender">The background worker thread</param>
        /// <param name="e">the event args</param>
        private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            SetColor(Color.WhiteSmoke);
            tvNameSpaces.Visible = true;
            tvNameSpaces.Enabled = true;
            tvNameSpaces.ExpandAll();
            pnlFlowClasses.Visible = true;
            pbStatus.Visible = false;
            lblStatus.Text = "Current file : " + _CurrFile;
            mnuDraw.Enabled = true;
            btnDrawDiagram.Enabled = true;
            pnlShowHide.Visible=true;
            Refresh();
            Application.DoEvents();
        }

        /// <summary>
        /// Returns true if the Type specified by the t parameter is a valid
        /// diagrammable type. e.i. Not System namespace, and is one that the
        /// user wants on the current diagram
        /// </summary>
        /// <param name="t"></param>
        /// <returns>true if the Type specified by the t parameter is a valid
        /// diagrammable type. e.i. Not System namespace, and is one that the
        /// user wants on the current diagram</returns>
        private bool IsWantedForDiagramType(Type t)
        {
            //check to see if the class lives in a namespace
            if (!string.IsNullOrEmpty(t.Namespace))
            {
                //dont really want user to trawl the standard System namespaces
                if (t.Namespace.StartsWith("System"))
                    return false;
            }

            //should Enums be shown, on final diagram
            if (!Program._AllowEnumOnDiagram)
            {
                if (t.BaseType != null)
                {
                    if (t.BaseType.FullName.Equals("System.Enum"))
                    {
                        return false;
                    }
                }
                else
                {
                    return false;
                }
            }
            //should MulticastDelegates be shown, on final diagram
            if (!Program._AllowDelegatesOnDiagram)
            {
                if (t.BaseType != null)
                {
                    if (t.BaseType.FullName.Equals("System.MulticastDelegate"))
                    {
                        return false;
                    }
                }
                else
                {
                    return false;
                }
            }
            //check for ApplicationSettingsBase
            if (t.BaseType != null)
            {
                if (t.BaseType.FullName.Equals("System.Configuration.ApplicationSettingsBase"))
                {
                    return false;
                }
            }
            //if we get to here its an allowable Type
            return true;
        }

        /// <summary>
        /// The background worker thread start doing work, calls the 
        /// internal StartAnylsisProcess() method
        /// </summary>
        /// <param name="sender">The background worker thread</param>
        /// <param name="e">the event args</param>
        private void bgw_DoWork(object sender, DoWorkEventArgs e)
        {

            //get Assembly from the wokers argument
            Assembly a = (Assembly)e.Argument;
            //start the anylsis process, making sure to marshall
            //to correct thread for GUI property change operation, which
            //will require the thread to be on the same thread as the handle
            //to the control
            if (this.InvokeRequired)
            {
                this.Invoke(new EventHandler(delegate
                {
                    StartAnylsisProcess(a);

                }));
            }
            else
            {
                StartAnylsisProcess(a);
            }
        }

        /// <summary>
        /// Loop through each Type in the current Assembly
        /// adding each ot the forms treeview by calling the
        /// internal addTypesToTree() method
        /// </summary>
        /// <param name="a">The current Assembly being examined</param>
        private void StartAnylsisProcess(Assembly a)
        {
            //loop through each Type in the current Assembly
            //adding each ot the forms treeview
            foreach (Type t in a.GetTypes())
            {
                if (string.IsNullOrEmpty(t.Namespace))
                {
                    addTypesToTree(_tvnRoot, t.Name, "No-Namespace Classes", t);
                }
                else
                {
                    addTypesToTree(_tvnRoot, t.Name, t.Namespace, t);
                }
            }
        }

        /// <summary>
        /// Returns true if the tree cotains a node with the name specified
        /// by the nodeName parameter
        /// </summary>
        /// <param name="nd">The start node to start the search from</param>
        /// <param name="nodeName">name to try and detect</param>
        /// <returns>true if the tree cotains a node with the name specified
        /// by the nodeName parameter</returns>
        private bool TreeContainsNode(TreeNode nd, String nodeName)
        {
            foreach (TreeNode tn in nd.Nodes)
            {
                if (tn.Text == nodeName)
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// If the user issues a check on a Namespace node
        /// set all the child nodes of the Namespace node to be
        /// the same checked state as the Namespace node
        /// </summary>
        /// <param name="sender">tvNameSpaces</param>
        /// <param name="e">the event args</param>
        private void tvNameSpaces_AfterCheck(object sender, TreeViewEventArgs e)
        {
            //If the user issues a check on a Namespace node
            //set all the child nodes of the Namespace node to be
            //the same checked state as the Namespace node
            if (!e.Node.Equals(_tvnRoot))
            {
                if (e.Node.Parent.Equals(_tvnRoot))
                {
                    foreach (TreeNode tn in e.Node.Nodes)
                    {
                        tn.Checked = e.Node.Checked;
                    }
                }
            }
        }

        /// <summary>
        /// If the user issues a right mouse click on a Namespace node
        /// show the context menu
        /// </summary>
        /// <param name="sender">tvNameSpaces</param>
        /// <param name="e">the event args</param>
        private void tvNameSpaces_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            //If the user issues a right mouse click on a Namespace node
            //show the context menu
            if (e.Button.Equals(MouseButtons.Right))
            {
                if (!e.Node.Equals(_tvnRoot))
                {
                    if (e.Node.Parent.Equals(_tvnRoot))
                    {
                        _tvnCurrent = e.Node;
                        cmnuTree.Show(tvNameSpaces, e.X, e.Y);
                    }
                }
            }
        }

        /// <summary>
        /// Creates a new ClassDrawerContainerPanel and adds it to the forms surface
        /// </summary>
        private void CreateClassPanel()
        {
            //remove old ClassDrawerContainerPanel (if we dont remove old referece is kept,
            //as such the old Column/Row rendering is also kept, which is bad news)
            this.splitContainer1.Panel2.Controls.Remove(pnlFlowClasses);
            //create a new ClassDrawerContainerPanel, as the rendering could be different
            //from last time, as user could have picked a different number of classes
            //to render
            pnlFlowClasses = new ClassDrawerContainerPanel();
            this.pnlFlowClasses.AutoScroll = true;
            this.pnlFlowClasses.BackColor = System.Drawing.Color.White;
            this.pnlFlowClasses.Dock = System.Windows.Forms.DockStyle.Fill;
            this.pnlFlowClasses.Location = new System.Drawing.Point(0, 0);
            this.pnlFlowClasses.Name = "pnlFlowClasses";
            this.pnlFlowClasses.Size = new System.Drawing.Size(569, 391);
            this.pnlFlowClasses.TabIndex = 0;
            this.splitContainer1.Panel2.Controls.Add(this.pnlFlowClasses);
            this.Refresh();
            Application.DoEvents();
        }



        /// <summary>
        /// Get a list of required classes for diagram (those checked in treeview),
        /// if there is at least 1 required class for diagram, proceed to create
        /// a new panel to contain the diagram, and then add the <see cref="DrawableClass">
        /// DrawableClass</see> controls to the panel one by one
        /// </summary>
        /// <param name="sender">mnuViewDiagram</param>
        /// <param name="e">the event args</param>
        private void mnuViewDiagram_Click(object sender, EventArgs e)
        {
            RenderPanel();
        }

        /// <summary>
        /// builds a list of all the required classes to be drawn
        /// </summary>
        private void RenderPanel()
        {
            //get a list of required classes for diagram (those checked in treeview)
            List<string> requiredClasses = new List<string>();
            foreach (TreeNode tn in _tvnCurrent.Nodes)
            {
                if (tn.Checked)
                {
                    requiredClasses.Add(tn.Text);
                }
            }
            //if there is at least 1 required class for diagram, proceed
            if (requiredClasses.Count > 0)
            {
                showContentsOnPanel(requiredClasses);
            }
            //they didnt select any nodes, so select them all
            else 
            {
                foreach (TreeNode tn in _tvnCurrent.Nodes)
                {
                    tn.Checked = true;
                    requiredClasses.Add(tn.Text);
                }
                showContentsOnPanel(requiredClasses);
            }
        }

        /// <summary>
        /// Creates a new <see cref="ClassDrawerContainerPanel">ClassDrawerContainerPanel</see>
        ///  panel and draws the required classes on it
        /// </summary>
        /// <param name="requiredClasses">A list of classes to be drawn</param>
        private void showContentsOnPanel(List<string> requiredClasses)
        {
                //get the list of drawable classes, and create a new panel to contain
                //teh diagram
                List<DrawableClass> dccs = new List<DrawableClass>();
                dccs = _nspaces.getDrawableClassesForDiagram(_tvnCurrent.Text, requiredClasses);
                CreateClassPanel();
                pnlFlowClasses.Controls.Clear();
                pnlFlowClasses.Visible = false;
                //add the DrawableClass controls to the panel one by one
                if (dccs != null)
                {
                    if (dccs.Count > 0)
                    {
                        foreach (DrawableClass dc in dccs)
                        {
                            dc.RegenerateControl();
                            dc.BackColor = pnlFlowClasses.BackColor;
                        }
                        pnlFlowClasses.ClassesToDraw = dccs;
                        pnlFlowClasses.LayoutControls();
                        mnuSave.Enabled = true;
                        btnSaveDiagram.Enabled = true;
                        mnuZoom.Enabled = true;
                        btnZoom.Enabled = true;
                        mnuPrint.Enabled = true;
                        btnPrint.Enabled = true;
                    }
                }
                //finally allow the fully populated panel to be shown
                pnlFlowClasses.Visible = true;
                pnlFlowClasses.Focus();
                this.Refresh();
                Application.DoEvents();
        }


        /// <summary>
        /// If namspace isnt empty, add the namspace to the current node and
        /// <see cref="Namspaces">Namspaces</see> object. Then check the type
        /// is actualy one that can be added to the diagram, could be System 
        /// (which is not allowed). Then add the type to the namespace by creating
        /// a new <see cref="DrawableClass">DrawableClass</see> object, for each
        /// allowable type
        /// </summary>
        /// <param name="nd">The current treenode to add new nodes to</param>
        /// <param name="typename">the current typename</param>
        /// <param name="nspace">the current namspace</param>
        /// <param name="t">the Type to examine</param>
        private void addTypesToTree(TreeNode nd, String typename, String nspace, Type t)
        {

            try
            {
                //if namspace isnt empty, add the namspace to the current node and
                //to the treeview and the Namspaces object
                //if (nspace != null)
                //{
                    if (!nspace.Trim().Equals(String.Empty))
                    {

                        //1st add the new namespace
                        if (!TreeContainsNode(nd, nspace))
                        {
                            nd.Nodes.Add(new TreeNode(nspace, 1, 1));
                            _nspaces.addNameSpace(nspace, null);
                        }

                        //then check the type is actualy one that can be added to the
                        //diagram, could be System (which is not allowed)
                        if (IsWantedForDiagramType(t))
                        {

                            //now add the type to the namespace, by getting namespace from the list of nodes 1st
                            foreach (TreeNode tn in nd.Nodes)
                            {
                                if (tn.Text == nspace)
                                {
                                    TreeNode currTypeNode = new TreeNode(typename, 2, 2);
                                    tn.Nodes.Add(currTypeNode);
                                    //create a new DrawableClass and add it to the current namespace
                                    
                                    DrawableClass dc=null;
                                    if (Program._isStandAloneApp)
                                    {
									    dc = new DrawableClass(t);
                                        //get the current user colors
                                        dc.ClassStartColor = Program._ClassStartColor;
                                        dc.ClassEndColor = Program._ClassEndColor;
                                        dc.ClassBorderColor = Program._ClassBorderColor;
                                    }
                                    if (dc != null)
                                    {
                                        //add to Namespaces object
                                        _nspaces.addNameSpace(nspace, dc);
                                    }

                                    lblStatus.Text = "Analysing type [" + t.Name + "]";
                                    pbStatus.Visible = true;
                                    Refresh();
                                    Application.DoEvents();
                                }
                            }
                        }
                    }
                //}
            }
            catch (Exception ex)
            {
                Program.ErrorBox(ex.Message);
            }

        }

        /// <summary>
        /// The btnDrawDiagram button click event, simply calls DrawDiagram()
        /// method
        /// </summary>
        /// <param name="sender">The btnDrawDiagram button</param>
        /// <param name="e">the event args</param>
        private void btnDrawDiagram_Click(object sender, EventArgs e)
        {
            DrawDiagram();
        }

        /// <summary>
        /// The mnuDraw button click event, simply calls DrawDiagram()
        /// method
        /// </summary>
        /// <param name="sender">The mnuDraw button</param>
        /// <param name="e">the event args</param>
        private void mnuDraw_Click(object sender, EventArgs e)
        {
            DrawDiagram();
        }

        /// <summary>
        /// Draws the diagram if the there is a tree node selected, 
        /// and its not the root node, and that it actually is
        /// a namespace node
        /// </summary>
        private void DrawDiagram()
        {
            //default error msg
            string errMsg = "You need to select which namespace\r\n" +
                            "to draw the diagram for.\r\n\r\n" +
                            "Please select a Namespace node and then retry";

            //check there is a node selected, and its the not root node, and that it actually is
            //a namespace node
            if (tvNameSpaces.SelectedNode != null)
            {
                if (!tvNameSpaces.SelectedNode.Equals(_tvnRoot))
                {
                    if (tvNameSpaces.SelectedNode.Parent.Equals(_tvnRoot))
                    {
                        _tvnCurrent = tvNameSpaces.SelectedNode;
                        RenderPanel();
                    }
                    else
                    {
                        Program.ErrorBox(errMsg);
                    }
                }
                else
                {
                    Program.ErrorBox(errMsg);
                }
            }
            else
            {
                Program.ErrorBox(errMsg);
            }
        }

        /// <summary>
        /// The mnuPrint menu clicked, so call Print() method
        /// </summary>
        /// <param name="sender">the mnuPrint menu</param>
        /// <param name="e">the event args</param>
        private void mnuPrint_Click(object sender, EventArgs e)
        {
            Print();
        }

        /// <summary>
        /// The btnPrint menu clicked, so call Print() method
        /// </summary>
        /// <param name="sender">the btnPrint menu</param>
        /// <param name="e">the event args</param>
        private void btnPrint_Click(object sender, EventArgs e)
        {
            Print();
        }

        /// <summary>
        /// Prints the current diagram to a printer of the user choice
        /// </summary>
        private void Print()
        {
            printDialog1.ShowNetwork=true;

            //allow user to pick their printer
            if (printDialog1.ShowDialog(this) == DialogResult.OK)
            {
                //get an image to print for the current diagram
                Bitmap RtnBmp=getBitmapForControl();
                //and now print it
                if (RtnBmp != null) 
                {
                    _BitmapPrintPageEventHandler = new BitmapPrintPageEventHandler(RtnBmp);
                    this.printDocument1.PrintPage -= _BitmapPrintPageEventHandler.PrintPage;
                    this.printDocument1.PrintPage += _BitmapPrintPageEventHandler.PrintPage;
                    this.printDocument1.Print();
                    this.printDocument1.PrinterSettings=printDialog1.PrinterSettings;
                }
                else 
                {
                    Program.ErrorBox("There was a problem generating the image\r\n" +
                                     "for the drawing control");
                }
            }
        }


        /// <summary>
        /// mnuZoom menu clicked, so call the internal Zoom() method
        /// </summary>
        /// <param name="sender">mnuZoom menu</param>
        /// <param name="e">the events args</param>
        private void mnuZoom_Click(object sender, EventArgs e)
        {
            Zoom();
        }

        /// <summary>
        /// btnZoom button clicked, so call the internal Zoom() method
        /// </summary>
        /// <param name="sender">btnZoom button</param>
        /// <param name="e">the events args</param>
        private void btnZoom_Click(object sender, EventArgs e)
        {
            Zoom();
        }

        /// <summary>
        /// btnZoom button or mnuZoom menu clicked, so show zoom form 
        /// for the current image
        /// </summary>
        /// <param name="sender">btnZoom button</param>
        /// <param name="e">the events args</param>
        private void Zoom ()
        {

            frmZoom fZoom = new frmZoom();
            Bitmap RtnBmp=getBitmapForControl();
            if (RtnBmp != null) 
            {
                fZoom.CurrentImage = RtnBmp;
                fZoom.ShowDialog(this);
            }
            else 
            {
                Program.ErrorBox("There was a problem generating the image\r\n" +
                                 "for the drawing control");
            }
        }

        /// <summary>
        /// Returns a Bitmap which represents the current diagram
        /// </summary>
        /// <returns>Bitmap which represents the current diagram</returns>
        private Bitmap getBitmapForControl()
        {
            Cursor.Current = Cursors.WaitCursor;
            int bmpSrcWidth = pnlFlowClasses.MaxSize.Width;
            int bmpSrcHeight = pnlFlowClasses.MaxSize.Height;
            //create a new ClassDrawerContainerPanel (which will not be shown on the form)
            ClassDrawerContainerPanel pnl = new ClassDrawerContainerPanel();
            pnlFlowClasses.SuspendLayout();
            pnlFlowClasses.Visible = false;
            this.Invalidate();
            Rectangle newBounds = new Rectangle(0, 0, bmpSrcWidth, bmpSrcHeight);
            pnl.Height = bmpSrcHeight;
            pnl.Width = bmpSrcWidth;
            pnl.Bounds = newBounds;
            pnl.BackColor = Color.White;
            pnl.SetBounds(0, 0, bmpSrcWidth, bmpSrcHeight);
            pnl.ClassesToDraw = pnlFlowClasses.ClassesToDraw;
            pnl.LayoutControls();
            Bitmap SrcBmp=null;
            Bitmap RtnBmp=null;

            try
            {
                SrcBmp = new Bitmap(bmpSrcWidth, bmpSrcHeight);
                pnl.DrawToBitmap(SrcBmp, newBounds);
                pnlFlowClasses.ResumeLayout();
                RenderPanel();
                RtnBmp = (Bitmap)SrcBmp.Clone();
            }
            catch (Exception)
            {
                if (SrcBmp != null) { SrcBmp.Dispose(); }
                GC.Collect();
                return null;
            }
            return RtnBmp;
        }


        /// <summary>
        /// Shows or hide the treeview by calling the ShowOrHide() method
        /// </summary>
        /// <param name="sender">the menuSHTree menu</param>
        /// <param name="e">the event args</param>
        private void menuSHTree_Click(object sender, EventArgs e)
        {
            ShowOrHide();
        }

        /// <summary>
        /// Shows or hide the treeview by calling the ShowOrHide() method
        /// </summary>
        /// <param name="sender">the picShownHideTreeview picture box</param>
        /// <param name="e">the event args</param>
        private void picShownHideTreeview_Click(object sender, EventArgs e)
        {
            ShowOrHide();
        }

        /// <summary>
        /// Toggles the state of the treeview show/hide and assigns the correct
        /// images to the menu/ picturebox
        /// </summary>
        private void ShowOrHide()
        {
            _TVShown = !_TVShown;

            if (_TVShown)
            {
                setWidthsForTree(true);
                menuSHTree.Image = global::AutoDiagramer.Resource1.HideTree16;
                picShownHideTreeview.Image = global::AutoDiagramer.Resource1.HideTree;
            }
            else 
            {
                setWidthsForTree(false);
                menuSHTree.Image = global::AutoDiagramer.Resource1.ShowTree16;
                picShownHideTreeview.Image = global::AutoDiagramer.Resource1.ShowTree;
            }
            this.Invalidate();
            Application.DoEvents();
        }

        /// <summary>
        /// Show/hides the treeview
        /// </summary>
        /// <param name="visible">true indicates the treeview should be shown</param>
        private void setWidthsForTree(bool visible)
        {
           tvNameSpaces.Visible=visible;
           pnlBannerTree.Visible=visible;
           pnlTreeContents.Visible=visible;
           if ( visible)
           {    
                splitContainer1.SplitterDistance = _oldTVWidth + pnlShowHide.Width;
           }
           else 
           {
                splitContainer1.SplitterDistance = pnlShowHide.Width;
           }
        }

        /// <summary>
        /// the frmMain form resize, used to capture the old treeview width
        /// for when it needs to be shown again
        /// </summary>
        /// <param name="sender">the frmMain form</param>
        /// <param name="e">the event args</param>
        private void frmMain_Resize(object sender, EventArgs e)
        {
            try 
            {
                if (_TVShown)
                {
                    _oldTVWidth = pnlTreeContents.Width;
                    splitContainer1.SplitterDistance = _oldTVWidth + pnlShowHide.Width;
                }
                else 
                {
                    splitContainer1.SplitterDistance = pnlShowHide.Width;
                }
            }
            catch (Exception)
            {
            }
        }

        /// <summary>
        /// the splitContainer1 split container moved, so keep a copy of the 
        /// position for when the treeview is reshown
        /// </summary>
        /// <param name="sender">the splitContainer1 split container moved</param>
        /// <param name="e">the event args</param>
        private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e)
        {
            if (_TVShown)
            {
               _oldTVWidth = splitContainer1.SplitterDistance;
            }
            else 
            {
                splitContainer1.SplitterDistance = 25;
            }
        }
        #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)


Written By
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 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • 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

Comments and Discussions