Pergunta

Estou criando controles (por exemplo, botão) em uma grade. Eu quero criar uma linha de conexão entre os controles. Digamos que você faça mousedown em um botão e solte o mouse sobre outro botão. Isso deve desenhar uma linha entre esses dois botões.

Alguém pode me ajudar ou me dar algumas idéias sobre como fazer isso?

Desde já, obrigado!

Foi útil?

Solução

Estou fazendo algo semelhante; Aqui está um resumo rápido do que eu fiz:

Arraste e solte

Para lidar com o arrasto e a queda entre os controles, há um pouco de literatura na web (Basta pesquisar WPF arrastar e soltar). A implementação padrão de arrastar e soltar é excessivamente complexa, IMO, e acabamos usando alguns DPs anexados para facilitar (semelhante a estes). Basicamente, você quer um método de arrasto que se pareça com o seguinte:

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

No alvo, defina o allowDrop como verdadeiro e adicione um evento para soltar:

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

Desenhando a linha

Agora para a parte mais complicada! Cada controle expõe um ancorpoint DependEncyProperty. Quando o evento LayoutUpdated é aumentado (ou seja, quando o controle se move/redimensiona/etc), o controle recalcula seu ancorto. Quando uma linha de conexão é adicionada, ela se liga às propostas de dependência dos pontos de ancoradouros da fonte e do destino. [EDITAR: Como Ray Burns apontou nos comentários, a tela e a grade só precisam estar no mesmo lugar; Eles não precisam estar na mesma hierarquia (embora possam ser)

Para atualizar a posição 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);
    }

Para criar a classe de linha (também pode ser feito em 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
        };
    }
}

Se você deseja ficar muito mais sofisticado, pode usar uma encadernação multivalupa na fonte e no destino e adicionar um conversor que cria a PathGeometria. Aqui está um exemplo do GraphSharp. Usando esse método, você pode adicionar setas ao final da linha, usar curvas bezier para torná -lo mais natural, direcionar a linha em torno de outros controles (Embora isso possa ser mais difícil do que parece), etc etc.


Veja também

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top