Click here to Skip to main content
15,881,757 members
Articles / Programming Languages / C#

XTree - Part II

Rate me:
Please Sign up or sign in to vote.
4.86/5 (10 votes)
29 May 2006CPOL4 min read 64.7K   604   62  
A template driven tree control.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;					// -- used by drag & drop stuff.
using System.Windows.Forms;
using System.Xml;

using Clifton.Tools.Xml;
using Clifton.Windows.Forms.XmlTree;

namespace Clifton.Windows.Forms
{
	public class GTreeEventArgs : EventArgs
	{
		protected bool handled;

		public bool Handled
		{
			get { return handled; }
			set { handled = value; }
		}

		public GTreeEventArgs()
		{
		}
	}

	public class XTree : TreeView, ISupportInitialize
	{
		public event EventHandler DisplayContextMenu;

		protected RootNode rootNode;
		protected Point mousePos;
		protected TreeNode selNode;
		protected string treeDefFileName;
		protected IXtreeNode selectedNodeInstance;

		protected Dictionary<string, NodeDef> nodeList;
		protected Dictionary<Type, NodeDef> controllerToNodeDefMap;
		protected Dictionary<string, NodeDef> nameToNodeDefMap;

		public Point MousePos
		{
			get { return mousePos; }
			set { mousePos = value; }
		}

		public string TreeDefinitionFileName
		{
			get { return treeDefFileName; }
			set { treeDefFileName = value; }
		}

		public XTree()
		{
		}

		public virtual void BeginInit()
		{
		}

		public virtual void EndInit()
		{
			if (treeDefFileName != null)
			{
				XmlDocument xdoc = new XmlDocument();
				xdoc.Load(treeDefFileName);
				Initialize(xdoc);
			}

			AllowDrop = true;

			DragOver += new DragEventHandler(OnXTreeDragOver);
			DragEnter += new DragEventHandler(OnXTreeDragEnter);
			ItemDrag += new ItemDragEventHandler(OnXTreeItemDrag);
			DragDrop += new DragEventHandler(OnXTreeDragDrop);
		}

		public virtual void Clear()
		{
			Nodes.Clear();
		}

		public void Initialize(XmlDocument xdoc)
		{
			nodeList = new Dictionary<string, NodeDef>();
			controllerToNodeDefMap = new Dictionary<Type, NodeDef>();
			nameToNodeDefMap = new Dictionary<string, NodeDef>();
//			LabelEdit = true;
			ImageList = new ImageList();
			MycroParser mp = new MycroParser();
			mp.Load(xdoc, null, null);
			mp.NamespaceMap[""] = "Clifton.Windows.Forms.XmlTree, Clifton.Windows.Forms";
			rootNode=(RootNode)mp.Process();
			nodeList[rootNode.Name] = rootNode;
			BuildFlatNodeList(rootNode);
//			TreeNode tn=CreateNodeAndRequiredChildren(rootNode);
//			Nodes.Add(tn);
		}

		public TreeNode AddNode(IXtreeNode inst, TreeNode parent)
		{
			NodeDef nodeDef = null;

			if (!controllerToNodeDefMap.ContainsKey(inst.GetType()))
			{
				throw new ApplicationException("The controller instance "+inst.GetType().Name+" is expected but not defined in a TypeName attribute of the tree schema.");
			}

			nodeDef = controllerToNodeDefMap[inst.GetType()];

			TreeNode tn = CreateNodeAndRequiredChildren(nodeDef, inst);

			if (parent != null)
			{
				parent.Nodes.Add(tn);
			}
			else
			{
				Nodes.Add(tn);
			}

			return tn;
		}

