问题
In brief:
Is there any built-in function in .Net 2.0 to Expand TreeNode
s when hovered over whilst a drag and drop operation is in progress?
I'm using C# in Visual Studio 2005.
In more detail:
I've populated a Treeview
control with a multi levelled, multinoded tree (think organisational chart or file/folder dialog) and I want to use drag and drop to move nodes within the tree.
The drag drop code works well, and I can drop onto any visible node, however I would like my control to behave like Windows Explorer does when dragging files over the folder pane. Specifically, I'd like each folder to open if hovered over for 1/2 second or so.
I've begun developing a solution using Threading
and a Sleep
method but I'm running into problems and wondered if there was something in place already, if not I will knuckle down and learn how to use threading (it's about time, but I was hoping to get this app out quickly)
Do I need to write my own code to handle Expanding a TreeNode
when hovered over in drag-drop mode?
回答1:
You can use the DragOver event; it fires repeatedly while you are dragging an object Opening after a delay can be done very easily with two extra variables that note the last object under the mouse and the time. No threading or other tricks required (lastDragDestination and lastDragDestinationTime in my example)
From my own code:
TreeNode lastDragDestination = null;
DateTime lastDragDestinationTime;
private void tvManager_DragOver(object sender, DragEventArgs e)
{
IconObject dragDropObject = null;
TreeNode dragDropNode = null;
//always disallow by default
e.Effect = DragDropEffects.None;
//make sure we have data to transfer
if (e.Data.GetDataPresent(typeof(TreeNode)))
{
dragDropNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
dragDropObject = (IconObject)dragDropNode.Tag;
}
else if (e.Data.GetDataPresent(typeof(ListViewItem)))
{
ListViewItem temp (ListViewItem)e.Data.GetData(typeof(ListViewItem));
dragDropObject = (IconObject)temp.Tag;
}
if (dragDropObject != null)
{
TreeNode destinationNode = null;
//get current location
Point pt = new Point(e.X, e.Y);
pt = tvManager.PointToClient(pt);
destinationNode = tvManager.GetNodeAt(pt);
if (destinationNode == null)
{
return;
}
//if we are on a new object, reset our timer
//otherwise check to see if enough time has passed and expand the destination node
if (destinationNode != lastDragDestination)
{
lastDragDestination = destinationNode;
lastDragDestinationTime = DateTime.Now;
}
else
{
TimeSpan hoverTime = DateTime.Now.Subtract(lastDragDestinationTime);
if (hoverTime.TotalSeconds > 2)
{
destinationNode.Expand();
}
}
}
}
回答2:
EDIT
I have a new solution, a bit far-fetched, but it works... It uses a DelayedAction
class to handle delayed execution of an action on the main thread :
DelayedAction<T>
public class DelayedAction<T>
{
private SynchronizationContext _syncContext;
private Action<T> _action;
private int _delay;
private Thread _thread;
public DelayedAction(Action<T> action)
: this(action, 0)
{
}
public DelayedAction(Action<T> action, int delay)
{
_action = action;
_delay = delay;
_syncContext = SynchronizationContext.Current;
}
public void RunAfterDelay()
{
RunAfterDelay(_delay, default(T));
}
public void RunAfterDelay(T param)
{
RunAfterDelay(_delay, param);
}
public void RunAfterDelay(int delay)
{
RunAfterDelay(delay, default(T));
}
public void RunAfterDelay(int delay, T param)
{
Cancel();
InitThread(delay, param);
_thread.Start();
}
public void Cancel()
{
if (_thread != null && _thread.IsAlive)
{
_thread.Abort();
}
_thread = null;
}
private void InitThread(int delay, T param)
{
ThreadStart ts =
() =>
{
Thread.Sleep(delay);
_syncContext.Send(
(state) =>
{
_action((T)state);
},
param);
};
_thread = new Thread(ts);
}
}
AutoExpandTreeView
public class AutoExpandTreeView : TreeView
{
DelayedAction<TreeNode> _expandNode;
public AutoExpandTreeView()
{
_expandNode = new DelayedAction<TreeNode>((node) => node.Expand(), 500);
}
private TreeNode _prevNode;
protected override void OnDragOver(DragEventArgs e)
{
Point clientPos = PointToClient(new Point(e.X, e.Y));
TreeViewHitTestInfo hti = HitTest(clientPos);
if (hti.Node != null && hti.Node != _prevNode)
{
_prevNode = hti.Node;
_expandNode.RunAfterDelay(hti.Node);
}
base.OnDragOver(e);
}
}
来源:https://stackoverflow.com/questions/1709581/whilst-using-drag-and-drop-can-i-cause-a-treeview-to-expand-the-node-over-which