Элементы управления списками

Последнее обновление: 28.01.2023

Эти элементы представлены в 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>
Элемент ItemsControl и списки в WPF и C#

Все элементы, размещенные внутри спискового элемента ListBox, представляют элементы списка.

Управление списком

Коллекция объектов внутри элемента-списка доступна в виде свойства Items. Для управления элементами из этой коллекции мы можем использовать следующие методы:

  • Add(object item): добавление элемента

  • Clear(): полная очистка коллекции

  • Insert(int index, object item): вставка элемента по определенному индексу в коллекции

  • Remove(object item): удаление элемента

  • RemoveAt(int index): удаление элемента по индексу

А свойство 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" на первое место в списке
        }
    }
}

В данном случае добавляем и удаляем ряд элементов с помощью различных методов и в итоге получим следующий список:

Методы управления списком в ListBox в WPF и C#

При вызове методов, которые используют индексы, как выше методы 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 выведет все объекты:

Вывод списка объектов в ListBox в WPF и C#

По умолчанию списковые элементы выводят то, что возвращает метод 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>
Свойство DisplayMemberPath в ListBox в WPF и C#

В этом случае в коде C# таким же образом можно добавлять и удалять данные:

var firstPerson = usersList.Items[0];   // получаем первый объект
usersList.Items.Remove(firstPerson);    // удаляем его
usersList.Items.Add(new Person { Name = "Kate", Age = 23 }); // добавляем новый объект

ItemsSource

Нам необязательно вручную заполнять значения элемента управления списком, так как мы можем установить свойство 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();
Обновление данных в ListBox в WPF и C#

Стоит отметить, что если в качестве источника данных выступает 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.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850