Comment espacer les éléments enfant d'un StackPanel?

un StackPanel:

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

Quelle est la meilleure façon d'espacer les éléments enfants pour qu'il y ait des espaces de taille égale entre eux, même si les éléments enfants eux-mêmes sont de tailles différentes? Peut-on le faire sans mettre de propriétés sur chacun des enfants?

158
demandé sur Dave Clemmer 2009-05-31 21:34:03

10 réponses

utiliser la marge ou le rembourrage, appliquée à la portée à l'intérieur du conteneur:

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

EDIT: dans le cas où vous voudriez réutiliser la marge entre deux conteneurs, vous pouvez convertir la valeur de marge à une ressource dans un cadre extérieur, F. E.

<Window.Resources>
    <Thickness x:Key="tbMargin">0,10,0,0</Thickness>
</Window.Resources>

et ensuite se référer à cette valeur dans la portée intérieure

<StackPanel.Resources>
    <Style TargetType="{x:Type TextBox}">
        <Setter Property="Margin" Value="{StaticResource tbMargin}"/>
    </Style>
</StackPanel.Resources>
239
répondu Sergey Aldoukhov 2009-06-01 02:17:30

une Autre belle approche peut être vu ici: http://blogs.microsoft.co.il/blogs/eladkatz/archive/2011/05/29/what-is-the-easiest-way-to-set-spacing-between-items-in-stackpanel.aspx

il montre comment créer un comportement attaché, de sorte que la syntaxe comme ceci fonctionnerait:

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

C'est le moyen le plus simple et le plus rapide de fixer la marge à plusieurs enfants d'un panneau, même s'ils ne sont pas du même type. (I. e. Boutons, Boîtes À textes, ComboBoxes, etc.)

73
répondu Elad Katz 2011-11-15 00:48:43

j'ai amélioré sur Elad Katz " réponse .

  • ajouter LastItemMargin propriété à MarginSetter pour manipuler spécialement le dernier article
  • ajouter espacement propriété attachée avec Propriétés verticales et horizontales qui ajoute l'espacement entre les éléments dans les listes verticales et horizontales et élimine toute marge de fuite à la fin de la liste

code Source dans les gist .

exemple:

<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>
10
répondu angularsen 2017-10-09 20:29:40

la chose que vous voulez vraiment faire est envelopper tous les éléments enfant. Dans ce cas, vous devez utiliser un contrôle d'articles et de ne pas recourir à des propriétés attachées horribles que vous finirez par avoir un million de pour chaque propriété que vous souhaitez coiffer.

<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

6
répondu bradgonesurfing 2015-06-16 10:18:32

+ 1 pour la réponse de Sergey. Et si vous voulez appliquer cela à tous vos StackPanels, vous pouvez le faire:

<Style TargetType="{x:Type StackPanel}">
    <Style.Resources>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Margin" Value="{StaticResource tbMargin}"/>
        </Style>
    </Style.Resources>
</Style>

mais attention: si vous définissez un style comme celui-ci dans votre application.xaml (ou un autre dictionnaire qui est fusionné dans l'Application.Ressources) peut remplacer le style par défaut de la commande. Pour la plupart des contrôles sans regard comme le stackpanel ce n'est pas un problème, mais pour les boîtes de textes etc Vous pouvez tomber sur ce problème , qui, heureusement, a quelques solutions de contournement.

4
répondu Andre Luus 2017-05-23 12:10:35

suite à la suggestion de Sergey, vous pouvez définir et réutiliser un Style entier (avec divers réglages de propriété, y compris la marge) au lieu d'un simple objet 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>

notez que le truc ici est l'utilisation de L'héritage de Style pour le style implicite, héritant du style dans un dictionnaire de ressources externe (probablement fusionné à partir du fichier XAML externe).

Sidenote:

au début, j'ai naïvement essayé d'utiliser le style implicite pour mettre la propriété Style du contrôle à cette ressource de Style externe (dire défini avec la clé "MyStyle"):

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

qui a causé la fermeture immédiate de Visual Studio 2010 avec une erreur de défaillance catastrophique (HRESULT: 0x8000ff (E_UNSPECTED)), comme décrit à https://connect.microsoft.com/VisualStudio/feedback/details/753211/xaml-editor-window-fails-with-catastrophic-failure-when-a-style-tries-to-set-style-property#

3
répondu George Birbilis 2012-07-11 10:15:45

le UniformGrid n'est peut-être pas disponible à Silverlight, mais quelqu'un l'a porté de WPF. http://www.jeff.wilcox.name/2009/01/uniform-grid/

2
répondu Jean Azzopardi 2009-05-31 18:22:27

grille.Colonnspacing , Grid.RowSpacing , StackPanel.Espacement sont maintenant sur UWP preview, tout permettra de mieux acomplish ce qui est demandé ici.

ces propriétés ne sont actuellement disponibles qu'avec le Windows 10 Fall Creators Update Insider SDK, mais devraient le rendre aux bits finaux!

2
répondu Pedro Lamas 2017-07-06 15:17:59

mon approche hérite du StackPanel.

Utilisation:

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

Tout ce qu'il faut est la classe courte suivante:

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();
        }
    }
}
1
répondu James M 2017-01-09 22:34:26

parfois, vous avez besoin de définir le rembourrage, pas de marge pour faire de l'espace entre les éléments plus petit que par défaut

0
répondu Serov Danil 2013-11-25 13:41:01