Как разнести дочерние элементы StackPanel? - PullRequest
167 голосов
/ 31 мая 2009

С учетом StackPanel:

<StackPanel>
  <TextBox Height="30">Apple</TextBox>
  <TextBox Height="80">Banana</TextBox>
  <TextBox Height="120">Cherry</TextBox>
</StackPanel>

Каков наилучший способ разнести дочерние элементы так, чтобы между ними были зазоры одинакового размера, даже если сами дочерние элементы имеют разные размеры? Можно ли это сделать без установки свойств для каждого из дочерних элементов?

Ответы [ 10 ]

260 голосов
/ 31 мая 2009

Использовать поля или отступы, примененные к области внутри контейнера:

<StackPanel>
    <StackPanel.Resources>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Margin" Value="0,10,0,0"/>
        </Style>
    </StackPanel.Resources> 
    <TextBox Text="Apple"/>
    <TextBox Text="Banana"/>
    <TextBox Text="Cherry"/>
</StackPanel>

РЕДАКТИРОВАТЬ: Если вы хотите повторно использовать поле между двумя контейнерами, вы можете преобразовать значение поля в ресурс во внешней области, например,

<Window.Resources>
    <Thickness x:Key="tbMargin">0,10,0,0</Thickness>
</Window.Resources>

и затем обратитесь к этому значению во внутренней области

<StackPanel.Resources>
    <Style TargetType="{x:Type TextBox}">
        <Setter Property="Margin" Value="{StaticResource tbMargin}"/>
    </Style>
</StackPanel.Resources>
80 голосов
/ 30 мая 2011

Еще один хороший подход можно увидеть здесь: http://blogs.microsoft.co.il/blogs/eladkatz/archive/2011/05/29/what-is-the-easiest-way-to-set-spacing-between-items-in-stackpanel.aspx

Показывает, как создать прикрепленное поведение, чтобы такой синтаксис работал:

<StackPanel local:MarginSetter.Margin="5">
   <TextBox Text="hello" />
   <Button Content="hello" />
   <Button Content="hello" />
</StackPanel>

Это самый простой и быстрый способ установить маржу для нескольких дочерних элементов панели, даже если они не одного типа. (Т.е. кнопки, текстовые поля, комбинированные списки и т. Д.)

12 голосов
/ 17 января 2015

Я улучшил ответ Элад Кац .

  • Добавление свойства LastItemMargin в MarginSetter для специальной обработки последнего элемента
  • Добавление вложенного свойства «Интервал» со свойствами «Вертикальный» и «Горизонтальный», которое добавляет интервалы между элементами в вертикальных и горизонтальных списках и устраняет любые поля в конце списка

Исходный код в Gist .

Пример:

<StackPanel Orientation="Horizontal" foo:Spacing.Horizontal="5">
  <Button>Button 1</Button>
  <Button>Button 2</Button>
</StackPanel>

<StackPanel Orientation="Vertical" foo:Spacing.Vertical="5">
  <Button>Button 1</Button>
  <Button>Button 2</Button>
</StackPanel>

<!-- Same as vertical example above -->
<StackPanel Orientation="Vertical" foo:MarginSetter.Margin="0 0 0 5" foo:MarginSetter.LastItemMargin="0">
  <Button>Button 1</Button>
  <Button>Button 2</Button>
</StackPanel>
8 голосов
/ 16 июня 2015

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

<ItemsControl>

    <!-- target the wrapper parent of the child with a style -->
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="Control">
            <Setter Property="Margin" Value="0 0 5 0"></Setter>
        </Style>
    </ItemsControl.ItemContainerStyle>

    <!-- use a stack panel as the main container -->
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <!-- put in your children -->
    <ItemsControl.Items>
        <Label>Auto Zoom Reset?</Label>
        <CheckBox x:Name="AutoResetZoom"/>
        <Button x:Name="ProceedButton" Click="ProceedButton_OnClick">Next</Button>
        <ComboBox SelectedItem="{Binding LogLevel }" ItemsSource="{Binding LogLevels}" />
    </ItemsControl.Items>
</ItemsControl>

enter image description here

5 голосов
/ 23 июня 2010

+ 1 за ответ Сергея. И если вы хотите применить это ко всем вашим StackPanels, вы можете сделать это:

<Style TargetType="{x:Type StackPanel}">
    <Style.Resources>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Margin" Value="{StaticResource tbMargin}"/>
        </Style>
    </Style.Resources>
