2009-05-31 86 views
146

给出一个StackPanel:如何分隔出StackPanel的子元素?

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

什么是最好的方式,空间了子元素,使它们之间存在着大小相等的差距,即使子元素本身的大小不同?可以在没有为每个孩子设置属性的情况下完成吗?

回答

228

使用保证金或填充,适用于容器内的范围:

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

编辑:如果你想重新使用两个容器之间的余量,可以边距值转换为外部范围内的资源,fe

<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

的范围的风格是*真棒*的方式来做到这一点 - 谢谢你的提示! – 2009-05-31 18:41:34

+1

如果我想将其用于整个项目,该怎么办? – 2012-11-28 13:09:50

+4

有人可以解释为什么这只适用于你明确定义类型(例如TextBox)?如果我使用FrameworkElement尝试此操作,以便所有的孩子都有空间,那么它就没有任何作用。 – Schneider 2014-01-28 18:48:20

4

+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中定义这样的风格(或者被合并到应用程序的另一个字典.Resources)可以覆盖控件的默认样式。对于大多数不寻常的控制,如堆叠面板,这不是一个问题,但对于文本框等,你可能偶然发现this problem,幸运的是有一些解决方法。

69

另外一个不错的方法可以在这里看到: 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> 

这是最简单的&最快的方法将保证金设置为面板的几个孩子,即使他们不属于同一类型。 (例如:按钮,文本框,组合框等)

3

对谢尔盖的建议之后,你就可以,而不只是一个厚度对象定义和重用的整体风格(带有各种属性setter,包括保证金):

<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文件合并)资源字典风格继承。

旁注:

起初,我天真地试图用隐式样式设置控制到外部样式资源的样式属性(比如用钥匙“myStyle的”定义)

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

造成的Visual Studio 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#

0

描述有时你需要设置填充,没有保证金做项目比默认

9

我对Elad Katz' answer改善小之间的空间。

  • LastItemMargin属性添加到MarginSetter特别处理的最后一个项目
  • 添加间距与垂直和水平的属性附加属性,增加了项目之间的间距在垂直和水平列表,并在列表的最后消除了任何后页边空白

Source code in 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> 
5

你真的想要做的事情是包装所有的子元素。在这种情况下,你应该使用物品控制,而不是诉诸可怕的附加属性,你将最终拥有一百万的你想要风格的每个属性。

<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

0

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