【问题标题】:How WPF layout deals with text wrappingWPF 布局如何处理文本换行
【发布时间】:2016-02-07 22:22:23
【问题描述】:

WPF 布局的工作原理是首先要求控件测量自身,然后根据它们在测量阶段生成的所需大小来排列控件。

我看不出在这种情况下如何处理文本换行。

包含要换行的文本的控件必须先知道其宽度,然后才能计算其高度。但在测量阶段宽度是未知的。当所有测量完成后,它将由排列阶段分配。所以测量时无法计算高度。

文本换行在 WPF 中是如何工作的?

【问题讨论】:

  • “但在测量阶段宽度未知。”那你觉得availableSize参数是干什么用的?请记住,布局是递归完成的。
  • @Clemens 是正确的。可用宽度在测量期间传入。所以包裹的文本将宽度然后向下扩展。
  • @Clemens availableWidth 是在子容器上完成任何布局之前父容器中的总可用宽度。分配给文本控件的最终宽度不一定与 availableWidth 具有相同的值。如果有,那么测量阶段将是多余的。在调用 Arrange 之前,文本控件不知道其实际宽度。
  • 带环绕的文本布局是根据测量阶段的可用宽度完成的。如果在极少数情况下最终宽度小于该值,则文本将在 x 维度上被截断,它不会重新计算换行,正如您似乎暗示的那样。
  • "availableWidth 是在任何布局完成之前父容器中的总可用宽度"。我建议您尝试以下操作:创建一个 FrameworkElement 派生类,覆盖其 MeasureOverride 和 ArrangeOverride 方法,添加一些大小的调试输出并调用基类方法。然后将该元素放在一个 2x2 的网格中,该网格填充了窗口区域。调整窗口大小并观察调试输出。

标签: wpf text word-wrap


【解决方案1】:

这个非常简单的自定义 TextBlock 展示了它的工作原理:

public class MyTextBlock : FrameworkElement
{
    private FormattedText formattedText;

    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register(
            "Text", typeof(string), typeof(MyTextBlock),
            new FrameworkPropertyMetadata(
                null,
                FrameworkPropertyMetadataOptions.AffectsMeasure,
                (o, e) =>
                {
                    ((MyTextBlock)o).formattedText = new FormattedText(
                        (string)e.NewValue,
                        CultureInfo.InvariantCulture,
                        FlowDirection.LeftToRight,
                        new Typeface("Segoe UI"),
                        24,
                        Brushes.Black);
                }));

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

    protected override Size MeasureOverride(Size availableSize)
    {
        var size = new Size();

        if (formattedText != null)
        {
            formattedText.MaxTextWidth = availableSize.Width;
            size.Width = formattedText.Width;
            size.Height = Math.Min(formattedText.Height, availableSize.Height);
        }

        return size;
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        if (formattedText != null)
        {
            formattedText.MaxTextWidth = finalSize.Width;
            formattedText.MaxTextHeight = finalSize.Height;
        }

        return finalSize;
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);

        if (formattedText != null)
        {
            drawingContext.DrawText(formattedText, new Point());
        }
    }
}

【讨论】:

    【解决方案2】:

    似乎 WPF 文本控件假定控件的最终大小在 Measure 阶段将与 availableSize 相同。如果这不是真的,文本布局将是错误的,文本将被截断。由于这种情况很少发生,如果需要,可以通过调整父容器控件来防止这种情况发生,这是可以接受的。

    【讨论】:

    • @Clemens 您的代码根据生成的文本布局从 MeasureOverride 返回所需的控件大小,假设控件的大小为 availableSize。然后父控件将为控件分配 finalSize。在 ArrangeOverride 中,您根据 finalSize 重新布局文本。如果 finalSize 比 availableSize 窄,您的文本将不再适合,因为它会占用比 MeasureOverride 中计算的更多空间。您的控件应该更高,但您无法知道 finalSize 会比 availableSize 更窄。
    • 那么,您的这种情况的真实示例(即代码)在哪里?重要的一点是 MeasureOverride 永远不会返回大于 availableSize 的 Size。它一定是一个恶意 Panel,它会排列一个尺寸小于它刚刚传递给它的 Measure 方法的子元素。即使这样,TextBlock 也会正确呈现,因为 FormattedText 的边界在 ArrangeOverride 中更新。
    猜你喜欢
    • 2013-03-08
    • 1970-01-01
    • 2015-04-21
    • 2018-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-25
    相关资源
    最近更新 更多