AlfaToad.CustomControls
Библиотека пользовательских контролов для Windows Forms (.NET Framework 4.8 и .NET 8).
Пакет: NuGet — AlfaToad.CustomControls
Компоненты
| Контрол |
Назначение |
ScrollableDetailPanel |
Прокручиваемый список строк с фото, полями товара и скидкой |
ScrollableOrderPanel |
Прокручиваемый список строк заказа |
BoundDataGridView |
Таблица с привязкой к DataSet и подсветкой столбца |
Установка
dotnet add package AlfaToad.CustomControls --version 4.2.1
<PackageReference Include="AlfaToad.CustomControls" Version="4.2.1" />
ScrollableDetailPanel — настраиваемые свойства
Поля данных (имена столбцов в DataTable)
| Поле на экране |
Свойство |
Значение по умолчанию |
| Фото |
ImageField |
Фото |
| Категория |
CategoryField |
Категория товара |
| Наименование |
NameField |
Наименование товара |
| Описание |
DescriptionField |
Описание товара |
| Производитель |
ManufacturerField |
Производитель |
| Поставщик |
SupplierField |
Поставщик |
| Цена |
PriceField |
Цена |
| Единица измерения |
UnitField |
Единица измерения |
| Количество на складе |
StockField |
Кол-во на складе |
| Скидка |
DiscountField |
Действующая скидка |
Подписи на экране
| Подпись |
Свойство |
Значение по умолчанию |
| Описание |
DescriptionCaption |
Описание товара: |
| Производитель |
ManufacturerCaption |
Производитель: |
| Поставщик |
SupplierCaption |
Поставщик: |
| Цена |
PriceCaption |
Цена: |
| Единица измерения |
UnitCaption |
Единица измерения: |
| Количество на складе |
StockCaption |
Количество на складе: |
| Заголовок столбца скидки |
DiscountCaption |
Действующая скидка |
Изображения
| Свойство |
Описание |
NoImage |
Картинка при отсутствии файла или пустом поле. По умолчанию не задана — область фото остаётся пустой. |
ImagesFolder |
Папка, из которой загружаются файлы по имени из ImageField. |
Размеры строки
| Свойство |
По умолчанию |
RowHeight |
160 |
ImageColumnWidth |
120 |
DiscountColumnWidth |
110 |
RowSpacing |
8 (зона сепаратора между строками) |
OutOfStockRowColor |
голубой #ADD8E6 (количество на складе ≤ 0) |
RowSeparatorColor |
Silver (линия между записями) |
ImageLeftPadding |
8 (отступ фото от левого края) |
DataSource |
источник данных в дизайнере (DataSet / DataTable / BindingSource) |
DataMember |
имя таблицы в DataSet |
ScrollableOrderPanel — настраиваемые свойства
Поля данных
| Поле на экране |
Свойство |
Значение по умолчанию |
| Артикул заказа |
ArticleField |
Артикул заказа |
| Статус |
StatusField |
Статус заказа |
| Адрес пункта выдачи |
AddressField |
Адрес пункта выдачи |
| Дата заказа |
OrderDateField |
Дата заказа |
| Дата доставки |
DeliveryDateField |
Дата доставки |
Подписи на экране
| Подпись |
Свойство |
Значение по умолчанию |
| Артикул |
ArticleCaption |
Артикул заказа |
| Статус |
StatusCaption |
Статус заказа |
| Адрес |
AddressCaption |
Адрес пункта выдачи |
| Дата заказа |
OrderDateCaption |
Дата заказа |
| Дата доставки |
DeliveryDateCaption |
Дата доставки |
Размеры строки
| Свойство |
По умолчанию |
RowHeight |
110 |
DeliveryColumnWidth |
140 |
Добавление, редактирование и удаление (CRUD)
Оба списка (ScrollableDetailPanel, ScrollableOrderPanel) поддерживают кнопки Добавить / Изменить / Удалить и клик по строке для редактирования. Формы добавления и редактирования открывает ваше приложение в обработчиках событий.
Права по ролям
| Свойство |
Описание |
По умолчанию |
AllowAdd |
Показать кнопку добавления |
false |
AllowEdit |
Показать кнопку изменения и открывать редактирование по клику на строку |
false |
AllowDelete |
Показать кнопку удаления |
false |
IsEditSessionActive |
Блокирует второе окно редактирования, пока true |
false |
ValidateBeforeDelete |
Функция проверки перед удалением; верните текст ошибки для запрета |
null |
При false соответствующая кнопка скрывается. При попытке действия без прав показывается MessageBox с пояснением.
События
| Событие |
Когда срабатывает |
RecordAddRequested |
Нажата кнопка «Добавить» |
RecordEditRequested |
Клик по строке или кнопка «Изменить» |
RecordDeleting |
Перед удалением; установите Cancel = true и DenyReason для отмены |
RecordDeleted |
Строка удалена из DataTable (вызовите Update в БД) |
После закрытия формы редактирования вызовите EndEditSession(), чтобы снова можно было открыть другое окно.
Пример для администратора (товары)
var panel = new ScrollableDetailPanel { Dock = DockStyle.Fill };
panel.AllowAdd = panel.AllowEdit = panel.AllowDelete = userIsAdmin;
panel.ShowQueryPanel = userIsManagerOrAdmin;
panel.ValidateBeforeDelete = row =>
{
var article = row["Артикул"]?.ToString();
return ProductExistsInOrders(article)
? "Товар присутствует в заказе и не может быть удалён."
: null;
};
panel.RecordAddRequested += (_, __) => OpenProductForm(null);
panel.RecordEditRequested += (_, e) => OpenProductForm(e.Row);
panel.RecordDeleted += (_, e) =>
{
tableAdapter.Update(dataSet);
panel.RefreshRows();
};
Пример для менеджера (заказы — только просмотр)
ordersPanel.AllowAdd = ordersPanel.AllowEdit = ordersPanel.AllowDelete = userIsAdmin;
// менеджер: все три свойства false — кнопки скрыты, клик только выделяет строку
Обработка исключительных случаев
Библиотека показывает MessageBox в ситуациях:
- действие недоступно для роли;
- не выбрана запись для изменения или удаления;
- уже открыто окно редактирования (
IsEditSessionActive);
ValidateBeforeDelete или RecordDeleting запретили удаление;
- ошибка при удалении строки из
DataTable.
BoundDataGridView — настраиваемые свойства
| Свойство |
Описание |
По умолчанию |
HighlightColumnName |
Столбец для подсветки ячеек |
Действующая скидка |
ApplyRowHighlight |
Включить подсветку |
true |
Appearance |
Палитра цветов |
см. ниже |
AppearancePalette — настройка цветов и шрифтов
Общий объект Appearance доступен у всех трёх контролов.
Редактор в дизайнере Visual Studio
- Выберите контрол на форме.
- В окне Свойства найдите
Appearance.
- Разверните узел — каждый цвет редактируется стандартным выбором цвета.
- Нажмите «…» рядом с
Appearance — откроется окно «Палитра оформления» со всеми цветами, шрифтами и порогом скидки; кнопка «По умолчанию» сбрасывает значения.
Изменения сразу применяются к контролу на форме в режиме дизайна.
| Свойство |
Назначение |
По умолчанию |
MainBackground |
Основной фон |
#FFFFFF |
SectionBackground |
Фон заголовков таблицы |
#F5DEB3 |
AccentBackground |
Линии сетки таблицы |
#DEB887 |
HighlightBackground |
Подсветка скидки > порога |
#FFDEAD |
SelectedRowBackground |
Фон выбранной строки списка |
#E8EEF8 |
PrimaryText |
Цвет текста |
Black |
SeparatorText |
Цвет разделителя \| |
Gray |
BorderColor |
Рамки строк списка |
Black |
RowSeparatorColor |
Линия между записями |
Silver |
OutOfStockBackground |
Фон строки без товара на складе |
#ADD8E6 |
StrikethroughPriceColor |
Перечёркнутая цена при скидке |
Red |
FinalPriceColor |
Итоговая цена при скидке |
Black |
FontFamily |
Шрифт |
Arial |
BaseFontSize |
Размер обычного текста |
9 |
TitleFontSize |
Размер заголовка строки |
10 |
HighlightThresholdPercent |
Порог подсветки скидки (%) |
17 |
Алгоритм расчёта скидки
Назначение
Вычисление итоговой цены товара с учётом действующей скидки и определение способа отображения цены на экране (ScrollableDetailPanel, BoundDataGridView).
Входные данные
| Обозначение |
Описание |
Источник |
Ц |
Исходная цена товара |
столбец Цена (PriceField) |
С |
Действующая скидка, % |
столбец Действующая скидка (DiscountField) |
П |
Порог подсветки скидки, % |
Appearance.HighlightThresholdPercent (по умолчанию 17) |
Выходные данные
| Обозначение |
Описание |
Ц_итог |
Итоговая цена (руб., 2 знака после запятой) |
Режим |
Способ отображения: «только цена» или «перечёркнутая + итоговая» |
Подсветка |
Применять ли фон подсветки для ячейки/блока скидки |
Формула
Если Ц > 0 и С > 0:
Ц_итог = ОКРУГЛ( Ц × (1 − С / 100) ; 2 )
Иначе:
Ц_итог = Ц
Округление — математическое, до 2 знаков (MidpointRounding.AwayFromZero).
Обозначения блок-схемы (ГОСТ 19.701-90)
| Символ |
Наименование по ГОСТ |
Назначение в схеме |
| ◯ (овал) |
Терминатор |
Начало / конец алгоритма |
| ▱ (параллелограмм) |
Данные |
Ввод исходных значений Ц, С, П |
| ▭ (прямоугольник) |
Процесс |
Вычисление, присваивание, форматирование |
| ◇ (ромб) |
Решение |
Проверка условия (да / нет) |
Блок-схема алгоритма (ГОСТ 19.701-90)
В диаграмме Mermaid: овал — терминатор, параллелограмм — ввод/вывод, прямоугольник — процесс, ромб — решение.
flowchart TD
A([НАЧАЛО]) --> B[/Ввод: Ц — цена, С — скидка %, П — порог подсветки/]
B --> C{Ц ≤ 0<br/>ИЛИ<br/>С ≤ 0?}
C -- ДА --> D[Ц_итог := Ц]
C -- НЕТ --> E[Ц_итог := ОКРУГЛ Ц × 1 − С / 100 ; 2]
D --> F{С > 0<br/>И<br/>Ц > 0?}
E --> F
F -- ДА --> G[Режим := перечёркнутая Ц + итоговая Ц_итог]
F -- НЕТ --> H[Режим := отображать только Ц]
G --> I{С > П?}
H --> I
I -- ДА --> J[Подсветка := включить HighlightBackground]
I -- НЕТ --> K[Подсветка := обычный фон]
J --> L[/Вывод: Ц_итог, Режим, Подсветка/]
K --> L
L --> M([КОНЕЦ])
Блок-схема в текстовом виде (ГОСТ 19.701-90)
┌─────────────────────────────────┐
│ НАЧАЛО │ ← терминатор (овал)
└───────────────┬─────────────────┘
▼
╔═════════════════════════════════╗
║ Ввод: Ц, С, П ║ ← данные (параллелограмм)
╚═════════════════════════════════╝
▼
┌───────────────┐
│ Ц ≤ 0 ИЛИ │
│ С ≤ 0 ? │ ← решение (ромб)
└───┬───────┬───┘
ДА │ │ НЕТ
▼ ▼
┌───────────────┐ ┌───────────────────────────────┐
│ Ц_итог := Ц │ │ Ц_итог := ОКРУГЛ(Ц×(1−С/100);2)│ ← процесс
└───────┬───────┘ └───────────────┬───────────────┘
└───────────┬───────────────┘
▼
┌───────────────┐
│ С > 0 И │
│ Ц > 0 ? │ ← решение (ромб)
└───┬───────┬───┘
ДА │ │ НЕТ
▼ ▼
┌───────────────────────┐ ┌─────────────────────────┐
│ Режим := перечёркнутая│ │ Режим := только Ц │
│ цена + Ц_итог │ └───────────┬─────────────┘
└───────────┬───────────┘ │
└───────────┬───────────────┘
▼
┌───────────────┐
│ С > П ? │ ← решение (ромб)
└───┬───────┬───┘
ДА │ │ НЕТ
▼ ▼
┌───────────────────────┐ ┌─────────────────────────┐
│ Подсветка := ДА │ │ Подсветка := НЕТ │
└───────────┬───────────┘ └───────────┬─────────────┘
└───────────┬───────────────┘
▼
╔═════════════════════════════════╗
║ Вывод: Ц_итог, Режим, ║ ← данные (параллелограмм)
║ Подсветка ║
╚═════════════════════════════════╝
▼
┌─────────────────────────────────┐
│ КОНЕЦ │ ← терминатор (овал)
└─────────────────────────────────┘
Таблица решений (ветвления)
| № |
Условие |
Действие |
| 1 |
Ц ≤ 0 или С ≤ 0 |
Ц_итог = Ц (скидка не применяется) |
| 2 |
Ц > 0 и С > 0 |
Ц_итог = ОКРУГЛ(Ц × (1 − С/100); 2) |
| 3 |
С > 0 и Ц > 0 |
Показать старую цену красной перечёркнутой и рядом итоговую чёрную |
| 4 |
иначе |
Показать только Ц |
| 5 |
С > П |
Подсветить ячейку/блок скидки цветом HighlightBackground |
| 6 |
С ≤ П |
Обычный фон |
Примеры расчёта
| Ц, руб. |
С, % |
Ц_итог, руб. |
Отображение |
| 1000 |
0 |
1000,00 |
1000,00 |
| 1000 |
15 |
850,00 |
1000,00 850,00 |
| 1000 |
20 |
800,00 |
1000,00 800,00 + подсветка (20 > 17) |
| 0 |
10 |
0,00 |
0,00 (скидка не считается) |
| 500 |
−5 |
500,00 |
500,00 (отрицательная скидка игнорируется) |
Соответствие коду библиотеки
| Шаг алгоритма |
Реализация |
Расчёт Ц_итог |
ScrollableDetailPanel.CalculateFinalPrice |
| Отображение цены в строке |
ProductListRow → PriceLinePanel |
| Подсветка скидки в списке |
Appearance.HighlightThresholdPercent |
| Подсветка в таблице |
BoundDataGridView.OnCellFormatting |
Примеры использования
Список товаров с привязкой к DataSet
using System.Data;
using System.Drawing;
using AlfaToad.CustomControls.Controls;
using AlfaToad.CustomControls.Theme;
var panel = new ScrollableDetailPanel { Dock = DockStyle.Fill };
panel.NoImage = Image.FromFile(@"C:\images\no-photo.png");
panel.ImagesFolder = @"C:\images\products";
panel.CategoryField = "CategoryName";
panel.NameField = "ProductName";
panel.DiscountField = "DiscountPercent";
panel.Appearance = new AppearancePalette
{
MainBackground = Color.White,
HighlightBackground = Color.FromArgb(255, 222, 173),
BorderColor = Color.Black,
HighlightThresholdPercent = 17m
};
DataSet dataSet = LoadProducts();
panel.BindDataSet(dataSet, "Products");
Поведение цены: если Действующая скидка > 0, основная цена отображается красной и перечёркнутой, рядом — итоговая цена чёрным (цена × (1 − скидка/100)).
Нет на складе: если Кол-во на складе ≤ 0, вся строка подсвечивается цветом OutOfStockRowColor.
Поиск, фильтрация и сортировка (ScrollableDetailPanel)
Панель включена по умолчанию (ShowQueryPanel = true). Всё применяется в реальном времени без кнопки «Найти».
| Элемент |
Поведение |
| Поиск |
Подстрока по всем текстовым столбцам (string), одновременно по нескольким полям (логика OR) |
| Сортировка |
Цена ↑/↓, кол-во на складе ↑/↓; выбор сохраняется при поиске и фильтре |
| Поставщик |
Выпадающий список; первый пункт Все поставщики сбрасывает фильтр |
| Свойство |
Описание |
ShowQueryPanel |
Показать панель (по умолчанию true) |
AllSuppliersCaption |
Текст сброса фильтра (Все поставщики) |
SearchFields |
Ограничить столбцы поиска (пусто = все текстовые) |
SupplierField |
Столбец для фильтра поставщика (Поставщик) |
PriceField / StockField |
Столбцы для сортировки |
panel.ShowQueryPanel = userIsManagerOrAdmin;
После TableAdapter.Fill(...) список и список поставщиков обновляются автоматически.
Список заказов
var orders = new ScrollableOrderPanel { Dock = DockStyle.Fill };
orders.Appearance = AppearancePalette.CreateDefault();
orders.Appearance.AccentBackground = Color.BurlyWood;
orders.BindDataSet(dataSet, "Orders");
Таблица с подсветкой скидки
var grid = new BoundDataGridView { Dock = DockStyle.Fill };
grid.Appearance = AppearancePalette.CreateDefault();
grid.HighlightColumnName = "Действующая скидка";
grid.ApplyDefaultStyle();
grid.BindDataSet(dataSet, "Products");
Смена цветов после создания
panel.Appearance.MainBackground = ColorTranslator.FromHtml("#FAFAFA");
panel.Appearance.HighlightBackground = ColorTranslator.FromHtml("#FFDEAD");
panel.Appearance.BorderColor = Color.DimGray;
panel.ApplyAppearance();