StackPanelの子要素をどのように配置しますか?


187

StackPanelの場合:

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

子要素自体のサイズが異なっていても、それらの間に等しいサイズのギャップができるように、子要素を間隔を空けるための最良の方法は何ですか?個々の子のそれぞれにプロパティを設定せずに行うことはできますか?


実際、個々のアイテムにパディングを追加するだけが最良のオプションのようです。
ZAR

回答:


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>

編集:2つのコンテナー間のマージンを再利用する場合は、マージンの値を外部スコープのリソースに変換できます。

<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

10
タイプ(TextBoxなど)を明示的に定義した場合にのみ機能する理由を誰かが説明できますか?FrameworkElementを使用してすべての子の間隔を空けてこれを試しても、効果はありません。
Jack Ukleja 2014年

4
のスタイルをすでに定義している場合、これはうまく機能しませんButton
Mark Ingram、

1
付記として、Labelあなたがこれでこれをしたいなら、Padding代わりに使う必要がありますMargin
Anthony Nichols

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 リンクが壊れています-> これはウェブアーカイブですこのリンクのです。

次のような構文が機能するように、アタッチされた動作を作成する方法を示します。

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

これは、たとえ同じタイプでなくても、マージンをパネルの複数の子に設定する最も簡単で最速の方法です。(つまり、ボタン、テキストボックス、コンボボックスなど)


5
これはこれを行うにはかなり興味深い方法です。それはあなたが物事をどのように配置したいかについて正確に多くの仮定を行い、最初/最後のアイテムのマージンを自動的に調整する機会さえ与えます。
アーメンテージ

2
汎用性のための+1。また、ブログ投稿を改善するために、if (fe.ReadLocalValue(FrameworkElement.MarginProperty) == DependencyProperty.UnsetValue)実際に子のマージンを設定する前に、いくつかの要素のマージンを手動で指定できるように追加しました。
Xerillio 2016

変更するコレクションにバインドされたItemsControlなどで、子アイテムが動的に追加/削除される場合、これは機能しないことに注意してください。
Drew Noakes 2017年

1
機能しない場合:プロジェクトをビルドします。それ以外の場合、ページはレンダリングされません。
バトル

2
リンク切れ!
デイブ

13

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

Eladの回答と同様に、これは、子アイテムがItemsControl変化するコレクションへのバインドなどで動的に追加/削除される場合は機能しません。親のLoadイベントが発生した瞬間からアイテムは静的であると想定しています。
Drew Noakes

また、あなたの要旨は404を与える
ドリューNoakes

gistへのリンクを更新しました。
Angularsen 2017年

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>

ここに画像の説明を入力してください


すばらしいヒントですが、これはスタイルのTargetTypeが "FrameworkElement"の場合にうまく機能します(それ以外の場合は、たとえばImageでは機能しません)
Puffin

1
私はこのアイデアが好きです。たった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にマージされる別のディクショナリ)でこのようなスタイルを定義すると、コントロールのデフォルトスタイルをオーバーライドできます。スタックパネルのようなほとんど見た目が悪いコントロールの場合は問題ありませんが、テキストボックスなどの場合、幸運にもいくつかの回避策があるこの問題に遭遇する可能性があります。


<Style.Resources>私がこれを試したとき、それがエラーを示していたと確信していますか?
grv_9098 2012年

申し訳ありませんが、手元で思い出せません。自分で試してみる必要があります。折り返しご連絡いたします。
Andre Luus

はい、機能します。StackPanelのすべてのTextBlockにTextWeight = "Bold"を設定するために同じことをしました。唯一の違いは、StackPanelでスタイルを明示的に設定したことです。
Andre Luus

心配してくれてありがとう。でも、まだ疑問があります。スコープスタイルと呼ばれていることは知っています。<Style.Resources>ではなく、<StackPanel.Resources>になると思います。あなたのコード部分を貼り付けることができればもっと素晴らしいでしょう...
grv_9098 '30

3

Sergeyの提案に従って、Thicknessオブジェクトだけでなく、Style全体(Marginを含むさまざまなプロパティセッター)を定義して再利用できます。

<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))で直ちにシャットダウンしました-with-catastrophic-failure-when-a-style-tries-to-set-style-property#




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

アイテム間のスペースをデフォルトより小さくするために、マージンではなくパディングを設定する必要がある場合があります

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.