Как заставить детей StackPanel заполнить максимальное пространство вниз?

356

Я просто хочу, чтобы текст был слева, а окно справки - справа.

Окно справки должно доходить до самого дна.

Если вы возьмете наружу StackPanelвнизу, это будет прекрасно работать.

Но по причинам макета (я вставляю UserControls динамически) мне нужно иметь обертку StackPanel.

Как получить GroupBoxрасширение до нижней части StackPanel, как вы можете видеть, я пытался:

  • VerticalAlignment="Stretch"
  • VerticalContentAlignment="Stretch"
  • Height="Auto"

XAML:

<Window x:Class="TestDynamic033.Test3"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Test3" Height="300" Width="600">
    <StackPanel 
        VerticalAlignment="Stretch" 
        Height="Auto">

        <DockPanel 
            HorizontalAlignment="Stretch" 
            VerticalAlignment="Stretch" 
            Height="Auto" 
            Margin="10">

            <GroupBox 
                DockPanel.Dock="Right" 
                Header="Help" 
                Width="100" 
                Background="Beige" 
                VerticalAlignment="Stretch" 
                VerticalContentAlignment="Stretch" 
                Height="Auto">
                <TextBlock Text="This is the help that is available on the news screen." TextWrapping="Wrap" />
            </GroupBox>

            <StackPanel DockPanel.Dock="Left" Margin="10" Width="Auto" HorizontalAlignment="Stretch">
                <TextBlock Text="Here is the news that should wrap around." TextWrapping="Wrap"/>
            </StackPanel>

        </DockPanel>
    </StackPanel>
</Window>

Ответ:

Спасибо Марк, используя DockPanelвместо того, чтобы StackPanelпрояснить это. В общем, DockPanelтеперь я все больше и больше использую макет WPF, вот фиксированный XAML:

<Window x:Class="TestDynamic033.Test3"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Test3" Height="300" Width="600" MinWidth="500" MinHeight="200">
    <DockPanel 
        VerticalAlignment="Stretch" 
        Height="Auto">

        <DockPanel 
            HorizontalAlignment="Stretch" 
            VerticalAlignment="Stretch" 
            Height="Auto" 
            MinWidth="400"
            Margin="10">

            <GroupBox 
                DockPanel.Dock="Right" 
                Header="Help" 
                Width="100" 
                VerticalAlignment="Stretch" 
                VerticalContentAlignment="Stretch" 
                Height="Auto">
                <Border CornerRadius="3" Background="Beige">
                    <TextBlock Text="This is the help that is available on the news screen." TextWrapping="Wrap" 

                Padding="5"/>
                </Border>
            </GroupBox>

            <StackPanel DockPanel.Dock="Left" Margin="10" Width="Auto" HorizontalAlignment="Stretch">
                <TextBlock Text="Here is the news that should wrap around." TextWrapping="Wrap"/>
            </StackPanel>

        </DockPanel>
    </DockPanel>
</Window>
Эдвард Тангей
источник
Исправлено форматирование - не нравилось переходить прямо из списка в код
Грег
1
Можете ли вы заставить GroupBox растягиваться таким образом самостоятельно? Если это так, начните добавлять свои родительские элементы один за другим, пока не узнаете, какой из них нарушает макет.
Дрю Ноакс
Роборг: приятно знать, что меня это озадачило, спасибо
Эдвард Тангей
1
Спасибо. Используя ваш ответ, я смог использовать 2 вложенных DockPanels для решения моей очень похожей проблемы!
Ябларго

Ответы:

344

Похоже, вы хотите, StackPanelчтобы последний элемент занимал все оставшееся пространство. Но почему бы не использовать DockPanel? Украсьте другие элементы в DockPanelwith DockPanel.Dock="Top", и тогда ваш элемент управления справкой сможет заполнить оставшееся пространство.

XAML:

<DockPanel Width="200" Height="200" Background="PowderBlue">
    <TextBlock DockPanel.Dock="Top">Something</TextBlock>
    <TextBlock DockPanel.Dock="Top">Something else</TextBlock>
    <DockPanel
        HorizontalAlignment="Stretch" 
        VerticalAlignment="Stretch" 
        Height="Auto" 
        Margin="10">

      <GroupBox 
        DockPanel.Dock="Right" 
        Header="Help" 
        Width="100" 
        Background="Beige" 
        VerticalAlignment="Stretch" 
        VerticalContentAlignment="Stretch" 
        Height="Auto">
        <TextBlock Text="This is the help that is available on the news screen." 
                   TextWrapping="Wrap" />
     </GroupBox>

      <StackPanel DockPanel.Dock="Left" Margin="10" 
           Width="Auto" HorizontalAlignment="Stretch">
          <TextBlock Text="Here is the news that should wrap around." 
                     TextWrapping="Wrap"/>
      </StackPanel>
    </DockPanel>
</DockPanel>

Если вы находитесь на платформе без DockPanelдоступных (например, WindowsStore), вы можете создать тот же эффект с сеткой. Вот приведенный выше пример, выполненный с использованием сетки:

