在使用拖放时,我可以让 Treeview 展开用户悬停的节点吗?
Posted
技术标签:
【中文标题】在使用拖放时,我可以让 Treeview 展开用户悬停的节点吗?【英文标题】:Whilst using drag and drop, can I cause a Treeview to Expand the node over which the user hovers? 【发布时间】:2010-12-15 03:21:11 【问题描述】:简而言之:
.Net 2.0 中是否有任何内置功能可以在拖放操作正在进行时将鼠标悬停在 TreeNode
s 上?
我在 Visual Studio 2005 中使用 C#。
更详细:
我已经使用多层次、多节点的树(想想组织结构图或文件/文件夹对话框)填充了 Treeview
控件,并且我想使用拖放来移动树中的节点。
拖放代码运行良好,我可以拖放到任何可见节点上,但是我希望我的控件在将文件拖动到文件夹窗格时表现得像 Windows 资源管理器一样。具体来说,如果将鼠标悬停在 1/2 秒左右,我希望打开每个文件夹。
我已经开始使用Threading
和Sleep
方法开发解决方案,但我遇到了问题,想知道是否已经有什么东西已经到位,如果没有,我会认真学习如何使用线程(是时候了,但我希望尽快推出这个应用程序)
我是否需要编写自己的代码来处理在拖放模式下悬停在 TreeNode
上时的扩展?
【问题讨论】:
【参考方案1】:您可以使用 DragOver 事件;拖动对象时它会反复触发 延迟后打开可以很容易地通过两个额外的变量来完成,这些变量记录鼠标下的最后一个对象和时间。不需要线程或其他技巧(在我的示例中为 lastDragDestination 和 lastDragDestinationTime)
来自我自己的代码:
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();
【讨论】:
+1 用于使用 DragOver 的解决方案。很好“我还在同一个节点上吗?”逻辑也是如此。 非常好,非常感谢。我没有意识到你可以在一个方法之外实例化,所以我学到了更多:-) 还有一个问题,虽然你指的是什么 IconObject?我从您的代码中得出了一个解决方案,而没有使用它。另外,我忽略了 listview 代码,因为它与我的情况无关。 IconObject ListView 是否相关? 这是来自一个项目,它可以让人们管理和关联不同的对象;它有业务、人员、计算机、软件等。业务包含部门,其中包含人员、计算机、计算机包含软件等。一些对象分配给其他对象(显示在 TreeView 中),而其他对象未分配并留在通用池(例如未分配的计算机池),显示在 ListViews 中。所有对象都继承自 IconObject,这是我定义公共属性和逻辑的基类。 (子类定义了一个列表/树图像、一些额外的属性等等)【参考方案2】:编辑
我有一个新的解决方案,有点牵强,但它确实有效......它使用DelayedAction
类来处理主线程上操作的延迟执行:
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);
【讨论】:
行不通...我试过了。在拖放模式下不会触发 OnNodeMouseHover。以上是关于在使用拖放时,我可以让 Treeview 展开用户悬停的节点吗?的主要内容,如果未能解决你的问题,请参考以下文章