Как я могу выделить дочерние элементы StackPanel?

С учетом StackPanel:

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

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

Ответ 1

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

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

EDIT: если вы захотите повторно использовать границу между двумя контейнерами, вы можете преобразовать значение поля в ресурс во внешней области, f.e.

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

Ответ 2

Еще один приятный подход можно увидеть здесь: 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>

Это самый простой и быстрый способ установить Margin для нескольких дочерних элементов панели, даже если они не одного типа. (I.e. Buttons, TextBoxes, ComboBoxes и т.д.)

Ответ 3

Я улучшил ответ Elad Katz.

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

Исходный код в 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>

Ответ 4

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

<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

+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), он может переопределить стиль элемента управления по умолчанию. Для большинства элементов управления, таких как stackpanel, это не проблема, но для текстовых полей и т.д. Вы можете наткнуться на эту проблему, что, к счастью, имеет некоторые обходные пути.

Ответ 6

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

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

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

Sidenote:

Сначала я наивно пытался использовать неявный стиль, чтобы установить свойство 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#

Ответ 8

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

Ответ 9

Мой подход наследует 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();
        }
    }
}

Ответ 10

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

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