TOC

This article is currently in the process of being translated into Polish (~96% done).

List controls:

ItemsControl

WPF ma szeroki zakres kontrolek służących do wyświetlania listy danych. Występują w różnych kształtach i odmianach. Różnią się stopniem złożoności oraz tym, jak wiele pracy są w stanie za nas wykonać. Najprostszym wariantem jest ItemsControl, który jest po prostu xaml'ową pętlą - programista w tym przypadku sam odpowiada za style i szablony dla prezentowanej listy. W wielu przypadkach właśnie na tym nam zależy.

A simple ItemsControl example

Zacznijmy od bardzo prostego przykładu, w którym ręcznie przekazujemy ciąg obiektów dla ItemsControl. Ukazuje to jak prosta jest ta kontrolka:

<Window x:Class="WpfTutorialSamples.ItemsControl.ItemsControlSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:system="clr-namespace:System;assembly=mscorlib"
        Title="ItemsControlSample" Height="150" Width="200">
    <Grid Margin="10">
		<ItemsControl>
			<system:String>ItemsControl Item #1</system:String>
			<system:String>ItemsControl Item #2</system:String>
			<system:String>ItemsControl Item #3</system:String>
			<system:String>ItemsControl Item #4</system:String>
			<system:String>ItemsControl Item #5</system:String>
		</ItemsControl>
	</Grid>
</Window>

Jak widzisz, nic nie wkazuje na to że wykorzystujemy kontrolkę do wyświetlania elementów zamiast np. ręcznie dodać 5 osobnych kontrolek typu TextBlock - domyślnie sama kontroka ItemsControl jest kompletnie niewidoczna. Gdy klikniesz na któryś z elementów, nic się nie dzieje, ponieważ ItemsControl nie ma funkcjonalności zaznaczania elementów, ani niczego podobnego.

ItemsControl a wiązanie danych

Oczywiście, ItemsControl nie został przeznaczony do wykorzystania z elementami zdefiniowanymi w xaml'u, tak jak w pierwszym przykładzie powyżej. Tak jak właściwie każda inna kontrolka w WPF, ItemsControl został stworzony z myślą o wiązaniu danych, gdzie za pomocą szablonu definiujemy jak mają zostać wyświetlone dla użytkownika obiekty wybranej kolekcji.

Aby to zademonstrować, przygotowałem przykład, w którym wyświetlamy listę rzeczy do zrobienia dla użytkownika. Przykład prezentuje jak elastyczne staje się używanie ItemsControl z chwilą gdy zdefiniujesz własne szablony. Wykorzystałem kontrolkę ProgressBar aby pokazać postęp wykonania rzeczy z listy. Najpierw trochę kodu, potem zrzut ekranu, a na koniec wyjaśnienie:

<Window x:Class="WpfTutorialSamples.ItemsControl.ItemsControlDataBindingSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ItemsControlDataBindingSample" Height="150" Width="300">
    <Grid Margin="10">
		<ItemsControl Name="icTodoList">
			<ItemsControl.ItemTemplate>
				<DataTemplate>
					<Grid Margin="0,0,0,5">
						<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>
			</ItemsControl.ItemTemplate>
		</ItemsControl>
	</Grid>
</Window>
using System;
using System.Windows;
using System.Collections.Generic;

namespace WpfTutorialSamples.ItemsControl
{
	public partial class ItemsControlDataBindingSample : Window
	{
		public ItemsControlDataBindingSample()
		{
			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 });

			icTodoList.ItemsSource = items;
		}
	}

	public class TodoItem
	{
		public string Title { get; set; }
		public int Completion { get; set; }
	}
}

Najważniejszą częścią tego przykładu jest szablon, który definiujemy wewnątrz ItemsControl wykorzystując tag DataTemplate wewnątrz właściwości ItemsControl.ItemTemplate. Dodajemy element Grid, aby utworzyć dwie kolumny: w pierwszej mamy TextBlock, który pokaże nazwę rzeczy do wykonania, a w drugiej mamy kontrolkę ProgressBar, której wartość (Value) wiążemy z właściwością Completion klasy TodoItem.

W tym momencie szablon prezentuje obiekty klasy TodoItem, którą zadeklarowaliśmy w pliku code-behind. Tam również stworzyliśmy instancje tych obiektów i dodaliśmy je do listy. W końcu, lista ta została przekazana jako właściwość ItemsSource naszej kontrolki ItemsControl, dzięki czemu kontrolka 'wie', jakie elementy wyświetlić. Każdy element z listy został wyświetlony z wykorzystaniem naszego szablonu, tak jak widać na powyższym zrzucie ekranu.

Właściwość ItemsPanelTemplate

