Desktop UI

DataGrid — колонки та базове відображення

Знайомство з DataGrid — потужним контролом для табличних даних з підтримкою редагування та сортування

DataGrid: колонки та базове відображення

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

У цій статті ми детально розберемо основи роботи з DataGrid: як створювати колонки, прив'язувати дані, налаштовувати відображення та використовувати різні типи колонок для різних типів даних.

Для кого ця стаття?Ця стаття призначена для студентів, які вже знайомі з базовими контролами колекцій (ListBox, ListView) та розуміють концепції Data Binding та MVVM. Якщо ви вже працювали з ItemsControl та DataTemplate, ви готові до вивчення DataGrid.

Коли використовувати DataGrid замість ListView?

Перш ніж занурюватися в технічні деталі, важливо зрозуміти, коли саме варто використовувати DataGrid.

✅ Використовуйте DataGrid

  • Табличні дані з багатьма колонками
  • Потрібне редагування даних inline
  • Необхідне сортування по колонках
  • Робота з великими наборами даних (тисячі рядків)
  • Потрібна валідація введених даних
  • Експорт даних у CSV/Excel

❌ Використовуйте ListView

  • Кастомний дизайн карток
  • Галереї зображень
  • Меню навігації
  • Списки повідомлень
  • Коли дизайн важливіший за функціональність
  • Нестандартні layouts (WrapPanel, Canvas)

Порівняльна таблиця

ФункціяDataGridListView + GridView
Редагування inline✅ Вбудоване❌ Потрібна ручна реалізація
Сортування✅ Автоматичне⚠️ Потрібна ручна реалізація
Валідація✅ Вбудована❌ Потрібна ручна реалізація
Кастомний дизайн⚠️ Обмежений✅ Повна свобода
Продуктивність✅ Віртуалізація✅ Віртуалізація
Складність⚠️ Середня✅ Проста

Перший DataGrid: Hello, Tabular Data!

Почнемо з найпростішого прикладу — відображення колекції об'єктів у таблиці:

Loading Avalonia WebAssembly...

Downloading .NET runtime (10MB)...

Avalonia vs WPF: В Avalonia DataGrid має дещо інший API порівняно з WPF. Наприклад, деякі властивості можуть мати інші назви або поведінку. Завжди перевіряйте документацію Avalonia для специфічних деталей.

AutoGenerateColumns: автоматична магія

Властивість AutoGenerateColumns="True" (за замовчуванням) автоматично створює колонки на основі публічних властивостей об'єктів у колекції. Це зручно для швидкого прототипування, але має обмеження:

Переваги:

  • Швидкий старт без написання XAML для колонок
  • Автоматичне визначення типу колонки (текст, чекбокс тощо)
  • Підходить для простих сценаріїв

Недоліки:

  • Немає контролю над порядком колонок
  • Неможливо налаштувати заголовки (використовуються назви властивостей)
  • Відображаються всі публічні властивості (навіть ті, що не потрібні)
  • Немає контролю над форматуванням
Best PracticeУ production-коді завжди використовуйте AutoGenerateColumns="False" та визначайте колонки вручну. Це дає повний контроль над відображенням та запобігає несподіванкам при зміні моделі даних.

Ручне визначення колонок

Для повного контролю над відображенням встановіть AutoGenerateColumns="False" та визначте колонки вручну:

Loading Avalonia WebAssembly...

Downloading .NET runtime (10MB)...

Розбір властивостей колонки

Кожна колонка має набір важливих властивостей:

Header — текст заголовка колонки. Може бути рядком або складним UI-елементом:

<DataGridTextColumn>
    <DataGridTextColumn.Header>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="💰 " />
            <TextBlock Text="Ціна" />
        </StackPanel>
    </DataGridTextColumn.Header>
</DataGridTextColumn>

Binding — прив'язка до властивості об'єкта. Підтримує всі можливості WPF Binding:

  • StringFormat для форматування
  • Converter для перетворення значень
  • Mode для режиму прив'язки (OneWay, TwoWay)

Width — ширина колонки:

  • Фіксована: Width="100"
  • Автоматична: Width="Auto" (підлаштовується під вміст)
  • Пропорційна: Width="*" (займає весь доступний простір)
  • Пропорційна з вагою: Width="2*" (вдвічі ширша за Width="*")

IsReadOnly — чи можна редагувати комірки цієї колонки:

<DataGridTextColumn Header="ID" 
                    Binding="{Binding Id}" 
                    IsReadOnly="True" />

Типи колонок DataGrid

DataGrid надає кілька вбудованих типів колонок для різних типів даних:

DataGridTextColumn: текстові дані

Найпоширеніший тип колонки для відображення та редагування тексту:

Loading Avalonia WebAssembly...

Downloading .NET runtime (10MB)...

DataGridCheckBoxColumn: булеві значення

Для відображення та редагування bool властивостей:

Loading Avalonia WebAssembly...

Downloading .NET runtime (10MB)...

Тристанові чекбоксиDataGridCheckBoxColumn підтримує тристанові чекбокси для bool? (nullable bool). Це корисно для представлення "невизначеного" стану:
<DataGridCheckBoxColumn Header="Підтверджено" 
                        Binding="{Binding IsConfirmed}" 
                        IsThreeState="True" />

DataGridComboBoxColumn: вибір зі списку

Для вибору значення з попередньо визначеного списку:

Loading Avalonia WebAssembly...

Downloading .NET runtime (10MB)...

Прив'язка до enumDataGridComboBoxColumn чудово працює з enum типами. Просто прив'яжіть ItemsSource до Enum.GetValues(typeof(YourEnum)):
public enum Priority { Low, Medium, High, Critical }

// У XAML або code-behind:
column.ItemsSource = Enum.GetValues(typeof(Priority));

DataGridTemplateColumn: повна свобода

Коли вбудованих типів колонок недостатньо, DataGridTemplateColumn дає повну свободу через DataTemplate:

Loading Avalonia WebAssembly...

Downloading .NET runtime (10MB)...

CellTemplate vs CellEditingTemplate

DataGridTemplateColumn підтримує два окремі шаблони:

  • CellTemplate — для режиму відображення (коли комірка не редагується)
  • CellEditingTemplate — для режиму редагування (коли користувач клікає на комірку)

Loading Avalonia WebAssembly...

Downloading .NET runtime (10MB)...

Frozen Columns: фіксовані колонки

Коли у вас багато колонок і потрібен горизонтальний скрол, корисно зафіксувати ліві колонки (наприклад, ID або назву), щоб вони завжди були видимі:

Loading Avalonia WebAssembly...

Downloading .NET runtime (10MB)...

Коли використовувати Frozen ColumnsФіксовані колонки особливо корисні для:
  • Таблиць з багатьма колонками (10+)
  • Фінансових звітів (фіксуємо назву рядка, скролимо місяці)
  • Порівняльних таблиць (фіксуємо назву параметра, скролимо варіанти)
  • Будь-яких широких таблиць, де важливо зберігати контекст рядка

Налаштування відображення

GridLinesVisibility: лінії сітки

Контролює видимість ліній між комірками:

<!-- Всі лінії -->
<DataGrid GridLinesVisibility="All" />

<!-- Тільки горизонтальні -->
<DataGrid GridLinesVisibility="Horizontal" />

<!-- Тільки вертикальні -->
<DataGrid GridLinesVisibility="Vertical" />

<!-- Без ліній -->
<DataGrid GridLinesVisibility="None" />

HeadersVisibility: видимість заголовків

<!-- Заголовки колонок та рядків -->
<DataGrid HeadersVisibility="All" />

<!-- Тільки заголовки колонок -->
<DataGrid HeadersVisibility="Column" />

<!-- Тільки заголовки рядків -->
<DataGrid HeadersVisibility="Row" />

<!-- Без заголовків -->
<DataGrid HeadersVisibility="None" />

AlternatingRowBackground: зебра-стиль

<DataGrid AlternatingRowBackground="#f9fafb">
    <!-- Колонки -->
</DataGrid>

RowHeight та ColumnWidth

<!-- Фіксована висота рядків -->
<DataGrid RowHeight="40" />

<!-- Мінімальна та максимальна висота -->
<DataGrid MinRowHeight="30" MaxRowHeight="100" />

<!-- Ширина колонок за замовчуванням -->
<DataGrid ColumnWidth="100" />

Прив'язка даних через MVVM

У реальних застосунках дані зазвичай прив'язуються через ViewModel:

public class ProductsViewModel : INotifyPropertyChanged
{
    private ObservableCollection<Product> _products;
    
    public ObservableCollection<Product> Products
    {
        get => _products;
        set
        {
            _products = value;
            OnPropertyChanged();
        }
    }
    
    public ProductsViewModel()
    {
        LoadProducts();
    }
    
    private void LoadProducts()
    {
        Products = new ObservableCollection<Product>
        {
            new Product { Id = 1, Name = "Ноутбук", Price = 25000, InStock = true },
            new Product { Id = 2, Name = "Миша", Price = 350, InStock = true },
            new Product { Id = 3, Name = "Клавіатура", Price = 1200, InStock = false }
        };
    }
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

XAML:

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="using:YourNamespace.ViewModels">
    <Window.DataContext>
        <vm:ProductsViewModel />
    </Window.DataContext>
    
    <DataGrid ItemsSource="{Binding Products}" 
              AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="ID" Binding="{Binding Id}" Width="60" />
            <DataGridTextColumn Header="Назва" Binding="{Binding Name}" Width="*" />
            <DataGridTextColumn Header="Ціна" Binding="{Binding Price, StringFormat='{}{0:C}'}" Width="120" />
            <DataGridCheckBoxColumn Header="В наявності" Binding="{Binding InStock}" Width="100" />
        </DataGrid.Columns>
    </DataGrid>
</Window>
ObservableCollection vs ListЗавжди використовуйте ObservableCollection<T> замість List<T> для прив'язки до DataGrid. ObservableCollection автоматично повідомляє UI про зміни в колекції (додавання, видалення елементів), тоді як List цього не робить.

🔵 Recap: Generics та Collections

Для студентів, які тільки опановують C#, важливо розуміти концепції, що лежать в основі роботи з DataGrid:

Generic Types (Узагальнені типи):

// ObservableCollection<T> — це generic клас
ObservableCollection<Product> products;  // T = Product
ObservableCollection<User> users;        // T = User

// Один клас працює з різними типами даних

IEnumerable та LINQ:

// DataGrid.ItemsSource приймає IEnumerable
IEnumerable<Product> products = GetProducts();
dataGrid.ItemsSource = products;

// Можна використовувати LINQ для фільтрації
var expensiveProducts = products.Where(p => p.Price > 1000);
dataGrid.ItemsSource = expensiveProducts;

INotifyPropertyChanged:

// Інтерфейс для повідомлення UI про зміни
public class Product : INotifyPropertyChanged
{
    private decimal _price;
    
    public decimal Price
    {
        get => _price;
        set
        {
            _price = value;
            OnPropertyChanged(); // Повідомляємо UI
        }
    }
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    protected void OnPropertyChanged([CallerMemberName] string name = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}

INotifyCollectionChanged:

// ObservableCollection реалізує цей інтерфейс
// Повідомляє UI про додавання/видалення елементів
products.Add(newProduct);    // UI автоматично оновиться
products.Remove(oldProduct); // UI автоматично оновиться

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

Рівень 1: Таблиця продуктів з 4 колонками

Мета: Навчитися створювати базовий DataGrid з різними типами колонок.

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

Вимоги:

  • Створіть клас Product з властивостями: Id (int), Name (string), Price (decimal), InStock (bool)
  • Використайте ObservableCollection<Product> у ViewModel
  • Створіть DataGrid з AutoGenerateColumns="False"
  • Додайте 4 колонки:
    • ID (текстова, ширина 60px, тільки для читання)
    • Назва (текстова, ширина *)
    • Ціна (текстова з форматуванням валюти, ширина 100px)
    • В наявності (чекбокс, ширина 100px)
  • Додайте кнопку "Додати продукт", яка додає новий елемент до колекції
  • Налаштуйте зебра-стиль (AlternatingRowBackground)

Підказка:

public class MainViewModel : INotifyPropertyChanged
{
    public ObservableCollection<Product> Products { get; set; }
    public ICommand AddProductCommand { get; }
    
    private void AddProduct()
    {
        Products.Add(new Product 
        { 
            Id = Products.Count + 1, 
            Name = "Новий продукт", 
            Price = 0, 
            InStock = true 
        });
    }
}

Рівень 2: DataGridTemplateColumn з кнопкою "Delete"

Мета: Опанувати створення кастомних колонок через DataGridTemplateColumn.

Завдання: Розширте попередній застосунок, додавши колонку з кнопкою видалення для кожного рядка.

Вимоги:

  • Додайте DataGridTemplateColumn з назвою "Дії"
  • У CellTemplate розмістіть кнопку "🗑️ Видалити"
  • При кліку на кнопку елемент повинен видалятися з колекції
  • Додайте діалог підтвердження перед видаленням
  • Стилізуйте кнопку (червоний колір при наведенні)
  • Додайте ще одну кнопку "✏️ Редагувати" поруч з видаленням
  • При кліку на "Редагувати" відкривається діалогове вікно з формою редагування

Підказка для прив'язки команди:

<DataGridTemplateColumn Header="Дії" Width="120">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal" Spacing="5">
                <Button Content="✏️" 
                        Command="{Binding DataContext.EditCommand, 
                                  RelativeSource={RelativeSource AncestorType=DataGrid}}"
                        CommandParameter="{Binding}" />
                <Button Content="🗑️" 
                        Command="{Binding DataContext.DeleteCommand, 
                                  RelativeSource={RelativeSource AncestorType=DataGrid}}"
                        CommandParameter="{Binding}" />
            </StackPanel>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

Рівень 3: DataGrid з ComboBoxColumn та CheckBoxColumn

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

Завдання: Розробіть Task Manager з підтримкою категорій, пріоритетів та статусів.

Вимоги:

  • Створіть клас TaskItem з властивостями:
    • Id (int)
    • Title (string)
    • Description (string)
    • Category (enum: Work, Personal, Shopping, Health)
    • Priority (enum: Low, Medium, High, Critical)
    • IsCompleted (bool)
    • DueDate (DateTime?)
    • AssignedTo (string)
  • Створіть DataGrid з наступними колонками:
    • ID (текст, 50px, read-only)
    • Виконано (чекбокс, 80px)
    • Назва (текст, *)
    • Категорія (DataGridComboBoxColumn, 120px)
    • Пріоритет (DataGridComboBoxColumn, 100px)
    • Термін (текст з форматуванням дати, 120px)
    • Відповідальний (текст, 150px)
    • Дії (template з кнопками, 100px)
  • Реалізуйте функціональність:
    • Додавання нового завдання через форму
    • Редагування завдання (подвійний клік або кнопка)
    • Видалення завдання з підтвердженням
    • Зміна статусу через чекбокс
    • Зміна категорії та пріоритету через ComboBox
  • Додайте візуальні індикатори:
    • Червоний фон для прострочених завдань
    • Зелений фон для виконаних завдань
    • Іконки пріоритету (🔴 Critical, 🟠 High, 🟡 Medium, 🟢 Low)
  • Додайте FrozenColumnCount="2" для фіксації чекбоксу та назви

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

  • Додайте фільтрацію по категоріях через ComboBox над таблицею
  • Реалізуйте пошук по назві завдання
  • Додайте експорт у CSV
  • Реалізуйте drag-and-drop для зміни порядку завдань
  • Додайте групування по категоріях

Приклад enum:

public enum TaskCategory
{
    Work,
    Personal,
    Shopping,
    Health
}

public enum TaskPriority
{
    Low,
    Medium,
    High,
    Critical
}

Приклад стилізації рядків:

<DataGrid.RowStyle>
    <Style TargetType="DataGridRow">
        <Style.Triggers>
            <DataTrigger Binding="{Binding IsCompleted}" Value="True">
                <Setter Property="Background" Value="#d1fae5" />
            </DataTrigger>
            <DataTrigger Binding="{Binding IsOverdue}" Value="True">
                <Setter Property="Background" Value="#fee2e2" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
</DataGrid.RowStyle>

Резюме

У цій статті ми детально розібрали основи роботи з DataGrid:

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

  • DataGrid — потужний контрол для табличних даних з вбудованою підтримкою редагування, сортування та валідації
  • AutoGenerateColumns зручний для прототипування, але в production завжди використовуйте ручне визначення колонок
  • Чотири основні типи колонок: DataGridTextColumn, DataGridCheckBoxColumn, DataGridComboBoxColumn, DataGridTemplateColumn
  • DataGridTemplateColumn надає повну свободу через CellTemplate та CellEditingTemplate
  • FrozenColumnCount дозволяє фіксувати ліві колонки при горизонтальному скролі
  • Завжди використовуйте ObservableCollection<T> для автоматичного оновлення UI

Важливі властивості:

  • Header — заголовок колонки
  • Binding — прив'язка до властивості об'єкта
  • Width — ширина колонки (фіксована, Auto, *)
  • IsReadOnly — заборона редагування
  • StringFormat — форматування значень

Наступні кроки: У наступній статті ми розглянемо просунуті можливості DataGrid: сортування, фільтрацію, групування, редагування та валідацію даних.

Глосарій

Основні терміни:
  • DataGrid — контрол для відображення та редагування табличних даних
  • AutoGenerateColumns — автоматичне створення колонок на основі властивостей об'єктів
  • DataGridTextColumn — колонка для текстових даних
  • DataGridCheckBoxColumn — колонка для булевих значень (чекбокси)
  • DataGridComboBoxColumn — колонка для вибору зі списку
  • DataGridTemplateColumn — колонка з кастомним шаблоном
  • CellTemplate — шаблон для відображення комірки
  • CellEditingTemplate — шаблон для редагування комірки
  • FrozenColumnCount — кількість зафіксованих лівих колонок
  • GridLinesVisibility — видимість ліній сітки
  • HeadersVisibility — видимість заголовків
  • AlternatingRowBackground — колір альтернативних рядків (зебра-стиль)
  • ObservableCollection — колекція, що повідомляє про зміни
  • INotifyPropertyChanged — інтерфейс для повідомлення про зміни властивостей

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

📚 Microsoft Docs: DataGrid

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

🎨 WPF Tutorial: DataGrid

Детальний туторіал по DataGrid з візуальними прикладами

⚡ Avalonia Docs: DataGrid

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

🔧 GitHub: DataGrid Examples

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