Panele WPF – StackPanel

1. Czym jest StackPanel?

StackPanel to panel layoutu, który układa swoje elementy potomne w jednej linii – pionowo lub poziomo. W przeciwieństwie do Grid, który operuje w dwóch wymiarach (wiersze i kolumny), StackPanel jest jednowymiarowy – elementy są po prostu dodawane jeden za drugim w określonym kierunku.

Główną zaletą StackPanel jest jego prostota. Nie trzeba definiować skomplikowanych struktur wierszy i kolumn – wystarczy dodać elementy do panelu, a zostaną one automatycznie ułożone w kolejności, w jakiej zostały zdefiniowane w XAML lub dodane programowo.

StackPanel automatycznie dostosowuje swój rozmiar do zawartości. W kierunku układania (pionowym lub poziomym) jego rozmiar jest sumą rozmiarów wszystkich elementów potomnych. W kierunku prostopadłym do układania może rozciągać się lub kurczyć w zależności od dostępnej przestrzeni.

Istotną cechą StackPanel jest to, że nie ogranicza rozmiarów swoich dzieci w kierunku układania. Jeśli dodamy za dużo elementów, StackPanel po prostu się wydłuży, nawet poza granice swojego kontenera nadrzędnego. To może prowadzić do sytuacji, gdzie część elementów staje się niewidoczna, dlatego StackPanel najlepiej sprawdza się z kontrolowaną liczbą elementów.

2. Orientacja – Vertical vs Horizontal

Najważniejszą właściwością StackPanel jest Orientation, która określa kierunek układania elementów.

Orientation=”Vertical” (domyślna)

<StackPanel Orientation="Vertical">
    <Button Content="Pierwszy przycisk"/>
    <Button Content="Drugi przycisk"/>
    <Button Content="Trzeci przycisk"/>
    <TextBlock Text="Tekst pod przyciskami"/>
</StackPanel>

W orientacji pionowej (domyślnej) elementy są układane jeden pod drugim, od góry do dołu. Każdy element zajmuje pełną szerokość StackPanel, a jego wysokość jest dostosowywana do zawartości lub jawnie ustawionej wartości.

Orientation=”Horizontal”

<StackPanel Orientation="Horizontal">
    <Button Content="Lewy"/>
    <Button Content="Środkowy"/>
    <Button Content="Prawy"/>
</StackPanel>

W orientacji poziomej elementy są układane obok siebie, od lewej do prawej. Każdy element zajmuje pełną wysokość StackPanel, a jego szerokość jest dostosowywana do zawartości.

Praktyczne porównanie orientacji

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    
    <TextBlock Grid.Row="0" Text="Orientacja pionowa:" FontWeight="Bold" Margin="5"/>
    <StackPanel Grid.Row="1" Orientation="Vertical" Background="LightBlue" Margin="5">
        <Button Content="Element 1" Height="30"/>
        <Button Content="Element 2" Height="30"/>
        <Button Content="Element 3" Height="30"/>
    </StackPanel>
    
    <Border Grid.Row="2" Background="White" Margin="5">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            
            <TextBlock Grid.Row="0" Text="Orientacja pozioma:" FontWeight="Bold" Margin="5"/>
            <StackPanel Grid.Row="1" Orientation="Horizontal" Background="LightGreen" 
                       VerticalAlignment="Top" Margin="5">
                <Button Content="Element 1" Width="80"/>
                <Button Content="Element 2" Width="80"/>
                <Button Content="Element 3" Width="80"/>
            </StackPanel>
        </Grid>
    </Border>
</Grid>

3. Właściwości wyrównania w StackPanel

StackPanel oferuje różne opcje wyrównywania elementów, które działają różnie w zależności od orientacji.

HorizontalAlignment w orientacji pionowej

<StackPanel Orientation="Vertical" Background="LightGray" Width="300">
    <Button Content="Left" HorizontalAlignment="Left" Width="100"/>
    <Button Content="Center" HorizontalAlignment="Center" Width="100"/>
    <Button Content="Right" HorizontalAlignment="Right" Width="100"/>
    <Button Content="Stretch" HorizontalAlignment="Stretch"/>
</StackPanel>

W orientacji pionowej można kontrolować, jak elementy są wyrównywane w poziomie:

  • Left – element jest wyrównany do lewej krawędzi
  • Center – element jest wyśrodkowany
  • Right – element jest wyrównany do prawej krawędzi
  • Stretch (domyślnie) – element rozciąga się na całą szerokość

VerticalAlignment w orientacji poziomej

<StackPanel Orientation="Horizontal" Background="LightGray" Height="100">
    <Button Content="Top" VerticalAlignment="Top" Height="30"/>
    <Button Content="Center" VerticalAlignment="Center" Height="30"/>
    <Button Content="Bottom" VerticalAlignment="Bottom" Height="30"/>
    <Button Content="Stretch" VerticalAlignment="Stretch"/>
</StackPanel>