W obu powyższych przykładach, wszystkie elementy są renderowane od góry do dołu, a każdy element zajmuje cały wiersz. Dzieje się tak, ponieważ ItemsControl domyślnie wrzuca wszystkie nasze elementy do pionowo zorientowanego StackPanel'u. Można to jednak bardzo łatwo zmienić, jako że ItemsControl pozwala zmienić rodzaj panelu wykorzystywany do przechowywania elementów kolekcji. Tu jest przykład:

<Window x:Class="WpfTutorialSamples.ItemsControl.ItemsControlPanelSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:system="clr-namespace:System;assembly=mscorlib"
        Title="ItemsControlPanelSample" Height="150" Width="250">
	<Grid Margin="10">
		<ItemsControl>
			<ItemsControl.ItemsPanel>
				<ItemsPanelTemplate>
					<WrapPanel />
				</ItemsPanelTemplate>
			</ItemsControl.ItemsPanel>
			<ItemsControl.ItemTemplate>
				<DataTemplate>
					<Button Content="{Binding}" Margin="0,0,5,5" />
				</DataTemplate>
			</ItemsControl.ItemTemplate>
			<system:String>Item #1</system:String>
			<system:String>Item #2</system:String>
			<system:String>Item #3</system:String>
			<system:String>Item #4</system:String>
			<system:String>Item #5</system:String>
		</ItemsControl>
	</Grid>
</Window>

Precyzujemy, że ItemsControl ma używać WrapPanel'u jako szablonu swojego panelu, poprzez zadeklarowanie go we właściwości ItemsPanelTemplate. Dla zabawy, dorzucamy także ItemTemplate powodujący wyświetlenie ciągów znaków jako przycisków. Możesz wykorzystywać dowolne panele WPF, ale niektóre są bardziej użyteczne od innych.

Kolejnym dobrym przykładem jest panel UniformGrid, w którym możemy zdefiniować ilość kolumn, powodując, że elementy zostaną rozmieszczone równomiernie w kolumnach o równej szerokości.

<Window x:Class="WpfTutorialSamples.ItemsControl.ItemsControlPanelSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:system="clr-namespace:System;assembly=mscorlib"
        Title="ItemsControlPanelSample" Height="150" Width="250">
	<Grid Margin="10">
		<ItemsControl>
			<ItemsControl.ItemsPanel>
				<ItemsPanelTemplate>
					<UniformGrid Columns="2" />
				</ItemsPanelTemplate>
			</ItemsControl.ItemsPanel>
			<ItemsControl.ItemTemplate>
				<DataTemplate>
					<Button Content="{Binding}" Margin="0,0,5,5" />
				</DataTemplate>
			</ItemsControl.ItemTemplate>
			<system:String>Item #1</system:String>
			<system:String>Item #2</system:String>
			<system:String>Item #3</system:String>
			<system:String>Item #4</system:String>
			<system:String>Item #5</system:String>
		</ItemsControl>
	</Grid>
</Window>

ItemsControl z suwakami

Gdy zaczniesz korzystać z ItemsControl, możesz napotkać bardzo częsty problem: domyślnie ItemsControl nie posiada żadnych suwaków, co znaczy że jeśli zawartość się w nim nie mieści, zostanie przycięta. Można to zobaczyć włączając aplikację z pierwszego przykładu z tego artykułu i zmniejszając rozmiar okna:

WPF czyni to jednak bardzo prostym do rozwiązania. Jest szereg możliwych rozwiązań, na przykład możesz zmienić szablon wykorzystywany przez ItemsControl aby zawierał kontrolkę ScrollViewer. Najprostszym rozwiązaniem jednak jest umieszczenie ScrollViewer'a wokół ItemsControl. Tu jest przykład:

<Window x:Class="WpfTutorialSamples.ItemsControl.ItemsControlSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:system="clr-namespace:System;assembly=mscorlib"
        Title="ItemsControlSample" Height="150" Width="200">
	<Grid Margin="10">
		<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
			<ItemsControl>
				<system:String>ItemsControl Item #1</system:String>
				<system:String>ItemsControl Item #2</system:String>
				<system:String>ItemsControl Item #3</system:String>
				<system:String>ItemsControl Item #4</system:String>
				<system:String>ItemsControl Item #5</system:String>
			</ItemsControl>
		</ScrollViewer>
	</Grid>
</Window>

Ustawiłem obie opcje widoczności na Auto, aby kontrolka wyświetlała suwaki tylko gdy są potrzebne. Jak widać na zrzucie ekranu, możemy teraz przewijać listę elementów.

Podsumowanie

Kontrolka ItemsControl jest świetna, gdy potrzebujesz pełnej kontroli nad sposobem wyświetlania danych, oraz gdy nie potrzebujesz aby elementy były zaznaczalne. Jeśli chcesz aby użytkownik był w stanie zaznaczać elementy z listy, skorzystaj z innych kontrolek, np. ListBox lub ListView. Zostaną one omówione w kolejnych rozdziałach.

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!