Jak narysować linie łączące między dwoma kontrolkami na siatce WPF

Tworzę kontrolki (powiedzmy przycisk) na siatce. Chcę stworzyć linię łączącą kontrolki. Powiedzmy, że robisz mousedown na jednym przycisku i zwolnij mysz nad innym przyciskiem. To powinno narysować linię między tymi dwoma przyciskami.

Czy ktoś może mi pomóc lub dać jakieś pomysły jak to zrobić?

Z góry dzięki!

Author: Scooby, 2010-05-13

1 answers

Robię coś podobnego; oto krótkie podsumowanie tego, co zrobiłem:

Drag & Drop

Do obsługi przeciągania i upuszczania między kontrolkami jest sporo literatury w Internecie ( wystarczy wyszukać WPF drag-and-drop ). Domyślna implementacja drag-and-drop jest zbyt złożona, IMO, i skończyło się na użyciu dołączonego DPs, aby to ułatwić (podobne do tych ). Zasadniczo chcesz metodę przeciągania, która wygląda mniej więcej tak:

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

Na celu, Ustaw AllowDrop na true, a następnie Dodaj zdarzenie do 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 -----
    }

Rysowanie linii

Teraz najtrudniejsza część! Każda kontrola wyświetla punkt kontrolny DependencyProperty. Gdy zdarzenie LayoutUpdated zostanie wywołane (tzn. gdy kontrolka przesunie / zmieni rozmiar / etc), kontrolka ponownie obliczy swój punkt kontrolny. Po dodaniu linii łączącej wiąże się ona z zależnościami zarówno punktów kotwiczenia źródła, jak i punktu docelowego. [EDIT : Jak zauważył Ray Burns w komentarze płótno i siatka po prostu muszą być w tym samym miejscu; nie muszą być w tej samej hierarchii (choć mogą być)]

Do aktualizacji pozycji 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);
    }

Do tworzenia klasy line (można to zrobić również w 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
        };
    }
}

Jeśli chcesz uzyskać dużo więcej, możesz użyć MultiValueBinding na źródle i miejscu docelowym i dodać konwerter, który tworzy PathGeometry. oto przykład z GraphSharp. używając tej metody można dodać strzałki do końca linii, użyj krzywych Beziera, aby wyglądać bardziej naturalnie, wyznacz linię wokół innych elementów sterujących (choć może to być trudniejsze niż się wydaje ) itp., itd.


Zobacz

 43
Author: Fraser,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-14 04:56:59