在上一个WPF画布给定点中心文画布、在上、中心、WPF

2023-09-03 10:07:24 作者:热情退散

我有一个 Controls.Canvas 上面有几种形状和想补充的是集中于特定点的文本标签(我画一棵树与标记的顶点)。什么是做到这一点编程方式在WPF中最简单的方法是什么?

I have a Controls.Canvas with several shapes on it and would like to add textual labels that are centered on given points (I'm drawing a tree with labelled vertices). What is the simplest way to do this programmatically in WPF?

我已经尝试设置的RenderTransform 并调用 Controls.Canvas.SetLeft 等,但无论位置标签,我想要它。 WPF似乎只在支持定位给定的左,右,上,下​​的坐标,而不是集中在一个给定的坐标和宽度属性 NaN的 ActualWidth的属性 0.0 时,我构建了画布

I have tried setting RenderTransform and calling Controls.Canvas.SetLeft etc. but neither position the label where I want it. WPF seems to support positioning only at given left, right, top and bottom coordinates and not centered on a given coordinate and the Width property is NaN and the ActualWidth property is 0.0 when I construct the Canvas.

推荐答案

您可以通过标签的边缘结合到 ActualWidth的的ActualHeight 的标签,并与-0.5乘以这些值。这会将通过其宽度的一半留下的标签;和它的一半的高度移动标签向上

You could achieve this by binding the margin of the label to the ActualWidth and ActualHeight of the label, and multiplying these values with -0.5. This moves the label left by half its width; and it moves the label upwards by half its height.

下面是一个例子:

XAML:

<Window x:Class="CenteredLabelTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:CenteredLabelTest"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:CenterConverter x:Key="centerConverter"/>
    </Window.Resources>
    <Canvas>
        <TextBlock x:Name="txt" Canvas.Left="40" Canvas.Top="40" TextAlignment="Center" Text="MMMMMM">
            <TextBlock.Margin>
                <MultiBinding Converter="{StaticResource centerConverter}">
                        <Binding ElementName="txt" Path="ActualWidth"/>
                        <Binding ElementName="txt" Path="ActualHeight"/>
                </MultiBinding>
            </TextBlock.Margin>
        </TextBlock>
        <Rectangle Canvas.Left="39" Canvas.Top="39" Width="2" Height="2" Fill="Red"/>
    </Canvas>
</Window>

红色矩形突出的坐标(40,40),其上标注MMMMMM为中心。

The red rectangle highlights the coordinate (40, 40) on which the label "MMMMMM" is centered.

转换:

public class CenterConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values[0] == DependencyProperty.UnsetValue || values[1] == DependencyProperty.UnsetValue)
        {
            return DependencyProperty.UnsetValue;
        }

        double width = (double) values[0];
        double height = (double)values[1];

        return new Thickness(-width/2, -height/2, 0, 0);
    }

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

结果是这样的:

The result looks like this:

为了做到这一点编程,定义一个附加属性 Mover.MoveToMiddle ,像这样的:

In order to do that programmatically, define an attached property Mover.MoveToMiddle, like this:

public class Mover : DependencyObject
{
    public static readonly DependencyProperty MoveToMiddleProperty =
        DependencyProperty.RegisterAttached("MoveToMiddle", typeof (bool), typeof (Mover),
        new PropertyMetadata(false, PropertyChangedCallback));

    public static void SetMoveToMiddle(UIElement element, bool value)
    {
        element.SetValue(MoveToMiddleProperty, value);
    }

    public static bool GetMoveToMiddle(UIElement element)
    {
        return (bool) element.GetValue(MoveToMiddleProperty);
    }

    private static void PropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        FrameworkElement element = sender as FrameworkElement;
        if (element == null)
        {
            return;
        }

        if ((bool)e.NewValue)
        {
            MultiBinding multiBinding = new MultiBinding();
            multiBinding.Converter = new CenterConverter();
            multiBinding.Bindings.Add(new Binding("ActualWidth") {Source = element});
            multiBinding.Bindings.Add(new Binding("ActualHeight") {Source = element});
            element.SetBinding(FrameworkElement.MarginProperty, multiBinding);
        }
        else
        {
            element.ClearValue(FrameworkElement.MarginProperty);
        }
    }

}

Mover.MoveToMiddle 设置为意味着该框架元素的边距自动绑定到其实际宽度和高度,使得所述框架元件移动到其中心点。

Setting Mover.MoveToMiddle to true means that the margin of that framework element is automatically bound to its actual width and height such that the framework element is moved to its center point.

您将使用它在你的XAML code是这样的:

You would use it in your XAML code like this:

<Window x:Class="CenteredLabelTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:CenteredLabelTest"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:CenterConverter x:Key="centerConverter"/>
    </Window.Resources>
    <Canvas>
        <TextBlock Canvas.Left="40" Canvas.Top="40" TextAlignment="Center" Text="MMMMMM"
              local:Mover.MoveToMiddle="True"/>
        <Rectangle Canvas.Left="39" Canvas.Top="39" Width="2" Height="2" Fill="Red"/>
    </Canvas>
</Window>

另一种方法是绑定的,而不是保证金的RenderTransform 。在这种情况下,转换器将返回

An alternative would be to bind to RenderTransform instead of Margin. In this case, the converter would return

return new TranslateTransform(-width / 2, -height / 2);

和附加属性的回调方法将包含这些行:

and the attached property's callback method would contain these lines:

if ((bool)e.NewValue)
{
    ...
    element.SetBinding(UIElement.RenderTransformProperty, multiBinding);
}
else
{
    element.ClearValue(UIElement.RenderTransformProperty);
}

该替代的优点是附加属性的效果是明显的,在Visual Studio设计(这是不是设置保证金属性时的情况)。

This alternative has the advantage that the effect of the attached property is visible in the Visual Studio designer (which is not the case when setting the Margin property).