如何让WPF中的控件填满可用空间?

354

一些 WPF 控件(比如 Button)如果你不指定它的高度,它似乎会愉快地占用其容器中所有可用的空间。

而有些控件,比如我现在需要使用的(多行)TextBoxListBox,更关心的是仅占用适合其内容的空间,而不会占用过多的空间。

如果你将这些控件放在 UniformGrid 中的单元格中,它们将扩展以适应可用的空间。但是,并非所有情况都适用于UniformGrid 实例。如果你有一个网格,其中有一些行设置为 * 高度,以将其高度分配给自身和其他 * 行怎么办?如果你有一个 StackPanel,其中包括一个 Label、一个 List 和一个 Button,那么如何让列表占用标签和按钮未占用的所有空间?

我认为这真的是一种基本的布局需求,但是我无法弄清楚如何让它们填充可以占用的空间(将它们放在 DockPanel 中并将其设置为填充也不起作用,因为 DockPanel 仅占用其子控件所需的空间)。

如果你必须调整 HeightWidthMinHeightMinWidth 等大小属性来设置可调整大小的GUI将会非常麻烦。

你可以将你的HeightWidth属性绑定到你所占据的网格单元格上吗?或者有其他方法可以做到这一点吗?


1
UniformGrid非常棒,是我新的默认标签。我喜欢StackPanel的简单性(让我想起了div),但UniformGrid正好满足我的需求。 - Terrance
1
关于UniformGrid。重点在于Uniform。似乎无法调整列宽或行高。只有当每个内容大小相同时才适用。我用Grid替换了我的StackPanel,然后Stretch就按照我预期的工作了。 - pdschuller
这篇文章帮助我在WPF中拉伸文本框。 - jpaugh
5个回答

279

还有一些属性可以设置,强制控件在原本不会填充可用空间的情况下填充。例如,您可以这样说:

您还可以设置一些属性,以便在控件原本不会填充可用空间的情况下强制其填充。例如,您可以这样说:

HorizontalContentAlignment="Stretch"

...通过强制控件内容水平拉伸的方式。或者您可以说:

HorizontalAlignment="Stretch"

... 使控件自身水平拉伸以填满其父控件。


116
所有这些案例中提到的最臭名昭著的面板是StackPanel。它不包含ContentAlignment属性,而将其子元素设置为Stretch在星号大小的网格中没有效果。非常令人沮丧。几乎可以说StackPanel是WPF团队编写的第一个容器,并因此受到影响。 - Brent Schooley
4
@Brent - 啊!我一直困在如何正确填充我的StackPanel内容上。谢谢!我以为这只是我做错了什么。 - Ashley Grenon
8
用Stack Panel的功能很有限,你可以通过使用UniformGrid并将行或列设置为0来实现类似的拉伸行为。 - ForbesLindesay
4
UniformGrid 只允许单元格大小相同。最终我使用了 Grid,似乎可以工作。很烦人的是 StackPanel 没有做它看起来应该做的事情。 - TarkaDaal
7
如果所有东西的大小都相同,可以使用UniformGrid;如果想要指定复杂的相对大小,则可以使用Grid;如果仅基于内容大小调整大小并忽略可用空间量,则可以使用stack panel。 - ForbesLindesay
显示剩余3条评论

176

每个派生自Panel的控件都实现了在Measure()Arrange()中执行不同布局逻辑:

  • Measure()确定面板及其子项的大小
  • Arrange()确定每个控件呈现的矩形

DockPanel的最后一个子项填充剩余空间。 您可以通过将LastChild属性设置为false来禁用此行为。

StackPanel要求每个子项提供其所需大小,然后堆叠它们。 堆栈面板对每个子项调用Measure(),可用大小为Infinity,然后使用子项的所需大小。

Grid占用所有可用空间,但会将每个子项设置为其所需大小,然后将其居中放置在单元格中。

您可以通过派生自Panel并重写MeasureOverride()ArrangeOverride()来实现自己的布局逻辑。

有关简单示例,请参见本文


6
@user3690202 Archive.org是你的朋友。链接已被恢复。 - ruffin
4
这个主题仍然存在于MSDN,但链接已更改为:https://learn.microsoft.com/en-us/dotnet/framework/wpf/controls/how-to-create-a-custom-panel-element - Andrea Antonangeli
4
“DockPanel的最后一个子元素填充剩余空间”:这是我理解的关键所在。我一直以为没有明确Dock属性的元素会填充空间。 - Spotted
但是DockPanel没有方向吗? 像往常一样,GUI设计师总是“太聪明了,不想使用逻辑简单的系统” “让我们把这个垃圾强加给下一代程序员” - Paul McCarthy
DockPanel的最后一个子元素填充剩余空间 - 不,它并没有这样做。 - Paul McCarthy
DockPanel的最后一个子元素会填充剩余空间 - 是的,它会。但是DockPanel有一个属性LastChildFill="False"可以修改这种行为。 - 75ntamas

26

嗯,我在发布后自己解决了,这是最尴尬的方式。 :)

看起来 StackPanel 的每个成员只会填充其最小请求的大小。

在 DockPanel 中,我将东西对齐的顺序弄错了。如果 TextBox 或 ListBox 是唯一没有对齐的停靠项,或者如果它们是最后添加的,则它们将按要求填充剩余空间。

我希望能看到更优雅的处理方法,但这样做也可以。


1
我想指出(在这个旧问题上!)“没有对齐”是一个关键短语,至少对我来说是这样。 - MikeBaz - MSFT
我很想知道可视化表单IDE设计师是否真正向程序员询问他们想要什么?自从Delphi 2009以来,我就没有见过好的设计师。 - Paul McCarthy

6
使用HorizontalAlignmentVerticalAlignment布局属性来控制元素在其父元素中的空间利用情况。当元素所需的空间小于其父元素提供的可用空间时,这些属性可以起到作用。
例如,StackPanel的宽度将与其包含的最宽元素一样宽。因此,所有较窄的元素都有一点多余的空间。对齐属性控制子元素如何使用额外的空间。
两个属性的默认值都是Stretch,因此子元素被拉伸以填充所有可用空间。其他选项包括HorizontalAlignment的Left、Center和Right,以及VerticalAlignment的Top、Center和Bottom。

36
如果这总是正确的,原始问题就不会被提出。例如,一个包含标签和文本框的水平 StackPanel,文本框右侧有额外的空间。文本框的宽度设置为自动。将HorizontalAlignment设置为Stretch也无法使文本框填充剩余的空间。 - Brent Schooley
没有什么是永远正确的,但这个答案适合我的需求:DockPanel、Image。 - alehro
"没有什么是永远正确的" 熵增加 真实总是正确的 你觉得这是一个程序员网站吗? - Paul McCarthy

0
在你的 XAML 代码中使用 SizeChanged="OnSizeChanged",然后在代码后台设置你想要的尺寸。
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
            TheScrollViewer.Height = MainWin.Height - 100;
        }

从长远来看,这对你会更好。

当你的经理过来问“把那个变大一点”,你不需要花整个下午去调整布局控件来让它正常工作。而且你也不必解释为什么要花一个下午来让它正常工作。


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