# AlfaToad.DataSetCRUDS NuGet-пакет готовых **форм добавления и редактирования** для контролов [AlfaToad.CustomControls](https://www.nuget.org/packages/AlfaToad.CustomControls). Достаточно указать `DataSet`, имя таблицы и делегат сохранения — CRUD для `ScrollableDetailPanel` и `ScrollableOrderPanel` работает автоматически. ## Возможности | Компонент | Назначение | |-----------|------------| | `DetailRecordEditForm` | Модальная форма добавления/редактирования записи каталога | | `OrderRecordEditForm` | Модальная форма добавления/редактирования записи заказа | | `DetailPanelCrudBinder` | Подключает `ScrollableDetailPanel` к форме и сохранению | | `OrderPanelCrudBinder` | Подключает `ScrollableOrderPanel` к форме и сохранению | | `DataSetCrudHost` | Единая точка настройки обеих панелей | | `DetailRecordFieldMap` | Сопоставление имён столбцов каталога | | `OrderRecordFieldMap` | Сопоставление имён столбцов заказов | | `DataSetCrudOptions` | Оформление, папка изображений, ресайз фото, ConnectionString, ClientUsersTable, валидация удаления, сохранение | | `AppDialog` | Единые окна: ошибка / предупреждение / информация / подтверждение | | `ViewDataTablePersistence` | **Insert/Update/Delete для view без TableAdapter.Update** | | `ViewPersistenceProfile` | Профиль SQL: `NormalizedCatalogView`, `NormalizedOrdersView` | ### Формы редактирования - Разметка и код в **одном файле** (без отдельного `Designer.cs`) - Палитра `AppearancePalette` из CustomControls - Валидация обязательных полей и диапазонов (цена, скидка, количество) - Предпросмотр изображения из `ImagesFolder` - ComboBox `DropDownList` с placeholder «— выберите —» для категории, производителя, поставщика, единицы измерения, статуса и клиента заказа - NumericUpDown для цены, количества и скидки - Валидация: цена ≥ 0 и не более 2 знаков после запятой; количество ≥ 0; скидка 0–100 - Кнопки «Обзор…» и «Очистить» — выбор и удаление изображения - Автономер заказа: MAX + 1 при добавлении, readonly при редактировании ### Биндеры - Автоматическая подписка на `RecordAddRequested`, `RecordEditRequested`, `RecordDeleted` - Вызов `EndEditSession()` после закрытия формы - Обновление списка через `RefreshRows()` после сохранения - Обработка ошибок сохранения в БД через `PersistChanges` ## Установка ```bash dotnet add package AlfaToad.DataSetCRUDS --version 1.0.5 ``` ```xml ``` Зависимость: `AlfaToad.CustomControls` ≥ 4.2.4 ## Сохранение view без TableAdapter.Update (глобальное решение) Visual Studio **не генерирует** `Update()` для представлений (`DbObjectType="View"`). Пакет решает это на уровне NuGet: достаточно создать Typed DataSet с **одним view** и использовать только `Fill()`. ```csharp using AlfaToad.DataSetCRUDS.Configuration; using AlfaToad.DataSetCRUDS.Persistence; // DataSet с view каталога — только Fill(), Update() в TableAdapter не нужен // ViewPersistenceProfile.NormalizedCatalogView — INSERT/UPDATE/DELETE в Product + справочники var persistCatalog = ViewDataTablePersistence.CreatePersistAction( dataSet, "vCatalog", connectionString, ViewPersistenceProfile.NormalizedCatalogView, settings.DetailFields); DetailPanelCrudBinder.WireUp(panel, dataSet, "vCatalog", persistCatalog, settings.DetailFields); ``` Для заказов: ```csharp // ViewPersistenceProfile.NormalizedOrdersView — INSERT/UPDATE/DELETE в dbo.[Order] + OrderItem; // ClientUserId передаётся параметром @ClientUserId (поиск по FullName в dbo.[User]) var persistOrders = ViewDataTablePersistence.CreatePersistAction( dataSet, "vOrders", connectionString, ViewPersistenceProfile.NormalizedOrdersView, orderFields: settings.OrderFields); ``` `ViewDataTablePersistence.ApplyChanges` выполняет INSERT/UPDATE/DELETE в таблицы `Product` / `[Order]` / `OrderItem`, переводя текстовые поля view (Поставщик, Категория, Статус…) в ID справочников. С **AlfaToad.DefaultScreens** — одна строка: ```csharp var data = new ApplicationDataContext { /* DataSet, Fill-делегаты */ } .WithViewDataPersistence(connectionString, settings); ``` **Ручное редактирование `.xsd` не требуется.** ## Пример `Program.cs` (полный сценарий с AlfaToad.DefaultScreens) См. также [AlfaToad.DefaultScreens README](https://www.nuget.org/packages/AlfaToad.DefaultScreens) — ниже тот же шаблон с комментариями к полям пакета: ```csharp using System; using System.Drawing; using System.Windows.Forms; using AlfaToad.CustomControls.Theme; using AlfaToad.DataSetCRUDS.Configuration; using AlfaToad.DefaultScreens.Configuration; using AlfaToad.DefaultScreens.Navigation; using YourApp; // ваш Typed DataSet (MirIgryshkiDataSet) using YourApp.YourDataSetTableAdapters; namespace YourApp { internal static class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); // ═══════════════════════════════════════════════════════════════ // ШАГ 1. DataSet и TableAdapter-ы // Для view достаточно Fill() — Insert/Update/Delete делает пакет // ═══════════════════════════════════════════════════════════════ var dataSet = new MirIgryshkiDataSet(); // замените на ваш Typed DataSet var tovarTableAdapter = new vCatalogTableAdapter(); var ordersTableAdapter = new vOrdersTableAdapter(); var usersTableAdapter = new vw_UsersLoginTableAdapter(); var connectionString = "Server=YOUR_SERVER;Database=YOUR_DATABASE;Trusted_Connection=True;"; // ═══════════════════════════════════════════════════════════════ // ШАГ 2. ApplicationScreenSettings // ═══════════════════════════════════════════════════════════════ var settings = ApplicationScreenSettings.CreateDefault(); settings.BackMenuText = "Назад"; settings.ImagesFolder = @"C:\Path\To\Your\Resources\import"; settings.ImageResizeWidth = 300; settings.ImageResizeHeight = 200; settings.ApplyResourcePaths(); // CreateCatalogCrudOptions() задаёт AutoResizeImages, DeleteImageFileOnRemove, // DeleteOldImageOnReplace, ConnectionString, ImageResizeWidth/Height // CreateOrderCrudOptions(clientUsers, connectionString) — ClientUsersTable для ComboBox клиента settings.DetailFields = new DetailRecordFieldMap { /* столбцы каталога */ }; settings.OrderFields = new OrderRecordFieldMap { ClientNameField = "ФИО авторизированного клиента" // ComboBox на форме заказа }; // ═══════════════════════════════════════════════════════════════ // ШАГ 6. ApplicationDataContext + WithViewDataPersistence // ViewPersistenceProfile.NormalizedCatalogView / NormalizedOrdersView // ═══════════════════════════════════════════════════════════════ var data = new ApplicationDataContext { CatalogDataSet = dataSet, CatalogTableName = "vCatalog", OrdersDataSet = dataSet, OrdersTableName = "vOrders", UsersDataSet = dataSet, UsersTableName = "vw_UsersLogin", LoadUsers = () => usersTableAdapter.Fill(dataSet.vw_UsersLogin), LoadCatalog = () => tovarTableAdapter.Fill(dataSet.vCatalog), LoadOrders = () => ordersTableAdapter.Fill(dataSet.vOrders), } .WithViewDataPersistence(connectionString, settings); new ApplicationNavigator(settings, data).Start(); } } } ``` ## Быстрый старт — каталог ```csharp using AlfaToad.CustomControls.Controls; using AlfaToad.DataSetCRUDS.Integration; var panel = new ScrollableDetailPanel { Dock = DockStyle.Fill }; panel.AllowAdd = panel.AllowEdit = panel.AllowDelete = true; DetailPanelCrudBinder.WireUp( panel, catalogDataSet, "CatalogItems", () => catalogTableAdapter.Update(catalogDataSet), options: new DataSetCrudOptions { ImagesFolder = @"C:\Path\To\Your\Resources\images", AutoResizeImages = true, DeleteImageFileOnRemove = true, DeleteOldImageOnReplace = true, ConnectionString = connectionString, ImageResizeWidth = 300, ImageResizeHeight = 200, PersistChanges = () => catalogTableAdapter.Update(catalogDataSet) }); ``` ## Быстрый старт — заказы ```csharp using AlfaToad.DataSetCRUDS.Integration; OrderPanelCrudBinder.WireUp( ordersPanel, ordersDataSet, "Orders", () => ordersTableAdapter.Update(ordersDataSet), orderFields, new DataSetCrudOptions { ClientUsersTable = clientUsersTable, // UserId + FullName, роль = Client DefaultClientName = "Иванов Иван Иванович", ConnectionString = connectionString }); ``` ## Обе панели через DataSetCrudHost ```csharp new DataSetCrudHost() .WireDetailPanel(detailPanel, catalogDataSet, "CatalogItems", () => taCatalog.Update(catalogDataSet)) .WireOrderPanel(orderPanel, ordersDataSet, "Orders", () => taOrders.Update(ordersDataSet)); ``` ## Настройка имён столбцов Если ваши столбцы в БД имеют другие имена, задайте карту полей: ```csharp var fields = new DetailRecordFieldMap { PrimaryKeyField = "Article", NameField = "Наименование товара", CategoryField = "Категория товара", ImageField = "Фото", PriceField = "Цена", StockField = "Кол-во на складе", DiscountField = "Действующая скидка" }; DetailPanelCrudBinder.WireUp(panel, dataSet, "vCatalog", persist, fields); ``` ## Валидация перед сохранением ```csharp var options = new DataSetCrudOptions { ValidateBeforeSave = row => { var key = row["RecordId"]?.ToString(); return IsUsedInOrders(key) ? "Запись используется в связанных данных." : null; }, PersistChanges = () => tableAdapter.Update(dataSet) }; ``` ## Пошаговая настройка (без WireUp) ```csharp var binder = new DetailPanelCrudBinder(panel) .Configure(dataSet, "CatalogItems", () => ta.Update(dataSet)) .Attach(); // При закрытии формы: binder.Detach(); ``` ## Платформы - .NET Framework 4.8 - .NET 8 (Windows)