Una imagen vale más que mil palabras, así que aquí está lo existente contra lo deseable:

enter image description here

Esto es lo que he intentado sin éxito:

 public MainWindow()
    {
        InitializeComponent();
        TextBlock txt = new TextBlock();
        txt.Text = "asdsahd sakh uj";
        txt.Background = Brushes.LightBlue;
        Canvas.SetLeft(txt, 100);
        container.Children.Add(txt);


        Line line = new Line();
        line.SetBinding(Line.X1Property, new Binding("Canvas.Left") {ElementName = "txt"});
        line.X2 = line.X1;
        line.Y1 = 0;
        line.Y2 = 100;
        line.StrokeThickness = 2;
        container.Children.Add(line);
    }
wpf
0
Erez 18 dic. 2011 a las 18:51
No estoy seguro de que la configuración TxtBlock txt - new TextBlock () le da un ElementName. Mire como establecer el ElementName explícitamente.
 – 
paparazzo
18 dic. 2011 a las 19:49
Gracias BalamBalam, lo probé, no ayudó.
 – 
Erez
18 dic. 2011 a las 20:05

1 respuesta

La mejor respuesta

En la siguiente solución, asumo que su Canvas tiene una altura y un ancho que pueden cambiar en tiempo de ejecución, y que su TextBlock también puede tener una altura y un ancho que también cambian dinámicamente. El enfoque parece complicado, pero no lo es. Por supuesto, hacer esto en XAML sería mucho, mucho más legible:

  1. Cree un TextBlock agregar al lienzo.
  2. Cree un Line agregar al lienzo.
  3. Utilice un MultiBinding para calcular la propiedad Canvas.Left para TextBlock. Si cambia el ancho de Canvas o TextBlock, este enlace se volverá a evaluar y IMultiValueConverter proporcionará un nuevo valor. El valor de Canvas.Left para TextBlock debe ser: Canvas.ActualWidth/2 - TextBlock.ActualWidth/2.
  4. Igual que en el paso 3, pero esta vez para el Line.
  5. Utilice un MultiBinding para calcular la propiedad Line.Y2 para Line. Suponiendo que desee dibujar la línea hasta el borde inferior de Canvas. Para calcular esto: Canvas.ActualHeight - TextBlock.ActualHeight. El enlace volverá a evaluar si alguno de los cambios y el IMultiValueConverter proporcionará un nuevo valor.
  6. Utilice un Binding para establecer la propiedad Canvas.Top en Line. En este ejemplo, el Line se dibuja inmediatamente debajo del TextBlock, por lo que podemos vincularnos a TextBlock.ActualHeight.

TextBlock y creación de líneas

    public MainWindow()
    {
        InitializeComponent();

        // Create TextBlock and Line
        var textBlock = new TextBlock {Text = "123456"};
        var line = new Line { StrokeThickness = 2, Stroke = new SolidColorBrush { Color = Colors.Black }, Y1 = 0 };
        myCanvas.Children.Add(textBlock);
        myCanvas.Children.Add(line);

        // Setup bindings
        InitializeTextBlockCanvasLeftBinding(myCanvas, textBlock);
        InitializeLineCanvasLeftBinding(myCanvas, line);
        InitializeLineY2Binding(myCanvas, textBlock, line);
        InitializeLineCanvasTopBinding(myCanvas, textBlock, line);
    }

    private void InitializeLineCanvasTopBinding(Canvas canvas, TextBlock textBlock, Line line)
    {
        var textBlockActualHeightBinding = new Binding
        {
            Source = textBlock,
            Path = new PropertyPath("ActualHeight"),
        };

        BindingOperations.SetBinding(line, Canvas.TopProperty, textBlockActualHeightBinding);
    }

    private void InitializeLineY2Binding(Canvas canvas, TextBlock textBlock, Line line)
    {
        var canvasActualHeightBinding = new Binding
        {
            Source = myCanvas,
            Path = new PropertyPath("ActualHeight"),
        };

        var textBlockActualHeightBinding = new Binding
        {
            Source = textBlock,
            Path = new PropertyPath("ActualHeight"),
        };

        var lineY2MultiBinding = new MultiBinding { Converter = new LineY1MultiConverter() };
        lineY2MultiBinding.Bindings.Add(textBlockActualHeightBinding);
        lineY2MultiBinding.Bindings.Add(canvasActualHeightBinding);

        BindingOperations.SetBinding(line, Line.Y2Property, lineY2MultiBinding);
    }

    private void InitializeLineCanvasLeftBinding(Canvas canvas, Line line)
    {
        var lineActualWidthBinding = new Binding
        {
            Source = line,
            Path = new PropertyPath("ActualWidth"),
        };

        var canvasActualWidthBinding = new Binding
        {
            Source = myCanvas,
            Path = new PropertyPath("ActualWidth"),
        };

        var lineLeftMultiBinding = new MultiBinding { Converter = new CanvasLeftCenterMultiConverter() };
        lineLeftMultiBinding.Bindings.Add(lineActualWidthBinding);
        lineLeftMultiBinding.Bindings.Add(canvasActualWidthBinding);

        BindingOperations.SetBinding(line, Canvas.LeftProperty, lineLeftMultiBinding);
    }

    private void InitializeTextBlockCanvasLeftBinding(Canvas canvas, TextBlock textBlock)
    {
        var textBlockActualWidthBinding = new Binding
        {
            Source = textBlock,
            Path = new PropertyPath("ActualWidth"),
        };

        var canvasActualWidthBinding = new Binding
        {
            Source = myCanvas,
            Path = new PropertyPath("ActualWidth"),
        };

        var textBlockLeftMultiBinding = new MultiBinding { Converter = new CanvasLeftCenterMultiConverter() };
        textBlockLeftMultiBinding.Bindings.Add(textBlockActualWidthBinding);
        textBlockLeftMultiBinding.Bindings.Add(canvasActualWidthBinding);

        BindingOperations.SetBinding(textBlock, Canvas.LeftProperty, textBlockLeftMultiBinding);
    }

