Контроли вибору — CheckBox, RadioButton, ComboBox, ListBox, DatePicker
CheckBox, RadioButton, GroupName, ComboBox, ListBox, SelectionMode, DatePicker, Calendar, IsThreeState, IsEditable, DisplayMemberPath, SelectedItem, SelectedIndex, BlackoutDates, CalendarDateRange.Вибір як концепція UI
Надати користувачу можливість обрати щось — одне з найфундаментальніших завдань у проєктуванні інтерфейсів. Але форматів, у яких реалізується цей вибір, чимало: чи вибирається одне значення чи декілька? Чи видимий список одразу, чи розкривається на вимогу? Чи значення відомі заздалегідь, чи вводяться вільно? Від відповіді на ці питання залежить, який саме контрол підходить для завдання.
WPF пропонує спеціалізований набір контролів вибору, кожен з яких оптимізований для конкретного UX-сценарію:
CheckBox
CheckBox-ів діють незалежно один від одного.RadioButton
ComboBox
ListBox
DatePicker / Calendar
DatePicker — компактне поле з випадаючим календарем; Calendar — повноцінний календар завжди видимий на екрані.CheckBox: незалежний прапорець
Архітектура та три стани
CheckBox успадковує від ToggleButton (який ми розглядали у статті про базові контроли), тому вся логіка IsChecked (nullable bool) і три стани вже знайомі. Різниця між ToggleButton і CheckBox — суто у зовнішньому вигляді та семантиці: ToggleButton виглядає як кнопка, CheckBox — як прапорець з підписом. Поведінка — ідентична.
Ключова властивість — IsChecked типу bool?:
Значення IsChecked | Стан | Подія |
|---|---|---|
true | Позначено (✓) | Checked |
false | Не позначено (□) | Unchecked |
null | Невизначений (─) | Indeterminate |
Тристановий режим (null) активується властивістю IsThreeState="True". У звичайному режимі — лише true та false.
CheckBox має чітке UX-призначення: "батьківський" прапорець, що представляє групу дочірніх. Якщо частина дочірніх позначена — батьківський у стані null (Indeterminate). Якщо всі позначені — true. Якщо жодного — false. Цей паттерн широко використовується у деревах налаштувань (наприклад, вибір файлів для оновлення, де можна виділити групу чи окремі елементи).Базове використання CheckBox
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<StackPanel Margin="20" Spacing="12">
<TextBlock Text="Налаштування сповіщень:" FontWeight="SemiBold" FontSize="14"/>
<CheckBox Content="Отримувати email-сповіщення" IsChecked="True"/>
<CheckBox Content="Отримувати SMS-сповіщення"/>
<CheckBox Content="Маркетингові листи" IsChecked="False"/>
<Separator Margin="0,4"/>
<TextBlock Text="Тристановий режим (IsThreeState=True):"
Foreground="Gray" FontSize="12"/>
<CheckBox Content="Вибрати всі (частково обрано)"
IsThreeState="True"
IsChecked="{x:Null}"/>
</StackPanel>
Реакція на зміну стану: події та IsChecked у code-behind
Підписуватись на окремі події Checked/Unchecked/Indeterminate або читати IsChecked у будь-який момент — обидва підходи правомірні. Для простих форм зручніше зчитувати IsChecked у момент підтвердження (натискання "Зберегти"), ніж стежити за кожною зміною окремо:
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<StackPanel Margin="20" Spacing="12">
<TextBlock Text="Параметри експорту:" FontWeight="SemiBold"/>
<CheckBox x:Name="chkIncludeImages" Content="Включити зображення" IsChecked="True"/>
<CheckBox x:Name="chkIncludeStyles" Content="Включити стилі CSS" IsChecked="True"/>
<CheckBox x:Name="chkMinifyOutput" Content="Мінімізувати вивід"/>
<CheckBox x:Name="chkOpenAfter" Content="Відкрити після експорту"/>
<Button Content="Експортувати"
Padding="12,6"
HorizontalAlignment="Left"
Margin="0,8,0,0"
Command="{Binding ShowMessageCommand}"
CommandParameter="Параметри зчитано!"/>
</StackPanel>
private void ExportButton_Click(object sender, RoutedEventArgs e)
{
bool includeImages = chkIncludeImages.IsChecked == true;
bool includeStyles = chkIncludeStyles.IsChecked == true;
bool minify = chkMinifyOutput.IsChecked == true;
bool openAfter = chkOpenAfter.IsChecked == true;
// Важлива деталь: IsChecked має тип bool? (nullable).
// Пряме порівняння "== true" повертає false для null та false.
// Не використовуйте (bool)checkbox.IsChecked — кине InvalidCastException якщо null.
var options = $"""
Зображення: {includeImages}
Стилі: {includeStyles}
Мінімізація: {minify}
Відкрити: {openAfter}
""";
MessageBox.Show(options, "Параметри експорту");
}
IsChecked в bool потребує обережності. (bool)checkBox.IsChecked — кине InvalidCastException, якщо значення null (тристановий режим). Безпечні варіанти: checkBox.IsChecked == true, checkBox.IsChecked ?? false, або checkBox.IsChecked.GetValueOrDefault().RadioButton: виключний вибір
GroupName: механізм ексклюзивності
RadioButton — ще один нащадок ToggleButton, але зі специфічною поведінкою: вибір одного RadioButton у групі автоматично скасовує усі інші у тій самій групі. Це реалізується через механізм GroupName.
Правила групування:
- Усі
RadioButton-и безGroupName, що знаходяться в одному батьківському контейнері, утворюють одну групу автоматично. RadioButton-и з однаковимGroupNameутворюють групу незалежно від їхнього розташування у дереві елементів — навіть якщо вони знаходяться у різнихStackPanel-ах чиGrid-ах.- Якщо один
RadioButtonмаєGroupName, а інший — ні, вони не є частиною спільної групи.
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<StackPanel Margin="20" Spacing="16">
<TextBlock Text="Оберіть план підписки:" FontWeight="SemiBold" FontSize="14"/>
<!-- Група 1: тарифний план (GroupName="plan") -->
<StackPanel Spacing="8">
<RadioButton Content="🆓 Безкоштовний — до 3 проєктів"
GroupName="plan"
IsChecked="True"/>
<RadioButton Content="⭐ Стартер — до 10 проєктів, $9/міс"
GroupName="plan"/>
<RadioButton Content="🚀 Про — необмежено, $29/міс"
GroupName="plan"/>
</StackPanel>
<Separator/>
<TextBlock Text="Цикл білінгу:" FontWeight="SemiBold"/>
<!-- Група 2: цикл білінгу (GroupName="billing") — незалежна від першої -->
<StackPanel Orientation="Horizontal" Spacing="20">
<RadioButton Content="Щомісяця"
GroupName="billing"
IsChecked="True"/>
<RadioButton Content="Щороку (знижка 20%)"
GroupName="billing"/>
</StackPanel>
</StackPanel>
GroupName, якщо у вікні є кілька незалежних груп RadioButton-ів. Покладатись на автоматичне групування за батьківським контейнером — це джерело важкозрозумілих помилок, особливо коли в майбутньому структура розкладки змінюється.Зчитування обраного RadioButton у code-behind
Оскільки RadioButton-и не мають єдиної властивості "обране значення" (на відміну від ComboBox.SelectedItem), у code-behind доводиться перебирати їх або перевіряти IsChecked кожного окремо:
private string GetSelectedPlan()
{
if (rbFree.IsChecked == true) return "Безкоштовний";
if (rbStarter.IsChecked == true) return "Стартер";
if (rbPro.IsChecked == true) return "Про";
return "Нічого не обрано";
}
IsChecked кожного RadioButton до окремого булевого поля ViewModel. Але це тема Блоку 6–7. Поки — code-behind цілком достатній.ComboBox: список, що розкривається
Концептуальна модель ComboBox
ComboBox — це контрол, що у звичайному стані займає місце одного рядка і показує лише поточний обраний елемент. При натисканні — розкривається drop-down список з усіма варіантами. Після вибору — знову згортається. Ця модель ідеальна для вибору з довгого списку варіантів, коли показувати всі одразу займало б забагато місця.
Під капотом ComboBox успадковує від Selector → ItemsControl. Це важлива деталь: він, як і ListBox, побудований на концепції Items та ItemsSource. У цій статті ми заповнюємо Items статично (у XAML або у code-behind) — прив'язку до колекцій через ItemsSource розглянемо у Блоці 6.
Ключові властивості ComboBox
ComboBoxItem-ів повертає сам ComboBoxItem. При прив'язці колекції — повертає об'єкт з колекції. За замовчуванням — null (нічого не обрано).-1 — нічого не обрано. Зручний для встановлення вибору за замовчуванням: SelectedIndex="0" обере перший елемент.SelectedValuePath. Якщо SelectedValuePath="Id", то SelectedValue повертає поле Id обраного об'єкта — без необхідності кастувати SelectedItem.true — у закритому стані замість статичного тексту відображається TextBox для ручного введення. Корисно для "combobox з автодоповненням". За замовчуванням — false.ItemsSource), яку потрібно відображати у списку. Без цієї властивості — відображається результат ToString(). Докладніше — у Блоці 6.IsEditable="True" — містить поточний текст у редагованому полі (введений вручну або відповідний обраному елементу).Статичне наповнення через XAML
Найпростіший спосіб заповнити ComboBox — додати ComboBoxItem-и безпосередньо у XAML:
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<StackPanel Margin="20" Spacing="16">
<TextBlock Text="Оберіть мову інтерфейсу:" Foreground="Gray" FontSize="13"/>
<ComboBox SelectedIndex="0" Width="240" HorizontalAlignment="Left">
<ComboBoxItem Content="🇺🇦 Українська"/>
<ComboBoxItem Content="🇬🇧 English"/>
<ComboBoxItem Content="🇩🇪 Deutsch"/>
<ComboBoxItem Content="🇫🇷 Français"/>
<ComboBoxItem Content="🇵🇱 Polski"/>
</ComboBox>
<TextBlock Text="Оберіть або введіть місто (IsEditable=True):"
Foreground="Gray" FontSize="13"/>
<ComboBox IsEditable="True"
Width="240"
HorizontalAlignment="Left"
Text="Київ">
<ComboBoxItem Content="Київ"/>
<ComboBoxItem Content="Харків"/>
<ComboBoxItem Content="Одеса"/>
<ComboBoxItem Content="Дніпро"/>
<ComboBoxItem Content="Львів"/>
</ComboBox>
<TextBlock Text="Вимкнений ComboBox (IsEnabled=False):"
Foreground="Gray" FontSize="13"/>
<ComboBox Width="240" HorizontalAlignment="Left"
IsEnabled="False" SelectedIndex="0">
<ComboBoxItem Content="Недоступний варіант"/>
</ComboBox>
</StackPanel>
Статичне наповнення через code-behind
Якщо список формується динамічно (наприклад, за результатами запиту, але ще без Binding), можна додати рядки безпосередньо у C#:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var countries = new[] { "Україна", "Польща", "Німеччина", "Франція", "США" };
foreach (var country in countries)
{
countryCombo.Items.Add(country);
}
countryCombo.SelectedIndex = 0; // Обираємо перший за замовчуванням
}
private void CountryCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// SelectedItem для рядків — це сам рядок
string selected = countryCombo.SelectedItem?.ToString() ?? "нічого";
statusLabel.Content = $"Обрано: {selected}";
}
ComboBox.Items лежать прості рядки — SelectedItem повертає string, кастування не потрібне. Якщо — ComboBoxItem-и — тоді (comboBox.SelectedItem as ComboBoxItem)?.Content?.ToString(). При прив'язці до колекції об'єктів — SelectedItem повертає сам об'єкт: (comboBox.SelectedItem as MyClass)?.Name. В усіх цих випадках SelectedValuePath суттєво спрощує роботу.ListBox: видимий список із вибором
ListBox проти ComboBox
Якщо ComboBox — це "акордеон" (вибір прихований до натискання), то ListBox — це "таблиця" (всі рядки видимі одразу). Обирайте ListBox, коли:
- Кількість елементів невелика (3–15) і показ усіх одночасно не переповнює UI.
- Потрібен множинний вибір (кілька елементів одразу) —
ComboBoxце не підтримує. - Важлива порівнянність варіантів — коли видимість усіх одночасно допомагає ухвалити рішення.
Ключова властивість, що відрізняє ListBox від ComboBox — SelectionMode:
| Значення | Поведінка |
|---|---|
Single | Вибір лише одного елемента (за замовчуванням) |
Multiple | Клік на елемент перемикає його виділення незалежно (без Ctrl) |
Extended | Виділення з Shift (діапазон) та Ctrl (окремі елементи), як у провіднику |
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<StackPanel Margin="20" Spacing="16">
<TextBlock Text="ListBox (Single — за замовчуванням):"
Foreground="Gray" FontSize="13"/>
<ListBox Width="220" HorizontalAlignment="Left" Height="120">
<ListBoxItem Content="🎨 Дизайн"/>
<ListBoxItem Content="💻 Розробка"/>
<ListBoxItem Content="📊 Аналітика"/>
<ListBoxItem Content="📣 Маркетинг"/>
<ListBoxItem Content="🤝 Підтримка"/>
</ListBox>
<TextBlock Text="ListBox (SelectionMode=Multiple — без Ctrl):"
Foreground="Gray" FontSize="13"/>
<ListBox SelectionMode="Multiple"
Width="220" HorizontalAlignment="Left" Height="120">
<ListBoxItem Content="✅ JavaScript"/>
<ListBoxItem Content="✅ TypeScript"/>
<ListBoxItem Content="C#"/>
<ListBoxItem Content="Python"/>
<ListBoxItem Content="Go"/>
</ListBox>
</StackPanel>
Зчитування вибраних елементів
Для Single-режиму — SelectedItem та SelectedIndex, як у ComboBox. Для Multiple/Extended — колекція SelectedItems:
// Single
private void SingleListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var item = singleListBox.SelectedItem as ListBoxItem;
statusText.Text = $"Обрано: {item?.Content}";
}
// Multiple / Extended
private void MultiListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selected = multiListBox.SelectedItems
.Cast<ListBoxItem>()
.Select(i => i.Content?.ToString())
.ToList();
statusText.Text = selected.Count > 0
? $"Обрано: {string.Join(", ", selected)}"
: "Нічого не обрано";
}
SelectedItems повертає IList, не IList<T>. Для роботи з ним потрібне приведення через Cast<T>() або перебір у циклі. При прив'язці до типізованих колекцій через ItemsSource — Cast<MyClass>() повертає об'єкти потрібного типу без обхідних рукоділь.DatePicker: вибір дати
Навіщо спеціалізований контрол для дати
Здається, введення дати через звичайний TextBox — очевидне рішення. Але це рішення, що плодить проблеми: різні формати дат у різних культурах (dd.MM.yyyy в Україні, MM/dd/yyyy в США), помилки введення, неможливість одразу побачити день тижня, необхідність самостійно валідувати рядок. DatePicker вирішує всі ці проблеми — він дає користувачу зручний календар і повертає розробнику готовий DateTime?.
DatePicker — компактний контрол: у нормальному стані показує поточну обрану дату (або порожнє поле), при натисканні на іконку — розкривається мінікалендар. Після вибору — знову згортається. Це аналог ComboBox, але для дат.
null — нічого не обрано. Найчастіше прив'язується до DateTime?-властивості ViewModel. Встановлення через XAML: SelectedDate="{x:Static sys:DateTime.Today}" (потребує xmlns:sys="clr-namespace:System;assembly=mscorlib").DisplayDateStart="{x:Static sys:DateTime.Today}".Short (стислий, відповідно до культури) або Long (розгорнутий). За замовчуванням — Short.Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<StackPanel Margin="20" Spacing="16">
<TextBlock Text="Звичайний DatePicker:" Foreground="Gray" FontSize="13"/>
<DatePicker Width="180" HorizontalAlignment="Left"/>
<TextBlock Text="Тільки майбутні дати (from today):"
Foreground="Gray" FontSize="13"/>
<DatePicker x:Name="futureDatePicker"
Width="180"
HorizontalAlignment="Left"
SelectedDateChanged="FutureDatePicker_Changed"/>
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="Обрана дата:" Foreground="Gray"/>
<TextBlock x:Name="selectedDateLabel"
Text="—"
FontWeight="Bold"
Foreground="#6366F1"/>
</StackPanel>
</StackPanel>
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Встановлюємо мінімальну відображувану дату — сьогодні
futureDatePicker.DisplayDateStart = DateTime.Today;
// Додаємо вихідні методом BlackoutDates.AddDatesInPast()
// (блокує всі минулі дати)
futureDatePicker.BlackoutDates.AddDatesInPast();
}
private void FutureDatePicker_Changed(object sender,
SelectionChangedEventArgs e)
{
if (futureDatePicker.SelectedDate is DateTime date)
{
selectedDateLabel.Text = date.ToString("dd MMMM yyyy, dddd",
new System.Globalization.CultureInfo("uk-UA"));
}
else
{
selectedDateLabel.Text = "—";
}
}
Блокування конкретних дат через BlackoutDates
Для форм бронювання типовий сценарій — заблокувати вже зайняті дати або вихідні. BlackoutDates приймає CalendarDateRange:
private void SetupBookingCalendar()
{
var picker = bookingDatePicker;
// Блокуємо всі минулі дати разом з сьогодні
picker.BlackoutDates.AddDatesInPast();
// Блокуємо конкретний діапазон (наприклад, технічна перерва)
picker.BlackoutDates.Add(
new CalendarDateRange(
new DateTime(2026, 4, 14),
new DateTime(2026, 4, 18)
)
);
// Блокуємо всі вихідні (суботи та неділі)
DateTime current = DateTime.Today;
DateTime endDate = current.AddYears(1);
while (current <= endDate)
{
if (current.DayOfWeek == DayOfWeek.Saturday
|| current.DayOfWeek == DayOfWeek.Sunday)
{
picker.BlackoutDates.Add(new CalendarDateRange(current));
}
current = current.AddDays(1);
}
}
DisplayDateChanged-подію Calendar і перефарбовують осередки через кастомний CalendarDayButtonStyle (це вже тема стилізації, Блок 8). Для навчальних цілей — цикл цілком прийнятний.Calendar: повноцінний вбудований календар
Calendar vs DatePicker
Calendar — це той самий "мінікалендар", що розкривається у DatePicker, але виведений як самостійний завжди видимий контрол. Використовуйте його, коли:
- Вибір дати — центральна дія екрану, а не допоміжна.
- Потрібно показати кілька дат або діапазон.
- Потрібен
SelectionMode="MultipleRange"—DatePickerне підтримує множинний вибір.
SingleDate та SingleRange).Multiple та MultipleRange). Доступна лише для читання, але можна викликати Add() для програматичного вибору.SingleDate (один день), SingleRange (суцільний діапазон), MultipleRange (кілька не суміжних діапазонів), None (вибір заблоковано — Calendar тільки для відображення).Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<StackPanel Margin="20" Spacing="20">
<TextBlock Text="Calendar (SingleDate, за замовчуванням):"
Foreground="Gray" FontSize="13"/>
<Calendar SelectionMode="SingleDate"
HorizontalAlignment="Left"/>
<TextBlock Text="Calendar (SingleRange — Shift+клік для діапазону):"
Foreground="Gray" FontSize="13"/>
<Calendar SelectionMode="SingleRange"
HorizontalAlignment="Left"/>
</StackPanel>
Calendar та DatePicker у реальному WPF буде помітно іншим (класична Aero-тема із сірими кнопками). Поведінка (SelectionMode, BlackoutDates, SelectedDate) — ідентична. Для стилізованого вигляду у WPF використовують бібліотеки тем (MahApps.Metro, ModernWPF).Практичні завдання
Ціль: Практика CheckBox, IsThreeState, RadioButton, GroupName, зчитування стану.
Завдання: Реалізуйте форму опитування "Ваш профіль розробника":
- Секція "Які технології ви використовуєте?" — 5–6
CheckBox-ів (C#, Python, JavaScript, Go, Rust, Java). Вгорі —CheckBox"Вибрати всі" зIsThreeState="True". При зміні дочірніх — батьківський автоматично переходить у відповідний стан (true/false/null) у code-behind. - Секція "Ваш досвід" —
RadioButton-и уGroupName="experience": "< 1 року", "1–3 роки", "3–5 років", "5+ років". Один обрано за замовчуванням. - Секція "Тип зайнятості" —
RadioButton-и уGroupName="employment": "Найманий", "Фрілансер", "Власник бізнесу". - Кнопка "Підсумок" — показує
MessageBoxз усіма обраними значеннями.
Ціль: Практика ComboBox, SelectionChanged, динамічне оновлення UI.
Завдання: Реалізуйте форму вибору країни доставки:
ComboBoxз 6–8 країнами (заповнити через code-behind уWindow_Loaded). Перша обрана за замовчуванням.- Під
ComboBox—TextBlockіз прапорцем та назвою країни (оновлюється черезSelectionChanged). Мапи прапорців: словникDictionary<string, string>у code-behind. - Другий
ComboBox— місто для обраної країни. При зміні країни — список міст у другомуComboBoxперезаповнюється (черезItems.Clear()→Items.Add()). - Кнопка "Підтвердити" →
MessageBoxіз "Обрано: країна, місто".
Ціль: Практика DatePicker, BlackoutDates, SelectedDateChanged, валідація діапазону.
Завдання: Форма бронювання номеру:
- Два
DatePicker— "Дата заїзду" та "Дата виїзду". Обидва маютьDisplayDateStart = DateTime.TodayтаBlackoutDates.AddDatesInPast(). - При виборі дати заїзду — автоматично встановлювати мінімальну дату виїзду як
дата заїзду + 1 день(черезcheckOutPicker.DisplayDateStart). - Заблокувати вихідні у обох
DatePicker-ах черезBlackoutDates(цикл по всіх вихідних протягом наступного року). - Під датами —
TextBlockіз "Кількість ночей: X" (обраховується як різниця дат у днях). - Кнопка "Забронювати" активна лише якщо обидві дати обрані і дата виїзду > дата заїзду. У інших випадках —
IsEnabled="False".
Розширення: додати ListBox (SelectionMode=Multiple) зі списком додаткових послуг: "Сніданок", "Паркінг", "Прибирання", "Спа". Підсумок вартості оновлюється динамічно.
Підсумок
Що ми вивчили у цій статті
Ця стаття охопила шість контролів вибору — від найпростіших прапорців до повноцінного датапікера. Узагальнимо ключові висновки.
CheckBox — незалежний прапорець із трьома станами (true/false/null). Тристановий режим (IsThreeState="True") використовується для "батьківських" прапорців, що представляють групу. При зчитуванні IsChecked завжди використовуйте == true або ?? false — ніколи пряме приведення.
RadioButton — виключний вибір одного варіанту з групи. Групування через GroupName — єдиний надійний спосіб, якщо у вікні кілька незалежних груп. Для зчитування обраного у code-behind — перевірка IsChecked кожного окремо; в MVVM — прив'язка до bool-властивостей ViewModel.
ComboBox — drop-down список з підтримкою редагованого введення (IsEditable). Статичне наповнення — через ComboBoxItem-и у XAML або Items.Add() у code-behind. SelectedItem/SelectedIndex/SelectedValue — три способи зчитати обраний елемент. ItemsSource для прив'язки до колекцій — у Блоці 6.
ListBox — видимий список з підтримкою множинного вибору (SelectionMode). Режим Extended відтворює поведінку провідника Windows (Shift + Ctrl). SelectedItems для множинного вибору повертає нетипізований IList — потрібен Cast<T>().
DatePicker — компактний вибір дати з калькулятором через BlackoutDates. DisplayDateStart/DisplayDateEnd ховають недоступні дати; BlackoutDates — блокують конкретні. AddDatesInPast() — зручний метод для "тільки майбутнє".
Calendar — повноцінний завжди видимий календар. SelectionMode="MultipleRange" дозволяє обирати кілька несуміжних діапазонів — можливість, недоступна у DatePicker.
Що далі
У наступній статті ми розглянемо контроли-контейнери вмісту — GroupBox, Expander, ScrollViewer, TabControl, Frame. Ці контроли не відображають дані самі по собі, а організовують інші контроли у структуровані групи, розділи та вкладки — будівельні блоки складних багатосторінкових форм.
Текстові контроли — TextBlock, TextBox, RichTextBox
Детально розглядаємо контроли для відображення та введення тексту у WPF — від легковісного TextBlock до повноцінного RichTextBox із FlowDocument. Розуміємо різницю між відображенням і введенням, форматованим і неформатованим текстом, захищеним введенням через PasswordBox.
Content Model — GroupBox, Expander, TabControl, StatusBar
Досліджуємо Content Model WPF — фундаментальну архітектурну концепцію, на якій побудовано всі контейнерні контроли. Вивчаємо GroupBox, Expander, TabControl та StatusBar як інструменти організації складних інтерфейсів.