<Grid Width="200" Height="200" Background="PowderBlue">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <StackPanel Grid.Row="0">
        <TextBlock>Something</TextBlock>
        <TextBlock>Something else</TextBlock>
    </StackPanel>
    <Grid Height="Auto" Grid.Row="1" Margin="10">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="100"/>
        </Grid.ColumnDefinitions>
        <GroupBox
            Width="100"
            Height="Auto"
            Grid.Column="1"
            Background="Beige"
            Header="Help">
            <TextBlock Text="This is the help that is available on the news screen." 
              TextWrapping="Wrap"/>
        </GroupBox>
        <StackPanel Width="Auto" Margin="10" DockPanel.Dock="Left">
            <TextBlock Text="Here is the news that should wrap around." 
              TextWrapping="Wrap"/>
        </StackPanel>
    </Grid>
</Grid>
Марк Хит
источник
18
Brilliant! Я провел последний час, пытаясь выяснить, как заставить StackPanel сделать это. С этого момента я сначала буду искать информацию о моем WPF (и другой).
paxdiablo
7
Я не могу поверить, сколько времени я потратил, пытаясь заставить StackPanels делать то, что я хотел. Спасибо, что поделился! Док-панели это то, что я хотел все время.
danglund
Кажется, для планшетов нет док-панели. telerik.com/forums/following-blog-post-and-comparison
JP Hellemons
1
Нет док-панели для приложений Магазина Windows.
Теоман Шипахи
Теперь все дело в универсальном приложении, а универсальные приложения еще не поддерживают DockPanel?
yonexbat
105

Причина, по которой это происходит, заключается в том, что панель стека измеряет каждый дочерний элемент с положительной бесконечностью в качестве ограничения для оси, на которой он укладывает элементы. Дочерние элементы управления должны возвращать, насколько большим они хотят быть (положительная бесконечность не является действительным возвращением от MeasureOverride по любой из осей), поэтому они возвращают наименьший размер, где все будет соответствовать. У них нет возможности узнать, сколько места они действительно должны заполнить.

Если вашему представлению не нужна функция прокрутки и приведенный выше ответ не соответствует вашим потребностям, я бы предложил создать собственную панель. Вероятно, вы можете получить прямо из StackPanel, и тогда все, что вам нужно будет сделать, это изменить метод ArrangeOverride, чтобы он разделял оставшееся пространство между дочерними элементами (предоставляя им одинаковое количество дополнительного пространства). Элементы должны хорошо отображаться, если им дают больше места, чем они хотели, но если вы дадите им меньше, вы начнете видеть глюки.

Если вы хотите иметь возможность прокручивать все это, то я боюсь, что все будет немного сложнее, потому что ScrollViewer дает вам бесконечное количество пространства для работы, которое поставит вас в то же положение, что и дочерние элементы. первоначально. В этой ситуации вы можете захотеть создать новое свойство на новой панели, которое позволит вам указать размер области просмотра, вы должны иметь возможность привязать это к размеру ScrollViewer. В идеале вы должны реализовать IScrollInfo , но это становится сложным, если вы собираетесь реализовать все это правильно.

Калеб Вир
источник
+1, я бы дал вам больше, но разрешено только 1, в первом параграфе было указано, что не удалось многим страницам Microsoft, а именно, почему бесконечность может возникать как высота / ширина, а также тот факт, что вы не можете полагаться на возвращение availableSize из MeasureOverride. ,
Эйдан
StackPanel внутри Grid решает его довольно обычную задачу с большой легкостью. Нижний бит может быть помещен в ScrollViewer, если это необходимо. Я занимаюсь WPF с 2006 года, и мне нужно было всего один раз создать собственную панель. Я не думаю, что это хорошая идея поощрять дополнительные сложности.
Крис Бордеман
@ChrisBordeman Я не уверен, что понимаю, как панель стека внутри сетки решает проблему. Идея состоит в том, чтобы один или несколько дочерних элементов растягивались на панели стека, чтобы заполнить доступное пространство. Помещение панели стека в сетку не заставляет это делать это?
Калеб Вир
61

Альтернативный метод - использовать Grid с одним столбцом и n строками. Установите высоту всех рядов Auto, а высоту самого нижнего ряда - 1*.

Я предпочитаю этот метод, потому что я обнаружил, что гриды имеют лучшую производительность компоновки, чем DockPanels, StackPanels и WrapPanels. Но если вы не используете их в ItemTemplate (где макет выполняется для большого количества элементов), вы, вероятно, никогда не заметите.

rcabr
источник
1
для меня лучшее решение. с помощью этого можно определить более одной растущей строки
niyou
18

Вы можете использовать SpicyTaco.AutoGrid - модифицированную версию StackPanel:

<st:StackPanel Orientation="Horizontal" MarginBetweenChildren="10" Margin="10">
   <Button Content="Info" HorizontalAlignment="Left" st:StackPanel.Fill="Fill"/>
   <Button Content="Cancel"/>
   <Button Content="Save"/>
</st:StackPanel>

Первая кнопка будет заполнена.

Вы можете установить его через NuGet:

Install-Package SpicyTaco.AutoGrid

Я рекомендую взглянуть на SpicyTaco.AutoGrid . Это очень полезно для форм в WPF DockPanel, StackPanelа также Gridпозволяет легко и изящно решить проблему растяжения. Просто посмотрите на readme на GitHub.

<st:AutoGrid Columns="160,*" ChildMargin="3">
    <Label Content="Name:"/>
    <TextBox/>

    <Label Content="E-Mail:"/>
    <TextBox/>

    <Label Content="Comment:"/>
    <TextBox/>
</st:AutoGrid>
Dvor_nik
источник