在 WPF 画布上的给定点居中文本 [英] Center text at a given point on a WPF Canvas

查看:17
本文介绍了在 WPF 画布上的给定点居中文本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有多个形状的 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 似乎只支持在给定的左、右、顶和底坐标处定位,而不以给定坐标为中心,并且 Width 属性是 NaNActualWidth<当我构造 Canvas 时,/code> 属性为 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.

推荐答案

您可以通过将标签的边距绑定到标签的 ActualWidthActualHeight 来实现这一点,并将这些值与 -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>

红色矩形突出显示了标签MMMMMM"居中的坐标 (40, 40).

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();
    }
}

结果如下:

为了以编程方式执行此操作,定义附加属性 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 设置为 true 意味着该框架元素的边距会自动绑定到其实际宽度和高度,从而将框架元素移动到其中心点.

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 代码中使用它:

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 而不是 Margin.在这种情况下,转换器将返回

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 设计器中可见(设置 Margin 属性时并非如此).

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).

这篇关于在 WPF 画布上的给定点居中文本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