TOC

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

Списочные элементы управления:

The ListBox control

В последней статье мы получили представление о ItemsControl, представляющий собой простейший список в WPF. ListBox является следующем элементом управления по этому направлению, который имеет немного расширенную функциональность. Одним из главных отличий является факт что элемент управления ListBox позволяет произвести различные варианты выбора - позволяет конечному пользователю выбрать один или несколько элементов списка и автоматически получить визуальный отклик на данное действие.

Ниже приведен пример очень простого использования элемента управления 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>

Если кратко: Мы декларируем элемент управления ListBox, внутри его определяем три элемента (ListBoxItem's) с своим текстом. Несмотря на то, что ListBoxItem является ContentControl, мы можем переопределить его содержание:

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

Для каждого ListBoxItem мы сейчас добавили StackPanel, в которой мы добавили элементы Image и TextBlock, что предоставило полное управление содержанием такое как визуализация текста, как вы можете увидеть на скриншоте, где различные цвета используются для каждого числа.

На скриншоте вы также можете заметить другое отличие между ItemsControl и ListBox: по умолчанию граница элемента управления отображается, делая ListBox похожим на фактический элемент управления, а не просто вывод.

Data binding the ListBox

Установка элементов списка ListBox вручную годится для первого примера, но в большинстве случаев ваш ListBox будет заполнятся элементами из источника данных через привязку. По умолчанию, привязанные к ListBox элементы отображаются с помощью их метода ToString(). Но этот вариант не всегда подходит, поэтому мы можем объявить шаблон, который будет использоваться для представления каждого элемента списка.

I have re-used the TODO based example from the ItemsControl article, where we build a cool TODO list using a simple Code-behind class and, in this case, a ListBox control for the visual representation. Here's the example:

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

Вся магия происходит в шаблоне ItemTemplate, который мы определили для ListBox. Здесь мы указали, что каждый элемент ListBox должен состоять из Grid'а, разделенного на две колонки. TextBlock в первой колонке для названия и ProgressBar во второй для статуса. Чтобы установить их значения, используется очень простая привязка данных (подробно рассмотрено в части о привязках этой статьи).

In the Code-behind file, we have declared a very simple TodoItem class to hold each of our TODO items. In the constructor of the window, we initialize a list, add three TODO items to it and then assign it to the ItemsSource of the ListBox. The combination of the ItemsSource and the ItemTemplate we specified in the XAML part, this is all WPF need to render all of the items as a TODO list.

Обратите внимание на свойство HorizontalContentAlignment, которое установлено в Stretch для ListBox. Выравнивание по умолчанию для ListBox всегда Left, что означает, что ширина элемента зависит от его содержимого. Результат? Хм, не совсем то, что мы хотели:

При использовании Stretch выравнивания, каждый элемент растягивается и занимает все доступное пространство, как это показано на предыдущем скриншоте.

Working with ListBox selection

Как уже упоминалось, ключевое различие между ItemsControl и ListBox заключается в том, что ListBox обрабатывает и отображает элементы выбранные пользователем. Таким образом множество вопросов ListBox построено вокруг работы с выделением. Чтобы помочь с некоторыми из этих вопросов, я создал более крупный пример, показывающий вам некоторые уловки, связанные с выделением:

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

As you can see, I have defined a range of buttons to the right of the ListBox, to either get or manipulate the selection. I've also changed the SelectionMode to Extended, to allow for the selection of multiple items. This can be done either programmatically, as I do in the example, or by the end-user, by holding down [Ctrl] or [Shift] while clicking on the items.

Для каждой кнопки я определил обработчик события Click. Каждое действие вполне объяснимо, используемый C# код достаточно простой, но если вам что-то непонятно, попробуйте запустить этот пример у себя и протестируйте все возможные варианты.

Заключение

ListBox очень похож на ItemsControl и он может использоваться в нескольких случаях точно также. Однако ListBox предоставляет больше возможностей относительно ItemsControl, особенно в случае необходимости выбора элемента. Если необходим расширенный функционал, как например наличие заголовков у колонок, вам необходимо взглянуть на ListView элемент, описание которого будет позже в руководстве и содержащее несколько статей объясняющие полный функционал ListView.

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!