		public TreeNode CreateNode(NodeDef node, IXtreeNode inst)
		{
			TreeNode tn = new TreeNode();
			tn.Text = node.Text;

			// If no controller is specified, use the text in the NodeDef.
			if (!(inst is PlaceholderInstance))
			{
				tn.Text = inst.Name;
			}

			tn.Tag = new NodeInstance(tn, node, inst);

			if (node.ImageList != null)
			{
				// Have images been loaded into the TreeView's image list?
				if (node.ImageOffset == -1)
				{
					// No.  Load them and save the offset.
					node.ImageOffset = ImageList.Images.Count;

					foreach (Image img in node.ImageList.Images)
					{
						ImageList.Images.Add(img);
					}

				}

				// Yes, just update the indices.
				tn.ImageIndex = node.ImageOffset + inst.IconIndex;
				tn.SelectedImageIndex = node.ImageOffset + inst.SelectedIconIndex;
			}

			return tn;
		}

		public TreeNode CreateNodeAndRequiredChildren(NodeDef node, IXtreeNode parentInst)
		{
			TreeNode tn = CreateNode(node, parentInst);

			foreach (NodeDef child in node.Nodes)
			{
				if (child.IsRequired)
				{
					IXtreeNode inst = child.CreateImplementingType(parentInst);
					TreeNode tnChild = CreateNodeAndRequiredChildren(child, inst);
					tn.Nodes.Add(tnChild);
					tn.Expand();
				}
			}

			return tn;
		}

		public ContextMenu BuildContextMenu(TreeNode tn, NodeDef n)
		{
			ContextMenu cm = new ContextMenu();
			bool first = true;

			// Populate from this node's popup collection.
			if (n.PopupItems.Count != 0)
			{
				foreach (Popup popup in n.PopupItems)
				{
					NodeMenuItem nmi = new NodeMenuItem(popup.Text, tn, n, null, popup);
					nmi.Click += new EventHandler(OnContextItem);
					nmi.Enabled = ((NodeInstance)tn.Tag).Instance.IsEnabled(popup.Tag, popup.Enabled);
					cm.MenuItems.Add(nmi);
				}

				first = false;
			}

			// For each child node, populate from the child's ParentPopupItems collection.
			foreach (NodeDef child in n.Nodes)
			{
				NodeDef refNode = child;

				// Resolve any referenced node.
				if (child.IsRef)
				{
					if (!nodeList.ContainsKey(child.RefName))
					{
						throw new ApplicationException("referenced node does not exist.");
					}

					refNode = nodeList[child.RefName];
				}

				if (refNode.ParentPopupItems.Count != 0)
				{
					if (!first)
					{
						cm.MenuItems.Add("-");
					}

					first = false;

					// Populate the items.
					foreach (Popup popup in refNode.ParentPopupItems)
					{
						NodeMenuItem nmi = new NodeMenuItem(popup.Text, tn, n, refNode, popup);
						nmi.Click += new EventHandler(OnContextItem);
						nmi.Enabled = ((NodeInstance)tn.Tag).Instance.IsEnabled(popup.Tag, popup.Enabled);
						cm.MenuItems.Add(nmi);
					}
				}
			}

			return cm;
		}

		public void Serialize(string fn)
		{
			XmlDocument xdoc = new XmlDocument();
			XmlDeclaration xmlDeclaration = xdoc.CreateXmlDeclaration("1.0", "utf-8", null);
			xdoc.InsertBefore(xmlDeclaration, xdoc.DocumentElement);
			XmlNode xnode = xdoc.CreateElement("XTree");
			xdoc.AppendChild(xnode);

			foreach (TreeNode tn in Nodes)
			{
				WriteNode(xdoc, xnode, tn);
			}

			xdoc.Save("tree.xml");

		}

		public void Deserialize(string fn)
		{
			Nodes.Clear();
			XmlDocument xdoc = new XmlDocument();
			xdoc.Load("tree.xml");
			XmlNode node = xdoc.DocumentElement;
			ReadNode(xdoc, node, Nodes, null);
		}

