【问题标题】:Whilst using drag and drop, can I cause a Treeview to Expand the node over which the user hovers?在使用拖放时,我可以让 Treeview 展开用户悬停的节点吗?
【发布时间】:2010-12-15 03:21:11
【问题描述】:

简而言之:

.Net 2.0 中是否有任何内置功能可以在拖放操作正在进行时将鼠标悬停在 TreeNodes 上?

我在 Visual Studio 2005 中使用 C#。

更详细:

我已经使用多层次、多节点的树(想想组织结构图或文件/文件夹对话框)填充了 Treeview 控件,并且我想使用拖放来移动树中的节点。

拖放代码运行良好,我可以拖放到任何可见节点上,但是我希望我的控件在将文件拖动到文件夹窗格时表现得像 Windows 资源管理器一样。具体来说,如果将鼠标悬停在 1/2 秒左右,我希望打开每个文件夹。

我已经开始使用Threading 和Sleep 方法开发解决方案,但我遇到了问题,想知道是否已经有什么东西已经到位,如果没有,我会认真学习如何使用线程(是时候了,但我希望尽快推出这个应用程序)

我是否需要编写自己的代码来处理在拖放模式下悬停在 TreeNode 上时的扩展?

【问题讨论】:

    标签: c# winforms treeview hover drag-and-drop


    【解决方案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。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-13
    • 2017-11-10
    • 2021-02-06
    相关资源
    最近更新 更多