【问题标题】:How do I space out the child elements of a StackPanel?如何分隔 StackPanel 的子元素?
【发布时间】:2009-05-31 17:34:03
【问题描述】:

给定一个 StackPanel:

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

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

【问题讨论】:

  • 真的只是为单个项目添加填充似乎是最好的选择。

标签: wpf silverlight xaml 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>

编辑:如果您想重新使用两个容器之间的边距,您可以将边距值转换为外部范围内的资源,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>

【讨论】:

  • 范围样式是一种真棒的方法 - 感谢您的提示!
  • 如果我想在整个项目中使用它怎么办?
  • 有人能解释一下为什么这仅在您明确定义类型(例如 TextBox)时才有效吗?如果我使用 FrameworkElement 尝试这样做,以便所有子级都隔开,它没有效果。
  • 如果您已经为 Button 定义了样式,这将无法正常工作。
  • 附带说明,如果您想使用Label 执行此操作,您必须使用Padding 而不是Margin
【解决方案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 链接已损坏 -> 此链接的 this is webarchive

它展示了如何创建附加行为,以便这样的语法可以工作:

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

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

【讨论】:

  • 这是一个非常有趣的方法。它对您希望如何分隔事物做出了很多假设,甚至让您有机会自动调整第一个/最后一个项目的边距。
  • +1 的多功能性。另外为了改进博客文章,在实际设置子元素的边距之前添加if (fe.ReadLocalValue(FrameworkElement.MarginProperty) == DependencyProperty.UnsetValue),它允许手动指定某些元素的边距。
  • 请注意,如果子项是动态添加/删除的,例如在绑定到变化集合的 ItemsControl 中,这将不起作用。
  • 如果不起作用:构建项目,否则将无法呈现页面。
  • 链接已损坏!
【解决方案3】:

我改进了Elad Katz' answer

  • 将 LastItemMargin 属性添加到 MarginSetter 以专门处理最后一项
  • 添加带有 Vertical 和 Horizo​​ntal 属性的 Spacing 附加属性,可在垂直和水平列表中的项目之间添加间距并消除列表末尾的任何尾随边距

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>

【讨论】:

  • 像 Elad 的回答一样,如果子项是动态添加/删除的,例如在绑定到不断变化的集合的 ItemsControl 中,这将不起作用。它假定从父级的 Load 事件触发的那一刻起,这些项目是静态的。
  • 另外,你的要点给出了 404。
  • 更新了要点的链接。
  • 这个太棒了。谢谢,angulararsen。
【解决方案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>

【讨论】:

  • 很好的提示,但将 Style TargetType 设置为“FrameworkElement”效果更好(否则它不适用于 Image,例如)
  • 我喜欢这个主意。只需添加一个:从 StackPanel (Margin="0 0 -5 0") 的边距中减去间距量也将抵消列表中最后一项之后的间距。
  • 这样做的问题是您设置的样式将覆盖您可能已经在项目上拥有的任何其他样式。要克服这个问题,请参阅这个相关问题/接受的答案here
【解决方案5】:

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

【讨论】:

  • 你确定吗,因为当我尝试这个时它显示错误。
  • 对不起,我不记得了。我得亲自试一试才能看到。我会回复你的。
  • 是的,它确实有效。只需在 StackPanel 中的所有 TextBlocks 上设置 TextWeight="Bold" 即可。唯一的区别是我在 StackPanel 上明确设置了样式。
  • 感谢您的关注,但我仍然有疑问。我知道它称为 Scope Style。我猜它会是 而不是 。如果你能粘贴你的代码片段会更好......
【解决方案6】:

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

导致 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】:

    Grid.ColumnSpacingGrid.RowSpacingStackPanel.Spacing 现在是 UWP 预览版,所有这些都可以更好地完成这里的要求。

    这些属性目前仅在 Windows 10 Fall Creators Update Insider SDK 中可用,但应该会达到最后阶段!

    【讨论】:

      【解决方案8】:

      UniformGrid 在 Silverlight 中可能不可用,但有人从 WPF 移植了它。 http://www.jeff.wilcox.name/2009/01/uniform-grid/

      【讨论】:

        【解决方案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】:

          有时您需要设置 Padding 而不是 Margin 以使项目之间的空间小于默认值

          【讨论】:

            【解决方案11】:

            通常,我使用Grid 而不是StackPanel,如下所示:

            横排

            <Grid>
             <Grid.ColumnDefinitions>
                <ColumnDefinition Width="auto"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition  Width="auto"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition  Width="auto"/>
             </Grid.ColumnDefinitions>
             <TextBox Height="30" Grid.Column="0">Apple</TextBox>
             <TextBox Height="80" Grid.Column="2">Banana</TextBox>
             <TextBox Height="120" Grid.Column="4">Cherry</TextBox>
            </Grid>
            

            竖排

            <Grid>
                 <Grid.ColumnDefinitions>
                    <RowDefinition Width="auto"/>
                    <RowDefinition Width="*"/>
                    <RowDefinition  Width="auto"/>
                    <RowDefinition Width="*"/>
                    <RowDefinition  Width="auto"/>
                 </Grid.ColumnDefinitions>
                 <TextBox Height="30" Grid.Row="0">Apple</TextBox>
                 <TextBox Height="80" Grid.Row="2">Banana</TextBox>
                 <TextBox Height="120" Grid.Row="4">Cherry</TextBox>
            </Grid>
            

            【讨论】:

            • 如果有人问如何使用Grid 实现结果,那么这将是一个很好的答案。问题是关于StackPanel,下面有一个很好的答案。为什么人们坚持不回答提出的问题?
            猜你喜欢
            • 1970-01-01
            • 2014-06-03
            • 1970-01-01
            • 2015-03-27
            • 1970-01-01
            • 2012-09-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多