WPF: стек-панель с FirstChildFill? - PullRequest
6 голосов
/ 28 февраля 2011

Мне нужен логичный и простой способ создания макета с одним набором элементов управления для заполнения, а остальные для закрепления.Я мог бы использовать:

<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 ]

3 голосов
/ 28 февраля 2011

Вы всегда можете написать свою собственную панель с различными правилами стыковки. Вы можете использовать стандартную реализацию 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 голос
/ 28 февраля 2011

Вы можете использовать DockPanel с StackPanel внутри. «Основной» контент будет отображаться последним, а не первым, но, по крайней мере, нижний контент будет отображаться в логическом порядке в вашем 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 в том же порядке, в котором оно отображается на экране, вы можете использовать Grid с двумя строками и 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>
0 голосов
/ 28 февраля 2011

В WPF нет такой панели, которая бы поддерживала это из коробки.Если вы хотите именно это поведение, вы можете создать свою собственную панель, но я бы НЕ советовал вам делать это, потому что она довольно нестандартная и нужна не так часто (что, если вам нужна панель с таким поведением, как MiddleChildFill?ваша собственная панель для этого случая тоже?).

Обычно этот вид макета достигается с помощью Grid (как вы упомянули).Да, это правда, что Grid имеет очень многословный синтаксис (вы могли бы упростить его, используя помощник, такой как this ), но это дает вам максимальную гибкость.И в конце концов, это то, что имеет значение.

...