Коли справа доходить до відображення табличних даних у desktop-застосунках, DataGrid — це король. Цей контрол поєднує в собі потужність, гнучкість та зручність використання, надаючи вбудовану підтримку сортування, редагування, валідації та багато іншого.
У цій статті ми детально розберемо основи роботи з DataGrid: як створювати колонки, прив'язувати дані, налаштовувати відображення та використовувати різні типи колонок для різних типів даних.
ListBox, ListView) та розуміють концепції Data Binding та MVVM. Якщо ви вже працювали з ItemsControl та DataTemplate, ви готові до вивчення DataGrid.Перш ніж занурюватися в технічні деталі, важливо зрозуміти, коли саме варто використовувати DataGrid.
✅ Використовуйте DataGrid
❌ Використовуйте ListView
| Функція | DataGrid | ListView + GridView |
|---|---|---|
| Редагування inline | ✅ Вбудоване | ❌ Потрібна ручна реалізація |
| Сортування | ✅ Автоматичне | ⚠️ Потрібна ручна реалізація |
| Валідація | ✅ Вбудована | ❌ Потрібна ручна реалізація |
| Кастомний дизайн | ⚠️ Обмежений | ✅ Повна свобода |
| Продуктивність | ✅ Віртуалізація | ✅ Віртуалізація |
| Складність | ⚠️ Середня | ✅ Проста |
Почнемо з найпростішого прикладу — відображення колекції об'єктів у таблиці:
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="600" Height="300">
<DataGrid Margin="20" AutoGenerateColumns="True">
<DataGrid.Items>
<DataGridCollectionViewSource>
<DataGridCollectionViewSource.Source>
<x:Array Type="{x:Type sys:Object}">
<!-- Дані будуть додані через code-behind -->
</x:Array>
</DataGridCollectionViewSource.Source>
</DataGridCollectionViewSource>
</DataGrid.Items>
</DataGrid>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var products = new List<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 },
new Product { Id = 4, Name = "Монітор", Price = 8000, InStock = true }
};
var dataGrid = this.FindControl<DataGrid>("ProductsGrid");
dataGrid.ItemsSource = products;
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public bool InStock { get; set; }
}
DataGrid має дещо інший API порівняно з WPF. Наприклад, деякі властивості можуть мати інші назви або поведінку. Завжди перевіряйте документацію Avalonia для специфічних деталей.Властивість AutoGenerateColumns="True" (за замовчуванням) автоматично створює колонки на основі публічних властивостей об'єктів у колекції. Це зручно для швидкого прототипування, але має обмеження:
Переваги:
Недоліки:
AutoGenerateColumns="False" та визначайте колонки вручну. Це дає повний контроль над відображенням та запобігає несподіванкам при зміні моделі даних.Для повного контролю над відображенням встановіть AutoGenerateColumns="False" та визначте колонки вручну:
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="700" Height="350">
<DataGrid Margin="20"
AutoGenerateColumns="False"
GridLinesVisibility="All"
HeadersVisibility="All">
<DataGrid.Columns>
<DataGridTextColumn Header="ID"
Binding="{Binding Id}"
Width="60" />
<DataGridTextColumn Header="Назва продукту"
Binding="{Binding Name}"
Width="*" />
<DataGridTextColumn Header="Ціна (грн)"
Binding="{Binding Price, StringFormat='{}{0:N2}'}"
Width="120" />
<DataGridCheckBoxColumn Header="В наявності"
Binding="{Binding InStock}"
Width="100" />
</DataGrid.Columns>
</DataGrid>
</Window>
Кожна колонка має набір важливих властивостей:
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 надає кілька вбудованих типів колонок для різних типів даних:
Найпоширеніший тип колонки для відображення та редагування тексту:
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="650" Height="300">
<DataGrid Margin="20" AutoGenerateColumns="False">
<DataGrid.Columns>
<!-- Звичайний текст -->
<DataGridTextColumn Header="Ім'я"
Binding="{Binding Name}"
Width="150" />
<!-- Форматування валюти -->
<DataGridTextColumn Header="Зарплата"
Binding="{Binding Salary, StringFormat='{}{0:C}'}"
Width="120" />
<!-- Форматування дати -->
<DataGridTextColumn Header="Дата народження"
Binding="{Binding BirthDate, StringFormat='{}{0:dd.MM.yyyy}'}"
Width="150" />
<!-- Форматування відсотків -->
<DataGridTextColumn Header="Ефективність"
Binding="{Binding Efficiency, StringFormat='{}{0:P0}'}"
Width="120" />
</DataGrid.Columns>
</DataGrid>
</Window>
Для відображення та редагування bool властивостей:
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="600" Height="300">
<DataGrid Margin="20" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Завдання"
Binding="{Binding Title}"
Width="*" />
<DataGridCheckBoxColumn Header="Виконано"
Binding="{Binding IsCompleted}"
Width="100" />
<DataGridCheckBoxColumn Header="Важливе"
Binding="{Binding IsImportant}"
Width="100" />
<DataGridCheckBoxColumn Header="Архів"
Binding="{Binding IsArchived}"
Width="100"
IsReadOnly="True" />
</DataGrid.Columns>
</DataGrid>
</Window>
DataGridCheckBoxColumn підтримує тристанові чекбокси для bool? (nullable bool). Це корисно для представлення "невизначеного" стану:<DataGridCheckBoxColumn Header="Підтверджено"
Binding="{Binding IsConfirmed}"
IsThreeState="True" />
Для вибору значення з попередньо визначеного списку:
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="600" Height="300">
<DataGrid Margin="20" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Продукт"
Binding="{Binding ProductName}"
Width="*" />
<DataGridComboBoxColumn Header="Категорія"
Width="150">
<DataGridComboBoxColumn.ItemsSource>
<x:Array Type="{x:Type sys:String}">
<sys:String>Електроніка</sys:String>
<sys:String>Одяг</sys:String>
<sys:String>Продукти</sys:String>
<sys:String>Книги</sys:String>
</x:Array>
</DataGridComboBoxColumn.ItemsSource>
</DataGridComboBoxColumn>
<DataGridTextColumn Header="Ціна"
Binding="{Binding Price, StringFormat='{}{0:C}'}"
Width="100" />
</DataGrid.Columns>
</DataGrid>
</Window>
DataGridComboBoxColumn чудово працює з enum типами. Просто прив'яжіть ItemsSource до Enum.GetValues(typeof(YourEnum)):public enum Priority { Low, Medium, High, Critical }
// У XAML або code-behind:
column.ItemsSource = Enum.GetValues(typeof(Priority));
Коли вбудованих типів колонок недостатньо, DataGridTemplateColumn дає повну свободу через DataTemplate:
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="700" Height="350">
<DataGrid Margin="20" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Користувач"
Binding="{Binding Name}"
Width="*" />
<DataGridTextColumn Header="Email"
Binding="{Binding Email}"
Width="200" />
<!-- Колонка з кастомним шаблоном -->
<DataGridTemplateColumn Header="Дії" Width="150">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"
Spacing="5">
<Button Content="✏️"
Width="30" Height="30"
ToolTip.Tip="Редагувати" />
<Button Content="🗑️"
Width="30" Height="30"
ToolTip.Tip="Видалити" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
DataGridTemplateColumn підтримує два окремі шаблони:
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="600" Height="300">
<DataGrid Margin="20" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Продукт"
Binding="{Binding Name}"
Width="*" />
<DataGridTemplateColumn Header="Рейтинг" Width="150">
<!-- Відображення: зірочки -->
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center">
<TextBlock Text="⭐" FontSize="16" />
<TextBlock Text="⭐" FontSize="16" />
<TextBlock Text="⭐" FontSize="16" />
<TextBlock Text="⭐" FontSize="16" />
<TextBlock Text="☆" FontSize="16" Opacity="0.3" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<!-- Редагування: слайдер -->
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Slider Minimum="0"
Maximum="5"
Value="{Binding Rating}"
TickFrequency="1"
IsSnapToTickEnabled="True" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
Коли у вас багато колонок і потрібен горизонтальний скрол, корисно зафіксувати ліві колонки (наприклад, ID або назву), щоб вони завжди були видимі:
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="600" Height="300">
<DataGrid Margin="20"
AutoGenerateColumns="False"
FrozenColumnCount="2">
<DataGrid.Columns>
<!-- Ці дві колонки будуть зафіксовані -->
<DataGridTextColumn Header="ID"
Binding="{Binding Id}"
Width="50" />
<DataGridTextColumn Header="Назва"
Binding="{Binding Name}"
Width="150" />
<!-- Ці колонки будуть скролитися -->
<DataGridTextColumn Header="Опис"
Binding="{Binding Description}"
Width="200" />
<DataGridTextColumn Header="Категорія"
Binding="{Binding Category}"
Width="150" />
<DataGridTextColumn Header="Постачальник"
Binding="{Binding Supplier}"
Width="150" />
<DataGridTextColumn Header="Країна"
Binding="{Binding Country}"
Width="100" />
</DataGrid.Columns>
</DataGrid>
</Window>
Контролює видимість ліній між комірками:
<!-- Всі лінії -->
<DataGrid GridLinesVisibility="All" />
<!-- Тільки горизонтальні -->
<DataGrid GridLinesVisibility="Horizontal" />
<!-- Тільки вертикальні -->
<DataGrid GridLinesVisibility="Vertical" />
<!-- Без ліній -->
<DataGrid GridLinesVisibility="None" />
<!-- Заголовки колонок та рядків -->
<DataGrid HeadersVisibility="All" />
<!-- Тільки заголовки колонок -->
<DataGrid HeadersVisibility="Column" />
<!-- Тільки заголовки рядків -->
<DataGrid HeadersVisibility="Row" />
<!-- Без заголовків -->
<DataGrid HeadersVisibility="None" />
<DataGrid AlternatingRowBackground="#f9fafb">
<!-- Колонки -->
</DataGrid>
<!-- Фіксована висота рядків -->
<DataGrid RowHeight="40" />
<!-- Мінімальна та максимальна висота -->
<DataGrid MinRowHeight="30" MaxRowHeight="100" />
<!-- Ширина колонок за замовчуванням -->
<DataGrid ColumnWidth="100" />
У реальних застосунках дані зазвичай прив'язуються через 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<T> замість List<T> для прив'язки до DataGrid. ObservableCollection автоматично повідомляє UI про зміни в колекції (додавання, видалення елементів), тоді як List цього не робить.Для студентів, які тільки опановують 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 автоматично оновиться
Мета: Навчитися створювати базовий DataGrid з різними типами колонок.
Завдання: Створіть застосунок для управління інвентарем магазину з таблицею продуктів.
Вимоги:
Product з властивостями: Id (int), Name (string), Price (decimal), InStock (bool)ObservableCollection<Product> у ViewModelDataGrid з AutoGenerateColumns="False"*)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
});
}
}
Мета: Опанувати створення кастомних колонок через 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>
Мета: Створити повнофункціональну систему управління завданнями з різними типами колонок.
Завдання: Розробіть 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 з наступними колонками:
*)DataGridComboBoxColumn, 120px)DataGridComboBoxColumn, 100px)FrozenColumnCount="2" для фіксації чекбоксу та назвиДодаткові виклики:
ComboBox над таблицеюПриклад 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, DataGridTemplateColumnDataGridTemplateColumn надає повну свободу через CellTemplate та CellEditingTemplateFrozenColumnCount дозволяє фіксувати ліві колонки при горизонтальному скроліObservableCollection<T> для автоматичного оновлення UIВажливі властивості:
Header — заголовок колонкиBinding — прив'язка до властивості об'єктаWidth — ширина колонки (фіксована, Auto, *)IsReadOnly — заборона редагуванняStringFormat — форматування значеньНаступні кроки:
У наступній статті ми розглянемо просунуті можливості DataGrid: сортування, фільтрацію, групування, редагування та валідацію даних.
Контроли колекцій — глибоке занурення
Архітектура ItemsControl та просунуте використання ListBox/ListView для роботи з великими наборами даних
DataGrid — сортування, фільтрація, редагування
Просунуті можливості DataGrid для роботи з великими наборами даних — сортування, фільтрація, групування, inline-редагування та валідація