using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using log4net;
using ValidationAspects;
namespace DelftTools.Controls.Swf.TreeViewControls
{
///
/// Summary description for Tree.
///
public class TreeView : System.Windows.Forms.TreeView, ITreeView
{
public event EventHandler SelectedNodeChanged;
public event Action BeforeWaitUntilAllEventsAreProcessed;
private const int TV_FIRST = 0x1100;
private const int TVM_SETBKCOLOR = TV_FIRST + 29;
private const int TVM_SETEXTENDEDSTYLE = TV_FIRST + 44;
private const int TVS_EX_DOUBLEBUFFER = 0x0004;
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private readonly TreeNodeList nodes;
private bool isUpdateSuspended;
private TreeViewController controller;
private int dropAtLocation;
private Point lastDragOverPoint;
private PlaceholderLocation lastPlaceholderLocation;
private ITreeNode nodeDropTarget;
private ITreeNode lastPlaceholderNode;
private Graphics placeHolderGraphics;
private bool bufferedNodeExpanded;
///
/// TreeView based on system windows forms component.
///
public TreeView()
{
controller = new TreeViewController(this);
ImageList = new ImageList
{
ColorDepth = ColorDepth.Depth32Bit
};
nodes = new TreeNodeList(base.Nodes);
DrawMode = TreeViewDrawMode.OwnerDrawAll;
LabelEdit = true;
HideSelection = false;
BeforeLabelEdit += TreeViewBeforeLabelEdit;
AfterCheck += TreeViewAfterCheck;
DragDrop += TreeViewDragDrop;
DragOver += TreeViewDragOver;
ItemDrag += TreeViewItemDrag;
DragLeave += TreeViewDragLeave;
MouseDown += TreeViewMouseDown;
MouseClick += TreeViewClick;
SelectNodeOnRightMouseClick = true; // default behaviour
// http://dev.nomad-net.info/articles/double-buffered-tree-and-list-views
// Enable default double buffering processing
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);
//SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
// Disable default CommCtrl painting on non-Vista systems
if (!NativeInterop.IsWinVista)
{
SetStyle(ControlStyles.UserPaint, true);
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Func OnProcessCmdKey { get; set; }
public bool SelectNodeOnRightMouseClick { get; set; }
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new ITreeNode SelectedNode
{
get
{
return ((ITreeView) this).SelectedNode;
}
set
{
((ITreeView) this).SelectedNode = value;
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public object Data
{
get
{
if (IsDisposed || controller == null)
{
return null;
}
return controller.Data;
}
set
{
if (IsDisposed || controller == null)
{
return;
}
controller.Data = value;
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Image Image { get; set; }
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IEnumerable AllLoadedNodes
{
get
{
return Enumerable.SelectMany(Nodes, GetAllLoadedNodes);
}
}
///
/// The nodepresenters handle building logic for dataobjects added to the tree.
///
public ICollection NodePresenters
{
get
{
return controller.NodePresenters;
}
}
///
/// List of all nodes that belong to the tree.
///
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new IList Nodes
{
get
{
return nodes;
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
ITreeNode ITreeView.SelectedNode
{
get
{
return (TreeNode) base.SelectedNode;
}
set
{
base.SelectedNode = (System.Windows.Forms.TreeNode) value;
if (SelectedNodeChanged != null)
{
SelectedNodeChanged(this, new EventArgs());
}
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ViewInfo ViewInfo { get; set; }
public bool IsUpdateSuspended
{
get
{
return isUpdateSuspended;
}
}
public bool SelectedNodeCanDelete()
{
return controller.CanDeleteNode(SelectedNode);
}
public bool SelectedNodeCanRename()
{
return controller.CanRenameNode(SelectedNode);
}
public new void CollapseAll()
{
foreach (var node in Nodes)
{
CollapseAll(node);
}
}
public void CollapseAll(ITreeNode node)
{
node.Collapse();
foreach (var childNode in node.Nodes)
{
CollapseAll(childNode);
}
}
public new void ExpandAll()
{
foreach (var node in Nodes)
{
ExpandAll(node);
}
}
public void ExpandAll(ITreeNode node)
{
node.Expand();
foreach (var childNode in node.Nodes)
{
ExpandAll(childNode);
}
}
///
/// Checks if label of current node can be edited and starts edit mode if this is the case.
///
public void StartLabelEdit()
{
if (!SelectedNodeCanRename())
{
return;
}
SelectedNode.BeginEdit();
}
public void EnsureVisible(object item) {}
public ITreeNodePresenter GetTreeViewNodePresenter([NotNull] object nodeData, ITreeNode node)
{
return controller.ResolveNodePresenterForData(nodeData, node == null ? null : node.Parent);
}
///
/// Create a new Node for this tree with default properties
///
///
public ITreeNode NewNode()
{
return new TreeNode(this);
}
public ITreeNode AddNewNode(ITreeNode parentNode, object nodeData, int insertionIndex = -1)
{
return controller.AddNewNode(parentNode, nodeData, insertionIndex);
}
///
/// Search all the nodes in the treeView, for a node with a matching tag.
///
///
/// if a node is not loaded, don't do so
///
public ITreeNode GetNodeByTag(object nodeData, bool skipUnLoadedNodes = true)
{
if (Nodes.Count > 0)
{
return GetNodeByTag(Nodes[0], nodeData, skipUnLoadedNodes);
}
return null;
}
public void UpdateNode(ITreeNode treeNode)
{
if (controller != null)
{
controller.UpdateNode(treeNode, treeNode.Tag);
}
}
public void RefreshChildNodes(ITreeNode treeNode)
{
if (controller != null)
{
controller.RefreshChildNodes(treeNode);
}
}
public void DeleteNodeData()
{
if (!SelectedNodeCanDelete())
{
MessageBox.Show("The selected item cannot be removed", "Confirm", MessageBoxButtons.OK);
return;
}
var message = string.Format("Are you sure you want to delete the following item: {0}", SelectedNode.Text);
if (MessageBox.Show(message, "Confirm", MessageBoxButtons.OKCancel) != DialogResult.OK)
{
return;
}
var presenter = GetTreeViewNodePresenter(SelectedNode.Tag, SelectedNode);
presenter.RemoveNodeData(SelectedNode.Parent.Tag, SelectedNode.Tag);
SelectedNode = (SelectedNode != null) ? SelectedNode.PreviousVisibleNode : Nodes.FirstOrDefault();
}
public override void Refresh()
{
if (Nodes.Count == 0 || controller == null)
{
return;
}
BeginUpdate();
try
{
var selectedNodePath = SelectedNode == null ? string.Empty : SelectedNode.FullPath;
// sometimes collection results in full refresh, as a result root node does not get expanded
bool rootNodeIsLoaded = Nodes[0].IsLoaded;
//don't use allNodes since update of childnodes is done by parent.
foreach (TreeNode node in Nodes)
{
controller.UpdateNode(node);
}
if (!string.IsNullOrEmpty(selectedNodePath))
{
var nodeToSelect =
Nodes.SelectMany(GetAllLoadedNodes).FirstOrDefault(n => n.FullPath == selectedNodePath);
if (nodeToSelect != null)
{
base.SelectedNode = (System.Windows.Forms.TreeNode) nodeToSelect;
}
}
// expand root node if children were added
if (!rootNodeIsLoaded && Nodes.Count > 0 && Nodes[0].Nodes.Count > 0)
{
Nodes[0].Expand();
}
Sort();
}
finally
{
EndUpdate();
}
}
public new void BeginUpdate()
{
if (!isUpdateSuspended)
{
isUpdateSuspended = true;
base.BeginUpdate();
}
}
public new void EndUpdate()
{
if (isUpdateSuspended)
{
isUpdateSuspended = false;
base.EndUpdate();
}
}
public IEnumerable GetAllLoadedNodes(ITreeNode currentNode)
{
var allChildNodes = new[]
{
currentNode
};
return (currentNode.IsLoaded)
? allChildNodes.Concat(currentNode.Nodes.SelectMany(GetAllLoadedNodes))
: allChildNodes;
}
public void WaitUntilAllEventsAreProcessed()
{
if (BeforeWaitUntilAllEventsAreProcessed != null)
{
BeforeWaitUntilAllEventsAreProcessed();
}
}
protected override void OnPaint(PaintEventArgs e)
{
if (GetStyle(ControlStyles.UserPaint))
{
Message m = new Message();
m.HWnd = Handle;
m.Msg = NativeInterop.WM_PRINTCLIENT;
m.WParam = e.Graphics.GetHdc();
m.LParam = (IntPtr) NativeInterop.PRF_CLIENT;
DefWndProc(ref m);
e.Graphics.ReleaseHdc(m.WParam);
}
base.OnPaint(e);
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
UpdateExtendedStyles();
if (!NativeInterop.IsWinXP || !Application.RenderWithVisualStyles)
{
NativeInterop.SendMessage(Handle, TVM_SETBKCOLOR, IntPtr.Zero, (IntPtr) ColorTranslator.ToWin32(BackColor));
}
controller.OnTreeViewHandleCreated();
}
///
/// Custom drawing.
///
///
protected override void OnDrawNode(DrawTreeNodeEventArgs e)
{
e.DrawDefault = false;
if (isUpdateSuspended)
{
return;
}
var selected = (e.State & TreeNodeStates.Selected) == TreeNodeStates.Selected;
((TreeNode) e.Node).DrawNode(e.Graphics, selected);
}
protected override void Dispose(bool disposing)
{
if (disposing && controller != null)
{
controller = null;
}
if (IsHandleCreated)
{
base.Dispose(disposing);
}
}
protected override void OnAfterLabelEdit(NodeLabelEditEventArgs e)
{
BeginUpdate();
try
{
var treeNode = e.Node as TreeNode;
if (treeNode != null)
{
//check e.Label for null, this indicates node edit was cancelled.
if (treeNode.IsUpdating || treeNode.Tag == null || e.Label == null)
{
return;
}
var nodePresenter = GetTreeViewNodePresenter(treeNode.Tag, treeNode);
if (nodePresenter == null)
{
return;
}
if (nodePresenter.CanRenameNodeTo(treeNode, e.Label))
{
// First set text to prevent eventHandlers from working with a node that
// is not yet updated (with the previous text)
// Setting the node text is done after this method is completed (and e.cancel is false).
treeNode.Text = e.Label;
nodePresenter.OnNodeRenamed(treeNode.Tag, e.Label);
}
else
{
e.CancelEdit = true;
return;
}
}
base.OnAfterLabelEdit(e);
BeginInvoke(new Action(Sort));
}
finally
{
EndUpdate();
}
}
///
/// Do not expand/collapse on doubleclick
///
///
protected override void DefWndProc(ref Message m)
{
const int WM_LBUTTONDBLCLK = 515;
const int WM_ERASEBKGND = 0x0014;
if (m.Msg == WM_LBUTTONDBLCLK)
{
//dont handle doubleclick in base class
}
else if (m.Msg == WM_ERASEBKGND)
{
return; //don't clear background (only causes flicker)
}
else
{
//log.DebugFormat(m.ToString());
base.DefWndProc(ref m);
}
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (Nodes.Count == 0)
{
if (OnProcessCmdKey != null)
{
return OnProcessCmdKey(keyData);
}
return base.ProcessCmdKey(ref msg, keyData);
}
if (SelectedNode == null)
{
SelectedNode = Nodes[0];
}
if (SelectedNode.IsEditing)
{
return base.ProcessCmdKey(ref msg, keyData);
}
switch (keyData)
{
case Keys.F2:
//start editing the label
StartLabelEdit();
return true;
case Keys.Apps:
if (SelectedNode != null && ContextMenu != null)
{
Point location = SelectedNode.Bounds.Location;
location.Offset(0, SelectedNode.Bounds.Height);
SelectedNode.ShowContextMenu(location);
}
return true;
// TODO: Customize completely within OnProcessCmdKey
case Keys.Delete:
if (OnProcessCmdKey == null && SelectedNode != null && SelectedNode.Tag != null)
{
DeleteNodeData();
return true;
}
break;
case Keys.F5:
//refresh treeview
Refresh();
return true;
case Keys.Space:
ITreeNode node = (TreeNode) SelectedNode;
if (node != null && node.ShowCheckBox)
{
//Toggle checked state
node.Checked = !node.Checked;
}
return true;
case Keys.Up:
{
//hack: we manually handle this because ms doesnot fire selectednodechanged
// Select the previous node
var treeNode = SelectedNode.PreviousVisibleNode;
if (treeNode != null)
{
SelectedNode = treeNode;
}
}
return true;
case Keys.Down:
{
//hack: we manually handle this because ms doesnot fire selectednodechanged
// Select the next node
var treeNode = SelectedNode.NextVisibleNode;
if (treeNode != null)
{
SelectedNode = treeNode;
}
}
return true;
case Keys.Right:
{
//hack: we manually handle this because ms doesnot fire selectednodechanged
if (SelectedNode.Nodes.Count > 0)
{
if (!SelectedNode.IsExpanded)
{
SelectedNode.Expand();
}
else
{
SelectedNode = SelectedNode.Nodes[0];
}
}
}
return true;
case Keys.Left:
{
//hack: we manually handle this because ms doesnot fire selectednodechanged
if (SelectedNode.IsExpanded)
{
SelectedNode.Collapse();
}
else
{
if (SelectedNode.Parent != null)
{
SelectedNode = SelectedNode.Parent;
}
}
}
return true;
case Keys.Home:
{
//hack: we manually handle this because ms doesnot fire selectednodechanged
SelectedNode = Nodes.First();
}
return true;
case Keys.End:
{
//hack: we manually handle this because ms doesnot fire selectednodechanged
SelectedNode = GetLastNode(Nodes.Last());
}
return true;
}
if (keyData == (Keys.Control | Keys.Shift | Keys.Right))
{
ExpandAll(SelectedNode);
SelectedNode.EnsureVisible();
return true;
}
if (keyData == (Keys.Control | Keys.Shift | Keys.Left))
{
CollapseAll(SelectedNode);
SelectedNode.EnsureVisible();
return true;
}
if (OnProcessCmdKey != null)
{
if (OnProcessCmdKey(keyData))
{
return true;
}
}
return base.ProcessCmdKey(ref msg, keyData);
}
private void UpdateExtendedStyles()
{
int Style = 0;
if (DoubleBuffered)
{
Style |= TVS_EX_DOUBLEBUFFER;
}
if (Style != 0)
{
NativeInterop.SendMessage(Handle, TVM_SETEXTENDEDSTYLE, (IntPtr) TVS_EX_DOUBLEBUFFER, (IntPtr) Style);
}
}
private new void Sort()
{
if (TreeViewNodeSorter == null)
{
return;
}
var oldSelectedNode = SelectedNode;
SuspendLayout();
base.Sort();
ResumeLayout();
SelectedNode = oldSelectedNode;
}
private ITreeNode GetLastNode(ITreeNode treeNode)
{
if (treeNode.IsLoaded && treeNode.Nodes.Count > 0)
{
return GetLastNode(treeNode.Nodes.Last());
}
return treeNode;
}
private void TreeViewMouseDown(object sender, MouseEventArgs e)
{
var treeView = sender as TreeView;
if (treeView == null)
{
return;
}
var treeNode = (ITreeNode) GetNodeAt(e.X, e.Y);
if (treeNode == null)
{
return;
}
// buffer expanded state for use in TreeViewClick
bufferedNodeExpanded = treeNode.IsExpanded;
}
private void TreeViewClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right && !SelectNodeOnRightMouseClick)
{
return;
}
var point = PointToClient(MousePosition);
var node = (TreeNode) GetNodeAt(point);
if (node == null)
{
return;
}
SelectedNode = node;
if (node.IsOnCheckBox(point))
{
node.Checked = !node.Checked;
}
if (node.IsOnExpandButton(point))
{
// Use buffered expanded state because it gets changed just
// before Click event is handled
if (bufferedNodeExpanded)
{
node.Collapse(true);
}
else
{
node.Expand();
}
}
}
private void TreeViewAfterCheck(object sender, TreeViewEventArgs e)
{
controller.OnNodeChecked((ITreeNode) e.Node);
}
///
/// Check if node label can be edited
///
///
///
private void TreeViewBeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
{
if (!controller.CanRenameNode(e.Node as ITreeNode))
{
e.CancelEdit = true;
}
}
private static ITreeNode GetNodeByTag(ITreeNode rootNode, object tag, bool skipUnLoadedNodes)
{
if (Equals(rootNode.Tag, tag))
{
return rootNode;
}
return rootNode.IsLoaded || !skipUnLoadedNodes
? rootNode.Nodes.Select(n => GetNodeByTag(n, tag, skipUnLoadedNodes)).FirstOrDefault(node => node != null)
: null;
}
///
/// Use Nodepresenter to see wether the current node can be dragged
/// set the allowed effects based on the possible dragoperation.
///
///
///
private void TreeViewItemDrag(object sender, ItemDragEventArgs e)
{
// gather allowed effects for the current item.
var sourceNode = (TreeNode) e.Item;
ITreeNodePresenter presenter = GetTreeViewNodePresenter(sourceNode.Tag, sourceNode);
if (presenter == null)
{
return;
}
DragOperations dragOperation = presenter.CanDrag(sourceNode.Tag);
DragDropEffects effects = WindowsFormsHelper.ToDragDropEffects(dragOperation);
if (effects == DragDropEffects.None)
{
return;
}
// store both treenode and tag of treenode in dataobject
// to be dragged.
IDataObject dataObject = new DataObject();
dataObject.SetData(typeof(TreeNode), sourceNode);
if (sourceNode.Tag != null)
{
dataObject.SetData(sourceNode.Tag.GetType(), sourceNode.Tag);
}
DoDragDrop(dataObject, effects);
}
///
/// Update the UI of the treeview for the current action
///
///
///
private void TreeViewDragOver(object sender, DragEventArgs e)
{
if (lastDragOverPoint.X == e.X && lastDragOverPoint.Y == e.Y)
{
return;
}
lastDragOverPoint = new Point(e.X, e.Y);
var point = PointToClient(lastDragOverPoint);
var nodeOver = GetNodeAt(point) as TreeNode;
var nodeDragging = e.Data.GetData(typeof(TreeNode)) as TreeNode;
if (nodeOver == null || nodeDragging == null || nodeOver == nodeDragging || nodeOver.IsChildOf(nodeDragging))
{
ClearPlaceHolders();
return;
}
ScrollIntoView(point, nodeOver, sender);
PlaceholderLocation placeholderLocation = GetPlaceHoldersLocation(nodeDragging, nodeOver, e);
if (null == nodeDropTarget)
{
return;
}
ITreeNodePresenter presenter = GetTreeViewNodePresenter(nodeDropTarget.Tag, nodeDropTarget);
DragOperations allowedOperations = presenter.CanDrop(nodeDragging.Tag, nodeDragging, nodeDropTarget,
WindowsFormsHelper.ToDragOperation(e.AllowedEffect));
e.Effect = WindowsFormsHelper.ToDragDropEffects(allowedOperations);
if (PlaceholderLocation.None == placeholderLocation)
{
return;
}
// determine if the node can be dropped based on the allowed operations.
// A node can also be a valid drop traget if it is the root item! (nodeDragging.Parent == null)
// This applies in any case to the MapLegendView
if (DragOperations.None != presenter.CanDrop(nodeDragging.Tag, nodeDragging, nodeDropTarget, allowedOperations))
{
DrawPlaceholder(nodeOver, placeholderLocation);
}
else
{
ClearPlaceHolders();
e.Effect = DragDropEffects.None;
}
}
private PlaceholderLocation GetPlaceHoldersLocation(ITreeNode nodeDragging, ITreeNode nodeOver, DragEventArgs e)
{
PlaceholderLocation loc = PlaceholderLocation.None;
int offsetY = PointToClient(Cursor.Position).Y - nodeOver.Bounds.Top;
if (offsetY < (nodeOver.Bounds.Height/3) && nodeDragging.NextNode != nodeOver)
{
if (nodeOver.Parent != null)
{
ITreeNodePresenter parentNodePresenter = GetTreeViewNodePresenter(nodeOver.Parent.Tag, nodeOver.Parent);
if (parentNodePresenter.CanInsert(nodeDragging.Tag, nodeDragging, nodeOver))
{
nodeDropTarget = nodeOver.Parent;
dropAtLocation = nodeOver.Parent == null ? 0 : nodeOver.Parent.Nodes.IndexOf(nodeOver);
loc = PlaceholderLocation.Top;
}
else
{
nodeDropTarget = nodeOver;
dropAtLocation = 0;
loc = PlaceholderLocation.Middle;
}
}
else
{
nodeDropTarget = nodeOver;
dropAtLocation = 0;
loc = PlaceholderLocation.Middle;
}
}
else if ((nodeOver.Parent != null) && (offsetY > (nodeOver.Bounds.Height - (nodeOver.Bounds.Height/3))) &&
nodeDragging.PreviousNode != nodeOver)
{
ITreeNodePresenter nodePresenter = GetTreeViewNodePresenter(nodeOver.Parent.Tag, nodeOver.Parent);
if (nodePresenter.CanInsert(nodeDragging.Tag, nodeDragging, nodeOver))
{
nodeDropTarget = nodeOver.Parent;
dropAtLocation = nodeOver.Parent == null
? 0
: nodeOver.Parent.Nodes.IndexOf(nodeOver) + 1;
loc = PlaceholderLocation.Bottom;
}
else
{
nodeDropTarget = nodeOver;
dropAtLocation = 0;
loc = PlaceholderLocation.Middle;
}
}
else if (nodeDragging != nodeOver && offsetY < (nodeOver.Bounds.Height - (nodeOver.Bounds.Height/4))
&& offsetY > (nodeOver.Bounds.Height/4))
{
nodeDropTarget = nodeOver;
dropAtLocation = 0;
loc = PlaceholderLocation.Middle;
}
if (loc == PlaceholderLocation.None ||
(loc == PlaceholderLocation.Middle && nodeDropTarget == nodeDragging.Parent))
{
ClearPlaceHolders();
e.Effect = DragDropEffects.None;
}
return loc;
}
///
/// handle scrolling
/// http://www.syncfusion.com/FAQ/windowsforms/faq_c91c.aspx
///
///
///
///
private static void ScrollIntoView(Point point, ITreeNode nodeOver, object sender)
{
var treeView = sender as TreeView;
if (treeView == null)
{
return;
}
int delta = treeView.Height - point.Y;
if ((delta < treeView.Height/2) && (delta > 0))
{
if (nodeOver.NextVisibleNode != null)
{
nodeOver.NextVisibleNode.ScrollTo();
}
}
if ((delta > treeView.Height/2) && (delta < treeView.Height))
{
if (nodeOver.PreviousVisibleNode != null)
{
nodeOver.PreviousVisibleNode.ScrollTo();
}
}
}
///
/// Event handler for drag drop. Inserts Node at droplocation when node is being moved
/// When node is being copied from other location Dropped should be handled externally
/// Triggers afterdrop event.
///
///
///
private void TreeViewDragDrop(object sender, DragEventArgs e)
{
ClearPlaceHolders();
Point point = PointToClient(new Point(e.X, e.Y));
var nodeOver = GetNodeAt(point) as TreeNode;
var nodeDragging = e.Data.GetData(typeof(TreeNode)) as TreeNode;
if (nodeOver == null || nodeDragging == null)
{
//this handler only deals with nodes.
ClearPlaceHolders();
if (nodeOver != null)
{
e.Effect = DragDropEffects.All;
}
return;
}
if (e.Effect.Equals(DragDropEffects.Move) && nodeDragging.Parent == nodeDropTarget &&
nodeOver.Index > nodeDragging.Index)
{
// src item higher up in the tree is removed. This means all indices shift by one.
if (dropAtLocation > 0)
{
dropAtLocation--;
}
}
//dropAtLocation should never be < 0
if (dropAtLocation < 0)
{
dropAtLocation = 0;
}
var parentNode = nodeDragging.Parent;
//ensure droptarget is loaded.
Trace.Assert(nodeDropTarget.Nodes != null);
try
{
controller.OnDragDrop(nodeDragging, parentNode, nodeDropTarget, WindowsFormsHelper.ToDragOperation(e.Effect), dropAtLocation);
}
catch (Exception ex)
{
log.Error("Error during drag/drop : " + ex.Message);
}
}
private void TreeViewDragLeave(object sender, EventArgs e)
{
ClearPlaceHolders();
}
///
/// Indicate the dragged node can be inserted either above or below
/// another node
///
private void DrawPlaceholder(ITreeNode node, PlaceholderLocation location)
{
if (lastPlaceholderNode == node && lastPlaceholderLocation == location)
{
return;
}
ClearPlaceHolders();
lastPlaceholderNode = node;
lastPlaceholderLocation = location;
placeHolderGraphics = CreateGraphics();
node.DrawPlaceHolder(location, CreateGraphics());
}
///
/// If placeholders were drawn before, refresh the tree
///
private void ClearPlaceHolders()
{
if (placeHolderGraphics != null)
{
lastPlaceholderNode = null;
base.Refresh();
placeHolderGraphics.Dispose();
placeHolderGraphics = null;
}
}
internal class NativeInterop
{
public const int WM_PRINTCLIENT = 0x0318;
public const int PRF_CLIENT = 0x00000004;
public static bool IsWinXP
{
get
{
OperatingSystem OS = Environment.OSVersion;
return (OS.Platform == PlatformID.Win32NT) &&
((OS.Version.Major > 5) || ((OS.Version.Major == 5) && (OS.Version.Minor == 1)));
}
}
public static bool IsWinVista
{
get
{
OperatingSystem OS = Environment.OSVersion;
return (OS.Platform == PlatformID.Win32NT) && (OS.Version.Major >= 6);
}
}
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
}
}
}