【问题标题】:Separator line drag/drop tree nodes c# WinForms分隔线拖放树节点 c# WinForms
【发布时间】:2017-08-23 11:34:04
【问题描述】:

我正在尝试复制以下拖放功能:

但是,我无法画出那条黑线。不知何故,我最终每个节点都有三行。我计算包含树节点的矩形的上部、中间和下部,并据此绘制其中一条线。如果我确实使用 treeView.Invalidate() 屏幕闪烁太多并且看不到线条。我也尝试过使用 graphics.clear(treeView.BackColor) 但它也会清除我的树节点。

演示:

代码 - 树事件:

public void ItemDrag(object sender, ItemDragEventArgs e)
    {
        TreeNode selectedNode = (TreeNode)e.Item;
        if (e.Button == MouseButtons.Left && !selectedNode.Name.Contains("=") && !selectedNode.Name.Contains("#"))
            _treeView.DoDragDrop(selectedNode, DragDropEffects.Move);

    }

public void DragEnter(object sender, DragEventArgs e)
    {
        e.Effect = e.AllowedEffect;
    }

public void DragOver(object sender, DragEventArgs e)
    {
        try
        {
            if (!mousePoint.Equals(Cursor.Position))
            {
                mousePoint = Cursor.Position;
                bool droppable;
                TreeNode destinationNode = null;
                Point pointInTree = _treeView.PointToClient(new Point(e.X, e.Y));
                if (e.Data.GetDataPresent(typeof(TreeNode)))
                {
                    destinationNode = _treeView.GetNodeAt(pointInTree);
                    TreeNode souceNode = (TreeNode) e.Data.GetData(typeof(TreeNode));
                    droppable = true;
                }

                else droppable = false;

                e.Effect = droppable ? DragDropEffects.Move : DragDropEffects.None;

                Point pt = _treeView.PointToClient(new Point(e.X, e.Y));
                _treeView.SelectedNode = _treeView.GetNodeAt(pt);

                int dropLocation = CalculateNodeHooverArea(destinationNode, pointInTree);
                if(_dropLocation!=dropLocation)
                {
                    switch (dropLocation)
                {
                    case 0:
                        DrawLine(NodePosition.Above);
                        break;
                    case 2:
                        DrawLine(NodePosition.Below);
                        break;
                    case 1:
                        DrawLine(NodePosition.In);
                        break;
                }
                    _dropLocation = dropLocation;
                }

            }
        }
        catch (Exception exception)
        {
            Debug.WriteLine("TreeViewDragOverEvent: " + exception.Message);
        }
    }
public void DragDrop(object sender, DragEventArgs e)
    {
        try
        {
            Point targetPoint = _treeView.PointToClient(new Point(e.X, e.Y));
            TreeNode targetNode = _treeView.GetNodeAt(targetPoint);
            TreeNode draggedNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
            if (!draggedNode.Equals(targetNode) && !draggedNode.Nodes.Find(targetNode.Name, true).Any() &&
                targetNode.Parent != null && !targetNode.Name.Contains("=") && !targetNode.Name.Contains("#"))
            {
                int nodeLocation = CalculateNodeHooverArea(targetNode, targetPoint);

                if (e.Effect == DragDropEffects.Move)
                    draggedNode.Remove();

                switch (nodeLocation)
                {
                    case 0:
                        if (targetNode.Parent != null)
                            targetNode.Parent.Nodes.Insert(targetNode.Index, draggedNode);
                        break;
                    case 1:
                        targetNode.Nodes.Add(draggedNode);
                        break;
                    case 2:
                        if (targetNode.Parent != null)
                            targetNode.Parent.Nodes.Insert(targetNode.Index + 1, draggedNode);
                        break;
                }
            }
            //SaveMemento();
            _treeView.Invalidate();
        }
        catch (Exception exception)
        {
            Debug.WriteLine(exception.Message);
        }
    }

