如何使StackPanel的子项向下填充最大空间?

408

我只想在左侧显示流动文本,右侧显示一个帮助框。

帮助框应该一直延伸到底部。

如果将下面的外部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>

答案:

感谢 Mark,使用 DockPanel 替换 StackPanel 解决了问题。总的来说,我现在在 WPF 布局中越来越多地使用 DockPanel,以下是已修复的 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>

已修复格式 - 它不喜欢从列表直接转到代码。 - Greg
1
你能让一个GroupBox自动拉伸吗?如果可以,就逐个添加父元素,直到找出破坏布局的元素为止。 - Drew Noakes
RoBorg: 知道了很好,那让我感到困惑,谢谢。 - Edward Tanguay
1
谢谢。使用您的答案,我能够使用2个嵌套的DockPanel来解决我的非常相似的问题! - Yablargo
4个回答

391

看起来你想要一个StackPanel,最后一个元素占用所有剩余的空间。但是为什么不使用DockPanel呢?在DockPanel中,用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>

20
太棒了!我花了最近一个小时试图想办法让StackPanel实现这个功能。从现在开始,我会首先来这里寻找我的WPF(和其他)信息。 - paxdiablo
8
我花了很多时间尝试让 StackPanels 做我想要的事情,简直不敢相信。谢谢你分享!一直以来我都想要的是 DockPanels。 - Dan Gøran Lunde
1
Windows Store 应用程序没有停靠面板。 - Teoman shipahi
现在一切都关乎通用应用程序,通用应用程序还不支持DockPanel吗? - yonexbat
@yonexbat,请查看我的另一种使用网格的答案。 - Mark Heath
显示剩余5条评论

116
这种情况发生的原因是堆栈面板会将每个子元素在沿着其堆叠元素的轴上的约束条件设置为正无穷大进行测量。子控件必须返回它们想要的大小(在任何轴上,正无穷大都不是MeasureOverride中有效的返回值),因此它们返回最小的大小,以适应所有内容。它们无法知道自己需要填充多少空间。
如果您的视图不需要滚动功能,并且上面的答案不符合您的需求,则建议实现自己的面板。您可以直接派生自StackPanel,然后您所需要做的就是更改ArrangeOverride方法,以便在其子元素之间分配剩余的空间(给每个元素相同数量的额外空间)。如果给它们比它们所需的更多的空间,那么它们应该能够正常渲染,但是如果给它们的空间变少了,就会出现故障。
如果您想要滚动整个内容,则恐怕事情会更加困难,因为ScrollViewer会为您提供无限的空间,这将使您处于最初子元素的相同位置。在这种情况下,您可能希望在新面板上创建一个属性,允许您指定视口大小,您应该能够将其绑定到ScrollViewer的大小上。理想情况下,您应该实现IScrollInfo,但如果要正确实现所有内容,那就会变得复杂起来。

1
+1,我想给你更多,但只允许1个,你的第一段指出了许多微软页面未能解决的问题,即为什么无限大可以作为高度/宽度出现以及您不能依赖于从MeasureOverride返回availableSize。 - Aidan
一个位于Grid内的StackPanel可以轻松地解决他常见的需求。如果需要,底部可以放置在ScrollViewer中。我从2006年开始使用WPF,只有一次需要自定义面板。我认为鼓励额外的复杂性并不是一个好主意。 - Chris Bordeman
@ChrisBordeman 我不确定我理解堆栈面板在网格内部如何解决问题。想法是让一个或多个子元素在堆栈面板中拉伸以填充可用空间。将堆栈面板放在网格内部并不能使其实现这一点,对吗? - Caleb Vear

73

另一种方法是使用只有一列和 n 行的网格(Grid)。将所有行的高度设置为Auto,并将最底部的行高设置为1*

我更喜欢这种方法,因为我发现与 DockPanels、StackPanels 和 WrapPanels 相比,Grids 具有更好的布局性能。但除非您在 ItemTemplate 中使用它们(其中正在为大量项执行布局),否则您可能永远不会注意到这一点。


2
对我来说,最好的解决方案是使用它来定义多个增长行。 - niyou

26
你可以使用 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表单,可以代替DockPanelStackPanelGrid,并且可以轻松而优雅地解决拉伸问题。只需在GitHub上查看自述文件即可。

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

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

    <Label Content="Comment:"/>
    <TextBox/>
</st:AutoGrid>

这应该放在顶部,非常有用,非常感谢! - IneedHelp
1
不要忘记添加 xmlns:st="http://schemas.spicytaco.io/"。 - Richard

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