|
|
@@ -14,7 +14,7 @@
|
|
|
>У каждой продукции в списке отображается изображение | 0.3
|
|
|
>При отсутствии изображения отображается картинка-заглушка из ресурсов | 0.3
|
|
|
|
|
|
-Для создания такого макета в **Авалонии** используется элемент **ListBox**
|
|
|
+Для создания такого макета в используется элемент **ListBox**
|
|
|
|
|
|
В разметке вместо **DataGrid** вставляем **ListBox**
|
|
|
|
|
|
@@ -22,8 +22,7 @@
|
|
|
<ListBox
|
|
|
Grid.Row="1"
|
|
|
Background="White"
|
|
|
- x:DataType="model:Product"
|
|
|
- ItemsSource="{Binding #root.productList}">
|
|
|
+ ItemsSource="{Binding productList}">
|
|
|
<!-- сюда потом вставить ListBox.ItemTemplate -->
|
|
|
</ListBox>
|
|
|
```
|
|
|
@@ -65,7 +64,7 @@
|
|
|
|
|
|
**В первой** колонке выводим изображение:
|
|
|
|
|
|
-**Авалонии** для вывода изображения нужно преобразовать имя файла в объект пиксельной графики: класс **Bitmap**. Есть два способа это сделать:
|
|
|
+Для вывода изображения нужно указывать URI файла. Есть два способа это сделать:
|
|
|
|
|
|
* Конвертер (в принципе ничего сложного, в инете куча примеров, но я не хочу пока нагружать вас лишними сущностями)
|
|
|
* Вычисляемое свойство. Этот вариант я и буду использовать.
|
|
|
@@ -74,38 +73,25 @@
|
|
|
<Image
|
|
|
Width="64"
|
|
|
Height="64"
|
|
|
- Source="{Binding ImageBitmap}" />
|
|
|
+ Source="{Binding ImageUri}" />
|
|
|
```
|
|
|
|
|
|
|
|
|
-Обратите внимание, в классе **Product** нет поля *ImageBitmap*. Для получения картинки я использую вычисляемое свойство *ImageBitmap* - в геттере проверяю есть ли такая картинка, т.к. наличие названия в базе не означает наличие файла на диске.
|
|
|
+Обратите внимание, в классе **Product** нет поля *ImageUri*. Для получения картинки я использую вычисляемое свойство *ImageUri* - в геттере проверяю есть ли такая картинка, т.к. наличие названия в базе не означает наличие файла на диске.
|
|
|
|
|
|
-Вычисляемое поле можно добавить в сгенерированный класс **Product** (файл `Models/Product.cs`), но этот файл может быть перезаписан при повторном реконструировании БД, поэтому лучше создавать свои классы в другом месте. Классы в C# могут быть описаны в нескольких файлах (главное чтобы они были в одном **namespace**), для этого используется ключевое слово **partial**:
|
|
|
-
|
|
|
-На демо экзамене есть критерии оценки за логическую и файловую структуру, поэтому куда попало классы писать не надо. Создайте каталог `Classes` и в нём класс **Product**. В созданном классе поменяйте **namespace** (напоминаю, оно должно быть таким же, как у модели) и добавьте в класс **Product** вычисляемое свойство:
|
|
|
+>На демо экзамене есть критерии оценки за логическую и файловую структуру, поэтому куда попало классы писать не надо. Создайте каталог `Classes` и в нём класс **Product**. В созданном классе поменяйте **namespace** (напоминаю, оно должно быть таким же, как у модели) и добавьте в класс **Product** вычисляемое свойство:
|
|
|
|
|
|
```cs
|
|
|
-namespace AvaloniaApplication1.esmirnov;
|
|
|
+namespace Application1.esmirnov;
|
|
|
|
|
|
public partial class Product
|
|
|
{
|
|
|
- public Bitmap? ImageBitmap
|
|
|
+ public Uri ImageUri
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
var imageName = Environment.CurrentDirectory + (Image ?? "");
|
|
|
- // windows лояльно относится к разным слешам в пути,
|
|
|
- // а вот linux не находит такие файлы,
|
|
|
- // поэтому меняю обратные слеши на прямые
|
|
|
- imageName = imageName.Replace('\\', '/');
|
|
|
-
|
|
|
- // если файл существует, то возвращаю его
|
|
|
- // иначе картинку заглушку
|
|
|
- var result = System.IO.File.Exists(imageName) ?
|
|
|
- new Bitmap(imageName) :
|
|
|
- new Bitmap(Environment.CurrentDirectory+"/picture.png");
|
|
|
-
|
|
|
- return result;
|
|
|
+ return System.IO.File.Exists(imageName) ? new Uri(imageName) : null;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -145,15 +131,9 @@ public string TypeAndName
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-Артикул выводится как есть
|
|
|
-
|
|
|
-**Строка материалов** (вычисляемое свойство *MaterialString*) должна формироваться динамически по таблице связей **ProductMaterial**, про неё раскажу ниже. Пока пишем заглушку:
|
|
|
+Артикул и строку материалов выводим как есть
|
|
|
|
|
|
-```cs
|
|
|
-public string MaterialString { get; } = "тут будет список используемых материалов";
|
|
|
-```
|
|
|
-
|
|
|
-**В третьей** колонке выводим **сумму материалов**, т.е. опять динамически будем формировать по таблице связей.
|
|
|
+**В третьей** колонке выводим **сумму материалов**.
|
|
|
|
|
|
```xml
|
|
|
<TextBlock
|
|
|
@@ -161,111 +141,6 @@ public string MaterialString { get; } = "тут будет список испо
|
|
|
Text="{Binding MaterialSum}"/>
|
|
|
```
|
|
|
|
|
|
-И пока тоже заглушка:
|
|
|
-
|
|
|
-```cs
|
|
|
-public string MaterialSum { get; } = "тут будет сумма используемых материалов";
|
|
|
-```
|
|
|
-
|
|
|
-Если мы сейчас запустим наше приложение, то не увидим **тип материала**. Дело в том, что по-умолчанию в модель загружаются данные только текущей таблицы (**Product**), а виртуальное свойство **ProductType** остаётся не заполненным.
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-Для того, чтобы считать связанные данные, нужно при чтении данных использовать метод **Include** (можно несколько раз для нескольких связанных таблиц):
|
|
|
-
|
|
|
->Количество включений не ограничено, но всё сразу лучше не загружать - C# достаточно "умный", чтобы вычислять нужные свойства только при отображении, поэтому *строку материалов* и *сумму* можно считать отдельно
|
|
|
-
|
|
|
-Меняем в конструкторе код получения данных:
|
|
|
-
|
|
|
-```cs
|
|
|
-using (var context = new esmirnovContext())
|
|
|
-{
|
|
|
- productList = context.Products
|
|
|
- .Include(product => product.ProductType)
|
|
|
- .Include(product => product.ProductMaterials)
|
|
|
- .ToList();
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-Теперь типы выводятся нормально:
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-## Расчёт материалов
|
|
|
-
|
|
|
-Материалы (название и цену) мы можем взять из таблицы **Material**, которая связана с продуктами (**Product**) отношением *многие-ко-многим* через таблицу **ProductMaterial**. Массив этих связей мы в продукты уже включили (*product.ProductMaterials*), осталось выбрать материалы (переписываем заглушки в файле `Classes/Product.cs`):
|
|
|
-
|
|
|
-```cs
|
|
|
-private string? _materialString = null;
|
|
|
-private double _materialSum = 0;
|
|
|
-
|
|
|
-public string MaterialString
|
|
|
-{
|
|
|
- get
|
|
|
- {
|
|
|
- if (_materialString == null)
|
|
|
- {
|
|
|
- using (var context = new esmirnovContext())
|
|
|
- {
|
|
|
- _materialString = "";
|
|
|
- foreach (var item in ProductMaterials)
|
|
|
- {
|
|
|
- var material = context.Materials
|
|
|
- .Where(m => m.Id == item.MaterialId).First();
|
|
|
-
|
|
|
- _materialString += material?.Title + ", ";
|
|
|
-
|
|
|
- _materialSum += Convert.ToDouble(material?.Cost ?? 0) *
|
|
|
- (item.Count ?? 0);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return _materialString;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-public double MaterialSum
|
|
|
-{
|
|
|
- get {
|
|
|
- return _materialSum;
|
|
|
- }
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-Что тут происходит?
|
|
|
-
|
|
|
-**Во-первых**, чтобы каждый раз не пересчитывать данные мы их кешируем:
|
|
|
-
|
|
|
-```cs
|
|
|
-private string? _materialString = null;
|
|
|
-private double _materialSum = 0;
|
|
|
-```
|
|
|
-
|
|
|
-Если материалы продукта вычисляются в первый раз (значение равно **null**), то происходит реальное чтение из базы, иначе возвращаем ранее вычисленное значение.
|
|
|
-
|
|
|
-**Во-вторых**, перебираем список материалов и формируем строку и сумму материалов
|
|
|
-
|
|
|
-```cs
|
|
|
-// перебираем массив материалов продукта
|
|
|
-// (значения из связи ProductMaterials,
|
|
|
-// полученные вместе с основным запросом к базе)
|
|
|
-foreach (var item in ProductMaterials)
|
|
|
-{
|
|
|
- // ищем материал по его Id
|
|
|
- var material = context.Materials
|
|
|
- .Where(m => m.Id == item.MaterialId)
|
|
|
- .First();
|
|
|
-
|
|
|
- // формируем строку
|
|
|
- _materialString += material?.Title + ", ";
|
|
|
-
|
|
|
- // и цену, учитывая количество материалов
|
|
|
- _materialSum += Convert.ToDouble(material?.Cost ?? 0) * (item.Count ?? 0);
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-Теперь отображается всё что требуется по заданию, причём мы не написали ни одного запроса к БД, всё за нас сделал ORM фреймворк.
|
|
|
-
|
|
|

|
|
|
|
|
|
## Вывод данных "плиткой"
|