		protected void ReadNode(XmlDocument xdoc, XmlNode node, TreeNodeCollection nodes, IXtreeNode parent)
		{
			foreach (XmlNode xn in node.ChildNodes)
			{
				IXtreeNode inst = nodeList[xn.Name].CreateImplementingType(parent);
				TreeNode tn = CreateNode(nodeList[xn.Name], inst);
				nodes.Add(tn);
				
				ReadNode(xdoc, xn, tn.Nodes, inst);

				if (Convert.ToBoolean(xn.Attributes["IsExpanded"].Value))
				{
					tn.Expand();
				}
			}
		}

		protected void WriteNode(XmlDocument xdoc, XmlNode xnode, TreeNode tn)
		{
			XmlNode xn = xdoc.CreateElement(((NodeInstance)tn.Tag).NodeDef.Name);
			xn.Attributes.Append(xdoc.CreateAttribute("Text"));
			xn.Attributes.Append(xdoc.CreateAttribute("IsExpanded"));
			xn.Attributes["Text"].Value = tn.Text;
			xn.Attributes["IsExpanded"].Value = tn.IsExpanded.ToString();
			xnode.AppendChild(xn);

			foreach (TreeNode child in tn.Nodes)
			{
				WriteNode(xdoc, xn, child);
			}
		}

		protected void BuildFlatNodeList(NodeDef node)
		{
			foreach (NodeDef child in node.Nodes)
			{
				if (child.Name == null)
				{
					throw new ApplicationException("NodeDef Name cannot be null.");
				}

				nodeList[child.Name] = child;
				controllerToNodeDefMap[child.ImplementingType] = child;
				nameToNodeDefMap[child.Name] = child;

				if (!child.Recurse)
				{
					BuildFlatNodeList(child);
				}
			}
		}

		protected override void  OnKeyDown(KeyEventArgs e)
		{
			base.OnKeyDown(e);

			if (e.KeyCode == Keys.F2)
			{
				if (!((NodeInstance)SelectedNode.Tag).NodeDef.IsReadOnly)
				{
					if (!SelectedNode.IsEditing)
					{
						LabelEdit = true;
						SelectedNode.BeginEdit();
					}
				}
			}
		}

		protected override void OnMouseDown(MouseEventArgs e)
		{
			base.OnMouseDown(e);
			selNode = GetNodeAt(e.Location);

			if (selNode != null)
			{
				if (e.Button == MouseButtons.Left)
				{
					// clicked twice on the same node?
					if (selNode == SelectedNode)
					{
						if (!((NodeInstance)selNode.Tag).NodeDef.IsReadOnly)
						{
							SelectedNode = selNode;

							if (!selNode.IsEditing)
							{
								LabelEdit = true;
								selNode.BeginEdit();
							}
						}
					}
					else
					{
						((NodeInstance)selNode.Tag).Instance.Select(selNode);
					}
				}
				else
				{
					SelectedNode = selNode;
					((NodeInstance)selNode.Tag).Instance.Select(selNode);
				}

				selectedNodeInstance = ((NodeInstance)SelectedNode.Tag).Instance;
			}
		}

		protected override void OnMouseUp(MouseEventArgs e)
		{
			base.OnMouseUp(e);

			if (e.Button == MouseButtons.Right)
			{
				mousePos = new Point(e.X, e.Y);
				OnDisplayContextMenu();
			}
		}

		protected override void OnAfterLabelEdit(NodeLabelEditEventArgs e)
		{
			if (e.Label != null)
			{
				base.OnAfterLabelEdit(e);
				LabelEdit = false;
			}
		}

		protected virtual void OnDisplayContextMenu()
		{
			GTreeEventArgs ea = new GTreeEventArgs();
			
			if (DisplayContextMenu != null)
			{
				DisplayContextMenu(this, ea);
			}

			if (!ea.Handled)
			{
				TreeNode tn = GetNodeAt(mousePos);
				NodeDef nodeDef;

				if (tn == null)
				{
					nodeDef = rootNode;
				}
				else
				{
					nodeDef = ((NodeInstance)tn.Tag).NodeDef;
				}

				ContextMenu cm=BuildContextMenu(tn, nodeDef);
				cm.Show(this, mousePos);
			}
		}

