Добавление тем в Windows Forms: Light, Nature и Dark

В современных десктопных приложениях часто нужна возможность менять тему интерфейса. Это улучшает удобство для пользователей и позволяет учитывать условия освещения и предпочтения. В Windows Forms можно реализовать темы, создавая управляемые наборы цветов и переключая их по нажатию кнопки.
В этой статье вы получите:
- пошаговое руководство по созданию проекта и элементов управления;
- готовые примеры кода для переключения тем;
- рекомендации по хранению и восстановлению выбранной темы;
- чек-листы для разработчиков, дизайнеров и тестировщиков;
- сравнительный список альтернатив и советы по совместимости.
Что вам нужно заранее
- Visual Studio (любой поддерживаемой версии для WinForms).
- Базовые знания C# и работы с дизайнером форм.
- Проект Windows Forms (Framework или .NET Core/5/6/7 — см. раздел совместимости).
Шаг 1. Создайте проект и расположите базовые элементы управления
- Создайте новый проект Windows Forms в Visual Studio.
- Добавьте на форму три больших кнопки (button1, button2, button3) и одну метку (label1).
- Отрегулируйте размеры и стиль через окно свойств.
В примере мы использовали следующие настройки через окно свойств:
| Контрол | Свойство | Значение |
|---|---|---|
| button1 | Size | 580, 200 |
| button1 | FlatStyle | Flat |
| button1 | Text | Users |
| button2 | Size | 580, 100 |
| button2 | FlatStyle | Flat |
| button2 | Text | Accounts |
| button3 | Size | 580, 100 |
| button3 | FlatStyle | Flat |
| button3 | Text | Permissions |
| label1 | Text | Copyright 2022 |
Шаг 2. Добавьте кнопку настроек тем и список тем
Создайте дополнительную кнопку, которая будет показывать/скрывать выбор темы, и три кнопки для самих тем (Light, Nature, Dark).
- Добавьте кнопку настроек и задайте свойства:
| Свойство | Значение |
|---|---|
| Name | btnThemeSettings |
| FlatStyle | Flat |
| Size | 200, 120 |
| Text | Themes |
- Добавьте три кнопки для тем и установите им начальные цвета и видимость в False:
| Контрол | Свойство | Значение |
|---|---|---|
| btnLightTheme | BackColor | WhiteSmoke |
| btnLightTheme | Size | 200, 80 |
| btnLightTheme | FlatStyle | Flat |
| btnLightTheme | Text | Light |
| btnLightTheme | Visible | False |
| btnNatureTheme | BackColor | DarkSeaGreen |
| btnNatureTheme | Size | 200, 80 |
| btnNatureTheme | FlatStyle | Flat |
| btnNatureTheme | Text | Nature |
| btnNatureTheme | Visible | False |
| btnDarkTheme | BackColor | DimGray |
| btnDarkTheme | ForeColor | White |
| btnDarkTheme | Size | 200, 80 |
| btnDarkTheme | FlatStyle | Flat |
| btnDarkTheme | Text | Dark |
| btnDarkTheme | Visible | False |
- Создайте обработчик клика для кнопки настроек тем и переключайте видимость трех кнопок тем:
private void btnThemeSettings_Click(object sender, EventArgs e)
{
btnNatureTheme.Visible = !btnNatureTheme.Visible;
btnLightTheme.Visible = !btnLightTheme.Visible;
btnDarkTheme.Visible = !btnDarkTheme.Visible;
}Запустите приложение и убедитесь, что по умолчанию кнопки тем скрыты, а по нажатию на Themes они появляются и исчезают.
Шаг 3. Управление наборами цветов (темами)
Лучше хранить цвета темы в одном месте — для этого подойдёт словарь Dictionary
- Внутри класса Form объявите перечисление и словари:
enum ThemeColor
{
Primary,
Secondary,
Tertiary,
Text
}
Dictionary Light = new Dictionary();
Dictionary Nature = new Dictionary();
Dictionary Dark = new Dictionary(); - В конструкторе формы инициализируйте словари:
public Form1()
{
InitializeComponent();
// Add dictionaries here
Light = new Dictionary() {
{ ThemeColor.Primary, Color.WhiteSmoke },
{ ThemeColor.Secondary, Color.Silver },
{ ThemeColor.Tertiary, Color.White },
{ ThemeColor.Text, Color.Black }
};
Nature = new Dictionary() {
{ ThemeColor.Primary, Color.DarkSeaGreen },
{ ThemeColor.Secondary, Color.AliceBlue },
{ ThemeColor.Tertiary, Color.Honeydew },
{ ThemeColor.Text, Color.Black }
};
Dark = new Dictionary() {
{ ThemeColor.Primary, Color.DimGray },
{ ThemeColor.Secondary, Color.DimGray },
{ ThemeColor.Tertiary, Color.Black },
{ ThemeColor.Text, Color.White }
};
// Apply default theme
ChangeTheme(Light[ThemeColor.Primary], Light[ThemeColor.Secondary], Light[ThemeColor.Tertiary]);
ChangeTextColor(Light[ThemeColor.Text]);
} Шаг 4. Функции смены темы и цвета текста
Создайте функции, которые применяют цвета к элементам интерфейса. Это упрощает повторное использование и тестирование.
private void ChangeTheme(Color primaryColor, Color secondaryColor, Color tertiaryColor)
{
// Change background color of buttons
btnThemeSettings.BackColor = primaryColor;
button1.BackColor = primaryColor;
button2.BackColor = secondaryColor;
button3.BackColor = secondaryColor;
this.BackColor = tertiaryColor;
}
private void ChangeTextColor(Color textColor)
{
// Change color of text
button1.ForeColor = textColor;
button2.ForeColor = textColor;
button3.ForeColor = textColor;
label1.ForeColor = textColor;
btnThemeSettings.ForeColor = textColor;
}И в обработчиках нажатия на кнопки тем используйте словари:
private void btnLightTheme_Click(object sender, EventArgs e)
{
ChangeTheme(Light[ThemeColor.Primary], Light[ThemeColor.Secondary], Light[ThemeColor.Tertiary]);
ChangeTextColor(Light[ThemeColor.Text]);
}
private void btnNatureTheme_Click(object sender, EventArgs e)
{
ChangeTheme(Nature[ThemeColor.Primary], Nature[ThemeColor.Secondary], Nature[ThemeColor.Tertiary]);
ChangeTextColor(Nature[ThemeColor.Text]);
}
private void btnDarkTheme_Click(object sender, EventArgs e)
{
ChangeTheme(Dark[ThemeColor.Primary], Dark[ThemeColor.Secondary], Dark[ThemeColor.Tertiary]);
ChangeTextColor(Dark[ThemeColor.Text]);
}Сохранение выбранной темы между запусками
Рекомендуемые варианты для хранения пользовательских настроек:
- Properties.Settings (Application Settings) — простая кросс-платформенная опция внутри .NET;
- JSON-файл в %AppData% (Environment.SpecialFolder.ApplicationData) — удобно версионировать и редактировать вручную;
- реестр Windows — использовать осторожно; подходит для специфичных корпоративных сценариев.
Пример сохранения через Properties.Settings:
// В Settings.settings добавьте ключ CurrentTheme типа string
private void SaveTheme(string themeName)
{
Properties.Settings.Default.CurrentTheme = themeName;
Properties.Settings.Default.Save();
}
private void LoadTheme()
{
var name = Properties.Settings.Default.CurrentTheme;
if (name == "Nature") btnNatureTheme.PerformClick();
else if (name == "Dark") btnDarkTheme.PerformClick();
else btnLightTheme.PerformClick();
}Вызовите LoadTheme() в конструкторе после InitializeComponent() и инициализации словарей.
Важно: не сохраняйте в настройках данные, требующие защиты (пароли, токены). Для них используйте безопасное хранилище.
Доступность и читаемость
- Проверяйте контраст текста и фона (WCAG подходы). Для базовой проверки используйте соотношение контраста 4.5:1 для нормального текста.
- Для тёмных тем используйте не чистый чёрный текст на ярких фонах — лучше слегка смещённые оттенки.
- Обеспечьте переключение с клавиатуры (Tab/Enter) и доступность для экранных читалок (AccessibleName, AccessibleDescription).
Совет: делайте тестовые сценарии с реальными пользователями и автоматическими проверками контраста.
Альтернативные подходы и расширяемость
- Темы как набор ресурсов (ResourceDictionary): хранить цвета и стили в ресурсах и менять их программно.
- Создание классической темы через интерфейс ITheme с методами ApplyTo(Form form) — удобно для расширяемости и unit-тестов.
- Использование библиотек UI-стилей и фреймворков (например, MetroFramework, MaterialSkin) для более богатых тем и готовых компонентов.
Когда этот подход может не подойти:
- если в приложении много кастомных отрисованных контролов, их придётся адаптировать вручную;
- если нужна динамическая смена стилей для каждой части приложения отдельно — лучше внедрить централизованный менеджер тем.
Совместимость и миграция
Таблица совместимости по платформам .NET:
| Платформа | Примечания |
|---|
| .NET Framework (4.6–4.8) | Полная поддержка WinForms; код из статьи работает без изменений. | .NET Core 3.1 | Поддержка WinForms доступна; возможны отличия в визуальном оформлении в зависимости от среды. | .NET 5/6/7 | Поддержка WinForms; рекомендуется тестировать поведение цветов и DPI.
При миграции из .NET Framework в .NET Core/.NET 5+ проверьте:
- корректность ссылок на System.Drawing и Color;
- поведение DPI и масштабирования;
- дескрипторы конфигурации Properties.Settings (иногда надо мигрировать вручную).
Чек-листы по ролям
Разработчик:
- создать словари тем и функции применения цветов;
- обработать сохранение/загрузку выбранной темы;
- обеспечить переключение с клавиатуры;
- покрыть код простыми unit-тестами (при наличии абстракции ITheme).
Дизайнер:
- подготовить наборы цветов с учётом контраста;
- удостовериться, что иконки/изображения выглядят в каждой теме;
- предоставить переменные для состояний hover/active/disabled.
QA:
- проверить все темы на разных разрешениях и масштабах DPI;
- проверить доступность клавиатурой и через скринридеры;
- тестировать сохранение темы между запусками.
Тесты и критерии приёмки
Критерии приёмки:
- по умолчанию приложение открывается в Light теме;
- при выборе темы цвета всех основных элементов изменяются корректно;
- выбранная тема сохраняется и восстанавливается при следующем запуске;
- контраст текста не падает ниже приемлемого уровня для основных элементов.
Минимальные тестовые случаи:
- Запуск приложения — проверка Light по умолчанию.
- Нажатие Themes — кнопки тем отображаются.
- Выбор Nature — все цвета соответствуют словарю Nature.
- Выбор Dark — проверка белого текста на тёмном фоне.
- Перезапуск приложения — тема сохраняется.
Ментальные модели и эвристики при выборе цветов
- Слойность: разделяйте Primary (главные элементы), Secondary (контекстные элементы), Tertiary (фон) и Text (типографика).
- Контраст: текст должен быть читаемым на любом фоне; используйте инструменты для расчёта контраста.
- Универсальность: выбирайте цвета, которые не ломают фирменную палитру при применении к иконкам и фотографиям.
Быстрый план внедрения (SOP)
- Спроектировать словари тем и перечисление ThemeColor.
- Добавить UI-кнопки для переключения и скрыть их по умолчанию.
- Реализовать ChangeTheme и ChangeTextColor.
- Добавить сохранение выбранной темы (Settings/JSON).
- Провести тесты на разных масштабах и с клавиатурой.
- Подготовить дизайн-проверку контраста.
Пример расширения: интерфейс ITheme и менеджер тем
Идея: абстрагировать тему через интерфейс, чтобы упростить тестирование и добавление новых тем.
public interface ITheme
{
string Name { get; }
Dictionary Palette { get; }
void ApplyTo(Form form);
} Менеджер тем может хранить список ITheme и обеспечивать уведомление о смене темы через событие.
Decision flowchart (Mermaid)
flowchart TD
A[Нужна тема в приложении?] --> B{Требования}
B -->|Простая| C[Использовать словари Color]
B -->|Расширяемая| D[Реализовать ITheme и ThemeManager]
B -->|Много кастомных контролов| E[Рефакторинг контролов и ручная адаптация]
C --> F[Сохранение через Settings или JSON]
D --> F
E --> F(Flowchart показывает простой выбор архитектуры для темы: словари для простых случаев, интерфейс для масштабируемых решений.)
Шаблон словаря тем (чек-лист для дизайнеров)
| Поле | Описание |
|---|---|
| Primary | Цвет главных кнопок/панелей |
| Secondary | Цвет вторичных кнопок/карточек |
| Tertiary | Фон формы |
| Text | Цвет основного текста |
Используйте этот шаблон для каждой темы и сохраняйте в одном месте (файл JSON, ресурс или класс).
Блок примечаний и ограничений
Important: Если ваше приложение содержит сторонние или кастомные контролы, убедитесь, что они поддерживают изменение BackColor и ForeColor. Не все сторонние компоненты корректно реагируют на программную смену цветов.
Note: В этой инструкции использованы встроенные цвета Visual Studio (Color.*). При желании замените их на свои HEX-значения через ColorTranslator.FromHtml(“#rrggbb”).
Краткое резюме
- Реализуйте темы через перечисление ThemeColor и Dictionary
для каждой темы. - Создайте функции ChangeTheme и ChangeTextColor для централизованного применения цветов.
- Сохраняйте выбор пользователя через Settings или JSON, и обеспечьте доступность и адекватный контраст.
- Для масштабируемости используйте интерфейсы и менеджеры тем.
1-line glossary:
- ThemeColor — перечисление слоёв цвета: Primary, Secondary, Tertiary, Text.
Если нужно, я могу подготовить ZIP с примером проекта, JSON-шаблонами тем и unit-тестами для менеджера тем.
Похожие материалы
Сброс настроек поиска Windows до стандартных
Как посмотреть старые снимки Street View
Воспроизведение и исследование звука в JES
Как сохранить веб‑страницу для офлайн‑чтения
Отключить кнопку «Параметры вставки» в Excel