首页 > 解决方案 > C# WPF - 在 TextBlocks 中对齐单行文本

问题描述

我有一个 StackPanel,里面有几个 TextBlock,如下所示:

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
    <TextBlock Text="First is quite lengthy" />
    <TextBlock Text="This one's shorter"/>
    <TextBlock Text="This one too"/>
    <TextBlock Text="This one is a bit longer again"/>
</StackPanel>

我想证明所有这些都是合理的,以便 TextBlocks 的左侧和右侧都垂直对齐 - 我该怎么做?

上面的代码如下所示:

在此处输入图像描述


粗略估计我想要实现的目标:

在此处输入图像描述

我尝试过使用 TextAlignment(Justify 对我不起作用)、FontStretch、Horizo​​ntalAlignment、DockPanel 和 Viewbox(因为我想保持字体大小不变,所以它不起作用)。如果不对文本中的一些空格进行硬编码,我可以做些什么来实现这一点?

标签: c#wpftextblock

解决方案


我惊讶地发现 WPF 默认情况下不支持对齐的单行 TextBlocks。但是,您可以相对轻松地自己实现此行为。我拼凑了一个快速的解决方案,它提供了一个用户控件,其唯一目的是绘制一个合理的单行文本。

解决方案非常简单,不支持文本换行、边距或任何其他特殊布局情况,只有最基本的 DependencyProperties。不过,这应该是一个很好的起点。

public partial class JustifiedLine : UserControl
{
    public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
        "Text",
        typeof(string),
        typeof(JustifiedLine),
        new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsRender));

    public static readonly DependencyProperty MinSpaceWidthProperty = DependencyProperty.Register(
        "MinSpaceWidth",
        typeof(double),
        typeof(JustifiedLine),
        new FrameworkPropertyMetadata(5.0, FrameworkPropertyMetadataOptions.AffectsRender));

    public string Text
    {
        get => (string)GetValue(TextProperty);
        set => SetValue(TextProperty, value);
    }

    public double MinSpaceWidth
    {
        get => (double)GetValue(MinSpaceWidthProperty);
        set => SetValue(MinSpaceWidthProperty, value);
    }

    public JustifiedLine()
    {
        InitializeComponent();
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        string[] words = Text.Split(' ');

        if (words.Length == 0)
            return;

        int numValidWords = 0;
        double totalWordsWidth = 0;

        /* add widths of all words */
        for (int i = 0; i < words.Length; i++)
        {
            if (String.IsNullOrWhiteSpace(words[i]))
                continue;

            FormattedText fText = MakeFormattedText(words[i]);
            totalWordsWidth += fText.Width;

            numValidWords++;
        }

        double fullWidth = GetFullWidth();

        double spaceWidth = (fullWidth - totalWordsWidth) / (numValidWords - 1);
        if (spaceWidth < MinSpaceWidth)
            spaceWidth = MinSpaceWidth;

        double currentWidth = 0;
        for (int i = 0; i < words.Length; i++)
        {
            if (String.IsNullOrWhiteSpace(words[i]))
                continue;

            FormattedText fText = MakeFormattedText(words[i]);

            drawingContext.DrawText(fText, new Point(currentWidth, 0));
            currentWidth += fText.Width + spaceWidth;

            /* if Height isn't specified, automatically set from text height */
            if (double.IsNaN(Height))
                Height = fText.Height;
        }
    }

    /* return either width of this control, or the parent control if not set. If neither is set, return 0.0 */
    private double GetFullWidth()
    {
        if (!double.IsNaN(Width))
            return Width;

        FrameworkElement parent = Parent as FrameworkElement;
        if (parent != null && !double.IsNaN(parent.ActualWidth))
            return parent.ActualWidth;

        return 0.0;
    }

    /* might want to bind font etc. to dependency property, these here are just placeholders */
    private FormattedText MakeFormattedText(string text)
    {
        return new FormattedText(
                text,
                CultureInfo.GetCultureInfo("en-us"),
                FlowDirection.LeftToRight,
                new Typeface("Verdana"),
                16,
                Brushes.White,
                VisualTreeHelper.GetDpi(this).PixelsPerDip);
    }
}

如果这样使用:

<StackPanel Width="300">
            <usercontrols:JustifiedLine Text="First is quite lengthy"/>
            <usercontrols:JustifiedLine Text="This one's shorter"/>
            <usercontrols:JustifiedLine Text="This one too"/>
            <usercontrols:JustifiedLine Text="This one is a bit longer again"/>
</StackPanel>

xmlns:usercontrols="clr-namespace:MyProject.controls.usercontrols"

这是我的结果:

产生的对齐线


推荐阅读