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

发布于 2024-09-01 04:05:30 字数 129 浏览 11 评论 0原文

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

有人可以帮助我或给我一些如何做到这一点的想法吗?

提前致谢!

I am creating controls (say button) on a grid. I want to create a connecting line between controls.
Say you you do mousedown on one button and release mouse over another button. This should draw a line between these two buttons.

Can some one help me or give me some ideas on how to do this?

Thanks in advance!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

来日方长 2024-09-08 04:05:30

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

Drag & 关于处理控件之间的拖放

,网上有很多文献(只需搜索WPF拖放 )。默认的拖放实现过于复杂,IMO,我们最终使用了一些附加的 DP 来使其更容易(与这些类似)。基本上,您需要一个看起来像这样的拖动方法:

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 -----
}

Drawing the Line

Now以完成棘手的部分!每个控件都公开一个 AnchorPoint DependencyProperty。当引发 LayoutUpdated 事件时(即当控件移动/调整大小等时),控件将重新计算其 AnchorPoint。添加连接线后,它会绑定到源和目标的 AnchorPoints 的 DependencyProperties。 [编辑:正如 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 的转换器。 这是 GraphSharp 的示例。 使用此方法,您可以将箭头添加到线的末端,使用贝塞尔曲线使其看起来更自然,将线绕其他控件布线(尽管如此可能比听起来更难),等等。


另请参阅

I'm doing something similar; here's a quick summary of what I did:

Drag & Drop

For handling the drag-and-drop between controls there's quite a bit of literature on the web (just search WPF drag-and-drop). The default drag-and-drop implementation is overly complex, IMO, and we ended up using some attached DPs to make it easier (similar to these). Basically, you want a drag method that looks something like this:

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

On the target, set AllowDrop to true, then add an event to 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 -----
}

Drawing the Line

Now for the tricky part! Each control exposes an AnchorPoint DependencyProperty. When the LayoutUpdated event is raised (i.e. when the control moves/resizes/etc), the control recalculates its AnchorPoint. When a connecting line is added, it binds to the DependencyProperties of both the source and destination's AnchorPoints. [EDIT: As Ray Burns pointed out in the comments the Canvas and grid just need to be in the same place; they don't need to be int the same hierarchy (though they may be)]

For updating the position 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);
}

For creating the line class (can be done in XAML, too):

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
        };
    }
}

If you want to get a lot fancier, you can use a MultiValueBinding on source and destination and add a converter which creates the PathGeometry. Here's an example from GraphSharp. Using this method, you could add arrows to the end of the line, use Bezier curves to make it look more natural, route the line around other controls (though this could be harder than it sounds), etc., etc.


See also

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文