处理画线的方法:

    private void DrawLine(NodePosition position)
    {
        Graphics g = _treeView.CreateGraphics();
        Pen customPen = new Pen(Color.DimGray, 1) { DashStyle = DashStyle.Dash };
        if (position == NodePosition.Above)
            g.DrawLine(customPen, new Point(0, _treeView.SelectedNode.Bounds.Top),
                new Point(_treeView.Width - 4, _treeView.SelectedNode.Bounds.Top));

        else if (position == NodePosition.Below)
            g.DrawLine(customPen, new Point(0, _treeView.SelectedNode.Bounds.Bottom),
                new Point(_treeView.Width - 4, _treeView.SelectedNode.Bounds.Bottom));

        else
        {
            g.DrawLine(customPen, new Point(_treeView.SelectedNode.Bounds.X + _treeView.SelectedNode.Bounds.Width,
                _treeView.SelectedNode.Bounds.Y +_treeView.SelectedNode.Bounds.Height / 2),
                new Point(_treeView.Width - 4, _treeView.SelectedNode.Bounds.Y + _treeView.SelectedNode.Bounds.Height / 2));
        }

        customPen.Dispose();
        g.Dispose();
    }

能否以某种方式解决这个问题,或者我应该寻找一种不同的方式来显示这类信息? (例如工具提示)?

【问题讨论】:

  • 糟糕的问题。源代码缺少依赖方法和变量(CalculateNodeHooverArea、mousepoint 等)
  • @stigzler 无关评论。该问题已有 2 年历史,已被标记为已回答。
  • @stigzler 不知道你是怎么解决这个问题的,这个问题很好而且很全面,它确实帮助了我。

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


【解决方案1】:

您真的不必每次到达新 TreeNode 附近时都画线。在我看来,每次在拖放事件中继续使用 Graphics 对象来绘制和擦除某些东西并不是一个干净的解决方案。相反,这就是我所说的实现相同目标的更“更清洁”的方式-

  • 在您的 Windows 窗体上,画一条线作为静态控件并将其可见性最初设置为 false。现在你怎么画一条线? 添加一个标签控件,添加一个实心或 3D 边框,清除文本,并设置一个固定的高度 - 可以是 2 个像素,宽度根据需要而定。将此标签放置在表单左下角的某个位置,它不会妨碍 UI 上的其他控件。

  • 代替DrawLine 方法,将其称为ShowLine 或其他名称。在该方法中,根据 TreeView 节点的位置,将这个新标签(实际上是一条线)的 X 和 Y 位置动态设置到新的位置,并使其可见。因此,每次在 DragOver 上,它都会在不同的 X 和 Y 位置上显示,并为您提供所需的相同体验。

  • 一旦项目被拖放到节点内(即拖放操作完成),将此行标签的可见性设置为 false,并将其 X 和 Y 位置设置回原来的位置(在这种情况下为左下角)。

【讨论】:

  • 这确实是一个消耗较少的选项。会试一试。
  • 是的,这确实更容易,但我仍然不知道它是如何从没想过的。谢谢你。现在干净多了。
【解决方案2】:

Invalidate() 是当您希望在不执行布局算法的情况下重绘控件时调用的正确方法。

问题似乎是您通过调用_treeView.CreateGraphics() 使用了一个新的Graphics 对象。

您可以尝试只使用invalidate a calculated region you want to update(绘制旧分隔线的位置)或者使用双缓冲方法,我更愿意将其用于完全自定义绘制的控件,但它可能值得一试:@987654322 @ update 如果不完全覆盖控件的绘制,它将无法工作。

当我考虑这个问题时...为什么不存储最后一行的坐标,一旦绘制另一条线,就用相同的笔大小和形状再次“擦除”(重绘)它背景的颜色。我知道,这读起来有点脏,但最终都是关于不引人注意且性能良好的 hack(例如考虑游戏引擎中的渲染技巧)。一旦用户滚动或执行任何其他操作会导致重绘失败,因为坐标确实发生了变化,您也不需要重绘它,因为整个控件都会被操作系统重绘。

【讨论】:

  • 如果我要尝试您的第一个解决方案,我将必须引用先前访问的区域,计算它(或之前执行此操作)然后使其无效。对我来说,这似乎是一项繁重的工作,而且相当肮脏。我也尝试过使用 treeView 组件的 OnPaint() 事件,但没有暴露
  • 是的,你这样说是有道理的。我会试一试,让你知道它是怎么回事。谢谢!
猜你喜欢
  • 2011-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多