2009-05-31 3 views
146

dato uno StackPanel:Come sposto gli elementi figlio di uno StackPanel?

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

Qual è il modo migliore per distanziare gli elementi figlio in modo che non vi siano lacune di pari dimensioni tra di loro, anche se gli elementi figli stessi sono di diverse dimensioni? Può essere fatto senza impostare le proprietà su ciascuno dei singoli bambini?

risposta

228

Margine o imbottitura, applicato al campo di applicazione all'interno del contenitore:

<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: Nel caso in cui si vorrebbe riutilizzare il margine tra due contenitori, è possibile convertire il valore del margine a un risorsa in un ambito esterno, f

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

e poi si riferiscono a questo valore del perimetro interno

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

Lo scoped Style è un * fantastico * modo di farlo - grazie per il suggerimento! –

+1

E se lo volessi usare per l'intero progetto? –

+4

Qualcuno può spiegare perché questo funziona solo quando si definisce esplicitamente il tipo (ad esempio TextBox)? Se provo a utilizzare FrameworkElement in modo che tutti i bambini siano distanziati, non ha alcun effetto. – Schneider

4

+1 richiesta di risposta di Sergey. E se si desidera applicare che a tutti i vostri StackPanels si può fare questo:

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

Ma attenzione: se si definisce uno stile come questo nella vostra App.xaml (o un altro dizionario che si fuse in Application .Risorse) è può sovrascrivere lo stile predefinito del controllo. Per i controlli per lo più senza look come lo stackpanel non è un problema, ma per le caselle di testo ecc. Potresti inciampare su this problem, che per fortuna ha qualche soluzione.

+0

Si è sicuri di averlo provato quando si è verificato un errore. –

+0

Mi spiace, non riesco a ricordarlo a memoria. Dovrò provarlo io stesso per vedere. Tornerò da te. –

+0

Questo sarà grato. –

69

Un altro approccio bello può essere visto qui: http://blogs.microsoft.co.il/blogs/eladkatz/archive/2011/05/29/what-is-the-easiest-way-to-set-spacing-between-items-in-stackpanel.aspx

Essa mostra come creare un comportamento allegato, in modo che la sintassi come questo dovrebbe funzionare:

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

Questo è il più facile & modo più veloce per imposta Margine su diversi figli di un pannello, anche se non sono dello stesso tipo. (pulsanti IE, caselle di testo, ComboBox, ecc)

+5

Questo è un modo piuttosto interessante per andare su questo. Fa molte supposizioni su come esattamente vuoi dare spazio alle cose, e ti dà anche l'opportunità di aggiustare automaticamente i margini sul primo/ultimo elemento. – Armentage

+1

+1 per versatilità. Anche per migliorare sul post del blog, aggiungendo 'if (fe.ReadLocalValue (FrameworkElement.MarginProperty) == DependencyProperty.UnsetValue)' prima di impostare effettivamente il margine del bambino, permette di specificare manualmente i margini per alcuni elementi. – Xerillio

+0

Si noti che questo non funziona se gli elementi secondari vengono aggiunti/rimossi dinamicamente, ad esempio in un oggetto ItemsControl associato a una raccolta di modifiche. –

3

In seguito su suggerimento di Sergey, è possibile definire e riutilizzare un intero stile (con vari setter di proprietà, tra cui Margine) invece di un oggetto Spessore:

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

Nota che il trucco è l'uso di stile di eredità per lo stile implicita, che eredita dalla stile in qualche dizionario risorse esterno (probabilmente unito da file XAML esterno).

Nota a margine:

In un primo momento, ho ingenuamente cercato di utilizzare lo stile implicita per impostare la proprietà Style del controllo a quella risorsa di stile esterno (dire definito con il tasto "MyStyle"):

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

che ha causato Visual Studio 2010 di chiudere immediatamente con l'errore catastrofico fallimento (HRESULT: 0x8000FFFF (E_UNEXPECTED)), come descritto nella https://connect.microsoft.com/VisualStudio/feedback/details/753211/xaml-editor-window-fails-with-catastrophic-failure-when-a-style-tries-to-set-style-property#

0

a volte è necessario impostare Riempimento, non margine per fare spazio tra gli elementi più piccoli del valore predefinito

9

Ho migliorato il Elad Katz' answer.

  • Aggiungi proprietà LastItemMargin a MarginSetter per gestire specialmente l'ultimo elemento
  • Add spaziatura proprietà associata con proprietà orizzontali e verticali che aggiunge la spaziatura tra gli elementi nelle liste verticali e orizzontali ed elimina qualsiasi margine finale alla fine della lista

Source code in gist.

Esempio:

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

Come la risposta di Elad, questo non funziona se gli elementi figli vengono aggiunti/rimossi dinamicamente, come in un 'ItemsControl' associato a una collezione che cambia. Presume che gli elementi siano statici dal momento in cui si verifica l'evento 'Load' del genitore. –

+0

Inoltre, il tuo elenco fornisce un 404. –

+0

Link aggiornato a gist. – angularsen

5

La cosa si vuole veramente fare è avvolgere tutti gli elementi figlio. In questo caso dovresti usare il controllo degli oggetti e non ricorrere a proprietà attaccate orribili che finirai per avere un milione di per ogni proprietà che desideri modellare.

<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

Ottimo suggerimento, ma funziona meglio con lo Style TargetType come "FrameworkElement" (altrimenti non funziona con Image, ad esempio) – Puffin

+0

Mi piace questa idea. Solo un'aggiunta: sottraendo la quantità di spaziatura dal margine dello StackPanel ('Margin =" 0 0 -5 0 "') si contrapporrà anche alla spaziatura dopo l'ultimo elemento nella lista. – label17

+0

Il problema con questo è che lo stile impostato sostituirà qualsiasi altro stile che potresti già avere sugli Elementi. Per risolvere questo problema, vedi questa domanda correlata/risposta accettata [qui] (http://stackoverflow.com/questions/4003709/wrap-something-around-each-item-in-an-itemscontrol) – waxingsatirical

0

Il mio approccio eredita 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> 

Tutto ciò che serve è la seguente classe breve:

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

Grid.ColumnSpacing, Grid.RowSpacing, StackPanel.Spacing sono ora in UWP anteprima, tutte le permetterà di migliorare compire quanto richiesto qui .

Queste proprietà sono attualmente disponibili solo con l'SDK Insider di Windows 10 Fall Creators Update, ma dovrebbero essere aggiunte ai bit finali!