cs_edit_product2.md 22 KB

Предыдущая лекция Следующая лекция
Подсветка элементов по условию. Дополнительные выборки.Массовая смена цены продукции. Содержание Вывод списка материалов продукта. CRUD материалов продукта

Добавление/редактирование продукции

Необходимо добавить возможность редактирования данных существующей продукции, а также добавление новой продукции в новом окне - форме для добавления/редактирования продукции.

Переходы на данное окно должны быть реализованы из главной формы списка: для редактирования - при нажатии на конкретный элемент, для добавления - при нажатии кнопки “Добавить продукцию”.

На форме должны быть предусмотрены следующие поля: артикул, наименование, тип продукта (выпадающий список), изображение, количество человек для производства, номер производственного цеха, минимальная стоимость для агента и подробное описание (с возможностью многострочного ввода).

Также необходимо реализовать вывод списка материалов, используемых при производстве продукции, с указанием количества. В список можно добавлять новые позиции и удалять существующие. При добавлении материалы должны выбираться из выпадающего списка с возможностью поиска по наименованию.

При открытии формы для редактирования все поля выбранного объекта должны быть подгружены в соответствующие поля из базы данных, а таблица заполнена актуальными значениями.

Стоимость продукции может включать сотые части, а также не может быть отрицательной. Система должна проверять существование продукта с введенным артикулом и не давать использовать один артикул для нескольких продуктов.

Пользователь может добавить/заменить изображение у продукции.

Для того чтобы администратор случайно не изменял несколько продуктов, предусмотрите невозможность открытия более одного окна редактирования.

В окне редактирования продукта должна присутствовать кнопка “Удалить”, которая удаляет продукт из базы данных. При этом должны соблюдаться следующие условия. Если у продукта есть информация о материалах, используемых при его производстве, или история изменения цен, то эта информация должна быть удалена вместе с продуктом. Но если у продукта есть информация о его продажах агентами, то удаление продукта из базы данных должно быть запрещено. После удаления продукта система должна сразу вернуть пользователя обратно к списку продукции.

После редактирования/добавления/удаления продукции данные в окне списка продукции должны быть обновлены.

Критерий Баллы
Реализован переход на окно добавления 0.1
Реализован переход на окно редактирования выбранного объекта 0.2
Присутствуют все поля для заполнения 0.5
При редактировании продукции в поля для ввода загружены данные из БД 0.3
Выбор типа продукта реализован в виде выпадающего списка со значениями из БД 0.3
Для ввода описания продукции предусмотрено многострочное поле для ввода 0.2
Реализован список используемых материалов для текущего продукта 0.3
В списке присутствует название материала и используемое количество 0.2
При редактировании продукции список материалов заполнен значениями из БД 0.2
В список можно добавлять новые позиции 0.3
Из списка можно удалять существующие позиции 0.2
При добавлении материалы выбираются из выпадающего списка со значениями из БД 0.3
В списке материалов реализована возможность поиска по наименованию 0.2
Список используемых материалов сохраняется в БД при добавлении 0.5
Список используемых материалов сохраняется в БД при редактировании 0.5
Стоимость продукции не может быть отрицательной 0.1
Стоимость продукции записывается только с точностью до сотых 0.2
Реализована проверка артикула на уникальность 0.3
Есть возможность выбрать изображение 0.2
Изображение продукции подгружается из БД при редактировании 0.2
Есть возможность заменить изображение 0.1
Данные при добавлении сохраняются в БД 0.5
Данные при редактировании изменяются в БД 0.5
Открывается только одно окно редактирования 0.1
Реализовано удаление выбранного продукта, у которого не заполнен список используемых материалов 0.2
Реализовано удаление продукта вместе с информацией об используемых материалах 0.5
Запрещено удаление продукта, по которому были выполнены продажи агентом 0.3
После удаления реализован автоматический переход обратно в список 0.1
После закрытия окна данные в таблице обновляются 0.3
Итого 7.9

Создание окна редактирования продукции

