给定一个StackPanel:

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

在子元素本身大小不同的情况下,怎样才能使它们之间的间隔大小相等呢?在不为每个单独的子节点设置属性的情况下可以做到这一点吗?


当前回答

按照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文件合并的)资源字典中的样式继承。

Sidenote:

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

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

这导致VisualStudio 2010立即关闭灾难性失败错误(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#

其他回答

Sergey的答案是+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。

用法:

<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();
        }
    }
}

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

编辑:如果你想在两个容器之间重用边距,你可以将边距值转换为外部作用域中的资源,例如。

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

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

按照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文件合并的)资源字典中的样式继承。

Sidenote:

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

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

这导致VisualStudio 2010立即关闭灾难性失败错误(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#