ScrollViewer и обтекание TextBlock

У меня есть следующий макет (упрощенно):

<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 ответа

Решение

Просто дайте TextBlock MaxWidth какой ActualWidth либо из GroupBox или в вашем случае даже layoutGrid (как твой GroupBox имеет одинаковую ширину). Это заставит TextBlock чтобы обернуть, когда это Width превышает это измерение и тем самым дает вам ваши требования.

Так что-то вроде:

<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" />

Я создал контрольную оболочку 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>

У меня были данные TextBlocks, связанные в<ItemsControl>и хотел, чтобы они складывались вертикально, как чат. Более длинные текстовые блоки не переносились.

я изменил<ScrollViewer> HorizontalScrollBarVisibilityотHiddenкDisabled. Это привело к переносу TextBlocks между строками даже при изменении размера:

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

Если вы думаете о своей ситуации логически, то вы поймете, что, конечно, 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 ограничение на это, оно никогда не закутается.

Другие вопросы по тегам