Для добавления и редактирования мы будем использовать одно и то же окно. Заголовок окна будем вычислять по наличию поля ID у продукции (у новой записи это поле равно 0)

  1. Создайте новое окно: EditProductWindow (в папке Windows - не забываем про логическую структуру проекта)

  2. В классе окна EditProductWindow добавьте свойство currentProduct, в котором будет храниться добавляемый/редактируемый экземпляр продукции:

    public Product currentProduct { get; set; }
    
  3. В разметке окна вставьте аттрибут Name

    <Window
        ...
        Name="root">
    
  4. В конструктор окна добавьте параметр типа Product, присвойте его ранее объявленному свойству. Задайте заголовок окна:

    public EditProductWindow(Product editProduct)
    {
        InitializeComponent();
        DataContext = this;
        currentProduct = editProduct;
        root.Title = currentProduct.ID == 0 ? "Новый продукт" : "Редактирование продукта";
    }
    
  5. В разметке окна редактирования продукции создайте сетку из трёх колонок: в первой у нас будет изображение, во второй редактируемые поля продукта, а в третей список материалов

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="auto"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="auto"/>
    </Grid.ColumnDefinitions>
    
  6. Во вторую колонку добавьте StackPanel с границами (чтобы визуальные компоненты не прилипали к границам окна) и в этом списке разместите редактируемые элементы

    На форме должны быть предусмотрены следующие поля: артикул, наименование, тип продукта (выпадающий список), изображение, количество человек для производства, номер производственного цеха, минимальная стоимость для агента и подробное описание

    <StackPanel
        Grid.Column="1" 
        Margin="5">
    
        <Label Content="Артикул"/>
        <TextBox
            Text="{Binding currentProduct.ArticleNumber}"/>
    
        <Label Content="Наименование продукта"/>
        <TextBox Text="{Binding currentProduct.Title}"/>
    
        ...
    
    </StackPanel>
    

    Обычные поля (числовые и строковые) наклепайте по шаблону сами, а я подробнее остановлюсь на полях: тип продукта, изображение и описание:

    • Выбор типа продукта из списка

      В классе окна объявляем свойство productTypeList - список типов продукции

      public List<ProductType> productTypeList { get; set; }
      

      И в конструкторе получаем его из БД:

      productTypeList = Globals.dataProvider.getProductTypes().ToList();
      

      В вёрстке окна редактирования продукции мы можем использовать выпадающий список, атрибут SelectedIndex которого позволяет отобразить текущее значение редактируемого элемента

      <ComboBox 
          ItemsSource="{Binding productTypeList}"
          SelectedIndex="{Binding selectedProductIndex}"/>
      

      Чтобы получить индекс (позицию в списке) текущего продукта нужно после получения списка типов продуктов добавить поиск:

      // в классе окна
      public int selectedProductIndex { get; set; } = -1;
      ...
      
      // в конструкторе окна
      if (currentProduct.ID > 0) {
          selectedProductIndex = productTypeList.FindIndex(pt =>  pt.ID == currentProduct.ProductTypeID);
      }
      
    • смена изображения продукции

      Вывод изображения производится как и в главном окне

      <Image
          Name="CurrentProductImage"
          Width="200" 
          Height="200"
          Source="{Binding currentProduct.ImageUri}" />
      

      А для смены изображения используем стандартный диалог открытия файлов, повесив его на кнопку Сменить картинку (кнопку добавьте сами в центральную колонку)

      Обработчик кнопки:

      private void ChangeImage_Click(object sender, RoutedEventArgs e)
      {
          OpenFileDialog GetImageDialog = new OpenFileDialog();
      
          // задаем фильтр для выбираемых файлов
          // до символа "|" идет произвольный текст, а после него шаблоны файлов разделенные точкой с запятой
          GetImageDialog.Filter = "Файлы изображений: (*.png, *.jpg)|*.png;*.jpg";
      
          // чтобы не искать по всему диску задаем начальный каталог
          GetImageDialog.InitialDirectory = Environment.CurrentDirectory;
          if (GetImageDialog.ShowDialog() == true)
          {
              // перед присвоением пути к картинке обрезаем начало строки, т.к. диалог возвращает полный путь
              currentProduct.Image = GetImageDialog.FileName.Substring(Environment.CurrentDirectory.Length);
      
              // обратите внимание, это другое окно и другой Invalidate, который реализуйте сами
              Invalidate();
          }
      }
      
    • Многострочное описание

      Тут просто - разрешаем переносы и задаем высоту элемента

      <Label Content="Описание продукта"/>
      <TextBox 
          AcceptsReturn="True"
          Height="200"
          Text="{Binding currentProduct.Description}"/>
      
  7. Сохранение введенных данных

    В разметку добавьте кнопку Сохранить и напишите обработчик

    private void SaveButton_Click(object sender, RoutedEventArgs e)
    {
        // перед сохранением мы должны выполнить несколько проверок, поэтому заворачиваем в исключение
        try
        {
            // могли изменить тип продукта - сохраняем ID
            if (ProductTypeComboBox.SelectedItem != null) 
            {
                currentProduct.ProductTypeID = ((ProductType)ProductTypeComboBox.SelectedItem).ID;
            } else
            {
                throw new Exception("Не выбран тип продукта");
            }
    
            // TODO проверить стоимость продукции (не более двух знаков после запятой и положительное число)
    
            // TODO проверка артикула (найти в базе запись с таким же артикулом и отличающимся ID)
    
            Globals.dataProvider.saveProduct(currentProduct);
            DialogResult = true;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }   
    }
    
  8. Реализация метода saveProduct

    public void saveProduct(Product product)
    {
        using (MySqlConnection db = new MySqlConnection(connectionString))
        {
            if (product.ID == 0)
            {
                // TODO новый продукт
            }
            else
            {
                // изменение существующего
                db.Execute("UPDATE Product SET Title=@Title, ProductTypeID=@ProductTypeID, " +
                    "ArticleNumber=@ArticleNumber, Description=@Description, " +
                    "Image=@Image, ProductionPersonCount=@ProductionPersonCount, " +
                    "ProductionWorkshopNumber=@ProductionWorkshopNumber, " +
                    "MinCostForAgent=@MinCostForAgent " +
                    "WHERE ID=@ID", product);
            }
        }
    }
    