		private void OnContextItem(object sender, EventArgs e)
		{
			NodeMenuItem nmi = (NodeMenuItem)sender;

			if (nmi.PopupInfo.IsAdd)
			{
				IXtreeNode parentInst = null;

				if (nmi.TreeNode != null)
				{
					parentInst = ((NodeInstance)nmi.TreeNode.Tag).Instance;
				}

				IXtreeNode inst = nmi.ChildNode.CreateImplementingType(parentInst);
				bool success=inst.AddNode(parentInst, nmi.PopupInfo.Tag);

				if (success)
				{
					TreeNode tn = CreateNodeAndRequiredChildren(nmi.ChildNode, inst);

					// This wouldn't be necessary if there was something like an ITreeNode interface
					// which both TreeView and TreeNode implemented to give you access to the Nodes collection!
					if (nmi.TreeNode == null)
					{
						Nodes.Add(tn);
					}
					else
					{
						nmi.TreeNode.Nodes.Add(tn);
						tn.Parent.Expand();
					}

					tn.TreeView.SelectedNode = tn;
					inst.Select(tn);
				}
			}
			else if (nmi.PopupInfo.IsRemove)
			{
				NodeInstance ni = (NodeInstance)nmi.TreeNode.Tag;
				IXtreeNode inst = ni.Instance;
				IXtreeNode parentInst = null;
				TreeNode parentNode = nmi.TreeNode.Parent;

				if (parentNode != null)
				{
					parentInst = ((NodeInstance)parentNode.Tag).Instance;
				}

				bool success = inst.DeleteNode(parentInst);

				if (success)
				{
					nmi.TreeNode.Remove();
				}
			}
		}
		
		// ------------ Drag & Drop Events --------------

		// Modified from Gabe Anguiano's work here: http://www.codeproject.com/cs/miscctrl/TreeViewReArr.asp

		private string NodeMap;
		private const int MAPSIZE = 128;
		private StringBuilder NewNodeMap = new StringBuilder(MAPSIZE);

		private void OnXTreeItemDrag(object sender, System.Windows.Forms.ItemDragEventArgs e)
		{
			DoDragDrop(e.Item, DragDropEffects.Move);
		}

		private void OnXTreeDragEnter(object sender, System.Windows.Forms.DragEventArgs e)
		{
			e.Effect = DragDropEffects.Move;
		}
		private void OnXTreeDragDrop(object sender, System.Windows.Forms.DragEventArgs e)
		{
			if (e.Data.GetDataPresent("System.Windows.Forms.TreeNode", false) && this.NodeMap != "")
			{
				TreeNode MovingNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");
				string[] NodeIndexes = this.NodeMap.Split('|');
				TreeNodeCollection InsertCollection = Nodes;
				TreeNode newParent=null;

				for (int i = 0; i < NodeIndexes.Length - 1; i++)
				{
					newParent=InsertCollection[Int32.Parse(NodeIndexes[i])];
					InsertCollection = newParent.Nodes;
				}

				if (InsertCollection != null)
				{
					try
					{
						int idx = Int32.Parse(NodeIndexes[NodeIndexes.Length - 1]);

						// Get the node controller for the moving node.
						NodeInstance ni = (NodeInstance)MovingNode.Tag;
						IXtreeNode inst = ni.Instance;
						// Get the node controller for the new parent.
						NodeInstance parent = (NodeInstance)newParent.Tag;
						IXtreeNode parentInst = parent.Instance;
						IXtreeNode movingNodeParentInst = ((NodeInstance)MovingNode.Parent.Tag).Instance;

						// Can only move to another parent of the same type.
						if (parentInst.GetType() == movingNodeParentInst.GetType())
						{
							inst.MoveTo(parentInst, movingNodeParentInst, idx);
							InsertCollection.Insert(idx, (TreeNode)MovingNode.Clone());
							SelectedNode = InsertCollection[idx];
							MovingNode.Remove();
						}
						else
						{
							// Remove markers.
							Refresh();
						}
					}
					catch (Exception ex)
					{
						MessageBox.Show(ex.Message);
					}
				}
			}
		}

