ScrollViewer 和 TextBlock 自动换行

3

我有以下布局(简化):

<Grid>
  <Grid.ColumnDefinitions>
    <ColumnDefinition MaxWidth="400" />
    <ColumnDefinition />
  </Grid.ColumnDefinitions>

  <!-- Code for Column=0 -->

  <ScrollViewer Grid.Column="1">
    <Grid x:Name="layoutGrid">

      <Grid.ColumnDefinitions>
        <Grid.ColumnDefinition Width="Auto" />
        <Grid.ColumnDefinition MinWidth="100" MaxWidth="400" />
        <Grid.ColumnDefinition Width="Auto" />
      </Grid.ColumnDefinitions>

      <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
      </Grid.RowDefinitions>


      <!-- Code for Row=0 and Row=1 -->

      <GroupBox Grid.ColumnSpan="3" Grid.Row=2>
        <TextBlock Text="{Binding ...}" TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Top" />
      </GroupBox>
    </Grid>
  </ScrollViewer>
</Grid>
  • 第一列应该占用所需的空间(有时可能是100像素,有时是500像素)。
  • 第二列应该扩展到可用空间,但不超过400个像素(会很难看)。
  • 第三列应该占用所需的空间(有时可以是200像素,有时是400像素)。
  • 在极少数情况下,如果layoutGrid需要比屏幕上可用的空间更多的空间,则应显示水平滚动条。
  • GroupBox应始终具有三列的总宽度(它应该扩展为它们的总宽度)。在这个空间中,文本框应该换行。 GroupBox不应该扩展到屏幕上所有可用的空间。

我该如何在xaml中实现这一点?似乎只要插入ScrollViewer,TextBlock就不再包装。

4个回答

5
只需为TextBlock设置一个MaxWidth,它是GroupBox或者在你的情况下甚至是layoutGrid(因为你的GroupBox具有相同的宽度)的ActualWidth。当TextBlockWidth超过该尺寸时,它将被强制换行,从而满足您的要求。例如:
<GroupBox x:Name="grpBox"
          Grid.Row="2"
          Grid.ColumnSpan="3">
  <TextBlock MaxWidth="{Binding ElementName=grpBox,
                                Path=ActualWidth}"
              HorizontalAlignment="Left"
              VerticalAlignment="Top"
              Text="{Binding ...}"
              TextWrapping="Wrap" />
</GroupBox>

或者

<TextBlock MaxWidth="{Binding ElementName=layoutGrid,
                              Path=ActualWidth}"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            Text="{Binding ...}"
            TextWrapping="Wrap" />

嗨Viv,感谢你的提示。无法在GroupBox或LayoutGrid上执行此操作,因为它们是TextBlock的父元素,并且它们将根据其子元素的请求设置宽度(在我们的情况下,其中一个子元素是TextBlock)。但是,如果我们将GroupBox从LayoutGrid中移除并将其放置在下方,那么它们的大小就不会直接相互影响,因此可以安全地应用您的建议。 - Goran
基本上可以工作了,不过缩小操作不能减小 ActualWidth。因此,只能进行放大操作。有没有解决办法呢? - bytecode77

4

我已经创建了一个控件包装器IgnoreWidthControl,用于此目的:

public class IgnoreWidthControl : ContentControl
{
    protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
    {
        base.OnRenderSizeChanged(sizeInfo);
        if (sizeInfo.WidthChanged)
            InvalidateMeasure();
    }

    protected override Size MeasureOverride(Size constraint)
    {
        constraint.Width = ActualWidth;
        Size size = new Size();
        UIElement child = GetFirstVisualChild();
        if (child != null)
        {
            child.Measure(constraint);
            size.Height = child.DesiredSize.Height;
        }

        return size;
    }

    private UIElement GetFirstVisualChild()
    {
        if (this.VisualChildrenCount <= 0)
            return null;
        return this.GetVisualChild(0) as UIElement;
    }
}

下面是使用示例:

<myc:IgnoreWidthControl>
    <TextBlock Text="Very long text which has to be wrapped. Yeah, it must be wrapped." TextWrapping="Wrap" />
</myc:IgnoreWidthControl>

2

我在一个 <ItemsControl> 中使用了 TextBlocks 数据绑定,并希望它们像聊天室一样垂直堆叠。但是较长的 TextBlocks 没有自动换行。

我将 <ScrollViewer>HorizontalScrollBarVisibilityHidden 改为 Disabled,这样即使调整大小,TextBlocks 也会跨越多行自动换行:

        <ScrollViewer 
          VerticalScrollBarVisibility="Auto" 
          HorizontalScrollBarVisibility="Disabled">
                      <ItemsControl 
                         VerticalAlignment="Stretch" 
                         Height="Auto" >

0

如果你从逻辑上考虑你的情况,那么你会明白当一个 TextBlock 没有设置其 Width 属性时,放置在 ScrollViewer 中是不会自动换行的。只有在告诉 TextBlock 何时开始换行时,它才能换行... 实际上,“何时”指的是“哪里”。需要告诉它:“从左边150像素开始,开始换行文本内容。”

现在我们可以这样告诉它:

<TextBlock Text="Some random long text string that is longer than 150 pixels long" 
    TextWrapping="Wrap" Width="150" />

或者像这样:

<TextBlock Text="Some random long text string that is longer than 150 pixels long" 
    TextWrapping="Wrap" MaxWidth="150" />

或者像这样:

<Grid Width="150">
    <TextBlock Text="Some random long text string that is longer than 150 pixels long" 
    TextWrapping="Wrap" />
</Grid>

或者像这样:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="150" />
    </Grid.ColumnDefinitions>
    <TextBlock Grid.Column="0" Text="Some random long text string that is longer than 
        150 pixels long" TextWrapping="Wrap" />
</Grid>

然而,如果您删除Width限制,则TextBlock将永远不知道何时应开始换行Text。通过将TextBlock放入ScrollViewer中,您告诉它它拥有所有可能需要的空间,因此,如果没有在其上设置Width限制,则它将永远不会换行。


你说的一切我都已经知道了。但是正如我所说,我不能使用固定宽度或者TextBlock。然而,如果我采用Viv提供的方法,它就会换行。我所需要做的就是让layoutGrid不要成为TextBlock的父级。这样我们就可以将TextBlock的宽度设置为layoutGrid的宽度。 - Goran

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接