TOC

This article has been localized into Czech by the community.

Ovládací prvky seznamu:

Ovládací prvek ListBox

V minulém článku jsme se podívali na ovládací prvek ItemsControl, který je pravděpodobně nejjednodušším seznamem ve WPF. Ovládací prvek ListBox je další v řadě, který přidává trochu více funkcionalit. Jedním z hlavních rozdílů je fakt, že ListBox ve skutečnosti zpracovává výběry, což umožňuje uživateli vybrat jednu nebo více položek ze seznamu a automaticky poskytnout vizuální zpětnou vazbu.

Zde je velmi jednoduchý příklad ovládacího prvku ListBox.

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

Je to tak jednoduché, jak to jen jde: Deklarujeme ovládací prvek ListBox a uvnitř něj deklarujeme tři položky ListBoxItem, každou s vlastním textem. Nicméně, vzhledem k tomu, že ListBoxItem je ve skutečnosti prvkem seznamu (ContentControl), můžeme pro něj definovat vlastní obsah (text):

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

Pro každou z položek ListBoxItem nyní přidáme StackPanel, do kterého vložíme obrázek a TextBlock. To nám dává plnou kontrolu nad obsahem a vykresleném textu, jak můžete vidět na obrázku, kde pro každé z čísel byly použity různé barvy.

Ze snímku si můžete také všimnout dalšího rozdílu při porovnání ovládacího prvku ItemsControl s ovládacím prvkem ListBox: Ve výchozím nastavení je kolem ovládacího prvku zobrazen rámeček, což mu dává vzhled skutečného ovládacího prvku, místo jen pouhého výstupu.

Data binding v ListBoxu

Ruční definování položek pro ListBox je dobrým prvním příkladem, ale většinou budou vaše ovládací prvky ListBox naplněny položkami z datového zdroje pomocí vazby dat (tzv. datového bindingu). Ve výchozím nastavení, pokud k ListBoxu přiřadíte seznam položek, bude použita jejich metoda ToString() pro reprezentaci každé položky. To ale jen zřídka odpovídá vašim potřebám, naštěstí ale můžeme snadno deklarovat šablonu, která bude použita pro vykreslení každé položky.

Využil jsem příklad založený na TODO z článku o ItemsControl, kde jsme vytvořili skvělý seznam úkolů (TODO list) pomocí jednoduché třídy Code-behind a v tomto případě ovládacího prvku ListBox pro vizuální reprezentaci. Zde je příklad:

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

Veškeré kouzlo se odehrává v ItemTemplate, který jsme definovali pro ListBox. Tam specifikujeme, že každá položka ListBoxu by měla sestávat z Gridu, rozděleného na dva sloupce, s TextBlockem zobrazujícím název v prvním sloupci a ProgressBar zobrazujícím stav dokončení ve druhém sloupci. Pro získání hodnot používáme velmi jednoduchou vazbu dat (data binding), což je vysvětleno v části tohoto tutoriálu věnované data bindingu.

V souboru Code-behind jsme deklarovali velmi jednoduchou třídu TodoItem pro uchování každé z našich položek TODO. V konstruktoru okna inicializujeme seznam, přidáme do něj tři položky TODO a poté ho přiřadíme k ItemsSource ListBoxu. Kombinací ItemsSource a ItemTemplate, kterou jsme specifikovali v části XAML, je to, co všechno WPF potřebuje k vykreslení všech položek seznamu TODO.

Všimněte si vlastnosti HorizontalContentAlignment, kterou jsem nastavil na Stretch u ListBoxu. Výchozí zarovnání obsahu pro položku ListBoxu je Left, což znamená, že každá položka zabere jen tolik horizontálního prostoru, kolik potřebuje. Výsledek? Ne zcela to, co chceme:

Použitím zarovnání Stretch je každá položka roztažena tak, aby zabrala celé dostupné místo, jak můžete vidět na předchozím snímku.

Práce s výběrem v ListBoxu

Jak bylo zmíněno, klíčový rozdíl mezi ovládacím prvkem ItemsControl a ListBoxem je, že ListBox zpracovává a zobrazuje pro vás uživatelský výběr. Proto se mnoho otázek týkajících se ListBoxu točí kolem práce s výběrem. Abych pomohl s některou z těchto otázek, vytvořil jsem rozsáhlejší příklad, který vám ukáže některé triky související s výběrem:

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

Jak vidíte, definoval jsem řadu tlačítek napravo od ListBoxu, aby bylo možné získat nebo manipulovat s výběrem. Také jsem změnil SelectionMode na Extended, aby bylo možné vybrat více položek. To lze provést buď programově, jak to dělám v příkladu, nebo uživatelsky, držením [Ctrl] nebo [Shift] při klikání na položky.

Pro každé z tlačítek jsem v Code-behind definoval obsluhu kliknutí. Každá akce by měla být poměrně samovysvětlující a použitý kód C# je docela jednoduchý, ale pokud máte stále pochybnosti, zkuste příklad spustit na svém počítači a vyzkoušejte různé možnosti, které příklad nabízí.

Shrnutí

Ovládací prvek ListBox je velmi podobný ovládacímu prvku ItemsControl a lze u něj použít mnoho stejných technik. ListBox ovšem nabízí trochu více funkcionalit, zejména co se týče zpracování výběru. Pro ještě více funkcionalit, jako jsou záhlaví sloupců, byste se měli podívat na ovládací prvek ListView, který je v tomto tutoriálu velmi důkladně popsaný a v několika článcích jsou vysvětleny všechny jeho funkce.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!