		private void OnXTreeDragOver(object sender, System.Windows.Forms.DragEventArgs e)
		{
			TreeNode NodeOver = GetNodeAt(PointToClient(Cursor.Position));
			TreeNode NodeMoving = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");


			// A bit long, but to summarize, process the following code only if the nodeover is null
			// and either the nodeover is not the same thing as nodemoving UNLESSS nodeover happens
			// to be the last node in the branch (so we can allow drag & drop below a parent branch)
			if (NodeOver != null && (NodeOver != NodeMoving || (NodeOver.Parent != null && NodeOver.Index == (NodeOver.Parent.Nodes.Count - 1))))
			{
				int OffsetY = PointToClient(Cursor.Position).Y - NodeOver.Bounds.Top;
//				int NodeOverImageWidth = ImageList.Images[NodeOver.ImageIndex].Size.Width + 8;
				Graphics g = CreateGraphics();

				// Image index of 1 is the non-folder icon
//				if (NodeOver.ImageIndex == 1)
				// MTC
				// If there are subnodes already, or we're at the last node, then draw the "before/after" markers.
				// What this prevents happening is the ability to add subnodes to a last node that has no children.
				// So we need to add an AND expression, that if we're at the last node AND the mouse Y is at the top third or bottom third of the node,
				// THEN we want to treat this as a "before/after" selection.  Otherwise, treat it as adding a child node.
				if ( (NodeOver.Nodes.Count > 0) || 
					( (NodeOver.Index == (NodeOver.Parent.Nodes.Count - 1)) &&
					  ( (OffsetY < NodeOver.Bounds.Height / 3) || (OffsetY > 2*NodeOver.Bounds.Height/3) ) )
					)
				{
					#region Standard Node
					if (OffsetY < (NodeOver.Bounds.Height / 2))
					{
						//this.lblDebug.Text = "top";

						#region If NodeOver is a child then cancel
						TreeNode tnParadox = NodeOver;
						while (tnParadox.Parent != null)
						{
							if (tnParadox.Parent == NodeMoving)
							{
								this.NodeMap = "";
								return;
							}

							tnParadox = tnParadox.Parent;
						}
						#endregion
						#region Store the placeholder info into a pipe delimited string
						SetNewNodeMap(NodeOver, false);
						if (SetMapsEqual() == true)
							return;
						#endregion
						#region Clear placeholders above and below
						this.Refresh();
						#endregion
						#region Draw the placeholders
						this.DrawLeafTopPlaceholders(NodeOver);
						#endregion
					}
					else
					{
						//this.lblDebug.Text = "bottom";

						#region If NodeOver is a child then cancel
						TreeNode tnParadox = NodeOver;
						while (tnParadox.Parent != null)
						{
							if (tnParadox.Parent == NodeMoving)
							{
								this.NodeMap = "";
								return;
							}

							tnParadox = tnParadox.Parent;
						}
						#endregion
						#region Allow drag drop to parent branches
						TreeNode ParentDragDrop = null;
						// If the node the mouse is over is the last node of the branch we should allow
						// the ability to drop the "nodemoving" node BELOW the parent node
						if (NodeOver.Parent != null && NodeOver.Index == (NodeOver.Parent.Nodes.Count - 1))
						{
							int XPos = PointToClient(Cursor.Position).X;
							if (XPos < NodeOver.Bounds.Left)
							{
								ParentDragDrop = NodeOver.Parent;

								// MTC - commented out--maybe there isn't an image list!!!
								//if (XPos < (ParentDragDrop.Bounds.Left - ImageList.Images[ParentDragDrop.ImageIndex].Size.Width))
								//{
								//    if (ParentDragDrop.Parent != null)
								//        ParentDragDrop = ParentDragDrop.Parent;
								//}
							}
						}
						#endregion
						#region Store the placeholder info into a pipe delimited string
						// Since we are in a special case here, use the ParentDragDrop node as the current "nodeover"
						SetNewNodeMap(ParentDragDrop != null ? ParentDragDrop : NodeOver, true);
						if (SetMapsEqual() == true)
							return;
						#endregion
						#region Clear placeholders above and below
						this.Refresh();
						#endregion
						#region Draw the placeholders
						DrawLeafBottomPlaceholders(NodeOver, ParentDragDrop);
						#endregion
					}
					#endregion
				}
				else
				{
					#region Folder Node
					if (OffsetY < (NodeOver.Bounds.Height / 3))
					{
						//this.lblDebug.Text = "folder top";

						#region If NodeOver is a child then cancel
						TreeNode tnParadox = NodeOver;
						while (tnParadox.Parent != null)
						{
							if (tnParadox.Parent == NodeMoving)
							{
								this.NodeMap = "";
								return;
							}

							tnParadox = tnParadox.Parent;
						}
						#endregion
						#region Store the placeholder info into a pipe delimited string
						SetNewNodeMap(NodeOver, false);
						if (SetMapsEqual() == true)
							return;
						#endregion
						#region Clear placeholders above and below
						this.Refresh();
						#endregion
						#region Draw the placeholders
						this.DrawFolderTopPlaceholders(NodeOver);
						#endregion
					}
					else if ((NodeOver.Parent != null && NodeOver.Index == 0) && (OffsetY > (NodeOver.Bounds.Height - (NodeOver.Bounds.Height / 3))))
					{
						//this.lblDebug.Text = "folder bottom";

						#region If NodeOver is a child then cancel
						TreeNode tnParadox = NodeOver;
						while (tnParadox.Parent != null)
						{
							if (tnParadox.Parent == NodeMoving)
							{
								this.NodeMap = "";
								return;
							}

							tnParadox = tnParadox.Parent;
						}
						#endregion
						#region Store the placeholder info into a pipe delimited string
						SetNewNodeMap(NodeOver, true);
						if (SetMapsEqual() == true)
							return;
						#endregion
						#region Clear placeholders above and below
						this.Refresh();
						#endregion
						#region Draw the placeholders
						DrawFolderTopPlaceholders(NodeOver);
						#endregion
					}
					else
					{
						//this.lblDebug.Text = "folder over";

						if (NodeOver.Nodes.Count > 0)
						{
							NodeOver.Expand();
							//this.Refresh();
						}
						else
						{
							#region Prevent the node from being dragged onto itself
							if (NodeMoving == NodeOver)
								return;
							#endregion
							#region If NodeOver is a child then cancel
							TreeNode tnParadox = NodeOver;
							while (tnParadox.Parent != null)
							{
								if (tnParadox.Parent == NodeMoving)
								{
									this.NodeMap = "";
									return;
								}

								tnParadox = tnParadox.Parent;
							}
							#endregion
							#region Store the placeholder info into a pipe delimited string
							SetNewNodeMap(NodeOver, false);
							NewNodeMap = NewNodeMap.Insert(NewNodeMap.Length, "|0");

							if (SetMapsEqual() == true)
								return;
							#endregion
							#region Clear placeholders above and below
							this.Refresh();
							#endregion
							#region Draw the "add to folder" placeholder
							DrawAddToFolderPlaceholder(NodeOver);
							#endregion
						}
					}
					#endregion
				}
			}
		}