W orientacji poziomej można kontrolować wyrównanie w pionie:

  • Top – element jest wyrównany do górnej krawędzi
  • Center – element jest wyśrodkowany w pionie
  • Bottom – element jest wyrównany do dolnej krawędzi
  • Stretch (domyślnie) – element rozciąga się na całą wysokość

4. Margin i Padding w StackPanel

Margin – odstępy między elementami

<StackPanel Orientation="Vertical" Background="LightBlue">
    <Button Content="Bez marginesu"/>
    <Button Content="Z marginesem" Margin="10"/>
    <Button Content="Margines tylko góra-dół" Margin="0,15,0,15"/>
    <Button Content="Różne marginesy" Margin="20,5,10,25"/>
</StackPanel>

Margin tworzy odstęp wokół elementu. W StackPanel jest szczególnie przydatny do tworzenia przestrzeni między kolejnymi elementami.

Padding – wewnętrzne wypełnienie StackPanel

<StackPanel Orientation="Vertical" Background="LightGray" Padding="20">
    <Button Content="Element 1"/>
    <Button Content="Element 2"/>
    <Button Content="Element 3"/>
</StackPanel>

Padding dodaje wewnętrzny odstęp w StackPanel – przestrzeń między krawędzią panelu a jego zawartością.

Kombinowanie Margin i Padding

<Border BorderBrush="Black" BorderThickness="2">
    <StackPanel Padding="15" Background="LightYellow">
        <TextBlock Text="Nagłówek" FontWeight="Bold" Margin="0,0,0,10"/>
        <Button Content="Przycisk 1" Margin="0,0,0,5"/>
        <Button Content="Przycisk 2" Margin="0,0,0,5"/>
        <TextBlock Text="Stopka" Margin="0,10,0,0" HorizontalAlignment="Center"/>
    </StackPanel>
</Border>

5. Praktyczne zastosowania StackPanel

Menu pionowe

<Border Background="DarkBlue" Width="150">
    <StackPanel Orientation="Vertical">
        <Button Content="Strona główna" Background="Transparent" 
                Foreground="White" BorderThickness="0" Height="40"/>
        <Button Content="Produkty" Background="Transparent" 
                Foreground="White" BorderThickness="0" Height="40"/>
        <Button Content="Usługi" Background="Transparent" 
                Foreground="White" BorderThickness="0" Height="40"/>
        <Button Content="Kontakt" Background="Transparent" 
                Foreground="White" BorderThickness="0" Height="40"/>
    </StackPanel>
</Border>

Pasek narzędzi poziomy

<Border Background="LightGray" Height="50">
    <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="10">
        <Button Content="Nowy" Width="60" Height="30" Margin="0,0,5,0"/>
        <Button Content="Otwórz" Width="60" Height="30" Margin="0,0,5,0"/>
        <Button Content="Zapisz" Width="60" Height="30" Margin="0,0,15,0"/>
        <Button Content="Wytnij" Width="60" Height="30" Margin="0,0,5,0"/>
        <Button Content="Kopiuj" Width="60" Height="30" Margin="0,0,5,0"/>
        <Button Content="Wklej" Width="60" Height="30"/>
    </StackPanel>
</Border>

Formularz z etykietami i polami

<StackPanel Orientation="Vertical" Margin="20" MaxWidth="300">
    <TextBlock Text="Formularz kontaktowy" FontSize="16" FontWeight="Bold" 
               HorizontalAlignment="Center" Margin="0,0,0,20"/>
    
    <TextBlock Text="Imię:"/>
    <TextBox Height="25" Margin="0,2,0,10"/>
    
    <TextBlock Text="Nazwisko:"/>
    <TextBox Height="25" Margin="0,2,0,10"/>
    
    <TextBlock Text="Email:"/>
    <TextBox Height="25" Margin="0,2,0,10"/>
    
    <TextBlock Text="Wiadomość:"/>
    <TextBox Height="80" TextWrapping="Wrap" AcceptsReturn="True" Margin="0,2,0,15"/>
    
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
        <Button Content="Wyślij" Width="70" Margin="0,0,10,0"/>
        <Button Content="Anuluj" Width="70"/>
    </StackPanel>
</StackPanel>

6. Sterowanie StackPanel z poziomu kodu C#

Tworzenie StackPanel programowo

public MainWindow()
{
    InitializeComponent();
    
    // Tworzenie StackPanel programowo
    StackPanel dynamicStack = new StackPanel();
    dynamicStack.Orientation = Orientation.Vertical;
    dynamicStack.Background = Brushes.LightBlue;
    dynamicStack.Margin = new Thickness(10);
    
    // Dodawanie elementów
    for (int i = 1; i <= 5; i++)
    {
        Button btn = new Button();
        btn.Content = $"Przycisk {i}";
        btn.Height = 30;
        btn.Margin = new Thickness(5);
        btn.Click += DynamicButton_Click;
        
        dynamicStack.Children.Add(btn);
    }
    
    // Dodanie StackPanel do głównego kontenera
    mainContainer.Children.Add(dynamicStack);
}

