Эти элементы представлены в WPF довольно широко. Все они являются производными от класса ItemsControl, который в свою очередь является наследником класса Control. Все они содержат коллекцию элементов. Элементы могут быть напрямую добавлены в коллекцию, возможна также привязка некоторого массива данных к коллекции.
Возьмем простейший элемент-список - ListBox и определим в файле MainWindow.xaml следующий интерфейс:
<Window x:Class="MetanitApp.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:sys="clr-namespace:System;assembly=mscorlib" xmlns:local="clr-namespace:MetanitApp" mc:Ignorable="d" Name="MainWindow" Height="200" Width="300"> <ListBox x:Name="usersList"> <sys:String>Tom</sys:String> <sys:String>Bob</sys:String> <sys:String>Sam</sys:String> <sys:String>Alice</sys:String> </ListBox> </Window>
Все элементы, размещенные внутри спискового элемента ListBox, представляют элементы списка.
Коллекция объектов внутри элемента-списка доступна в виде свойства Items. Для управления элементами из этой коллекции мы можем использовать следующие методы:
А свойство Count позволяет узнать, сколько элементов в коллекции.
Например, оставим предыдущий интерфейс и в файле связанного кода MainWindow.xaml.cs добавим и удалим программно несколько элементов:
using System.Windows; namespace MetanitApp { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); usersList.Items.Remove("Sam"); // удаляем элемент "Sam" usersList.Items.RemoveAt(1); // удаляем второй элемент usersList.Items.Add("Kate"); // Добавляем элемент "Kate" usersList.Items.Insert(0, "Mike"); // Вставляем элемент "Mike" на первое место в списке } } }
В данном случае добавляем и удаляем ряд элементов с помощью различных методов и в итоге получим следующий список:
При вызове методов, которые используют индексы, как выше методы RemoveAt(index)
и Insert(index, object)
, следует учитывать, что передаваемые индексы
должны быть действительны. Естественно мы не можем удалить, к примеру, второй объект, если списке только один объект или вообще нет объектов. В этом случае мы можем проверять
длину списка:
if(usersList.Items.Count > 1) // если в списке больше 1 элемента { usersList.Items.Insert(1, "Mike"); // Вставляем элемент "Mike" на второе место usersList.Items.RemoveAt(2); // удаляем третий элемент }
Списковые элементы могут отображать не только простые строки или числа, но и сложные данные. Например, создадим в коде новый класс Person:
public class Person { public string Name { get; set; } = ""; public int Age { get; set; } public override string ToString() { return $"Name: {Name} Age: {Age}"; } }
Теперь создадим в xaml набор объектов этого класса Person и выведем в их в списке:
<Window x:Class="MetanitApp.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:sys="clr-namespace:System;assembly=mscorlib" xmlns:local="clr-namespace:MetanitApp" mc:Ignorable="d" Title="MainWindow" Height="200" Width="300"> <ListBox x:Name="usersList"> <local:Person Name="Tom" Age="38" /> <local:Person Name="Bob" Age="42" /> <local:Person Name="Sam" Age="25" /> <local:Person Name="Alice" Age="34" /> </ListBox> </Window>
Поскольку мы используем класс, определенный в текущем проекте, то соответственно у нас обязательно должно быть подключено пространство имен проекте. В моем случае проект (и пространство имен) называется "MetanitApp". Соответственно у меня это пространство имен проецируется на префикс local:
xmlns:local="clr-namespace:MetanitApp"
В принципе по умолчанию WPF уже его подключает. После этого через префикс "local" можно обращаться к классу Person: "local:Person".
Кроме того, чтобы не возникало проблем с разметкой XAML, желательно сделать перестроение проекта. И в итоге ListBox выведет все объекты:
По умолчанию списковые элементы выводят то, что возвращает метод ToString()
объекта. Именно для этой демонстрации и реализован данный метод в классе Person выше.
Но также с помощью свойства DisplayMemberPath можно установить свойство объекта, значение которого будет отображаться в списке. Например, отобразим только имя:
<Window x:Class="MetanitApp.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:MetanitApp" mc:Ignorable="d" Title="MainWindow" Height="200" Width="300"> <ListBox x:Name="usersList" DisplayMemberPath="Name"> <local:Person Name="Tom" Age="38" /> <local:Person Name="Bob" Age="42" /> <local:Person Name="Sam" Age="25" /> <local:Person Name="Alice" Age="34" /> </ListBox> </Window>
В этом случае в коде C# таким же образом можно добавлять и удалять данные:
var firstPerson = usersList.Items[0]; // получаем первый объект usersList.Items.Remove(firstPerson); // удаляем его usersList.Items.Add(new Person { Name = "Kate", Age = 23 }); // добавляем новый объект
Нам необязательно вручную заполнять значения элемента управления списком, так как мы можем установить свойство ItemsSource, задав в качестве параметра коллекцию, из которой будет формироваться элемент управления списком. Например, в коде xaml-разметки определим пустой список:
<Window x:Class="MetanitApp.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" mc:Ignorable="d" Title="MainWindow" Height="200" Width="300"> <ListBox x:Name="usersList" /> </Window>
А в файле отделенного кода выполним наполнение списка:
using System.Windows; using System.Collections.Generic; namespace MetanitApp; public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); usersList.ItemsSource = new List<Person>() { new Person{Name="Tom", Age=38}, new Person {Name="Bob", Age=42}, new Person{Name="Sam", Age=25} }; // устанавливаем отображаемое свойство usersList.DisplayMemberPath = "Name"; } }
Но стоит учитывать, что если мы наполняем элемент управления списком через свойство ItemsSource, то мы не сможем использовать
выше рассмотренные методы Add/Remove/RemoveAt/Insert/Clear
для добавления/удаления элементов списка. В этом случае для управления элементами непосредственно к самому источнику данных.
Например, определим в коде xaml кнопку для добавления одного объекта:
<Window x:Class="MetanitApp.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" mc:Ignorable="d" Title="MainWindow" Height="200" Width="300"> <StackPanel> <ListBox x:Name="usersList" DisplayMemberPath="Name" /> <Button Content="Add" Click="Button_Click" Margin="5" Width="80" HorizontalAlignment="Left" /> </StackPanel> </Window>
По нажатию на кнопку будет вызываться метод Button_Click
. И в файле связанного кода C# определим данный метод и наполним ListBox данными:
using System.Windows; using System.Collections.Generic; namespace MetanitApp; public partial class MainWindow : Window { List<Person> people = new List<Person>() { new Person{Name="Tom", Age=38}, new Person {Name="Bob", Age=42}, new Person{Name="Sam", Age=25} }; public MainWindow() { InitializeComponent(); usersList.ItemsSource = people; } private void Button_Click(object sender, RoutedEventArgs e) { people.Add(new Person { Name = "Mike", Age = 29 }); usersList.Items.Refresh(); } }
Здесь в качестве источника данных выступает список people, и из этого списка ListBox будет брать данные.
В обработчике нажатия кнопки добавляем в список people один объект Person для теста. Обратите внимание, что добавляем именно в стандартный список List<Person>
,
а не в ListBox. ListBox в данном случае просто отображает данные. Но чтобы ListBox обновил свое визуальное представление, после изменения списка people обновляем данные в ListBox:
usersList.Items.Refresh();
Стоит отметить, что если в качестве источника данных выступает ObservableCollection
, то нам не надо вызывает отобновление данных списка, так как коллекция ObservableCollection сама извещает систему
об изменениях.
Все элементы управления списками поддерживают выделение входящих элементов. Выделенный элемент(ы) можно получить с помощью свойств SelectedItem(SelectedItems), а получить индекс выделенного элемента - с помощью свойства SelectedIndex. Свойство SelectedValue позволяет получить значение выделенного элемента.
При выделении элемента в списке генерируется событие SelectionChanged, которое мы можем обработать. Например, возьмем предыдущий список:
<Window x:Class="MetanitApp.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:MetanitApp" mc:Ignorable="d" Title="MainWindow" Height="200" Width="300"> <ListBox x:Name="usersList" DisplayMemberPath="Name" SelectionChanged="usersList_SelectionChanged"> <local:Person Name="Tom" Age="38" /> <local:Person Name="Bob" Age="42" /> <local:Person Name="Sam" Age="25" /> <local:Person Name="Alice" Age="34" /> </ListBox> </Window>
А в файле кода определим обработчик для этого события:
using System.Windows; using System.Windows.Controls; namespace MetanitApp; public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void usersList_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (usersList.SelectedItem is Person person) { MessageBox.Show(person.Name); } } }
Важно учитывать, что так как в разметке xaml в списке определены элементы Person, то в коде мы можем привести объект usersList.SelectedItem
к типу Person.