如何在网格WPF上的两个控件之间绘制连接线

问题描述 投票:20回答:1

我正在网格上创建控件(例如按钮)。我想在控件之间创建一条连接线。假设您将鼠标放在一个按钮上,然后将鼠标放到另一个按钮上。这应该在这两个按钮之间画一条线。

有人可以帮助我或给我一些有关如何执行此操作的想法吗?

提前感谢!

wpf wpf-controls grid lines connector
1个回答
46
投票

我正在做类似的事情;这是我所做的快速摘要:

拖放

为了处理控件之间的拖放,网络上有很多文献(just search WPF drag-and-drop)。 IMO的默认拖放实现过于复杂,我们最终使用了一些附加的DP来简化操作(similar to these)。基本上,您需要一种类似于以下内容的拖动方法:

private void onMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    UIElement element = sender as UIElement;
    if (element == null)
        return;
    DragDrop.DoDragDrop(element, new DataObject(this), DragDropEffects.Move);
}

在目标上,将AllowDrop设置为true,然后将一个事件添加到Drop:

private void onDrop(object sender, DragEventArgs args)
{
    FrameworkElement elem = sender as FrameworkElement;
    if (null == elem)
        return;
    IDataObject data = args.Data;
    if (!data.GetDataPresent(typeof(GraphNode))
        return;
    GraphNode node = data.GetData(typeof(GraphNode)) as GraphNode;
    if(null == node)
        return;

            // ----- Actually do your stuff here -----
}

画线

现在是棘手的部分!每个控件都公开一个AnchorPoint DependencyProperty。引发LayoutUpdated事件时(即控件移动/调整大小等时),控件将重新计算其AnchorPoint。添加连接线后,它会绑定到源和目标的AnchorPoints的DependencyProperties。 [EDIT:正如雷·伯恩斯(Ray Burns)在评论中指出的那样,画布和网格只需要放在同一位置即可;它们不需要是相同的层次结构(尽管可能是同一层次)]

用于更新位置DP:

private void onLayoutUpdated(object sender, EventArgs e)
{
    Size size = RenderSize;
    Point ofs = new Point(size.Width / 2, isInput ? 0 : size.Height);
    AnchorPoint = TransformToVisual(node.canvas).Transform(ofs);
}

用于创建线类(也可以在XAML中完成):

public sealed class GraphEdge : UserControl
{
    public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(Point), typeof(GraphEdge), new FrameworkPropertyMetadata(default(Point)));
    public Point Source { get { return (Point) this.GetValue(SourceProperty); } set { this.SetValue(SourceProperty, value); } }

    public static readonly DependencyProperty DestinationProperty = DependencyProperty.Register("Destination", typeof(Point), typeof(GraphEdge), new FrameworkPropertyMetadata(default(Point)));
    public Point Destination { get { return (Point) this.GetValue(DestinationProperty); } set { this.SetValue(DestinationProperty, value); } }

    public GraphEdge()
    {
        LineSegment segment = new LineSegment(default(Point), true);
        PathFigure figure = new PathFigure(default(Point), new[] { segment }, false);
        PathGeometry geometry = new PathGeometry(new[] { figure });
        BindingBase sourceBinding = new Binding {Source = this, Path = new PropertyPath(SourceProperty)};
        BindingBase destinationBinding = new Binding { Source = this, Path = new PropertyPath(DestinationProperty) };
        BindingOperations.SetBinding(figure, PathFigure.StartPointProperty, sourceBinding);
        BindingOperations.SetBinding(segment, LineSegment.PointProperty, destinationBinding);
        Content = new Path 
        {
            Data = geometry,
            StrokeThickness = 5,
            Stroke = Brushes.White,
            MinWidth = 1,
            MinHeight = 1
        };
    }
}

如果您想得到更多的爱好者,可以在源和目标上使用MultiValueBinding,并添加一个创建PathGeometry的转换器。 Here's an example from GraphSharp.使用此方法,可以在行尾添加箭头,使用Bezier曲线使其看起来更自然,在其他控件(though this could be harder than it sounds)等周围绕线,等等。


另请参见

© www.soinside.com 2019 - 2024. All rights reserved.