		private void SetNewNodeMap(TreeNode tnNode, bool boolBelowNode)
		{
			NewNodeMap.Length = 0;

			if (boolBelowNode)
				NewNodeMap.Insert(0, (int)tnNode.Index + 1);
			else
				NewNodeMap.Insert(0, (int)tnNode.Index);
			TreeNode tnCurNode = tnNode;

			while (tnCurNode.Parent != null)
			{
				tnCurNode = tnCurNode.Parent;

				if (NewNodeMap.Length == 0 && boolBelowNode == true)
				{
					NewNodeMap.Insert(0, (tnCurNode.Index + 1) + "|");
				}
				else
				{
					NewNodeMap.Insert(0, tnCurNode.Index + "|");
				}
			}
		}//oem

		private bool SetMapsEqual()
		{
			if (this.NewNodeMap.ToString() == this.NodeMap)
				return true;
			else
			{
				this.NodeMap = this.NewNodeMap.ToString();
				return false;
			}
		}//oem

		private void DrawLeafTopPlaceholders(TreeNode NodeOver)
		{
			Graphics g = CreateGraphics();

			// MTC
//			int NodeOverImageWidth = ImageList.Images[NodeOver.ImageIndex].Size.Width + 8;
			int LeftPos = NodeOver.Bounds.Left; // MTC -NodeOverImageWidth;
			int RightPos = Width - 4;

			Point[] LeftTriangle = new Point[5]{
												   new Point(LeftPos, NodeOver.Bounds.Top - 4),
												   new Point(LeftPos, NodeOver.Bounds.Top + 4),
												   new Point(LeftPos + 4, NodeOver.Bounds.Y),
												   new Point(LeftPos + 4, NodeOver.Bounds.Top - 1),
												   new Point(LeftPos, NodeOver.Bounds.Top - 5)};

			Point[] RightTriangle = new Point[5]{
													new Point(RightPos, NodeOver.Bounds.Top - 4),
													new Point(RightPos, NodeOver.Bounds.Top + 4),
													new Point(RightPos - 4, NodeOver.Bounds.Y),
													new Point(RightPos - 4, NodeOver.Bounds.Top - 1),
													new Point(RightPos, NodeOver.Bounds.Top - 5)};


			g.FillPolygon(System.Drawing.Brushes.Black, LeftTriangle);
			g.FillPolygon(System.Drawing.Brushes.Black, RightTriangle);
			g.DrawLine(new System.Drawing.Pen(Color.Black, 2), new Point(LeftPos, NodeOver.Bounds.Top), new Point(RightPos, NodeOver.Bounds.Top));

		}//eom

