TOC

This article has been localized into Russian by the community.

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

Элемент управления ListBox

В последней статье мы получили представление о 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 похожим на фактический элемент управления, а не просто вывод.

Привязка данных к ListBox

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

Для примера я снова реализую такой же TODO список как из статьи про ItemsControl, где мы создавали классный список задач, используя простой класс с кодом, но теперь ещё и добавим элемент интерфейса ListBox для визуального представления. Вот пример:

<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 во второй для статуса. Чтобы установить их значения, используется очень простая привязка данных (подробно рассмотрено в части о привязках этой статьи).

В файле с исходным кодом мы объявили очень простой класс TodoItem для хранения каждого из наших элементов TODO. В конструкторе класса ListBoxDataBindingSample мы инициализировали List и добавили в него три объекта TodoItem, а затем назначили этот List как источник данных для элемента интерфейса ListBox, названного "lbTodoList" в XAML коде. Комбинация ItemsSource и ItemTemplate, которую мы указали в части XAML, - это все, что нужно WPF для отображения всех элементов ListBox в виде списка задач.

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

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

Работа с выделением в ListBox

Как уже упоминалось, ключевое различие между 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; }
	}
}

Как вы можете видеть, я определил несколько кнопок справа от ListBox, и для получения или для управления выбранного элементом списка. Я также изменил обычный режим (SelectionMode) в расширенный (Extended), который позволил выбирать несколько элементов из списка. Это может быть сделано или программным способом, как я и сделал в этом примере, или самим пользователем, удерживая клавишу [Ctrl] или [Shift]при выборе элементов списка (щелкая мышкой на соответствующем элементе списка)

Для каждой кнопки я определил обработчик события 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!