This article is currently in the process of being translated into Russian (~99% done).
The ItemsControl
WPF имеет множество элементов управления для отображения списков данных. Они бывают разного вида, сложности и количества выполняемой для вас работы. Простейший из них это ItemsControl, шаблоны и стили которого нужно писать вручную, часто это именно то что нам надо.
Простой пример ItemsControl
Начнем с простейшего примера, где мы вручную заполним ItemsControl парочкой элементов. Так мы увидим насколько ItemsControl простой:
<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>
Можно заметить что здесь нет повторяющихся элементов управления отвечающих за отдельный элемент списка, вместо этого вручную добавлены 5 произвольных элементов TextBlock. Если кликнуть по элементу списка, то ничего не произойдёт, потому что выделение элементов и другие механики попросту отсутствуют.
ItemsControl с привязкой данных
Конечно, не подразумевается, что ItemsControl будет использоваться с элементами, добавленными вручную в разметке, как мы делали это в первом примере. Как и многие другие элементы управления в WPF, ItemsControl сделан для привязки данных (data binding), в которой мы используем шаблон, для того чтобы определить, как наш описанный в коде класс должен быть представлен пользователю.
Для демонстрации я сделал пример, в котором отображается список дел пользователя, а чтобы показать гибкость шаблонов, я использовал ProgressBar для вывода процента завершенности. Сначала код, потом картинка, и затем пояснения:
<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; }
}
}
Главная часть примера - это шаблон, указанный внутри ItemsControl с помощью ItemsControl.ItemTemplate с тэгом DataTemplate внутри. Добавим панель Grid, чтобы получить две колонки: в первой у нас TextBlock, показывающий название задачи, а во второй колонке ProgressBar, значение которого мы привязали к свойству Completion.
Теперь шаблон представляет класс TodoItem, который мы декларировали в коде, там же создали несколько объектов класса и добавили их в список. Затем этот список присвоен свойству ItemsSource нашего ItemsControl, который сделает остальное за нас. Как видим из скриншота, элементы списка отображаются по шаблону.
Свойство ItemsPanelTemplate
В примерах выше, все элементы отображаются сверху вниз и занимают всю ширину. Это происходит потому что ItemsControl по умолчанию помещает все наши элементы в вертикальную StackPanel. Это легко изменить, ведь ItemsControl позволяет указать панель для содержания всех элементов. Например:
<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>
С помощью свойства ItemsPanelTemplate мы указали что ItemsControl должен использовать WrapPanel в качестве шаблона, и просто для забавы добавили ItemTemplate меняющий отображение строк так что они выглядят как кнопки. Можно использовать любую панель WPF, но некоторые полезней других.
Еще один хороший пример с панелью UniformGrid, для которой можно указать количество колонок и наши элементы отобразятся в одинаковых по размеру колонках.
<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 с прокруткой
Используя ItemsControl, вы можете столкнуться с обычной проблемой: По умолчанию ItemsControl не имеет бегунков для прокрутки, так что если содержимое не вмещается, оно будет скрыто. Это видно если взять наш первый пример и поменять размер окна:
Это легко решаемо в WPF. Есть несколько возможных решений, например, можно изменить шаблон для ItemsControl, включив в него ScrollViewer, но простейшее решение - это поместить ItemsControl внутрь ScrollViewer. Вот пример:
<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>
Я установил опции видимости на Auto, так бегунки будут видны по надобности. На картинке видно что теперь есть возможность прокрутки.
Сводка
ItemsControl хорош если требуется полный контроль отображения данных и не требуется возможность выделения элементов. Если всё же нужна возможность выбора, лучше выбрать ListBox или ListView, которые будут описаны в следующих статьях.