web-dev-qa-db-ja.com

グリッドWPF上の2つのコントロール間に接続線を描画する方法

グリッド上にコントロール(ボタンなど)を作成しています。コントロール間に接続線を作成したい。あるボタンでマウスダウンを行い、別のボタンの上でマウスを離したとします。これにより、これら2つのボタンの間に線が引かれます。

誰かが私を助けたり、これを行う方法についていくつかのアイデアを教えてもらえますか?

前もって感謝します!

19
Scooby

私は似たようなことをしています。これが私がしたことの簡単な要約です:

ドラッグドロップ

コントロール間のドラッグアンドドロップを処理するために、Web上にかなりの量の文献があります( 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 -----
    }

線を引く

トリッキーな部分です!各コントロールは、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を作成するコンバーターを追加できます。 GraphSharpの例です。 この方法を使用すると、線の端に矢印を追加したり、ベジェ曲線を使用してより自然に見えるようにしたり、他のコントロールの周りに線をルーティングしたりできます( ただしこれは思ったより難しいかもしれません )など。


も参照してください

44
Robert Fraser