This article has been localized into German by the community.
Die ListBox
Im letzten Artikel haben wir uns das ItemsControl angesehen, die wahrscheinlich die einfachste Liste im WPF ist. Das ListBox-Control ist das nächste Control in der Reihe, das etwas mehr Funktionalität bietet. Einer der Hauptunterschiede besteht darin, dass sich das Listbox-Control tatsächlich mit Selektionen beschäftigt, so dass der Endbenutzer ein oder mehrere Elemente aus der Liste auswählen kann und automatisch visuelles Feedback dazu erhält.
Hier ist ein sehr einfaches Beispiel für ein ListBox-Steuerelement.
<Window x:Class="WpfTutorialSamples.ListBox_control.ListBoxSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListBoxSample" Height="120" Width="200">
<Grid Margin="10">
<ListBox>
<ListBoxItem>ListBox Item #1</ListBoxItem>
<ListBoxItem>ListBox Item #2</ListBoxItem>
<ListBoxItem>ListBox Item #3</ListBoxItem>
</ListBox>
</Grid>
</Window>
Das ist so einfach wie es nur geht: Wir deklarieren ein ListBox-Control, und innerhalb davon deklarieren wir drei ListBoxItems, die jeweils einen eigenen Text enthalten. Da es sich bei dem ListBoxItem jedoch um ein ContentControl handelt, können wir dafür eigene Inhalte definieren:
<Window x:Class="WpfTutorialSamples.ListBox_control.ListBoxSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListBoxSample" Height="120" Width="200">
<Grid Margin="10">
<ListBox>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<Image Source="/WpfTutorialSamples;component/Images/bullet_blue.png" />
<TextBlock>ListBox Item #1</TextBlock>
</StackPanel>
</ListBoxItem>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<Image Source="/WpfTutorialSamples;component/Images/bullet_green.png" />
<TextBlock>ListBox Item #2</TextBlock>
</StackPanel>
</ListBoxItem>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<Image Source="/WpfTutorialSamples;component/Images/bullet_red.png" />
<TextBlock>ListBox Item #3</TextBlock>
</StackPanel>
</ListBoxItem>
</ListBox>
</Grid>
</Window>
Für jedes der ListBoxItems fügen wir nun ein StackPanel hinzu, in dem wir ein Image und einen TextBlock hinzufügen. Dies gibt uns die volle Kontrolle über den Inhalt sowie die Textdarstellung, wie Sie auf dem Screenshot sehen können, wo für jede der Zahlen unterschiedliche Farben verwendet wurden.
Aus dem Screenshot können Sie auch einen weiteren Unterschied beim Vergleich des Items Controls mit der Listbox erkennen: Standardmäßig wird ein Rand um das Steuerelement herum angezeigt, so dass es wie ein tatsächliches Steuerelement aussieht und nicht nur der Inhalt angezeigt wird.
Datenbindung bei der ListBox
Die manuelle Definition von Elementen für die Listbox ist ein gutes erstes Beispiel, aber in den meisten Fällen werden Ihre ListBox-Steuerelemente mit Objekten aus einer Datenquelle per Datenbindung gefüllt. Wenn Sie eine Liste von Elementen an die ListBox binden, wird standardmäßig deren ToString()-Methode verwendet, um jedes Element darzustellen. Selten will man das haben, aber zum Glück können wir leicht eine Vorlage deklarieren, die verwendet wird, um jedes Element zu rendern.
Ich habe das To do-Listen-Beispiel aus dem ItemsControl-Artikel wiederverwendet, wo wir eine coole To do-Liste mit einer einfachen Code-behind-Klasse und, in diesem Fall, einem Listbox-Control für die visuelle Darstellung erstellen. Hier ist das Beispiel:
<Window x:Class="WpfTutorialSamples.ListBox_control.ListBoxDataBindingSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListBoxDataBindingSample" Height="150" Width="300">
<Grid Margin="10">
<ListBox Name="lbTodoList" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Title}" />
<ProgressBar Grid.Column="1" Minimum="0" Maximum="100" Value="{Binding Completion}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
using System;
using System.Windows;
using System.Collections.Generic;
namespace WpfTutorialSamples.ListBox_control
{
public partial class ListBoxDataBindingSample : Window
{
public ListBoxDataBindingSample()
{
InitializeComponent();
List<TodoItem> items = new List<TodoItem>();
items.Add(new TodoItem() { Title = "Complete this WPF tutorial", Completion = 45 });
items.Add(new TodoItem() { Title = "Learn C#", Completion = 80 });
items.Add(new TodoItem() { Title = "Wash the car", Completion = 0 });
lbTodoList.ItemsSource = items;
}
}
public class TodoItem
{
public string Title { get; set; }
public int Completion { get; set; }
}
}
Die ganze Magie geschieht in der ItemTemplate, die wir für die Listbox definiert haben. Dort legen wir fest, dass jedes ListBox-Element aus einem Raster bestehen soll, das in zwei Spalten unterteilt ist, mit einem TextBlock, der den Titel in der ersten und einer ProgressBar, die den Fertigstellungsstatus in der zweiten Spalte anzeigt. Um die Werte herauszubekommen, verwenden wir einige sehr einfache Datenbindungen, die alle im Abschnitt zur Datenbindung dieses Tutorials erläutert werden.
In der Code-behind-Datei haben wir eine sehr einfache TodoItem-Klasse deklariert, um jedes unserer To do-Elemente aufzunehmen. Im Konstruktor des Fensters initialisieren wir eine Liste, fügen ihr drei To do-Elemente hinzu und weisen sie dann der ItemsSource der Listbox zu. Die Kombination aus der ItemSource und der ItemTemplate, die wir im XAML-Teil spezifiziert haben, ist alles, was WPF benötigt, um die Elemente als To do-Liste darzustellen.
Bitte beachten Sie die Eigenschaft HorizontalContentAlignment, die ich in der Listbox auf Stretch gesetzt habe. Die standardmäßige Ausrichtung des Inhalts für ein Listbox-Element ist Left, was bedeutet, dass jedes Element nur so viel horizontalen Platz einnimmt, wie es benötigt. Das Ergebnis? Nun, nicht ganz das, was wir wollen:
Durch Verwendung der Ausrichtung "Stretch" wird jedes Element so in die Breite gezogen, dass es den vollen verfügbaren Platz einnimmt, wie Sie am obigen Screenshot sehen können.
Umgang mit der Auswahl in der ListBox
Wie bereits erwähnt, besteht ein wesentlicher Unterschied zwischen dem ItemsControl und der ListBox darin, dass die ListBox die Benutzerauswahl für Sie bearbeitet und anzeigt. Daher dreht sich eine Menge Listbox-Fragen darum, irgendwie mit der Auswahl zu arbeiten. Um bei einigen dieser Fragen zu helfen, habe ich ein größeres Beispiel erstellt, das Ihnen einige auswahlbezogene Tricks zeigt:
<Window x:Class="WpfTutorialSamples.ListBox_control.ListBoxSelectionSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListBoxSelectionSample" Height="250" Width="450">
<DockPanel Margin="10">
<StackPanel DockPanel.Dock="Right" Margin="10,0">
<StackPanel.Resources>
<Style TargetType="Button">
<Setter Property="Margin" Value="0,0,0,5" />
</Style>
</StackPanel.Resources>
<TextBlock FontWeight="Bold" Margin="0,0,0,10">ListBox selection</TextBlock>
<Button Name="btnShowSelectedItem" Click="btnShowSelectedItem_Click">Show selected</Button>
<Button Name="btnSelectLast" Click="btnSelectLast_Click">Select last</Button>
<Button Name="btnSelectNext" Click="btnSelectNext_Click">Select next</Button>
<Button Name="btnSelectCSharp" Click="btnSelectCSharp_Click">Select C#</Button>
<Button Name="btnSelectAll" Click="btnSelectAll_Click">Select all</Button>
</StackPanel>
<ListBox Name="lbTodoList" HorizontalContentAlignment="Stretch" SelectionMode="Extended" SelectionChanged="lbTodoList_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Title}" />
<ProgressBar Grid.Column="1" Minimum="0" Maximum="100" Value="{Binding Completion}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</Window>
using System;
using System.Windows;
using System.Collections.Generic;
namespace WpfTutorialSamples.ListBox_control
{
public partial class ListBoxSelectionSample : Window
{
public ListBoxSelectionSample()
{
InitializeComponent();
List<TodoItem> items = new List<TodoItem>();
items.Add(new TodoItem() { Title = "Complete this WPF tutorial", Completion = 45 });
items.Add(new TodoItem() { Title = "Learn C#", Completion = 80 });
items.Add(new TodoItem() { Title = "Wash the car", Completion = 0 });
lbTodoList.ItemsSource = items;
}
private void lbTodoList_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
if(lbTodoList.SelectedItem != null)
this.Title = (lbTodoList.SelectedItem as TodoItem).Title;
}
private void btnShowSelectedItem_Click(object sender, RoutedEventArgs e)
{
foreach(object o in lbTodoList.SelectedItems)
MessageBox.Show((o as TodoItem).Title);
}
private void btnSelectLast_Click(object sender, RoutedEventArgs e)
{
lbTodoList.SelectedIndex = lbTodoList.Items.Count - 1;
}
private void btnSelectNext_Click(object sender, RoutedEventArgs e)
{
int nextIndex = 0;
if((lbTodoList.SelectedIndex >= 0) && (lbTodoList.SelectedIndex < (lbTodoList.Items.Count - 1)))
nextIndex = lbTodoList.SelectedIndex + 1;
lbTodoList.SelectedIndex = nextIndex;
}
private void btnSelectCSharp_Click(object sender, RoutedEventArgs e)
{
foreach(object o in lbTodoList.Items)
{
if((o is TodoItem) && ((o as TodoItem).Title.Contains("C#")))
{
lbTodoList.SelectedItem = o;
break;
}
}
}
private void btnSelectAll_Click(object sender, RoutedEventArgs e)
{
foreach(object o in lbTodoList.Items)
lbTodoList.SelectedItems.Add(o);
}
}
public class TodoItem
{
public string Title { get; set; }
public int Completion { get; set; }
}
}
Wie Sie sehen können, habe ich eine Reihe von Schaltflächen rechts neben der Listbox definiert, um die Auswahl entweder zu erhalten oder zu verändern. Ich habe auch den SelectionMode (Auswahlmodus) auf Extended (Erweitert) geändert, um die Auswahl mehrerer Elemente zu ermöglichen. Dies kann entweder programmgesteuert erfolgen, wie ich es im Beispiel tue, oder durch den Endbenutzer, indem er [Strg] oder [Umschalt] gedrückt hält, während er auf die Elemente klickt.
Für jede der Schaltflächen habe ich im Code-Behind einen Click-Handler definiert. Jede Aktion sollte ziemlich selbsterklärend sein und der C#-Code ist ziemlich einfach, aber wenn Sie trotzdem Zweifel haben, dann lassen Sie das Beispielprogramm auf Ihrem Rechner laufen und probieren Sie mit den verschiedenen Möglichkeiten herum.
Zusammenfassung
Das Listbox-Control ist ähnlich wie das ItemsControl und es können einige der gleichen Techniken verwendet werden. Die Listbox bietet im Vergleich zum ItemsControl etwas mehr Funktionalität, insbesondere die Auswahlbehandlung. Für noch mehr Funktionalität, wie z.B. Spaltenüberschriften, sollten Sie einen Blick auf das ListView-Control werfen, das später in diesem Tutorial mit mehreren Artikeln, die alle Funktionen erklären, sehr ausführlich beschrieben wird.