Обтекание 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 больше не переносится.


person Goran    schedule 12.12.2013    source источник


Ответы (3)


Просто дайте 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>

or

<TextBlock MaxWidth="{Binding ElementName=layoutGrid,
                              Path=ActualWidth}"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            Text="{Binding ...}"
            TextWrapping="Wrap" />
person Viv    schedule 12.12.2013
comment
Привет, Вив, спасибо за подсказку. Это невозможно сделать с GroupBox или LayoutGrid, так как это родительские элементы TextBlock, и они будут устанавливать свою ширину в соответствии с запросом своих дочерних элементов (в нашем случае один из дочерних элементов — TextBlock). Но совет можно смело применять, если мы уберем GroupBox за пределы layoutGrid и разместим прямо под ним, тогда их размеры напрямую друг на друга не влияют, так что ваш совет можно применить. - person Goran; 13.12.2013
comment
Почти работает, однако уменьшение размера не уменьшает ActualWidth. Поэтому возможно только увеличение размера. Есть ли какое-нибудь решение для этого? - person bytecode77; 06.12.2016

Для этой цели я создал оболочку управления 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>
person xmedeko    schedule 22.12.2016

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

person Sheridan    schedule 12.12.2013
comment
Все, что ты сказал, я уже знал. Но, как я уже сказал, у меня не может быть фиксированной ширины или TextBlock. Однако это завершится, если я воспользуюсь подходом, предложенным Вив. Все, что мне нужно было сделать, это сделать layoutGrid не родительским для TextBlock. Таким образом, мы можем установить ширину TextBlock равной ширине layoutGrid. - person Goran; 13.12.2013