Открытие окна редактирования для существующей и новой продукции

  • для редактирования существующей продукции в списке продукции реализуем обработчик двойного клика

    private void ProductListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        var product = (sender as ListBox).SelectedItem as Product;
    
        // в создаваемое окно передаем выбранный продукт
        var newEditWindow = new EditProductWindow(product);
        if ((bool)newEditWindow.ShowDialog())
        {
            Invalidate();
        }
    }
    
  • для создания нового продукта в разметке главного окна создайте кнопку "Добавить продукцию" (в левой или верхней панели) и в её обработчике создайте новый экземпляр продукта, а дальше то же, что и в предыдущем пункте

    var newEditWindow = new EditProductWindow(new Product());
    ...
    

Удаление продукции

В окне редактирования продукта должна присутствовать кнопка “Удалить”, которая удаляет продукт из базы данных. При этом должны соблюдаться следующие условия. Если у продукта есть информация о материалах, используемых при его производстве, или история изменения цен, то эта информация должна быть удалена вместе с продуктом. Но если у продукта есть информация о его продажах агентами, то удаление продукта из базы данных должно быть запрещено. После удаления продукта система должна сразу вернуть пользователя обратно к списку продукции.

Добавьте кнопку Удалить в среднюю колоку (рядом с кнопкой сохранить). Атрибут Visible привяжите к ID продукта (т.е. у нового продукта кнопки удалить быть не должно). Можно реализовать через Binding, но можно задать в конструкторе

И реализуйте обработчик клика по этой кнопке

private void DeleteProductButton_Click(object sender, RoutedEventArgs e)
{
    using (var context = new esmirnovContext())
    {
        try
        {
            // проверки, требуемые по ТЗ

            // TODO исключение, если есть продажи
            var saleCount = Globals.dataProvider.saleCount(currentProduct.ID);

            if (saleCount > 0)
                throw new Exception("Нельзя удалять продукт с продажами");

            // TODO удаление списка материалов продукта
            Globals.dataProvider.removeProductMaterial(currentProduct.ID);

            // TODO удаление истории изменения цен (если есть)

            // TODO удаление продукта
            Globals.dataProvider.removeProduct(currentProduct.ID);
            DialogResult = true;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);            
        }
    }
}

Задание

Самостоятельно реализовать код помеченный комментарием TODO


Предыдущая лекция Следующая лекция
Подсветка элементов по условию. Дополнительные выборки.Массовая смена цены продукции. Содержание Вывод списка материалов продукта. CRUD материалов продукта