</Style>

Но будьте осторожны: если вы определите подобный стиль в вашем App.xaml (или другом словаре, который объединен с Application.Resources), он может переопределить стиль по умолчанию контроль. Для большинства элементов управления без внешнего вида, таких как панель стека, это не проблема, но для текстовых полей и т. Д. Вы можете наткнуться на эту проблему , которая, к счастью, имеет некоторые обходные пути.

3 голосов
/ 06 июля 2017

Grid.ColumnSpacing , Grid.RowSpacing , StackPanel.Spacing теперь в предварительном просмотре UWP, все позволит лучше соответствовать тому, что запрашивается здесь.

Эти свойства в настоящее время доступны только в пакете обновления 10 для Windows 10 Fall Creators Update Insider, но они должны дойти до конца!

3 голосов
/ 11 июля 2012

Следуя совету Сергея, вы можете определить и повторно использовать весь Стиль (с различными установщиками свойств, включая Маржу) вместо просто объекта Толщина:

<Style x:Key="MyStyle" TargetType="SomeItemType">
  <Setter Property="Margin" Value="0,5,0,5" />
  ...
</Style>

...

  <StackPanel>
    <StackPanel.Resources>
      <Style TargetType="SomeItemType" BasedOn="{StaticResource MyStyle}" />
    </StackPanel.Resources>
  ...
  </StackPanel>

Обратите внимание, что хитрость здесь заключается в использовании наследования стилей для неявного стиля, унаследованного от стиля в некотором внешнем (возможно, объединенном из внешнего файла XAML) словаре ресурсов.

Sidenote:

Сначала я наивно пытался использовать неявный стиль, чтобы установить свойство Style элемента управления для этого внешнего ресурса Style (скажем, определенного с помощью ключа "MyStyle"):

<StackPanel>
  <StackPanel.Resources>
    <Style TargetType="SomeItemType">
      <Setter Property="Style" Value={StaticResource MyStyle}" />
    </Style>
  </StackPanel.Resources>
</StackPanel>

, что привело к немедленному завершению работы Visual Studio 2010 с ошибкой CATASTROPHIC FAILURE (HRESULT: 0x8000FFFF (E_UNEXPECTED)), как описано в https://connect.microsoft.com/VisualStudio/feedback/details/753211/xaml-editor-window-fails-with-catastrophic-failure-when-a-style-tries-to-set-style-property#

2 голосов
/ 16 апреля 2016

Мой подход наследует StackPanel.

Использование:

<Controls:ItemSpacer Grid.Row="2" Orientation="Horizontal" Height="30" CellPadding="15,0">
    <Label>Test 1</Label>
    <Label>Test 2</Label>
    <Label>Test 3</Label>
</Controls:ItemSpacer>

Все, что нужно, это следующий короткий класс:

using System.Windows;
using System.Windows.Controls;
using System;

namespace Controls
{
    public class ItemSpacer : StackPanel
    {
        public static DependencyProperty CellPaddingProperty = DependencyProperty.Register("CellPadding", typeof(Thickness), typeof(ItemSpacer), new FrameworkPropertyMetadata(default(Thickness), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnCellPaddingChanged));
        public Thickness CellPadding
        {
            get
            {
                return (Thickness)GetValue(CellPaddingProperty);
            }
            set
            {
                SetValue(CellPaddingProperty, value);
            }
        }
        private static void OnCellPaddingChanged(DependencyObject Object, DependencyPropertyChangedEventArgs e)
        {
            ((ItemSpacer)Object).SetPadding();
        }

        private void SetPadding()
        {
            foreach (UIElement Element in Children)
            {
                (Element as FrameworkElement).Margin = this.CellPadding;
            }
        }

        public ItemSpacer()
        {
            this.LayoutUpdated += PART_Host_LayoutUpdated;
        }

        private void PART_Host_LayoutUpdated(object sender, System.EventArgs e)
        {
            this.SetPadding();
        }
    }
}
2 голосов
/ 31 мая 2009

UniformGrid может быть недоступен в Silverlight, но кто-то перенес его из WPF. http://www.jeff.wilcox.name/2009/01/uniform-grid/

0 голосов
/ 25 ноября 2013

иногда вам нужно установить отступы, а не поля, чтобы пространство между элементами было меньше, чем по умолчанию

...