MultiConverter para calcular la longitud de la línea

public class LineY1MultiConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values != null && values.Length == 2 && values[0] is double && values[1] is double)
        {
            var canvasHeight = (double)values[1];
            var textBlockHeight = (double) values[0];
            return canvasHeight - textBlockHeight;
        }

        return DependencyProperty.UnsetValue;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Conversor múltiple para centrar elementos horizontalmente en el lienzo

public class CanvasLeftCenterMultiConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values != null && values.Length == 2 && values[0] is double && values[1] is double)
        {
            var canvasMiddle = (double)values[1]/2;
            var elementHalf = (double) values[0]/2;
            return canvasMiddle - elementHalf;
        }

        return DependencyProperty.UnsetValue;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Editar

Para que el código anterior sea más claro, en realidad es solo una traducción 1 a 1 del siguiente XAML (usando el MultiValueConverters de arriba):

    <Canvas x:Name="myCanvas" Background="Pink">
        <TextBlock x:Name="myTextBlock" Text="1234567890">
            <Canvas.Left>
                 <MultiBinding Converter="{l:CanvasLeftCenterMultiConverter}">
                    <Binding RelativeSource="{RelativeSource Mode=Self}" Path="ActualWidth" />
                    <Binding ElementName="myCanvas" Path="ActualWidth" />
                </MultiBinding>
            </Canvas.Left>
        </TextBlock>
        <Line StrokeThickness="2" Stroke="Black" Canvas.Top="{Binding ElementName=myTextBlock, Path=ActualHeight}" Y1="0">
            <Line.Y2>
                <MultiBinding Converter="{l:LineY1MultiConverter}">
                    <Binding ElementName="myTextBlock" Path="ActualHeight" />
                    <Binding ElementName="myCanvas" Path="ActualHeight" />
                </MultiBinding>
            </Line.Y2>
            <Canvas.Left>
                <MultiBinding Converter="{l:CanvasLeftCenterMultiConverter}">
                    <Binding RelativeSource="{RelativeSource Mode=Self}" Path="ActualWidth" />
                    <Binding ElementName="myCanvas" Path="ActualWidth" />
                </MultiBinding>
            </Canvas.Left>
        </Line>
    </Canvas>

Como puede ver, si el tamaño del lienzo cambia, el tamaño del texto cambia o el grosor de la línea cambia, las vinculaciones aseguran que se calculen nuevos valores para Canvas.Left y Canvas.Top.

¡Espero que esto ayude!

0
FunnyItWorkedLastTime 19 dic. 2011 a las 21:40
Es tan largo que no lo probé todavía, pero supongo que es suficiente y aún más ... gracias
 – 
Erez
19 dic. 2011 a las 01:42