web-dev-qa-db-fra.com

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

Étant donné un StackPanel:

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

Quel est le meilleur moyen d'espacer les éléments enfants afin 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 définir de propriétés sur chacun des enfants?

172
GraemeF

Utilisez Marge ou Padding, appliqué à la portée dans le 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>

ÉDITER: si vous souhaitez réutiliser la marge entre deux conteneurs, vous pouvez convertir la valeur de la marge en ressource dans une portée externe, par exemple.

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

puis se référer à cette valeur dans la portée interne

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

Une autre approche de Nice peut être vue 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é afin que la syntaxe suivante fonctionne:

<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 définir une marge sur plusieurs enfants d’un panneau, même s’ils ne sont pas du même type. (Ie. Boutons, TextBoxes, ComboBoxes, etc.)

81
Elad Katz

J'ai amélioré réponse d'Elad Katz .

  • Ajoutez la propriété LastItemMargin à MarginSetter pour gérer spécialement le dernier élément
  • Ajouter une propriété attachée d'espacement avec des propriétés verticales et horizontales qui ajoute un espacement entre les éléments des listes verticales et horizontales et élimine toute marge de fin à la fin de la liste.

Code source dans 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>
13
angularsen

Ce que vous voulez vraiment faire est d’envelopper tous les éléments enfants. Dans ce cas, vous devez utiliser un contrôle d’objets et ne pas recourir à d’horribles propriétés attachées, ce qui vous donnera un million pour chaque propriété que vous souhaitez styler.

<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

8
bradgonesurfing

+1 pour la réponse de Sergey. Et si vous souhaitez 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 méfiez-vous: si vous définissez un style comme celui-ci dans votre App.xaml (ou un autre dictionnaire fusionné dans Application.Resources), il can remplace le style par défaut du contrôle. Ce n'est pas un problème pour les contrôles généralement superflus comme le stackpanel, mais vous pouvez tomber sur ce problème , ce qui heureusement a quelques solutions de contournement.

6
Andre Luus

Pour donner suite à la suggestion de Sergey, vous pouvez définir et réutiliser un style entier (avec divers paramètres de propriété, y compris la marge) au lieu d'un simple objet d'épaisseur:

<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 l'astuce consiste ici à utiliser Style Inheritance pour le style implicite, héritant du style de certains dictionnaires de ressources externes (probablement fusionnés à partir d'un fichier XAML externe).

Note:

Au début, j'ai essayé naïvement d'utiliser le style implicite pour définir la propriété Style du contrôle sur cette ressource Style externe (définie avec la clé "MyStyle"):

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

ce qui a provoqué l'arrêt immédiat de Visual Studio 2010 avec l'erreur CATASTROPHIC FAILURE (HRESULT: 0x8000FFFF (E_UNEXPECTED)), comme décrit à la section https://connect.Microsoft.com/VisualStudio/feedback/details/753211/xaml-editor -window-failure-with-catastrophichic-fail-when-a-style-essaye-set-style-property #

3
George Birbilis

Grid.ColumnSpacing , Grid.RowSpacing , StackPanel.Spacing sont maintenant en mode de prévisualisation UWP, tout permettra de mieux répondre à ce qui est demandé ici.

Ces propriétés ne sont actuellement disponibles qu'avec le Kit de développement logiciel (SDK) Insider Update de Windows 10 Fall Creators, mais devraient en faire partie finale!

3
Pedro Lamas

Mon approche hérite de StackPanel.

Usage:

<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 dont vous avez besoin est la courte classe 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();
        }
    }
}
2
James M

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

2
Jean Azzopardi

parfois, vous devez définir le rembourrage, pas la marge pour créer un espace entre les éléments inférieurs

0
Serov Danil