| Подсветка элементов по условию. Дополнительные выборки.Массовая смена цены продукции. | Содержание | Создание, изменение продукции |
| Критерий | Баллы |
|---|---|
| Реализован переход на окно добавления | 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)
Создайте новое окно: EditWindow
В классе окна EditWindow добавьте свойство CurrentProduct, в котором будет храниться добавляемый/редактируемый экземпляр продукции:
public Product CurrentProduct { get; set; }
И геттер для названия окна:
public string WindowName {
get {
return CurrentProduct.ID == 0 ? "Новый продукт" : "Редактирование продукта";
}
}
В конструктор окна добавьте параметр типа Product и присвойте его ранее объявленному свойству:
public EditWindow(Product EditProduct)
{
InitializeComponent();
DataContext = this;
CurrentProduct = EditProduct;
}
В разметке окна вместо фиксированного названия вставьте привязку к свойству WindowName
<Window
...
Title="{Binding WindowName}">
В окне создайте сетку из трёх колонок: в первой у нас будет изображение, во второй редактируемые поля продукта, а в третей список материалов
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
Во вторую колонку добавьте StackPanel с границами (чтобы визуальные компоненты не прилипали к границам окна) и в этом списке разместите редактируемые элементы
На форме должны быть предусмотрены следующие поля: артикул, наименование, тип продукта (выпадающий список), изображение, количество человек для производства, номер производственного цеха, минимальная стоимость для агента и подробное описание
<StackPanel
Margin="5">
<Label Content="Артикул"/>
<TextBox Text="{Binding CurrentProduct.ArticleNumber}"/>
<Label Content="Наименование продукта"/>
<TextBox Text="{Binding CurrentService.Title}"/>
...
</StackPanel>
Обычные поля наклепайте по шаблону сами, а я подробнее остановлюсь на полях: тип продукта, изображение и описание:
Выбор типа продукта из списка
В классе окна объявляем свойство ProductTypes - список типов продукции
public IEnumerable<ProductType> ProductTypes { get; set; }
И в конструкторе получаем его из поставщика данных
ProductTypes = Globals.DataProvider.GetProductTypes();
В выпадающий список мы должны передать собственно список выбираемых объектов (ItemsSource) и текущий объект (SelectedItem) из этого списка. Но у нас в модели Product нет объекта ProductType, есть отдельные поля ProductTypeID и ProductTypeTitle. Изменим модель, вместо этих полей сделаем поле CurrentProductType, значение которого будем получать из списка типов по ID
В поставщик данных добавим переменную, в которой будет храниться список типов продукции:
private List<ProductType> ProductTypes = null;
Исправим метод GetProductTypes, чтобы он считывал список только в первый раз (поиск объектов происходит по хешу и нам важно, чтобы этот список был всегда один и тот же)
public IEnumerable<ProductType> GetProductTypes()
{
if (ProductTypes == null)
{
ProductTypes = new List<ProductType>();
...
В модели Product убираем свойства ProductTypeTitle, ProductTypeID и добавляем CurrentProductType (после этого пересоберите проект и исправьте возникшие ошибки)
// public string ProductTypeTitle { get; set; }
// public int ProductTypeID { get; set; }
public ProductType CurrentProductType { get; set; }
И в классе MySQLDataProvider переделайте получение типа продукта:
// NewProduct.ProductTypeID = Reader.GetInt32("ProductTypeID");
// NewProduct.ProductTypeTitle = Reader["ProductTypeTitle"].ToString();
NewProduct.CurrentProductType = GetProductType(Reader.GetInt32("ProductTypeID"));
Реализация метода GetProductType:
private ProductType GetProductType(int Id)
{
// тут заполнится список типов продукции, если он ещё пустой
GetProductTypes();
return ProductTypes.Find(pt => pt.ID == Id);
}
Теперь в верстке окна редактирования продукции мы можем использовать выпадающий список
<ComboBox
ItemsSource="{Binding ProductTypes}"
SelectedItem="{Binding CurrentProduct.CurrentProductType}"/>
смена изображения продукции
Вывод изображения производится как и в главном окне
<Image
Width="200"
Height="200"
Source="{Binding CurrentProduct.ImagePreview,TargetNullValue={StaticResource defaultImage}}" />
А для смены изображения используем стандартный диалог Windows, повесив его на кнопку Сменить картинку (кнопку добавьте сами в StackPanel)
Обработчик кнопки:
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="2cm"
Text="{Binding CurrentProduct.Description}"/>
Сохранение введенных данных
В разметку добавьте кнопку Сохранить и напишите обработчик
private void Button_Click(object sender, RoutedEventArgs e)
{
// вся работа с БД должна быть завернута в исключения
try
{
// сюда добавлять проверки
// метод SaveProduct реализуем ниже
Globals.DataProvider.SaveProduct(CurrentProduct);
DialogResult = true;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
В интерфейсе IDataProvider объявляем метод SaveProduct:
void SaveProduct(Product ChangedProduct);
И реализуем его в классе поставщика данных
public void SaveProduct(Product ChangedProduct)
{
Connection.Open();
try
{
if (ChangedProduct.ID == 0)
{
// новый продукт - добавляем запись
string Query = @"INSERT INTO Product
(Title,
ProductTypeID,
ArticleNumber,
Description,
Image,
ProductionPersonCount,
ProductionWorkshopNumber,
MinCostForAgent)
VALUES
(@Title,
@ProductTypeID,
@ArticleNumber,
@Description,
@Image,
@ProductionPersonCount,
@ProductionWorkshopNumber,
@MinCostForAgent)";
MySqlCommand Command = new MySqlCommand(Query, Connection);
Command.Parameters.AddWithValue("@Title", ChangedProduct.Title);
Command.Parameters.AddWithValue("@ProductTypeID", ChangedProduct.CurrentProductType.ID);
Command.Parameters.AddWithValue("@ArticleNumber", ChangedProduct.ArticleNumber);
Command.Parameters.AddWithValue("@Description", ChangedProduct.Description);
Command.Parameters.AddWithValue("@Image", ChangedProduct.Image);
Command.Parameters.AddWithValue("@ProductionPersonCount", ChangedProduct.ProductionPersonCount);
Command.Parameters.AddWithValue("@ProductionWorkshopNumber", ChangedProduct.ProductionWorkshopNumber);
Command.Parameters.AddWithValue("@MinCostForAgent", ChangedProduct.MinCostForAgent);
Command.ExecuteNonQuery();
}
else
{
// существующий продукт - изменяем запись
string Query = @"UPDATE Product
SET
Title = @Title,
ProductTypeID = @ProductTypeID,
ArticleNumber = @ArticleNumber,
Description = @Description,
Image = @Image,
ProductionPersonCount = @ProductionPersonCount,
ProductionWorkshopNumber = @ProductionWorkshopNumber,
MinCostForAgent = @MinCostForAgent
WHERE ID = @ID";
MySqlCommand Command = new MySqlCommand(Query, Connection);
Command.Parameters.AddWithValue("@Title", ChangedProduct.Title);
Command.Parameters.AddWithValue("@ProductTypeID", ChangedProduct.CurrentProductType.ID);
Command.Parameters.AddWithValue("@ArticleNumber", ChangedProduct.ArticleNumber);
Command.Parameters.AddWithValue("@Description", ChangedProduct.Description);
Command.Parameters.AddWithValue("@Image", ChangedProduct.Image);
Command.Parameters.AddWithValue("@ProductionPersonCount", ChangedProduct.ProductionPersonCount);
Command.Parameters.AddWithValue("@ProductionWorkshopNumber", ChangedProduct.ProductionWorkshopNumber);
Command.Parameters.AddWithValue("@MinCostForAgent", ChangedProduct.MinCostForAgent);
Command.Parameters.AddWithValue("@ID", ChangedProduct.ID);
Command.ExecuteNonQuery();
}
}
finally
{
Connection.Close();
}
}
Открытие окна редактирования для существующей и новой продукции
для редактирования существующей продукции в списке продукции реализуем обработчик двойного клика
private void ProductListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
// в создаваемое окно передаем выбранный продукт
var NewEditWindow = new EditWindow(ProductListView.SelectedItem as Product);
if ((bool)NewEditWindow.ShowDialog())
{
// при успешном сохранении продукта перечитываем список продукции
ProductList = Globals.DataProvider.GetProducts();
}
}
для создания нового продукта в разметке главного окна создайте кнопку "Добавить продукцию" (либо в верхней панели, либо в левой) и в её обработчике создайте новый экземпляр продукта
var NewEditWindow = new EditWindow(new Product());
...
Проверки перед сохранением продукта
Все проверки вставляем в обработчик кнопки "Сохранить" окна редактирования, т.к. это относится к бизнес-логике, до вызова метода сохранения продукта
Стоимость продукции не может быть отрицательной
if (ChangedProduct.MinCostForAgent < 0)
throw new Exception("Цена продукта не может быть отрицательной");
Стоимость продукции записывается только с точностью до сотых
Реализована проверка артикула на уникальность
Тут по идее надо делать запрос к базе, но у нас есть метод получения списка продукции и мы можем искать в нём используя LINQ-запросы