В прошлых темах были рассмотрены примеры привязки отдельных объектов к элементам интерфейса. Но, как правило, приложения оперируют не одиночными данными, а большими наборами, коллекциями объектов. Для работы непосредственно с наборами данных в WPF определены различные элементы управления списками, такие как ListBox, ListView, DataGrid, TreeView, ComboBox.
Их отличительной особенностью является то, что они наследуются от базового класса ItemsControl и поэтому наследуют ряд общей функциональности для работы с данными. Прежде всего можно выделить свойства:
Items: устанавливает набор объектов внутри элемента
ItemsSource: ссылка на источник данных
ItemStringFormat: формат, который будет использоваться для форматирования строк, например, при переводе в строку числовых значений
ItemContainerStyle: стиль, который устанавливается для контейнера каждого элемента (например, для ListBoxItem или ComboBoxItem)
ItemTemplate: представляет шаблон данных, который используется для отображения элементов
ItemsPanel: панель, которая используется для отображения данных. Как правило, применяется VirtualizingStackPanel
DisplayMemberPath: свойство, которое будет использоваться для отображения в списке каждого объекта
При работе с элементами управления списками важно понимать, что эти элементы предназначены прежде всего для отображения данных, а не для хранения. В каких-то ситуациях мы, конечно, можем определять небольшие списки непосредственно внутри элемента. Например:
<ListBox> <ListBox.Items> <ListBoxItem>iPhone 6S Plus</ListBoxItem> <ListBoxItem>Nexus 6P</ListBoxItem> <ListBoxItem>Galaxy S7 Edge</ListBoxItem> </ListBox.Items> </ListBox>
Но в большинстве случае предпочтительнее использовать привязку к спискам и разделять источник данных от их представления или визуализации. Например, определим ListBox:
<ListBox x:Name="phonesList" />
А в коде c# создадим источник данных и установим привязку к нему:
public partial class MainWindow : Window { List<string> phones; public MainWindow() { InitializeComponent(); phones = new List<string> {"iPhone 6S Plus", "Nexus 6P", "Galaxy S7 Edge" }; phonesList.ItemsSource = phones; } }
В примере выше в качестве источника данных использовался список List. Также в качестве источника мы бы могли использовать другой какой-нибудь тип набора данных - массив, объект HashSet и т.д. Но нередко в качестве источника применяется класс ObservableCollection, который находится в пространстве имен System.Collections.ObjectModel. Его преимущество заключается в том, что при любом изменении ObservableCollection может уведомлять элементы, которые применяют привязку, в результате чего обновляется не только сам объект ObservableCollection, но и привязанные к нему элементы интерфейса.
Например, рассмотрим следующую ситуацию. У нас кроме элемента ListBox есть текстовое поле и кнопка для добавления нового объекта:
<Window x:Class="DataApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:DataApp" mc:Ignorable="d" Title="MainWindow" Height="250" Width="300" Name="mainWindow"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <ListBox x:Name="phonesList" /> <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0 15 0 0" HorizontalAlignment="Center"> <TextBox Name="phoneTextBox" Width="190" /> <Button Content="Сохранить" MaxWidth="70" Margin="10 0 0 0" Click="Button_Click" /> </StackPanel> </Grid> </Window>
В файле кода определим обработчик кнопки, в котором новый элемент добавлялся бы в источник данных:
public partial class MainWindow : Window { List<string> phones; public MainWindow() { InitializeComponent(); phones = new List<string> {"iPhone 6S Plus", "Nexus 6P", "Galaxy S7 Edge" }; phonesList.ItemsSource = phones; } private void Button_Click(object sender, RoutedEventArgs e) { string phone = phoneTextBox.Text; // добавление нового объекта phones.Add(phone); } }
По нажатию на кнопку должно произойти добавления в список phones введенной в текстовое поле строки. И мы ожидаем, что после добавления ListBox отобразит нам добавленный объект. Однако так как в качестве источника применяется List, то обновления элемента ListBox не произойдет. Поэтому заменим List на ObservableCollection:
public partial class MainWindow : Window { ObservableCollection<string> phones; public MainWindow() { InitializeComponent(); phones = new ObservableCollection<string> {"iPhone 6S Plus", "Nexus 6P", "Galaxy S7 Edge" }; phonesList.ItemsSource = phones; } private void Button_Click(object sender, RoutedEventArgs e) { string phone = phoneTextBox.Text; // добавление нового объекта phones.Add(phone); } }
И теперь у нас уже не возникнет подобной проблемы: