Desktop UI

Контроли колекцій — глибоке занурення

Архітектура ItemsControl та просунуте використання ListBox/ListView для роботи з великими наборами даних

Контроли колекцій: глибоке занурення

Коли ви працюєте з даними у desktop-застосунках, рано чи пізно виникає потреба відобразити список об'єктів — продуктів, користувачів, замовлень, повідомлень. У WPF та Avalonia для цього існує потужна система контролів колекцій, яка дозволяє не просто показати дані, а й повністю контролювати їхній вигляд, поведінку та продуктивність.

У цій статті ми розберемо архітектуру ItemsControl — базового класу для всіх контролів колекцій, дослідимо, як WPF перетворює колекцію об'єктів на UI-елементи, і навчимося створювати складні візуалізації даних через ListBox та ListView.

Для кого ця стаття?Ця стаття призначена для студентів, які вже знайомі з базовими контролами WPF/Avalonia (Button, TextBox, StackPanel) та розуміють концепцію Data Binding. Якщо ви вже створювали прості інтерфейси з кнопками та текстовими полями, але хочете навчитися працювати зі списками даних — ви в правильному місці.

Навіщо потрібні контроли колекцій?

Уявіть, що вам потрібно відобразити список із 100 продуктів. Ви могли б створити 100 окремих TextBlock елементів вручну, але це було б абсолютно непрактично. Контроли колекцій вирішують цю проблему елегантно:

🔄 Автоматична генерація UI

Ви передаєте колекцію об'єктів, а контрол автоматично створює візуальні елементи для кожного об'єкта.

📊 Шаблонізація

Ви визначаєте один шаблон (DataTemplate), який застосовується до всіх елементів колекції.

⚡ Віртуалізація

Для великих списків створюються лише видимі елементи, що економить пам'ять та підвищує продуктивність.

🎯 Інтерактивність

Вбудована підтримка виділення, прокрутки, навігації клавіатурою та інших взаємодій.

Архітектура ItemsControl

ItemsControl — це базовий клас для всіх контролів, що відображають колекції даних. Від нього успадковуються ListBox, ListView, ComboBox, TreeView та інші. Розуміння його архітектури — ключ до майстерного володіння контролами колекцій.

Як працює ItemsControl: від даних до UI

Коли ви встановлюєте властивість ItemsSource, запускається складний процес перетворення даних на візуальні елементи:

Loading diagram...
graph LR
    A[ItemsSource<br/>List&lt;Product&gt;] --> B[ItemContainerGenerator]
    B --> C[ItemsPanel<br/>VirtualizingStackPanel]
    C --> D1[ItemContainer 1<br/>ListBoxItem]
    C --> D2[ItemContainer 2<br/>ListBoxItem]
    C --> D3[ItemContainer 3<br/>ListBoxItem]
    D1 --> E1[ContentPresenter]
    D2 --> E2[ContentPresenter]
    D3 --> E3[ContentPresenter]
    E1 --> F1[DataTemplate<br/>Product UI]
    E2 --> F2[DataTemplate<br/>Product UI]
    E3 --> F3[DataTemplate<br/>Product UI]
    
    style A fill:#3b82f6,stroke:#1d4ed8,color:#ffffff
    style B fill:#f59e0b,stroke:#b45309,color:#ffffff
    style C fill:#8b5cf6,stroke:#6d28d9,color:#ffffff
    style D1 fill:#64748b,stroke:#334155,color:#ffffff
    style D2 fill:#64748b,stroke:#334155,color:#ffffff
    style D3 fill:#64748b,stroke:#334155,color:#ffffff
    style F1 fill:#10b981,stroke:#047857,color:#ffffff
    style F2 fill:#10b981,stroke:#047857,color:#ffffff
    style F3 fill:#10b981,stroke:#047857,color:#ffffff

Розберемо кожен етап детально:

1. ItemsSource — це колекція об'єктів, яку ви прив'язуєте до контрола. Це може бути List<T>, ObservableCollection<T>, масив або будь-який інший тип, що реалізує IEnumerable.

2. ItemContainerGenerator — внутрішній механізм WPF, який відповідає за створення контейнерів для кожного елемента даних. Він знає, який тип контейнера потрібен (для ListBox це ListBoxItem, для ComboBoxComboBoxItem).

3. ItemsPanel — панель, яка розташовує контейнери. За замовчуванням це VirtualizingStackPanel (вертикальний список), але ви можете замінити її на WrapPanel, UniformGrid або навіть Canvas.

4. ItemContainer — обгортка навколо кожного елемента даних. Вона надає функціональність виділення, hover-ефектів, фокусу та інших інтерактивних можливостей.

5. ContentPresenter — елемент, який відображає вміст контейнера згідно з DataTemplate.

6. DataTemplate — шаблон, який визначає, як саме виглядатиме кожен елемент даних.

Чому це важливо розуміти?

Розуміння цієї архітектури дозволяє вам:

  • Оптимізувати продуктивність: знаючи про віртуалізацію, ви можете правильно налаштувати ItemsPanel.
  • Кастомізувати вигляд: розуміючи різницю між ItemTemplate (шаблон даних) та ItemContainerStyle (стиль контейнера), ви можете створювати складні візуалізації.
  • Діагностувати проблеми: коли щось працює не так, як очікувалося, ви знаєте, на якому етапі шукати проблему.

ItemsPanel: зміна розташування елементів

За замовчуванням ItemsControl використовує VirtualizingStackPanel для вертикального розташування елементів. Але що, якщо вам потрібен горизонтальний список? Або сітка? Або навіть довільне розташування?

Заміна ItemsPanel

Властивість ItemsPanel дозволяє замінити стандартну панель на будь-яку іншу:

Loading Avalonia WebAssembly...

Downloading .NET runtime (10MB)...

Avalonia vs WPF: В Avalonia рендеринг може відрізнятися від реального WPF, особливо в стилізації ListBoxItem. У реальному WPF елементи матимуть більш виражені рамки при виділенні.

WrapPanel для адаптивних сіток

WrapPanel автоматично переносить елементи на новий рядок, коли вони не вміщуються по ширині. Це ідеально для галерей зображень або карткових інтерфейсів:

Loading Avalonia WebAssembly...

Downloading .NET runtime (10MB)...

UniformGrid для рівномірних сіток

UniformGrid розподіляє елементи по рівномірній сітці з однаковими розмірами комірок:

Loading Avalonia WebAssembly...

Downloading .NET runtime (10MB)...

Порівняння панелей

ПанельРозташуванняВіртуалізаціяВикористання
StackPanelВертикально/горизонтально❌ НіНевеликі списки
VirtualizingStackPanelВертикально/горизонтально✅ ТакВеликі списки (за замовчуванням)
WrapPanelАдаптивна сітка❌ НіГалереї, теги
UniformGridРівномірна сітка❌ НіКаталоги продуктів
CanvasАбсолютне позиціонування❌ НіДіаграми, графіки
Віртуалізація та продуктивністьЯкщо у вас список із тисячами елементів, обов'язково використовуйте VirtualizingStackPanel. Без віртуалізації WPF створить візуальні елементи для всіх об'єктів одразу, що призведе до затримок при завантаженні та великого споживання пам'яті.

ItemContainerStyle: стилізація контейнерів

Коли ви працюєте з ListBox, кожен елемент даних обгортається в ListBoxItem — це і є контейнер. ItemContainerStyle дозволяє стилізувати ці контейнери, змінюючи їхній вигляд при наведенні, виділенні або в звичайному стані.

Базова стилізація контейнера

Loading Avalonia WebAssembly...

Downloading .NET runtime (10MB)...

Alternating Row Colors (зебра-стиль)

Для покращення читабельності довгих списків часто використовують чергування кольорів рядків. WPF надає для цього властивість AlternationCount:

Loading Avalonia WebAssembly...

Downloading .NET runtime (10MB)...

AlternationCount та AlternationIndexAlternationCount визначає, скільки різних стилів чергуються (зазвичай 2 для зебри). AlternationIndex — це attached property, яка містить індекс поточного елемента в циклі чергування (0, 1, 0, 1...). Ви можете використовувати AlternationIndex у тригерах для застосування різних стилів.

Складна стилізація з іконками та бейджами

Loading Avalonia WebAssembly...

Downloading .NET runtime (10MB)...

ListBox vs ListView: коли що використовувати

ListBox та ListView — два найпопулярніші контроли для відображення списків. Хоча вони дуже схожі, між ними є важливі відмінності.

ListBox: простота та гнучкість

ListBox — це базовий контрол для відображення списків. Він простий, легкий у налаштуванні та підходить для більшості сценаріїв.

Переваги ListBox:

  • Простота використання
  • Повна свобода в кастомізації через DataTemplate
  • Легка стилізація
  • Підтримка множинного виділення

Коли використовувати:

  • Прості списки елементів
  • Меню навігації
  • Списки з кастомним дизайном
  • Коли не потрібна табличність

ListView: розширені можливості

ListView успадковується від ListBox і додає підтримку різних режимів відображення, зокрема GridView для табличного вигляду.

Переваги ListView:

  • Вбудована підтримка колонок через GridView
  • Можливість сортування по колонках
  • Більш структурований вигляд для табличних даних

Коли використовувати:

  • Табличні дані з колонками
  • Коли потрібне сортування по колонках
  • Файлові менеджери, списки контактів
  • Коли структура даних важливіша за дизайн

📋 ListBox

Використовуйте для:

  • Меню та навігації
  • Списків повідомлень
  • Галерей зображень
  • Кастомних карткових інтерфейсів

📊 ListView

Використовуйте для:

  • Таблиць з даними
  • Файлових браузерів
  • Списків контактів
  • Звітів та аналітики

Приклад ListView з GridView

Loading Avalonia WebAssembly...

Downloading .NET runtime (10MB)...

Avalonia vs WPF: В Avalonia немає класу ListView з GridView як у WPF. Замість цього використовується DataGrid для табличних даних або ListBox з кастомним DataTemplate для імітації колонок.

🔵 Recap: OOP концепції в контролах колекцій

Для студентів, які тільки опановують ООП, важливо зрозуміти, як принципи об'єктно-орієнтованого програмування застосовуються в архітектурі контролів колекцій:

Успадкування (Inheritance):

Object → DispatcherObject → DependencyObject → Visual → UIElement 
  → FrameworkElement → Control → ItemsControl → ListBox

Кожен клас у цій ієрархії додає нову функціональність. ItemsControl додає можливість роботи з колекціями, ListBox додає виділення елементів.

Інкапсуляція (Encapsulation):ItemContainerGenerator — це приклад інкапсуляції. Ви не бачите, як саме створюються контейнери, але можете використовувати результат його роботи.

Поліморфізм (Polymorphism): Властивість ItemsPanel приймає будь-яку панель, що успадковується від Panel. Це дозволяє підставляти різні реалізації (StackPanel, WrapPanel, UniformGrid) без зміни коду ItemsControl.

Композиція (Composition):ItemsControl складається з багатьох компонентів: ItemsPanel, ItemContainerGenerator, ScrollViewer. Кожен компонент відповідає за свою частину функціональності.

Практичні завдання

Рівень 1: Горизонтальний ListBox з WrapPanel

Мета: Навчитися змінювати ItemsPanel для створення адаптивного розташування елементів.

Завдання: Створіть застосунок з горизонтальним списком кольорових карток. Коли вікно звужується, картки повинні автоматично переноситися на новий рядок.

Вимоги:

  • Використайте ListBox з WrapPanel як ItemsPanel
  • Створіть колекцію з 10-15 кольорів (можна використати hex-коди)
  • Кожна картка повинна бути квадратом 80×80 пікселів
  • При наведенні картка повинна збільшуватися до 90×90 пікселів (використайте ScaleTransform)
  • При кліку на картку її hex-код повинен копіюватися в TextBox

Підказка:

<ListBox.ItemsPanel>
    <ItemsPanelTemplate>
        <WrapPanel />
    </ItemsPanelTemplate>
</ListBox.ItemsPanel>

Рівень 2: Галерея зображень з DataTemplate

Мета: Опанувати створення складних візуалізацій через DataTemplate та роботу з зображеннями.

Завдання: Створіть галерею зображень у стилі Pinterest з картками різної висоти.

Вимоги:

  • Створіть клас Photo з властивостями: Title, Author, ImageUrl, Likes
  • Використайте ObservableCollection<Photo> як джерело даних
  • Кожна картка повинна містити:
    • Зображення (можна використати placeholder URLs з https://picsum.photos/)
    • Назву фото
    • Ім'я автора
    • Кількість лайків з іконкою ❤️
  • Використайте WrapPanel для адаптивного розташування
  • При кліку на картку вона повинна відкриватися в повному розмірі (можна використати Window або Popup)

Структура класу:

public class Photo
{
    public string Title { get; set; }
    public string Author { get; set; }
    public string ImageUrl { get; set; }
    public int Likes { get; set; }
}

Рівень 3: Карткова сітка продуктів з ItemContainerStyle

Мета: Створити професійний інтерфейс каталогу продуктів з анімаціями та інтерактивністю.

Завдання: Розробіть каталог інтернет-магазину з картками продуктів, фільтрацією та анімаціями.

Вимоги:

  • Створіть клас Product з властивостями: Name, Price, Category, ImageUrl, Rating, InStock
  • Використайте UniformGrid з 3 колонками
  • Кожна картка повинна містити:
    • Зображення продукту
    • Назву
    • Ціну (з форматуванням валюти)
    • Рейтинг (зірочки ⭐)
    • Бейдж "В наявності" / "Немає в наявності"
    • Кнопку "Додати в кошик"
  • Реалізуйте ItemContainerStyle з анімаціями:
    • При наведенні картка піднімається (TranslateTransform) та з'являється тінь
    • При кліку картка злегка "натискається" (ScaleTransform)
  • Додайте ComboBox для фільтрації по категоріях
  • Використайте ICollectionView для фільтрації без зміни вихідної колекції

Додаткові виклики:

  • Додайте пошук по назві продукту
  • Реалізуйте сортування по ціні (зростання/спадання)
  • Додайте індикатор "NEW" для нових продуктів (додайте властивість IsNew)
  • Реалізуйте "швидкий перегляд" при наведенні (показ додаткової інформації)

Приклад структури:

public class Product : INotifyPropertyChanged
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public string Category { get; set; }
    public string ImageUrl { get; set; }
    public double Rating { get; set; }
    public bool InStock { get; set; }
    public bool IsNew { get; set; }
}

public class MainViewModel : INotifyPropertyChanged
{
    public ObservableCollection<Product> Products { get; set; }
    public ICollectionView ProductsView { get; set; }
    public string SearchText { get; set; }
    public string SelectedCategory { get; set; }
    
    private void ApplyFilters()
    {
        ProductsView.Filter = item =>
        {
            var product = item as Product;
            // Логіка фільтрації
        };
    }
}

Резюме

У цій статті ми детально розібрали архітектуру контролів колекцій у WPF та Avalonia:

Ключові концепції:

  • ItemsControl — базовий клас для всіх контролів колекцій, який перетворює дані на UI через ланцюжок: ItemsSource → ItemContainerGenerator → ItemsPanel → ItemContainer → DataTemplate
  • ItemsPanel дозволяє змінювати розташування елементів (StackPanel, WrapPanel, UniformGrid, Canvas)
  • ItemContainerStyle надає контроль над виглядом контейнерів (hover, selection, alternating colors)
  • ListBox — простий та гнучкий контрол для більшості сценаріїв
  • ListView — розширений контрол з підтримкою табличного вигляду через GridView

Важливі принципи:

  • Використовуйте VirtualizingStackPanel для великих списків (тисячі елементів)
  • Розділяйте відповідальність: ItemTemplate для даних, ItemContainerStyle для контейнера
  • AlternationCount та AlternationIndex для зебра-стилю
  • Вибирайте правильний контрол: ListBox для кастомного дизайну, ListView для табличних даних

Наступні кроки: У наступній статті ми розглянемо DataGrid — найпотужніший контрол для роботи з табличними даними, який надає вбудовану підтримку сортування, фільтрації, редагування та валідації.

Глосарій

Основні терміни:
  • ItemsControl — базовий клас для контролів, що відображають колекції даних
  • ItemsSource — властивість для прив'язки колекції даних до контрола
  • ItemContainerGenerator — внутрішній механізм WPF для створення контейнерів елементів
  • ItemsPanel — панель, яка розташовує контейнери елементів
  • ItemContainer — обгортка навколо елемента даних (ListBoxItem, ComboBoxItem тощо)
  • ItemTemplate — шаблон для відображення даних елемента
  • ItemContainerStyle — стиль для контейнера елемента
  • Віртуалізація — техніка оптимізації, коли створюються лише видимі елементи
  • AlternationCount — кількість стилів, що чергуються (для зебра-ефекту)
  • AlternationIndex — індекс елемента в циклі чергування
  • WrapPanel — панель, що автоматично переносить елементи на новий рядок
  • UniformGrid — сітка з рівномірними комірками
  • GridView — режим відображення ListView для табличних даних

Додаткові ресурси

📚 Microsoft Docs: ItemsControl

Офіційна документація по ItemsControl з прикладами та best practices

🎨 WPF Tutorial: ListBox Styling

Детальний туторіал по стилізації ListBox з візуальними прикладами

⚡ Avalonia Docs: ItemsControl

Документація Avalonia по контролам колекцій та їх особливостям

🔧 GitHub: WPF Samples

Офіційні приклади від Microsoft з різними сценаріями використання ItemsControl