		private void DrawLeafBottomPlaceholders(TreeNode NodeOver, TreeNode ParentDragDrop)
		{
			Graphics g = CreateGraphics();

			// MTC
//			int NodeOverImageWidth = ImageList.Images[NodeOver.ImageIndex].Size.Width + 8;
			// Once again, we are not dragging to node over, draw the placeholder using the ParentDragDrop bounds
			int LeftPos, RightPos;
			// MTC
			//if (ParentDragDrop != null)
			//    LeftPos = ParentDragDrop.Bounds.Left - (ImageList.Images[ParentDragDrop.ImageIndex].Size.Width + 8);
			//else
			LeftPos = NodeOver.Bounds.Left; // MTC -NodeOverImageWidth;
			RightPos = Width - 4;

			Point[] LeftTriangle = new Point[5]{
												   new Point(LeftPos, NodeOver.Bounds.Bottom - 4),
												   new Point(LeftPos, NodeOver.Bounds.Bottom + 4),
												   new Point(LeftPos + 4, NodeOver.Bounds.Bottom),
												   new Point(LeftPos + 4, NodeOver.Bounds.Bottom - 1),
												   new Point(LeftPos, NodeOver.Bounds.Bottom - 5)};

			Point[] RightTriangle = new Point[5]{
													new Point(RightPos, NodeOver.Bounds.Bottom - 4),
													new Point(RightPos, NodeOver.Bounds.Bottom + 4),
													new Point(RightPos - 4, NodeOver.Bounds.Bottom),
													new Point(RightPos - 4, NodeOver.Bounds.Bottom - 1),
													new Point(RightPos, NodeOver.Bounds.Bottom - 5)};


			g.FillPolygon(System.Drawing.Brushes.Black, LeftTriangle);
			g.FillPolygon(System.Drawing.Brushes.Black, RightTriangle);
			g.DrawLine(new System.Drawing.Pen(Color.Black, 2), new Point(LeftPos, NodeOver.Bounds.Bottom), new Point(RightPos, NodeOver.Bounds.Bottom));
		}//eom

