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
}