在WPF画布上的给定点处居中文本

Jon*_*rop 18 .net c# wpf f#

我有Controls.Canvas几个形状,并希望添加以给定点为中心的文本标签(我正在绘制带有标记顶点的树).在WPF中以编程方式执行此操作的最简单方法是什么?

我尝试过设置RenderTransform和调用Controls.Canvas.SetLeft等,但是没有将标签放在我想要的位置.WPF似乎只支持在给定的左,右,顶部和底部坐标处定位,而不是以给定坐标为中心,Width属性是NaN,并且ActualWidth属性是0.0我构造时的属性Canvas.

小智 15

您可以通过将标签的边距绑定到标签ActualWidthActualHeight标签,并将这些值乘以-0.5 来实现此目的.这会使标签向左移动一半宽度; 它将标签向上移动一半高度.

这是一个例子:

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>
Run Code Online (Sandbox Code Playgroud)

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

转换器:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

结果如下:

中心标签

为了以编程方式执行此操作,请定义附加属性Mover.MoveToMiddle,如下所示:

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

}
Run Code Online (Sandbox Code Playgroud)

设置Mover.MoveToMiddletrue表示该框架元素的边距自动绑定到其实际宽度和高度,以便框架元素移动到其中心点.

您可以在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 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>
Run Code Online (Sandbox Code Playgroud)

另一种方法是绑定RenderTransform而不是Margin.在这种情况下,转换器将返回

return new TranslateTransform(-width / 2, -height / 2);
Run Code Online (Sandbox Code Playgroud)

附加属性的回调方法将包含以下行:

if ((bool)e.NewValue)
{
    ...
    element.SetBinding(UIElement.RenderTransformProperty, multiBinding);
}
else
{
    element.ClearValue(UIElement.RenderTransformProperty);
}
Run Code Online (Sandbox Code Playgroud)

此替代方案的优点是附加属性的效果在Visual Studio设计器中可见(在设置Margin属性时不是这种情况).


yet*_*sta 11

这也有效,但约束力较小.

public class CenterOnPoint
{
  public static readonly DependencyProperty CenterPointProperty =
     DependencyProperty.RegisterAttached("CenterPoint", typeof (Point), typeof (CenterOnPoint),
     new PropertyMetadata(default(Point), OnPointChanged));

  public static void SetCenterPoint(UIElement element, Point value)
  {
     element.SetValue(CenterPointProperty, value);
  }

  public static Point GetCenterPoint(UIElement element)
  {
     return (Point) element.GetValue(CenterPointProperty);
  }

  private static void OnPointChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  {
     var element = (FrameworkElement)d;
     element.SizeChanged -= OnSizeChanged;
     element.SizeChanged += OnSizeChanged;
     var newPoint = (Point)e.NewValue;
     element.SetValue(Canvas.LeftProperty, newPoint.X - (element.ActualWidth / 2));
     element.SetValue(Canvas.TopProperty, newPoint.Y - (element.ActualHeight / 2));
  }

  private static void OnSizeChanged(object sender, SizeChangedEventArgs e)
  {
     var element = (FrameworkElement) sender;
     var newPoint = GetCenterPoint(element);
     element.SetValue(Canvas.LeftProperty, newPoint.X - (e.NewSize.Width / 2));
     element.SetValue(Canvas.TopProperty, newPoint.Y - (e.NewSize.Height / 2));
  }
}
Run Code Online (Sandbox Code Playgroud)

你就这样用它......

label.SetValue(CenterOnPoint.CenterPointProperty, new Point(100, 100));
Run Code Online (Sandbox Code Playgroud)

  • 人们可能会看到这些答案并认为这对于问题的简单要求来说太复杂了,但这不仅仅是向您展示如何使标签居中.这段代码定义了一个行为,它将任何`FrameworkElement`集中在一个点上并使其保持居中,即使该元素的大小可能正在改变. (4认同)