		private void DrawFolderTopPlaceholders(TreeNode NodeOver)
		{
			Graphics g = CreateGraphics();
			// MTC
//			int NodeOverImageWidth = ImageList.Images[NodeOver.ImageIndex].Size.Width + 8;

			int LeftPos, RightPos;
			LeftPos = NodeOver.Bounds.Left; // MTC -NodeOverImageWidth;
			RightPos = Width - 4;

			Point[] LeftTriangle = new Point[5]{
												   new Point(LeftPos, NodeOver.Bounds.Top - 4),
												   new Point(LeftPos, NodeOver.Bounds.Top + 4),
												   new Point(LeftPos + 4, NodeOver.Bounds.Y),
												   new Point(LeftPos + 4, NodeOver.Bounds.Top - 1),
												   new Point(LeftPos, NodeOver.Bounds.Top - 5)};

			Point[] RightTriangle = new Point[5]{
													new Point(RightPos, NodeOver.Bounds.Top - 4),
													new Point(RightPos, NodeOver.Bounds.Top + 4),
													new Point(RightPos - 4, NodeOver.Bounds.Y),
													new Point(RightPos - 4, NodeOver.Bounds.Top - 1),
													new Point(RightPos, NodeOver.Bounds.Top - 5)};


			g.FillPolygon(System.Drawing.Brushes.Black, LeftTriangle);
			g.FillPolygon(System.Drawing.Brushes.Black, RightTriangle);
			g.DrawLine(new System.Drawing.Pen(Color.Black, 2), new Point(LeftPos, NodeOver.Bounds.Top), new Point(RightPos, NodeOver.Bounds.Top));

		}//eom
		private void DrawAddToFolderPlaceholder(TreeNode NodeOver)
		{
			Graphics g = CreateGraphics();
			int RightPos = NodeOver.Bounds.Right + 6;
			Point[] RightTriangle = new Point[5]{
													new Point(RightPos, NodeOver.Bounds.Y + (NodeOver.Bounds.Height / 2) + 4),
													new Point(RightPos, NodeOver.Bounds.Y + (NodeOver.Bounds.Height / 2) + 4),
													new Point(RightPos - 4, NodeOver.Bounds.Y + (NodeOver.Bounds.Height / 2)),
													new Point(RightPos - 4, NodeOver.Bounds.Y + (NodeOver.Bounds.Height / 2) - 1),
													new Point(RightPos, NodeOver.Bounds.Y + (NodeOver.Bounds.Height / 2) - 5)};

			this.Refresh();
			g.FillPolygon(System.Drawing.Brushes.Black, RightTriangle);
		}//eom
	}
}

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
Architect Interacx
United States United States
Blog: https://marcclifton.wordpress.com/
Home Page: http://www.marcclifton.com
Research: http://www.higherorderprogramming.com/
GitHub: https://github.com/cliftonm

All my life I have been passionate about architecture / software design, as this is the cornerstone to a maintainable and extensible application. As such, I have enjoyed exploring some crazy ideas and discovering that they are not so crazy after all. I also love writing about my ideas and seeing the community response. As a consultant, I've enjoyed working in a wide range of industries such as aerospace, boatyard management, remote sensing, emergency services / data management, and casino operations. I've done a variety of pro-bono work non-profit organizations related to nature conservancy, drug recovery and women's health.

Comments and Discussions