Как я могу выделить дочерние элементы 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#
Ответ 7
UniformGrid может быть недоступен в Silverlight, но кто-то портировал его из WPF. http://www.jeff.wilcox.name/2009/01/uniform-grid/
Ответ 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, но должны сделать это до финальных бит!