2007-11-20 20:26:30 +00:00

634 lines
21 KiB
C#

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();
}
}
}