但是,我在绘制黑线时遇到了麻烦。不知何故,我最终以每个节点三行为单位。我计算包含树节点的矩形的上部,中部和下部,并根据它绘制其中一条线。如果我使用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();
}
这可以通过某种方式解决,还是应该以不同的方式展示这类信息? (例如工具提示)?
答案 0 :(得分:1)
Invalidate()
是正确的调用方法。
问题似乎是您通过调用_treeView.CreateGraphics()
来使用新的Graphics对象。
您可以尝试invalidate a calculated region you want to update(绘制旧分隔线的位置)或者使用双缓冲方法,我宁愿用于完全自定义绘制的控件,但它可能值得一试: Remove OnPaintBackground() 更新,但不会覆盖控件的绘图,但不会有效。
当我考虑这个问题时...为什么不存储最后一行的坐标,一旦另一条线被绘制,"擦除" (重复)它再次使用相同的笔大小和形状,但背景的颜色。我知道,这看起来有点脏,但最后它都是关于不可察觉且表现良好的黑客(例如考虑游戏引擎中的渲染技巧)。一旦用户滚动或做任何其他会导致过度修复失败的原因因为coords确实改变了,你就不需要重写它,因为整个控件都被操作系统重新绘制。
答案 1 :(得分:1)
每次到达新TreeNode附近时,您都不必绘制线条。在我看来,每次在拖放事件中使用Graphics对象来绘制和擦除内容都不是一个干净的解决方案。相反,这就是我所说的更“清洁”的方式 -
在Windows窗体上,绘制一条线作为静态控件,并将其可见性最初设置为false。现在你怎么画一条线? 添加标签控件,添加实体或3D边框,清除文本,并设置固定高度 - 可以是2个像素,也可以是所需的宽度。将此标签放在表单左下角的某个位置,不会出现在UI上的其他控件中。
而不是DrawLine
方法,将其称为ShowLine
或其他内容。在此方法中,根据TreeView节点的位置动态地将此新标签的X和Y位置(实际上是一条线)设置到新位置,并使其可见。因此,每次使用DragOver时,它都会在不同的X和Y位置显示,并为您提供所需的相同体验。
将项目放入节点内(即拖放操作完成)后,将此线条标签的可见性设置为false,并将其X和Y位置设置回原始位置(在这种情况下左下角)。