如何隔开StackPanel的子元素?


187

给定一个StackPanel:

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

即使子元素本身的大小不同,分隔子元素以使它们之间存在相等大小的间隙的最佳方法是什么?是否可以在不为每个孩子设置属性的情况下完成?


实际上,仅向单个项目添加填充似乎是最佳选择。
zar

Answers:


278

使用“边距”或“填充”,将其应用于容器内的范围:

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

5
限定范围的样式是一种很棒的方式-感谢您的提示!
安娜·贝茨

2
如果要在整个项目中使用该怎么办?
grv_9098 2012年

10
有人可以解释为什么只有在您明确定义类型(例如TextBox)时才起作用吗?如果我使用FrameworkElement尝试此操作,以使所有子级都隔开,那么它将无效。
Jack Ukleja 2014年

4
如果您已经为定义了样式,则此方法将无法正常工作Button
马克·英格拉姆

1
作为一个侧面说明,如果你想这样做了Label,你必须使用Padding替代Margin
安东尼·尼科尔斯

84

可以在此处看到另一种不错的方法:http : //blogs.microsoft.co.il/blogs/eladkatz/archive/2011/05/29/what-is-the-easiest-way-to-set-spacing-between- items-in-stackpanel.aspx 链接已断开-> 此链接是webarchive

它显示了如何创建附加行为,这样的语法将起作用:

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

这是将“边距”设置为面板的多个子级的最简单,最快的方法,即使它们不是同一类型。(即按钮,文本框,组合框等)


5
这是一种非常有趣的方法。它对您要如何正确放置物品进行了很多假设,甚至为您提供了自动调整第一个/最后一个项目的边距的机会。
Armentage,2011年

2
多功能性+1。为了改进博客文章,if (fe.ReadLocalValue(FrameworkElement.MarginProperty) == DependencyProperty.UnsetValue)在实际设置子项的边距之前添加了它,它允许手动为某些元素指定边距。
Xerillio '16

请注意,如果子项是动态添加/删除的,例如在绑定到更改集合的ItemsControl中,则此方法不起作用。
Drew Noakes

1
如果它不起作用:构建项目,否则将无法呈现页面。
战斗

2
链接坏了!
戴夫

13

我改进了埃拉德·卡茨的回答

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

就像Elad的答案一样,如果子项是动态添加/删除的(例如ItemsControl绑定到变化的集合中),则此方法将无效。假设从父级Load事件触发时起这些项是静态的。
Drew Noakes

此外,您要点给出了一个404
德鲁诺克斯

更新了要点的链接。
angularsen

9

您真正想做的是包装所有子元素。在这种情况下,您应该使用项目控件,而不要使用可怕的附加属性,对于要样式化的每个属性,最终将拥有一百万个属性。

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

在此处输入图片说明


很好的技巧,但这在Style TargetType样式为“ FrameworkElement”时效果更好(例如,它不适用于Image)
Puffin

1
我喜欢这个主意。仅需增加一点:从StackPanel(Margin="0 0 -5 0")的边距中减去间隔的数量也将抵消列表中最后一项之后的间隔。
label17

这样做的问题是,您设置的样式将覆盖项目上可能已经具有的任何其他样式。为了克服这个问题,请在此处
waxingsatirical

6

+1为谢尔盖的答案。如果要将其应用于所有StackPanel,则可以执行以下操作:

<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这样的大多数看起来不清晰的控件,这不是问题,但是对于文本框等,您可能会偶然发现此问题,幸运的是,它有一些解决方法。


<Style.Resources>当我尝试显示错误时,您是否确定呢?
grv_9098 2012年

抱歉,我记不起来。我必须自己尝试才能看到。我会尽快给您回复。
Andre Luus 2012年

是的,它确实有效。只需对StackPanel中的所有TextBlocks设置TextWeight =“ Bold”即可。唯一的区别是我在StackPanel上显式设置了样式。
安德烈·路斯(Andre Luus)2012年

感谢您的关注,但我仍然有疑问。我知道它叫做Scope Style。我猜它将是<StackPanel.Resources>而不是<Style.Resources>。如果您可以粘贴自己的代码段,
那就更好了

3

遵循Sergey的建议,您可以定义和重用整个Style(具有各种属性设置器,包括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>

请注意,此处的技巧是将样式继承用于隐式样式,该样式继承是从某些外部(可能是从外部XAML文件合并)的资源字典中的样式继承的。

边注:

首先,我天真地尝试使用隐式样式将控件的Style属性设置为该外部Style资源(例如,用键“ MyStyle”定义):

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

https://connect.microsoft.com/VisualStudio/feedback/details/753211/xaml-editor-window-fails中所述,这导致Visual Studio 2010立即关闭,并发生CATASTROPHIC FAILURE错误(HRESULT:0x8000FFFF(E_UNEXPECTED))。尝试设置样式属性时发生灾难性故障#




2

我的方法继承了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();
        }
    }
}

0

有时您需要设置填充而不是边距,以使项目之间的间隔小于默认值

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.