private void Form1_Load(object sender, System.EventArgs e) { // Create some sample data of around 500 nodes. root = TreeSample.SampleData.Create(2,6); DrawTree(); } private void DrawTree() { Bitmap bm = null; int width = 0; int height = 0; try { // Set our original starting position for the // root of the tree. root.X = 10; root.Y = 10; // Without actually drawing the nodes, set // the x and y property values of where they'll // be drawn. using(TreeGenerator tg = new TreeGenerator(8,8,24,24)) { tg.InitializeXYCoordinates(root); // Using the internal MaxCurrentX and MaxCurrentY // values which take into account node placement // and any text shown, get the dimensions that we // should use to size the image to match our hierarchy // size. height = tg.GetImageHeight(); width = tg.GetImageWidth(); // Create the image from scratch. We do // this outside the generator class to offer // maximum flexibility concerning the image for // this such as transparency and image background. // This also gives us the flexibility to draw // numerous trees in different locations on the // same image. bm = new Bitmap(width,height,PixelFormat.Format32bppArgb); Graphics g = Graphics.FromImage(bm); g.SmoothingMode = SmoothingMode.AntiAlias; // Draw the background color. Comment these // four lines out and insert code to populate // the .BackGroundImage if desired. Rectangle bgImg = new Rectangle(0,0,width,height); SolidBrush bgBrush = new SolidBrush(Color.White); g.FillRectangle(bgBrush,bgImg); bgBrush.Dispose(); // Draw the node hierarchy use the root node // as a starting point. tg.DrawNodes(g,root); g.Dispose(); // Save our image to a file if we want // to see the entire thing in a browser. bm.Save("test.gif",ImageFormat.Gif); // Get a quick peek on the windows forms. this.pictureBox1.Width = bm.Width; this.pictureBox1.Height = bm.Height; this.pictureBox1.Image = bm; } } catch (Exception err) { Debug.WriteLine(err.Message); } } private void pictureBox1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) { try { DataNode node = root.GetDataNodeByPosition(root,e.X,e.Y); if (node == null) { return; } if (node.LineColor == Color.Orange) { SetUnSelectedThreadDisplay(node,node.UniqueID); } else { SetSelectedThreadDisplay(node,node.UniqueID); } DrawTree(); } catch (Exception err) { Debug.WriteLine(err.Message); } } private void SetSelectedThreadDisplay(DataNode node,string uniqueID) { try { if (node.UniqueID == uniqueID) { SetSelectedNodeDisplay(node); SetParentNodeDisplay(node); return; } for(int i=0;i<node.Nodes.Count;i++) { SetSelectedThreadDisplay(node.Nodes[i],uniqueID); } } catch (Exception) { throw; } return; } private void SetUnSelectedThreadDisplay(DataNode node,string uniqueID) { try { if (node.UniqueID == uniqueID) { SetUnSelectedNodeDisplay(node); SetUnSelectedParentNodeDisplay(node); return; } for(int i=0;i<node.Nodes.Count;i++) { SetUnSelectedThreadDisplay(node.Nodes[i],uniqueID); } } catch (Exception) { throw; } return; } private void SetParentNodeDisplay(DataNode node) { try { if (node.Parent == null) { return; } SetSelectedNodeDisplay(node.Parent); SetParentNodeDisplay(node.Parent); } catch (Exception) { throw; } return; } private void SetUnSelectedParentNodeDisplay(DataNode node) { try { if (node.Parent == null) { return; } SetUnSelectedNodeDisplay(node.Parent); SetUnSelectedParentNodeDisplay(node.Parent); } catch (Exception) { throw; } return; } private void SetSelectedNodeDisplay(DataNode node) { try { node.LineColor = Color.Red; node.LineDashStyle = DashStyle.Solid; node.LineThickness = 2; } catch (Exception) { throw; } return; } private void SetUnSelectedNodeDisplay(DataNode node) { try { node.LineColor = Color.Gray; node.LineDashStyle = DashStyle.Dash; node.LineThickness = 1; } catch (Exception) { throw; } return; }
using System; using System.Collections; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Drawing.Text; using System.Diagnostics; using EggHeadCafe.Drawing; namespace EggHeadCafe.Drawing { public class TreeGenerator : EggHeadCafe.Drawing.Shared,IDisposable { public TreeGenerator(int markerHeight, int markerWidth, int connectorWidth, int connectorHeight) { MarkerHeight = markerHeight; MarkerWidth = markerWidth; ConnectorHeight = connectorHeight; ConnectorWidth = connectorWidth; dummy = new Bitmap(5,5); graphicFunctions = Graphics.FromImage(dummy); } public void Dispose() { if (Disposed) { return; } graphicFunctions.Dispose(); dummy.Dispose(); } public int GetImageWidth() { return MaxCurrentX + ConnectorWidth; } public int GetImageHeight() { return MaxCurrentY + ConnectorHeight; } public void InitializeXYCoordinates(DataNode parentNode) { DataNode node; int index = 0; int x = 0; SizeF textSize; try { if (parentNode.Parent == null) { parentNode.RelativeIndex = "1"; MaxCurrentY = 0; MaxCurrentX = 0; } x = parentNode.X + ConnectorWidth; if (MaxCurrentY == 0) { MaxCurrentY = parentNode.Y; } if (MaxCurrentX == 0) { MaxCurrentX = x; } if (x > MaxCurrentX) { MaxCurrentX = x; } if (parentNode.DrawText) { if (parentNode.Text.Trim().Length > 0) { textSize = graphicFunctions.MeasureString(parentNode.Text, parentNode.TextFont); parentNode.TextWidth = (int)textSize.Width; parentNode.TextHeight = (int)textSize.Height; MaxCurrentX = x + (int)textSize.Width; } } for(int i=0;i<parentNode.Nodes.Count;i++) { MaxCurrentY += ConnectorHeight; node = parentNode.Nodes[i]; index = i + 1; node.RelativeIndex = node.Parent.RelativeIndex + "." + index.ToString(); node.X = x; node.Y = MaxCurrentY; InitializeXYCoordinates(node); } } catch (Exception) { throw; } } public void DrawNodes(Graphics g,DataNode parentNode) { try { // DrawLineConnector(g,parentNode); DrawAngleConnector(g,parentNode); DrawMarker(g,parentNode); DrawText(g,parentNode); for(int i=0;i<parentNode.Nodes.Count;i++) { DrawNodes(g,parentNode.Nodes[i]); } } catch (Exception) { throw; } } } }
using System; using System.Collections; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Drawing.Text; using System.Diagnostics; using EggHeadCafe.Drawing; namespace EggHeadCafe.Drawing { public class Shared { protected int MarkerHeight = 10; protected int MarkerWidth = 10; protected int ConnectorWidth = 24; protected int ConnectorHeight = 24; // Keep track of the maximum point to the // right and the maximum point towards // the bottom. Makes sure that each // element can be drawn below or to // the right of the previous one. protected int MaxCurrentX = 0; protected int MaxCurrentY = 0; // Use a dummy bitmap and graphics // object enabling us to use methods // prior to actually creating the real // graphic. ie measure text protected Bitmap dummy; protected Graphics graphicFunctions; protected bool Disposed = false; public int GetMarkerCenterX(int position) { return position + (int)(MarkerWidth * .50); } public int GetMarkerCenterY(int position) { return position + (int)(MarkerHeight * .5); } public void DrawLineConnector(Graphics g,DataNode node) { float parentX = 0; float parentY = 0; float childX = 0; float childY = 0; try { if (node.Parent == null) { return; } // I stored the positions in local variables to make // it easier for you to provide fancier line drawing // capabilities. parentX = (float)GetMarkerCenterX(node.Parent.X); parentY = (float)GetMarkerCenterY(node.Parent.Y); childX = (float)GetMarkerCenterX(node.X); childY = (float)GetMarkerCenterY(node.Y); parentY = parentY + (float)(MarkerHeight * .5); PointF pt1 = new PointF(parentX,parentY); PointF pt2 = new PointF(childX,childY); PointF[] points = { pt1, pt2 }; Pen fillPen = new Pen(node.LineColor,node.LineThickness); fillPen.DashStyle = node.LineDashStyle; g.DrawLines(fillPen,points); fillPen.Dispose(); points = null; } catch (Exception) { throw; } return; } public void DrawAngleConnector(Graphics g,DataNode node) { float parentX = 0; float parentY = 0; float midX = 0; float midY = 0; float childX = 0; float childY = 0; try { if (node.Parent == null) { return; } // I stored the positions in local variables to make // it easier for you to provide fancier line drawing // capabilities. // Set the connector points to the middle of // the marker nodes. The existing .X and .Y // coordinates are the very top and left of the marker. parentX = (float)GetMarkerCenterX(node.Parent.X); parentY = (float)GetMarkerCenterY(node.Parent.Y); midX = (float)GetMarkerCenterX(node.Parent.X); midY = (float)GetMarkerCenterY(node.Y); childX = (float)GetMarkerCenterX(node.X); childY = (float)GetMarkerCenterY(node.Y); parentY = parentY + (float)(MarkerHeight * .5); PointF pt1 = new PointF(parentX,parentY); PointF pt2 = new PointF(midX,midY); PointF pt3 = new PointF(childX,childY); PointF[] points = { pt1, pt2,pt3 }; Pen fillPen = new Pen(node.LineColor,node.LineThickness); fillPen.DashStyle = node.LineDashStyle; g.DrawLines(fillPen,points); fillPen.Dispose(); points = null; } catch (Exception) { throw; } return; } public void DrawMarker(Graphics g,DataNode node) { try { Rectangle rect = new Rectangle(node.X,node.Y,MarkerWidth,MarkerHeight); SolidBrush fillBrush = new SolidBrush(node.MarkerColor); g.FillEllipse(fillBrush,rect); fillBrush.Dispose(); } catch (Exception) { throw; } return; } public void DrawText(Graphics g,DataNode node) { float x = 0F; float y = 0F; try { if (!node.DrawText) { return; } if (node.Text.Trim().Length < 1) { return; } SolidBrush TextBrush = new SolidBrush(node.TextColor); StringFormat sf = new StringFormat(); sf.Alignment = StringAlignment.Near; sf.Trimming = StringTrimming.EllipsisCharacter; sf.LineAlignment = StringAlignment.Center; g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; // Set the text just to the left of the marker // and add a little bit of spacing between // to the right of the marker before drawing // the text. x = (float)GetMarkerCenterX(node.X); y = (float)GetMarkerCenterY(node.Y); x += MarkerWidth; g.DrawString(node.Text,node.TextFont,TextBrush,x,y,sf); TextBrush.Dispose(); sf.Dispose(); } catch (Exception) { throw; } return; } } }
using System; using System.Collections; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Diagnostics; namespace EggHeadCafe.Drawing { public class DataNode { private bool StopRecursion = false; private DataNode ReturnNode = null; public DataNode() { mNodes = new EggHeadCafe.Drawing.DataNodeCollection(this); } private string mRelativeIndex = ""; public string RelativeIndex { get { return mRelativeIndex; } set { mRelativeIndex = value; } } private EggHeadCafe.Drawing.DataNodeCollection mNodes = null; public EggHeadCafe.Drawing.DataNodeCollection Nodes { get { return mNodes; } set { mNodes = value; } } private EggHeadCafe.Drawing.DataNode mParent = null; public EggHeadCafe.Drawing.DataNode Parent { get { return mParent; } set { mParent = value; } } private System.Drawing.Color mBackColor = System.Drawing.Color.Black; public System.Drawing.Color BackColor { get { return mBackColor; } set { mBackColor = value; } } private System.Drawing.Color mTextColor = System.Drawing.Color.Black; public System.Drawing.Color TextColor { get { return mTextColor; } set { mTextColor = value; } } private System.Drawing.Color mMarkerColor = System.Drawing.Color.Black; public System.Drawing.Color MarkerColor { get { return mMarkerColor; } set { mMarkerColor = value; } } private System.Drawing.Color mLineColor = System.Drawing.Color.Black; public System.Drawing.Color LineColor { get { return mLineColor; } set { mLineColor = value; } } private int mLineThickness = 1; public int LineThickness { get { return mLineThickness; } set { mLineThickness = value; } } private DashStyle mLineDashStyle = DashStyle.DashDot; public System.Drawing.Drawing2D.DashStyle LineDashStyle { get { return mLineDashStyle; } set { mLineDashStyle = value; } } private System.Drawing.Font mTextFont = null; public System.Drawing.Font TextFont { get { return mTextFont; } set { mTextFont = value; } } private bool mDrawText = false; public bool DrawText { get { return mDrawText; } set { mDrawText = value; } } private string mUniqueID = ""; public string UniqueID { get { return mUniqueID; } set { mUniqueID = value; } } private string mText = ""; public string Text { get { return mText; } set { mText = value; } } private int mX = 0; public int X { get { return mX; } set { mX = value; } } private int mY = 0; public int Y { get { return mY; } set { mY = value; } } private int mTextWidth = 0; public int TextWidth { get { return mTextWidth; } set { mTextWidth = value; } } private int mTextHeight = 0; public int TextHeight { get { return mTextHeight; } set { mTextHeight = value; } } private int mConnectorAngle = 90; public int ConnectorAngle { get { return mConnectorAngle; } set { mConnectorAngle = value; } } public DataNode GetDataNodeByUniqueID(DataNode node,string uniqueID) { try { StopRecursion = false; ReturnNode = null; GetDataNodeByKey(node,uniqueID); if (ReturnNode != null) { return ReturnNode; } } catch (Exception) { throw; } finally { StopRecursion = false; } return null; } public DataNode GetDataNodeByPosition(DataNode node,int x,int y) { try { StopRecursion = false; ReturnNode = null; GetDataNodeByXAndY(node,x,y); if (ReturnNode != null) { return ReturnNode; } } catch (Exception) { throw; } finally { StopRecursion = false; } return null; } private void GetDataNodeByKey(DataNode node,string uniqueID) { try { if (StopRecursion) { return; } if (node.UniqueID == uniqueID) { ReturnNode = node; StopRecursion = true; return; } for(int i=0;i<node.Nodes.Count;i++) { GetDataNodeByKey(node.Nodes[i],uniqueID); } } catch (Exception) { throw; } return; } private void GetDataNodeByXAndY(DataNode node,int x,int y) { try { if (StopRecursion) { return; } int textRight = node.X + node.TextWidth; int textBottom = node.Y + node.TextHeight; if ((x >= node.X) && (x <= textRight)) { if ((y >= node.Y) && (y <= textBottom)) { ReturnNode = node; StopRecursion = true; return; } } for(int i=0;i<node.Nodes.Count;i++) { GetDataNodeByXAndY(node.Nodes[i],x,y); } } catch (Exception) { throw; } return; } } }