private void DynamicButton_Click(object sender, RoutedEventArgs e)
{
    Button btn = sender as Button;
    MessageBox.Show($"Kliknięto: {btn.Content}");
}

Dynamiczne dodawanie i usuwanie elementów

private void DodajElement_Click(object sender, RoutedEventArgs e)
{
    // Tworzenie nowego elementu
    TextBlock newElement = new TextBlock();
    newElement.Text = $"Element {myStackPanel.Children.Count + 1}";
    newElement.Background = Brushes.LightYellow;
    newElement.Padding = new Thickness(5);
    newElement.Margin = new Thickness(2);
    
    // Dodanie na koniec
    myStackPanel.Children.Add(newElement);
}

private void WstawElement_Click(object sender, RoutedEventArgs e)
{
    // Wstawienie na określonej pozycji
    Button newButton = new Button();
    newButton.Content = "Wstawiony przycisk";
    newButton.Height = 25;
    
    // Wstaw na pozycji 1 (jako drugi element)
    if (myStackPanel.Children.Count > 0)
    {
        myStackPanel.Children.Insert(1, newButton);
    }
    else
    {
        myStackPanel.Children.Add(newButton);
    }
}

private void UsunOstatni_Click(object sender, RoutedEventArgs e)
{
    // Usuń ostatni element
    if (myStackPanel.Children.Count > 0)
    {
        myStackPanel.Children.RemoveAt(myStackPanel.Children.Count - 1);
    }
}

private void WyczyscWszystko_Click(object sender, RoutedEventArgs e)
{
    // Usuń wszystkie elementy
    myStackPanel.Children.Clear();
}

Iterowanie przez elementy StackPanel

private void ZmienWszystkiePrzyciski_Click(object sender, RoutedEventArgs e)
{
    foreach (UIElement element in myStackPanel.Children)
    {
        if (element is Button button)
        {
            button.Background = Brushes.Yellow;
            button.Content = $"Zmieniony {button.Content}";
        }
    }
}

private void PoliczElementy_Click(object sender, RoutedEventArgs e)
{
    int buttonsCount = 0;
    int textBlocksCount = 0;
    
    foreach (UIElement element in myStackPanel.Children)
    {
        if (element is Button)
            buttonsCount++;
        else if (element is TextBlock)
            textBlocksCount++;
    }
    
    MessageBox.Show($"Przyciski: {buttonsCount}, TextBlocks: {textBlocksCount}");
}

Animowane dodawanie elementów

private async void DodajZAnimacją_Click(object sender, RoutedEventArgs e)
{
    for (int i = 1; i <= 3; i++)
    {
        Button newBtn = new Button();
        newBtn.Content = $"Animowany {i}";
        newBtn.Height = 30;
        newBtn.Margin = new Thickness(5);
        newBtn.Opacity = 0; // Zacznij przezroczyście
        
        myStackPanel.Children.Add(newBtn);
        
        // Prosta animacja pojawiania się
        for (double opacity = 0; opacity <= 1; opacity += 0.1)
        {
            newBtn.Opacity = opacity;
            await Task.Delay(50);
        }
    }
}

7. StackPanel vs Grid – kiedy używać którego?

Używaj StackPanel gdy:

  • Potrzebujesz prostego, liniowego układu elementów
  • Elementy mają być ułożone jeden za drugim w określonym kierunku
  • Nie potrzebujesz złożonego pozycjonowania
  • Tworzysz menu, paski narzędzi, listy formularzy
  • Liczba elementów jest kontrolowana i nie spowoduje przepełnienia

Przykład idealny dla StackPanel:

<StackPanel Orientation="Horizontal">
    <Button Content="Cofnij"/>
    <Button Content="Dalej"/>
    <Button Content="Odśwież"/>
</StackPanel>

Używaj Grid gdy:

  • Potrzebujesz złożonego layoutu z wieloma kolumnami i wierszami
  • Elementy mają mieć określone pozycje względem siebie
  • Chcesz elastyczne rozmiary (Auto, *, stałe)
  • Tworzysz formularze z etykietami i polami w kolumnach
  • Potrzebujesz rozciągania elementów przez wiele komórek

Przykład idealny dla Grid:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    
    <TextBlock Grid.Column="0" Text="Nazwa:"/>
    <TextBox Grid.Column="1"/>
</Grid>

Podsumowanie

StackPanel to prosty, ale bardzo użyteczny panel layoutu w WPF. Jego główną zaletą jest prostota – elementy są układane jeden za drugim bez konieczności definiowania skomplikowanych struktur.

Kluczowe aspekty StackPanel do zapamiętania:

  • Układa elementy liniowo – pionowo lub poziomo
  • Właściwość Orientation kontroluje kierunek układania
  • Automatycznie dostosowuje swój rozmiar do zawartości
  • Idealny dla menu, pasków narzędzi i prostych list
  • Można łatwo manipulować jego zawartością programowo