WPF:StackPanel与FirstChildFill?

时间:2011-02-28 13:59:18

标签: c# wpf layout fill

我想要一种逻辑和简单的方法来生成一个布局,其中一个控件集要填充,其余的要停靠。我可以用:

<DockPanel LastChildFill="True">
  <Button Content="3" DockPanel.Dock="Bottom" />
  <Button Content="2" DockPanel.Dock="Bottom" />
  <Button Content="1" />
</DockPanel>

但它的使用不是很直观。我也可以这样做:

<Grid>
  <Grid.RowDefinitions>
    <RowDefinition Height="*" />
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
  </Grid.RowDefinitions>
  <Button Content="1" Grid.Row="0" />
  <Button Content="2" Grid.Row="1" />
  <Button Content="3" Grid.Row="2" />
</Grid>

但它也有很多xaml。我真正想要的是这样的:

<StackPanel Fill="None|First|Last">
  <Button Content="1" />
  <Button Content="2" />
  <Button Content="3" />
</StackPanel>

如何在不必像DockPanel那样反转项目的情况下实现这一目标,而不是像Grid一样使用固定数量的行和附加属性?

3 个答案:

答案 0 :(得分:3)

您始终可以使用不同的停靠规则编写自己的面板。您可以使用标准的DockPanel实现(在框架源中可用 - 看起来并不复杂)并使用您喜欢的规则创建类似的东西。您甚至可以创建一个派生自DockPanel并覆盖ArrangeOverride的类。

但我个人只会使用停靠面板,除了您不喜欢关于哪个成员填充的规则外,它会完全符合您的要求。

如果您插入/删除行,IME网格有一个可怕的维护问题,因为您发现自己无休止地调整行数 - 在这方面,DockPanel更容易。

更新

在这里,我已经否认了你自己这样做的乐趣 - 这只是框架源的简化/逆转版本:

public class BottomDockingPanel : DockPanel
{
    protected override Size ArrangeOverride(Size arrangeSize)
    {
        UIElementCollection children = InternalChildren;
        int totalChildrenCount = children.Count;

        double accumulatedBottom = 0;

        for (int i = totalChildrenCount-1; i >=0 ; --i)
        {
            UIElement child = children[i];
            if (child == null) { continue; }

            Size childDesiredSize = child.DesiredSize;
            Rect rcChild = new Rect(
                0,
                0,
                Math.Max(0.0, arrangeSize.Width - (0 + (double)0)),
                Math.Max(0.0, arrangeSize.Height - (0 + accumulatedBottom)));

            if (i > 0)
            {
                accumulatedBottom += childDesiredSize.Height;
                rcChild.Y = Math.Max(0.0, arrangeSize.Height - accumulatedBottom);
                rcChild.Height = childDesiredSize.Height;
            }

            child.Arrange(rcChild);
        }
        return (arrangeSize);
    }
}

答案 1 :(得分:1)

您可以在其中使用带有StackPanel的DockPanel。 “主要”内容将显示在最后而不是第一个,但至少底部内容将在XAML中按逻辑顺序显示。如果你愿意将填充的内容放在最后,这将是最简单的方法。

<DockPanel LastChildFill="True">
  <StackPanel DockPanel.Dock="Bottom">
    <Button Content="Bottom 1" />
    <Button Content="Bottom 2" />
    <Button Content="Bottom 3" />
  </StackPanel>
  <Button Content="Main" />
</DockPanel>

或者,如果您希望所有内容(包括填充的内容)以与屏幕上显示的顺序相同的顺序显示在XAML中,您可以使用第二行中包含两行和第二行的StackPanel的网格。正如您所指出的,网格需要相当多的XAML,但嵌套的StackPanel将使您不必为每个新项添加RowDefinition和Grid.Row。

<Grid>
  <Grid.RowDefinitions>
    <RowDefinition Height="*" />
    <RowDefinition Height="Auto" />
  </Grid.RowDefinitions>
  <Button Grid.Row="0" Content="Main" />
  <StackPanel Grid.Row="1">
    <Button Content="Bottom 1" />
    <Button Content="Bottom 2" />
    <Button Content="Bottom 3" />
  </StackPanel>
</Grid>

答案 2 :(得分:0)

WPF中没有这样的面板可以支持这种开箱即用的功能。如果你想要这个特殊的行为,你可以创建自己的面板,但我不建议你这样做,因为它是非常自定义的并且不需要那么频繁(如果想要一个像MiddleChildFill这样的行为的面板怎么办?会创建那个案子也是你自己的小组?)。

通常使用Grid(如您所述)实现此类布局。是的,Grid具有非常冗长的语法(您可以使用this之类的帮助器来简化它),但它为您提供了最大的灵活性。在一天结束时,这才重要。