using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; // For DragHelper using System.Reflection; using VEPROMS.CSLA.Library; namespace Volian.Controls.Library { public delegate void vlnTreeViewEvent(object sender, vlnTreeEventArgs args); public delegate bool vlnTreeViewBoolEvent(object sender, vlnTreeEventArgs args); public delegate TreeNode vlnTreeViewTreeNodeEvent(object sender, vlnTreeEventArgs args); //public delegate void vlnTreeViewDDEvent(object sender, System.Windows.Forms.DragEventArgs args); public partial class vlnTreeEventArgs { #region Business Methods private TreeNode _Node; public TreeNode Node { get { return _Node; } set { _Node = value; } } private TreeNode _Destination=null; public TreeNode Destination { get { return _Destination; } set { _Destination = value; } } private int _Index; public int Index { get { return _Index; } set { _Index = value; } } #endregion #region Factory Methods private vlnTreeEventArgs() { ;} public vlnTreeEventArgs(TreeNode node) { _Node = node; } public vlnTreeEventArgs(TreeNode node, TreeNode destination, int index) { _Node = node; _Destination = destination; _Index = index; } #endregion } public partial class vlnTreeView : TreeView { private static readonly log4net.ILog _MyLog = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); #region Events public event vlnTreeViewEvent NodeDragDrop; private void OnNodeDragDrop(object sender, vlnTreeEventArgs args) { if (NodeDragDrop != null) NodeDragDrop(sender, args); } public event vlnTreeViewEvent NodeMove; private void OnNodeMove(object sender, vlnTreeEventArgs args) { if (NodeMove != null) NodeMove(sender, args); } public event vlnTreeViewEvent NodeCopy; private void OnNodeCopy(object sender, vlnTreeEventArgs args) { if (NodeCopy != null) NodeCopy(sender, args); } public event vlnTreeViewBoolEvent NodeDelete; private bool OnNodeDelete(object sender, vlnTreeEventArgs args) { bool result = true; if (NodeDelete != null) result = NodeDelete(sender, args); return result; } public event vlnTreeViewTreeNodeEvent NodeNew; private TreeNode OnNodeNew(object sender, vlnTreeEventArgs args) { if (NodeNew != null) return NodeNew(sender, args); else return new TreeNode("New Item"); } public event vlnTreeViewEvent NodeProperties; private void OnNodeProperties(object sender, vlnTreeEventArgs args) { if (NodeProperties != null) NodeProperties(sender, args); } public event vlnTreeViewEvent NodeSelectionChange; private void OnNodeSelectionChange(object sender, vlnTreeEventArgs args) { if (NodeSelectionChange != null) NodeSelectionChange(sender, args); } #endregion #region Business Methods ImageList _dragImageList = new ImageList(); #endregion #region Constructors public vlnTreeView() { InitializeComponent(); this.AllowDrop = true; DragHelper.InitCommonControls(); this.ItemDrag += new ItemDragEventHandler(tv_ItemDrag); this.DragDrop += new DragEventHandler(tv_DragDrop); this.DragEnter += new DragEventHandler(tv_DragEnter); this.DragLeave += new EventHandler(tv_DragLeave); this.DragOver += new DragEventHandler(tv_DragOver); this.MouseDown += new MouseEventHandler(tv_MouseDown); base.AfterSelect += new TreeViewEventHandler(tv_AfterSelect); } private void tv_AfterSelect(object sender, TreeViewEventArgs e) { OnNodeSelectionChange(sender, new vlnTreeEventArgs(e.Node)); } void tv_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Right) { TreeNode tn = this.GetNodeAt(new Point(e.X, e.Y)); if (tn != null) { this.SelectedNode = tn; Application.DoEvents(); // Display Menu ToolStripMenuItem mi = new ToolStripMenuItem(); ContextMenu cm = new ContextMenu(); cm.MenuItems.Add("New...",new EventHandler(mi_Click)); cm.MenuItems.Add("Cut", new EventHandler(mi_Click)); cm.MenuItems.Add("Copy", new EventHandler(mi_Click)); cm.MenuItems.Add("Paste", new EventHandler(mi_Click)); cm.MenuItems.Add("Delete", new EventHandler(mi_Click)); cm.MenuItems.Add("Properties...", new EventHandler(mi_Click)); cm.Show(this, new Point(e.X, e.Y)); } } } void mi_Click(object sender, EventArgs e) { MenuItem mi = (MenuItem)sender; switch (mi.Text) { case "New..."://Add a new child node at the current location // Should Give the option to the controling code to Add a New Node SelectedNode.Expand(); TreeNode tn = OnNodeNew(this, new vlnTreeEventArgs(SelectedNode)); if (tn != null) { SelectedNode.Nodes.Add(tn); if (LabelEdit) tn.BeginEdit(); else SelectedNode = tn; } break; case "Cut"://Cut the selected node // Cut to Clipboard break; case "Copy"://Copy the selected node // Add to Clipboard break; case "Paste"://Paste the clipboard node // this is either a copy or a move depending upon where it came from // if it is from a cut then when the paste is done it should be set to a copy break; case "Delete"://Delete the selected node if(OnNodeDelete(this, new vlnTreeEventArgs(SelectedNode))) SelectedNode.Remove(); break; case "Properties..."://Show the properties for the selected node OnNodeProperties(this,new vlnTreeEventArgs(SelectedNode)); break; default: MessageBox.Show(string.Format("Unrecognized Menu Item '{0}'", mi.Text)); break; } } #endregion #region Cursor private bool SetupDragCursor(ImageList il, TreeNode tn) { // Reset image list used for drag image il.Images.Clear(); int howBig = tn.Bounds.Size.Width + this.Indent; if (howBig > 256) howBig = 256; il.ImageSize = new Size(howBig, tn.Bounds.Height); // Create new bitmap // This bitmap will contain the tree node image to be dragged Bitmap bmp = new Bitmap(tn.Bounds.Width + this.Indent, tn.Bounds.Height); // Get graphics from bitmap Graphics gfx = Graphics.FromImage(bmp); // Draw node icon into the bitmap if (this.ImageList != null) gfx.DrawImage(this.ImageList.Images[0], 0, 0); // Draw node label into bitmap gfx.DrawString(tn.Text, this.Font, new SolidBrush(this.ForeColor), // new SolidBrush(Color.Blue), (float)this.Indent, 1.0f); // Add bitmap to imagelist _dragImageList.Images.Add(bmp); // Get mouse position in client coordinates Point p = this.PointToClient(Control.MousePosition); // Compute delta between mouse position and node bounds int dx = p.X + this.Indent - tn.Bounds.Left; int dy = p.Y - tn.Bounds.Top; // Begin dragging image return DragHelper.ImageList_BeginDrag(_dragImageList.Handle, 0, dx, dy); } private void tv_ItemDrag(object sender, System.Windows.Forms.ItemDragEventArgs e) { // Get drag node and select it try { TreeNode dragNode = (TreeNode)e.Item; Type t = dragNode.GetType(); if(_MyLog.IsInfoEnabled)_MyLog.InfoFormat("Item Drag {0} - {1}", t.FullName, t.BaseType.FullName); Type t2 = Type.GetType(t.FullName); if(t2 != null) if(_MyLog.IsInfoEnabled)_MyLog.InfoFormat("Item Drag {0} - {1}", t2.FullName, t2.BaseType.FullName); this.SelectedNode = dragNode; if (SetupDragCursor(_dragImageList, dragNode)) { this.DoDragDrop(dragNode, DragDropEffects.Move | DragDropEffects.Copy);// Begin dragging DragHelper.ImageList_EndDrag();// End dragging image } } catch (Exception ex) { if(_MyLog.IsErrorEnabled)_MyLog.Error("tv_ItemDrag", ex); } } #endregion public enum DropPosition : int { Child = 0, Before = 1, After = 2 } private class DropLocation { #region Business Methods private TreeNode _dropNode; public TreeNode DropNode { get { return _dropNode; } set { _dropNode = value; } } private int _index; public int Index { get { return _index; } set { _index = value; } } private DropPosition _position; public DropPosition Position { get { return _position; } set { _position = value; } } DateTime _lastScroll; public DateTime LastScroll { get { return _lastScroll; } } private string _location = string.Empty; #endregion #region Constructors public DropLocation(TreeView tv, System.Windows.Forms.DragEventArgs e, DateTime lastScroll) { _lastScroll = lastScroll; _dropNode = tv.GetNodeAt(tv.PointToClient(new Point(e.X, e.Y))); if (_dropNode == null) return; int OffsetY = tv.PointToClient(Cursor.Position).Y - _dropNode.Bounds.Top; if (OffsetY < _dropNode.Bounds.Height / 3) // First Third - Before { _index = _dropNode.Index; _dropNode = _dropNode.Parent; _position = DropPosition.Before; _location = string.Format("Before1 {0}[{1}] y={2}", _dropNode.Text, _index, OffsetY); } else if ((OffsetY / 2) < _dropNode.Bounds.Height / 3) // Second Third - Child { _location = string.Format("Child {0} y={1}", _dropNode.Text, OffsetY); _position = DropPosition.Child; _index = 0; if (_dropNode.Parent == null) { if(_MyLog.IsInfoEnabled)_MyLog.Info("Root Node"); } } else // Last Third - After Now I need to check the X value { if (_dropNode.NextVisibleNode !=null && _dropNode.Nodes.Count > 0)// Has Children - Insert first child { // _dropNode = _dropNode.Nodes[0]; _index = 0; _position = DropPosition.Before; _location = string.Format("Before2 {0}[{1}] y={2}", _dropNode.Text, _index, OffsetY); } else // No Children - Insert Next at various levels { Point pt = tv.PointToClient(new Point(e.X, e.Y)); TreeNode nextParent = _dropNode.NextNode; if (nextParent != null) nextParent = nextParent.Parent; do { _index = _dropNode.Index; _dropNode = _dropNode.Parent; } while (pt.X < _dropNode.Bounds.X && _dropNode != nextParent); _location = string.Format("After {0}[{1}] y={2}", _dropNode.Text, _index, OffsetY); _position = DropPosition.After; } } LimitMoves(e); } public void LimitMoves(DragEventArgs e) { if ((e.KeyState & 8) == 0) { //TreeNode dragNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode"); //TreeNode dragNode = (TreeNode)e.Data.GetData("TreeTest.FolderTreeNode"); TreeNode dragNode = vlnTreeView.GetTreeNodeFromData(e.Data); switch (_position) { case DropPosition.Before: if (dragNode == _dropNode.Nodes[_index] || dragNode == _dropNode.Nodes[_index].PrevNode) { // if(_MyLog.IsInfoEnabled)_MyLog.InfoFormat("Before {0} {1} {2} {3} {4}", dragNode.Text ,_position.ToString() , _dropNode.Nodes[_index].Text // ,_dropNode.Nodes[_index].PrevNode,_dropNode.Nodes[_index].NextNode); _dropNode = null; } break; case DropPosition.Child: if (dragNode.Parent == _dropNode) { // if(_MyLog.IsInfoEnabled)_MyLog.InfoFormat("Child {0} {1} {2} {3} {4} {5}", dragNode.Text ,_position.ToString() , _dropNode.Nodes[_index].Text // ,_dropNode.Nodes[_index].PrevNode,_dropNode.Nodes[_index].NextNode,DateTime.Now); _dropNode = null; } break; case DropPosition.After: if (dragNode == _dropNode.Nodes[_index] || dragNode == _dropNode.Nodes[_index].NextNode) { // if(_MyLog.IsInfoEnabled)_MyLog.InfoFormat("After {0} {1} {2} {3} {4}", dragNode.Text ,_position.ToString() , _dropNode.Nodes[_index].Text // ,_dropNode.Nodes[_index].PrevNode,_dropNode.Nodes[_index].NextNode); _dropNode = null; } break; } } } #endregion public override string ToString() { return string.Format("{0}[{1}].{2}", _dropNode.Text, _index, _position.ToString()); } #region Drawing private void TreeNodeTriangle(Graphics g) { Rectangle r = _dropNode.Bounds; int RightPos = r.Right + 6; Point[] RightTriangle = new Point[]{ new Point(RightPos, r.Y ), new Point(RightPos - (r.Height / 2), r.Y + (r.Height / 2)), new Point(RightPos, r.Y + r.Height), new Point(RightPos - (r.Height / 3), r.Y + (r.Height / 2)) }; g.FillPolygon(System.Drawing.Brushes.Black, RightTriangle); } private void InsertPointer(TreeNode tn, Graphics g) { TreeView tv = _dropNode.TreeView; Rectangle r2 = _dropNode.Nodes[_index].Bounds; Rectangle r3 = tn.Bounds; int y = (_position == DropPosition.Before ? r2.Y : r3.Bottom); int x = r2.Left; if (y == 0) { return; } // if(_MyLog.IsInfoEnabled)_MyLog.InfoFormat("Line at {0} Node {1}[{2}] {3}", _location, _dropNode.Text, _index, _position.ToString()); Color lc = (_position == DropPosition.After ? Color.Red : Color.Blue); Brush lb = (_position == DropPosition.After ? Brushes.Red : Brushes.Blue); Point[] RightTriangle; if (_position == DropPosition.After) { RightTriangle = new Point[]{ new Point(x, y ), new Point(x+4, y+4), new Point(x+8, y)}; } else { RightTriangle = new Point[]{ new Point(x, y), new Point(x+4, y-4), new Point(x+8, y)}; } g.DrawLine(new System.Drawing.Pen(lc, 2), new Point(r2.Left, y), new Point(tv.Width - 8, y)); g.FillPolygon(lb, RightTriangle); } public void ShowLocation(System.Windows.Forms.DragEventArgs e, bool ScrollOnly) { //if (e.Effect == DragDropEffects.None) return; if (_dropNode != null) { // if(_MyLog.IsInfoEnabled)_MyLog.InfoFormat("ShowLocation1 {0} {1}", e.Effect.ToString(), DateTime.Now.Millisecond); DragHelper.ImageList_DragShowNolock(false); TreeView tv = _dropNode.TreeView; TreeNode tmp = tv.GetNodeAt(tv.PointToClient(new Point(e.X, e.Y))); // if (!ScrollOnly) // { if (ScrollTreeView(tmp) || !ScrollOnly) { if(_MyLog.IsInfoEnabled)_MyLog.InfoFormat("ShowLocation2 {0} {1}", e.Effect.ToString(), DateTime.Now.Millisecond); tv.Refresh(); if (e.Effect != DragDropEffects.None) { //tv.SelectedNode = dropNode; Graphics g = tv.CreateGraphics(); TreeNodeTriangle(g); if (_position != DropPosition.Child)InsertPointer(tmp, g); } } // } // else ScrollTreeView(tmp); DragHelper.ImageList_DragShowNolock(true); } } #endregion public void ShowLocation() { if(_MyLog.IsInfoEnabled)_MyLog.InfoFormat("{0}[{1}] {2}", _dropNode.Text, _index, _position.ToString()); } #region AutoScroll private bool ScrollTreeView(TreeNode tn) { bool retval = false; TimeSpan ts = new TimeSpan(DateTime.Now.Ticks - _lastScroll.Ticks); if (ts.Milliseconds > 100)// This controls the scroll speed { int top = tn.Bounds.Top; _lastScroll = DateTime.Now; if (tn.TreeView.ClientSize.Height < tn.Bounds.Bottom) tn.EnsureVisible();// Make sure that the current node is visible if (tn.NextVisibleNode != null && tn.TreeView.ClientSize.Height < tn.NextVisibleNode.Bounds.Bottom) tn.NextVisibleNode.EnsureVisible();// Make sure that the next node is visible else if (tn.PrevVisibleNode != null && tn.PrevVisibleNode.PrevVisibleNode != null && tn.PrevVisibleNode.PrevVisibleNode.IsVisible == false) tn.PrevVisibleNode.PrevVisibleNode.EnsureVisible();// Make sure that the previous node is visible } else if (tn.PrevVisibleNode != null && tn.PrevVisibleNode.IsVisible == false) tn.PrevVisibleNode.EnsureVisible();// Make sure that the previous node is visible retval = (top != tn.Bounds.Top); // if (retval) if(_MyLog.IsInfoEnabled)_MyLog.Info("Scroll"); } return retval; } #endregion public bool Equals(DropLocation dl) { return (dl != null && _lastScroll.Equals(dl.LastScroll) && _dropNode.Equals(dl.DropNode) && _position.Equals(dl.Position)); } } private DropLocation _LastDropLocation = null; private void tv_DragOver(object sender, System.Windows.Forms.DragEventArgs e) { try { TreeNode dragNode = GetTreeNodeFromData(e.Data); // Compute drag position and move image Point formP = this.FindForm().PointToClient(new Point(e.X, e.Y)); DragHelper.ImageList_DragMove(formP.X - this.Left, formP.Y - this.Top); DropLocation dl = new DropLocation(this, e, _LastDropLocation == null ? DateTime.Now : _LastDropLocation.LastScroll); string s = string.Empty; if (dl.DropNode == null) { e.Effect = DragDropEffects.None; } else { DragDropEffects ee = e.Effect; if ((e.KeyState & 8) == 8) ee = DragDropEffects.Copy; // Copy it else ee = DragDropEffects.Move; // Move it if (IsChild(dragNode, dl.DropNode)) ee = DragDropEffects.None; // Don't copy or move to a child node if (e.Effect != ee) e.Effect = ee; dl.ShowLocation(e, dl.Equals(_LastDropLocation)); _LastDropLocation = dl; } } catch (Exception ex) { if(_MyLog.IsErrorEnabled)_MyLog.Error("tv_DragOver", ex); } } private static TreeNode GetTreeNodeFromData(IDataObject datobj) { foreach (string s in datobj.GetFormats()) { try { return (TreeNode)datobj.GetData(s); } catch (Exception ex) { if(_MyLog.IsErrorEnabled)_MyLog.Error("GetTreeNodeFromData", ex); } } return null; } private void tv_DragDrop(object sender, System.Windows.Forms.DragEventArgs e) { try { TreeNode dragNode = GetTreeNodeFromData(e.Data); DragHelper.ImageList_DragLeave(this.Handle); int index = _LastDropLocation.Index + (_LastDropLocation.Position == DropPosition.After ? 1 : 0); if (dragNode.Parent == _LastDropLocation.DropNode && dragNode.Index <= _LastDropLocation.Index) index--; if (e.Effect == DragDropEffects.Move)// If Move Remove drag node from parent dragNode.Remove(); else dragNode = Clone(dragNode); _LastDropLocation.DropNode.Nodes.Insert(index, dragNode); this.SelectedNode = dragNode; OnNodeDragDrop(sender, new vlnTreeEventArgs(dragNode, _LastDropLocation.DropNode,index)); } catch (Exception ex) { if(_MyLog.IsErrorEnabled)_MyLog.Error("tv_DragDrop", ex); } } private void DumpMembers(object o) { Type t = o.GetType(); if(_MyLog.IsInfoEnabled)_MyLog.InfoFormat("\r\n\r\nMembers for type {0}", t.ToString()); MemberInfo[] mis = t.GetMembers(); int i = 0; foreach (MemberInfo mi in mis) { i++; try { if(mi.MemberType != MemberTypes.Method) if(_MyLog.IsInfoEnabled)_MyLog.InfoFormat("{0} {1} {2}", i, mi.Name, mi.MemberType); // if (fi.Name == "TreeView") // fi.SetValue(o, null); } catch (Exception ex) { if(_MyLog.IsErrorEnabled)_MyLog.Error("DumpMembers", ex); } } } private TreeNode Clone(TreeNode tn) { TreeNode tmp = (TreeNode)tn.Clone(); ExpandMatch(tmp,tn); return tmp; } private void tv_DragDropOld(object sender, System.Windows.Forms.DragEventArgs e) { TreeNode dragNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");// Get the drag node DragHelper.ImageList_DragLeave(this.Handle); TreeNode cloneNode = (TreeNode)dragNode.Clone();// copy the source node ExpandMatch(cloneNode, dragNode); _LastDropLocation.DropNode.Nodes.Insert(_LastDropLocation.Index + (_LastDropLocation.Position == DropPosition.After ? 1 : 0), cloneNode); if (e.Effect == DragDropEffects.Move)// If Move Remove drag node from parent dragNode.Remove(); this.SelectedNode = cloneNode; } private void tv_DragEnter(object sender, System.Windows.Forms.DragEventArgs e) { DragHelper.ImageList_DragEnter(this.Handle, e.X - this.Left, e.Y - this.Top); } private void tv_DragLeave(object sender, System.EventArgs e) { DragHelper.ImageList_DragLeave(this.Handle); this.Refresh(); } private void ExpandMatch(TreeNode tn1, TreeNode tn2) { if (tn2.IsExpanded) tn1.Expand(); foreach (TreeNode tc in tn2.Nodes) ExpandMatch(tn1.Nodes[tc.Index], tc); } private bool IsChild(TreeNode parent, TreeNode child) { if (parent.Equals(child)) return true;// Check against self foreach (TreeNode tc in parent.Nodes) if (IsChild(tc, child)) return true;//Check all children return false;// Must not be a child at this level } } public class DragHelper { [DllImport("comctl32.dll")] public static extern bool InitCommonControls(); [DllImport("comctl32.dll", CharSet = CharSet.Auto)] public static extern bool ImageList_BeginDrag(IntPtr himlTrack, int iTrack, int dxHotspot, int dyHotspot); [DllImport("comctl32.dll", CharSet = CharSet.Auto)] public static extern bool ImageList_DragMove(int x, int y); [DllImport("comctl32.dll", CharSet = CharSet.Auto)] public static extern void ImageList_EndDrag(); [DllImport("comctl32.dll", CharSet = CharSet.Auto)] public static extern bool ImageList_DragEnter(IntPtr hwndLock, int x, int y); [DllImport("comctl32.dll", CharSet = CharSet.Auto)] public static extern bool ImageList_DragLeave(IntPtr hwndLock); [DllImport("comctl32.dll", CharSet = CharSet.Auto)] public static extern bool ImageList_DragShowNolock(bool fShow); static DragHelper() { InitCommonControls(); } } }