Урок 4. Редактирование списка
На прошлом уроке создали экран редактирования заказа, но реализацию возможности изменять клиента и редактировать позиции заказа оставил на этот урок.
Скачайте архив с изображениями и разархивируйте его в папку проекта \Template\Projects\1. Template\MobileForms\Images.
Редактирование клиента
Так как список клиентов может быть большим и неудобным для использования ComboBox, то договорились для выбора клиента использовать отдельный экран. В прошлом уроке на экран редактирования заказа справа от поля с именем клиента добавили кнопку, которую планировали использовать для перехода на форму со списком клиентов. Самостоятельно создайте пустую форму списка клиентов и команду открытия этой формы, которая будет вызываться по ранее созданной кнопке:
<Command Name="ClientSelectFormShowCommand" Type="FormShowCommand" Assembly="Commands">
<Xml Type="Path">TemplateClientSelect.xml</Xml>
<Show Type="None" />
<Parameters>
<Parameter Name="ClientId">
<DataConnection SourceDataConnection="OrderPrimaryGetDataConnection">
<Fields>
<Field Name="ClientId" />
</Fields>
</DataConnection>
</Parameter>
</Parameters>
</Command>Для удобства вызова списка клиентов, новую команду так же можно вызывать при клике по имени пользователя. Реализуйте эту возможность самостоятельно.
На форму списка клиентов передаем значение ClientId текущего клиента в заказе, полученного из базы данных. Помним, что запрос на получение списка должен возвращать все неархивные записи и запись, на которую ссылается редактируемый заказ.
Строка поиска
Чтобы пользователю было удобно работать с потенциально большим списком, добавим возможность поиска клиента по имени.
Строка поиска будет представлена объектом типа Panel с именем SearchPanel, содержащим элементы:
SearchButton - хоть и является объектом типа Button, но это просто изображение лупы, не выполняющее никакой команды
SearchTextBox - текстовое поле для ввода строки поиска
ClearSearchButton - кнопка с изображением повернутого крестика для очистки строки поиска
Объект SearchPanel будет иметь настройки BorderCorner и BackColor аналогичные тем, что делали для панелей блоков на экране заказа. Высота панели 40 единиц.
Объект SearchButton будет иметь код:
Самостоятельно создайте объект SearchTextBox, который будет справа от SearchButton без отступов. По высоте текстовое поле должно совпадать с высотой панели, следовательно отступ сверху будет нулевым. Ширина текстового поля должна учитывать размер кнопки ClearSearchButton и оставлять отступ справа в 10 единиц. Для объекта SearchTextBox текст-подсказки будет "Поиск". А его цвет зависит от оформления системы: для темной темы используется DarkGray, а для светлой темы - LightGray. Цвет основного текста (тэг <ForeColor>) тоже зависит от выбранного оформления системы: для темной темы используется WhiteSmoke, а для светлой темы - MostlyBlack. Значение тэга <BackColor> можно тянуть из аналогичного свойства объекта SearchPanel. В тэге <FontStyle> укажем значение TextFont. По умолчанию TextBox имеет рамку, чтобы ее убрать используем тэг <Multiline> со значением True.
Кнопка ClearSearchButton для сброса значения объекта SearchTextBox будет иметь вид:
Самостоятельно создайте команду ResetSearchStringValueSetCommand.
Запустите приложение и проверьте отображение строки поиска на экране выбора клиента.


Отлично!
Самостоятельно добавьте на форму список с клиентами ClientCollectionView. В качестве источника данных используйте SecondaryGetDataConnection, в котором должны быть отфильтрованные по строке поиска данный из PrimaryGetDataConnection.
Запрос на получение списка клиентов должен содержать поля:
В результате у вас должна получиться форма вида:


Запустите приложение и проверьте работу фильтра списка:


Выбор клиента
Добавим на форму параметры, в которых будем возвращать на родительскую форму данные выбранного клиента:
Добавьте команды типа ValueSetCommand, в которых всем параметрам формы, включая ClientId, присваиваются данные выбранного клиента.
Все команды объединим в одну последовательность:
Добавим условие на изменение выбранной записи, в котором будем проверять значение get-проперти SelectedItemChanged:
На это условие создадим Execution, в котором будем сохранять данные в параметры, ставить флаг изменений и уходить с экрана выбора клиента:
Как видите на форме используем паттерн Updated - добавьте в файл параметр Updated и создайте команду для его обновления.
Замена клиента в заказе
Вернемся в файл описания карточки заказа и реализуем обновления данных о клиенте в OrderPrimaryGetDataConnection. Самостоятельно создайте команду типа ValueSetCommand, в которой заменяйте значения в полях DataConnection соответствующих возвращаемым параметрам. Реализуйте Execution, в котором будет вызываться эта команда, используя паттерн Updated.
Запустите приложение и проверьте функциональность смены клиента в заказе.
Дополнительно: на кнопку смены клиента наложите ограничение, если заказ оплачен.
Редактирование позиций заказа
Начнем с реализации возможности добавление позиций в заказ и изменение кол-ва товара в позиции. Для этого будем использовать отдельную форму со списком ТМЦ. А затем добавим удаление позиций с помощью свайпа в карточке заказа.
Подготовка
Первым делом создадим ConvertDataConnection, который будет преобразовывать OrderPositionPrimaryGetDataConnection:
Здесь мы добавляем поле ID, которое будет временным идентификатором, который будем передавать на форму со списком товаров, чтобы правильно выставить флаги Added, Upated и Deleted. О них подробнее позже. Так же добавили поле IsNew, флаг которым будем помечать позиции заказа не сохраненные в базе данных.
В качестве значения нового поля указали объект CounterVariable, который является переменной-счетчиком и каждый раз при обращении увеличивает текущее значение на единицу и возвращает его.
Укажем OrderPositionConvertDataConnection в качестве источника данных для объекта OrderPositionCollectionView.
Создадим форму, которую будем использовать для выбора товаров из каталога. Назовем ее TemplateMaterialList.xml.
Создадим команду для открытия новой формы:
На форму каталога передаем параметр MaterialArray с массивом добавленных товаров в заказ.
На экране редактирования заказа добавьте кнопку Изменить, укажите для нее команду MaterialListFormShowCommand. Кнопку разместите справа над списком позиций заказов:
Список товаров
Перейдем в файл новой формы и в него добавим строку поиска, как делали на форме выбора клиента.
С родительской формы передаем параметр MaterialArray, добавим его на форму, а чтобы было удобно работать с передаваемым массивом данных создадим ArrayGetDataConnection:
Создайте самостоятельно MaterialPrimaryGetDataConnection, который будет получать данные из запроса:
В качестве параметра в запрос передаем массив MaterialId, полученных с родительской формы.
Здесь так же возвращаем два поля с ценой за единицу товара: UnitPrice и UnitPriceString - значение из первого будем возвращать на родительскую форму и использовать для расчета итоговой стоимости, а второе для отображения в карточке товара.
Так же возвращаем два флага:
NotUse - true, если в заказе нет позиции с этим товаром, по этому флагу будет отображаться кнопка "Добавить" в карточке товара,
Selected - true, если в заказе есть позиция с этим товаром, по этому флагу будет отображаться количество товара и кнопки изменения.
Обернем загружающее соединение в ConvertDataConnection и добавим новые поля:
Поле TotalPrice необходимо для хранения итоговой суммы по выбранному товару.
В полях Quantity и ID подставляем соответствующие значения из массива полученного от родительской формы.
Поля Added, Updated и Deleted необходимы, чтобы при передачи на родительскую форму различать новые товары, которые нужно добавить через set-проперти AddRows, позиции в которых нужно обновить количество товара, и позиции подлежащие удалению из заказа.
Создайте MaterialCollectionView для отображения списка товаров. Добавьте в шаблон карточки элемента списка описание полей для наименования товара, его категории и цены за единицу.
Сразу создадим условие, в котором будем проверять наличие значения в поле ID:
Отлично, теперь можем реализовывать логику изменения позиций в заказе.
Добавление товара
В шаблоне карточки списка товаров MaterialCollectionView опишем кнопку Добавить:
Создадим команду MaterialAddValueSetCommand типа ValueSetCommand, используемую на кнопке:
В команде находим индекс строки в MaterialConvertDataConnection по значению MaterialId элемента, по кнопке которого сделали тап, и в найденной строке меняем значения флагов и ставим количество равное единице. В результате кнопка исчезнет из карточки и вместо нее должны появиться элементы управления количеством товара в заказе.
Так же изменяем значения в полях Added, Updated и Deleted. Новые значения для Added и Updated зависят от наличия временного идентификатора на тот случай, если сначала удалили товар, и сразу нажали на кнопку Добавить. Если у товара есть временный ID, то мы должны рассматривать изменение существующего, а не добавление нового.
Управление количеством
Первой опишем кнопку уменьшения количества:
В условие QuantityOfSelectedMaterialGreater1Condition проверяем количество на больше единицы, чтобы определить, какую команду следует выполнить. Если количество больше единицы, то будем его уменьшать на единицу, а если равно единицы, то меняем значения флагов NotUse и Selected на противоположные, а количество сбрасываем на 0.
Самостоятельно напишите условие и обе команды. Не забудьте менять значения в полях Added, Updated и Deleted, учитывая при этом временный ID. Если у товара нет временного идентификатора, то можно просто сбросить количество и не помечать его на удаление из карточки заказа. При уменьшении количества товара важно правильно
Текущее количество товара будем отображать в объекте:
Кнопка увеличения количества товара, будет выполнять только одну команду:
Самостоятельно создайте команду MaterialIncrementQuantityValueSetCommand.
Добавим в файл формы описание цвета, который используется в тэгах <BackColor> созданных элементов:
Запустим приложение, чтобы проверить отображение и работу новых элементов:
Отлично! Теперь можем реализовать логику передачи данных на родительский экран, где будем обновлять список позиций заказа.
Передача изменений
В заголовок формы (HeadPanel) добавим кнопку Сохранить:
Создайте самостоятельно команду SaveSequentialCommand типа SequentialCommand, оставив ее пока пустой.
Добавим параметры формы, в которых будем возвращать данные по товарам:
Создадим команды на запись данных в параметры.
Первой будет команда для параметра MaterialArrayToAdd. В нем будем получать из MaterialConvertDataConnection все записи, у которых в поле Added стоит значение True:
В параметр MaterialArrayToEdit будем добавлять те записи, у которых в поле Updated стоит значение True и дополнительно будем проверять значение в поле Added, чтобы оно было равно False:
В параметре храним все поля выбранных записей, т.к. информация о ТМЦ могла быть изменена в базе данных.
В параметре MaterialArrayToDelete будем передавать массив временных идентификаторов товаром, помеченных на удаление:
В карточке заказа будем определять, какие позиции удалить только на форме, а какие пометить для удаления в базе данных.
Объединим все три команды в ранее созданную команду SaveSequentialCommand:
Не забываем про паттерн Updated, чтобы на родительском экране корректно отработал Execution.
Внесение изменений
Вернемся на родительскую форму чтобы добавить логику обработки полученных значений и внесения изменений в список позиций заказа.
Первым делом создайте условие MaterialListFormUpdatedTrueEqualCondition для проверки возвращаемого параметра Updated и Execution, который будет выполняться по этому условию.
Далее будет описывать команды на внесение изменений в OrderPositionConvertDataConnection и добавлять их в созданный Execution.
Команда на добавление позиций в заказ:
Здесь разбираем данные из параметра MaterialArrayToAdd с помощью <Array> и добавляем новые строки в OrderPositionConvertDataConnection. Новые позиции помечаем в поле IsNew значением True.
Команда на изменение позиций заказа:
С помощью get-проперти RowsIndicesOf у ConvertDataConnection находим индексы строк, в которых нужно обновить информацию о товарах.
Чтобы при удалении позиций различать позиции сохраненные в базе данных и недавно добавленные в заказ, создадим объект Variable вида:
Объединяем два массива по временному идентификатору записей, и через селектор Number получаем индексы строк. Дальше в командах будем проверять значение во втором поле результирующей таблицы и по нему выделять позиции заказа сохраненные в базе данных.
Первом создадим команду для отмечания позиций на удаление из базы данных:
В поле Deleted ставим значение True - тем самым помечаем для удаления. Дополнительно сбрасываем значения итоговой цены и количества товара в позиции.
Позиций заказа, которые еще не сохраняли в базе данных, удаляем из OrderPositionConvertDataConnection:
Отлично! Добавим Новые команды в Execution:
Запустим приложение и проверим функциональность на добавление, изменение и удаление позиций заказа. Позже добавим логику сохранения изменений в базе данных, а пока реализуем логику удаления позиции заказа из списка через свайп-меню.
Удаление позиции заказа
Для удаления позиции заказа из списка будем использовать контекстное меню, вызываемое свайпом карточки влево. Направление свайпа и элементы меню настраиваются - поэтому при необходимости можете настроить вызов меню через свайп вправо, или даже реализовать два разных меню на каждое направление свайпа.
SwipeMenu
Свайп-меню настраивается в тэге <SwipeMenu>, определенном в CollectionView. Добавим в описание объекта OrderPositionCollectionView следующий код:
Тэг <RightItems> содержит список элементов меню <SwipeItem>, которые будут отображаться справа от карточки при свайпе влево. Если необходимо отображать меню слева от карточки (при свайпе вправо), то используем тэг <LeftItems>.
Элементы свайп-меню описываются тегом <SwipeItem>, в котором указывается текст надписи, путь до изображения, цвет фона и последовательность команд.
Добавим в файл описание используемого цвета для кнопки удаления:
Запустим приложение, чтобы проверить отображение свайп-меню:


Команды удаления
Первым делом создадим команду для подтверждения у пользователя намерения удалить выбранный товар:
Команду OrderPositionDeleteMessageBoxCommand добавим в тэг <Commands> для <SwipeMenu> списка позиций заказа.
Создадим условие проверки, что выбранная запись была недавно добавлена в заказ и еще не сохранена в базе данных:
Создайте самостоятельно команду на удаление новой позиции заказа, назвав ее, например SwipeDeleteValueSetCommand, и команду на удаление из базы данных, например SwipeDeleteFromDatabaseCommand. Индексы строк, редактируемых в командах, следует получать из OrderPositionConvertDataConnection по значению из поля ID выбранной позиции заказа.
Создадим Execution, который будет отслеживать значение параметра Ok команды OrderPositionDeleteMessageBoxCommand, и в зависимости от условия SelectedOrderPositionIsNewEqualCondition будет вызывать одну из двух созданных выше команд.
Запустим приложение и проверим удаление позиций заказа.
Отлично! На следующем уроке рассмотрим работу с JSON на форме, для удобного сохранения
Итоги
На уроке мы рассмотрели .
Ответы
В архиве присутствуют xml-файлы форм для мобильного приложения и серверный xml-файл, также лежит бэкап базы данных и файл с запросами на изменение структуры базы данных - с помощью файлов можете проверить себя.
Архив не содержит xml-файлы форм десктопного приложения.
Last updated