WT. Практика (Mobile)
Платформа WTПрактикаСинтаксисБаза знаний
  • Приветствие
  • ОСНОВНОЙ
    • Урок 1. Hello World
    • Урок 2. Аутентификация пользователей в программе
    • Урок 3. Форма списка
    • Урок 4. Редактирование списка
    • Урок 5. Работа с JSON
    • Урок 6. Главный экран. Фильтры
    • Урок 7. Автоматическое обновление данных
    • Урок 8. Пользовательские настройки
    • Урок 9. Взаимодействие с другими приложениями и функциями телефона
  • КАСТОМИЗАЦИЯ
    • Урок 10. Кастомный элемент
Powered by GitBook
On this page
  • Редактирование клиента
  • Строка поиска
  • Выбор клиента
  • Замена клиента в заказе
  • Редактирование позиций заказа
  • Подготовка
  • Список товаров
  • Добавление товара
  • Управление количеством
  • Передача изменений
  • Внесение изменений
  • Удаление позиции заказа
  • SwipeMenu
  • Команды удаления
  • Итоги
  • Ответы
  1. ОСНОВНОЙ

Урок 4. Редактирование списка

Last updated 1 month ago

На прошлом уроке создали экран редактирования заказа, но реализацию возможности изменять клиента и редактировать позиции заказа оставил на этот урок.

Скачайте архив с изображениями и разархивируйте его в папку проекта \Template\Projects\1. Template\MobileForms\Images.

Редактирование клиента

Так как список клиентов может быть большим и неудобным для использования ComboBox, то договорились для выбора клиента использовать отдельный экран. В прошлом уроке на экран редактирования заказа справа от поля с именем клиента добавили кнопку, которую планировали использовать для перехода на форму со списком клиентов. Самостоятельно создайте пустую форму списка клиентов и команду открытия этой формы, которая будет вызываться по ранее созданной кнопке:

TemplateOrderEdit.xml
<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 будет иметь код:

TemplateClientSelect.xml
<MyObject Name="SearchButton" Type="Button" Assembly="BaseControls">
  <Top>2</Top>
  <Left>5</Left>
  <Height>35</Height>
  <Width>35</Width>
  <BackColor>
    <Object Name="SearchPanel">
      <Property Name="BackColor" />
    </Object>
  </BackColor>
  <BackgroundImage>Images\magnify.png</BackgroundImage>
  <BackgroundImageLayout>Zoom</BackgroundImageLayout>
</MyObject>

Самостоятельно создайте объект SearchTextBox, который будет справа от SearchButton без отступов. По высоте текстовое поле должно совпадать с высотой панели, следовательно отступ сверху будет нулевым. Ширина текстового поля должна учитывать размер кнопки ClearSearchButton и оставлять отступ справа в 10 единиц. Для объекта SearchTextBox текст-подсказки будет "Поиск". А его цвет зависит от оформления системы: для темной темы используется DarkGray, а для светлой темы - LightGray. Цвет основного текста (тэг <ForeColor>) тоже зависит от выбранного оформления системы: для темной темы используется WhiteSmoke, а для светлой темы - MostlyBlack. Значение тэга <BackColor> можно тянуть из аналогичного свойства объекта SearchPanel. В тэге <FontStyle> укажем значение TextFont. По умолчанию TextBox имеет рамку, чтобы ее убрать используем тэг <Multiline> со значением True.

Кнопка ClearSearchButton для сброса значения объекта SearchTextBox будет иметь вид:

TemplateClientSelect.xml
<MyObject Name="ClearSearchButton" Type="Button" Assembly="BaseControls">
  <Top>5</Top>
  <Left>
    <Calculate>
      <Expression>{0} + 2</Expression>
      <Items>
        <Item>
          <Object Name="SearchTextBox">
            <Property Name="Right" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Left>
  <Height>30</Height>
  <Width>30</Width>
  <BackColor>
    <Object Name="SearchPanel">
      <Property Name="BackColor" />
    </Object>
  </BackColor>
  <BackgroundImage>Images\close.png</BackgroundImage>
  <BackgroundImageLayout>Zoom</BackgroundImageLayout>
  <Commands>
    <Command Name="ResetSearchStringValueSetCommand" />
  </Commands>
</MyObject>

Самостоятельно создайте команду ResetSearchStringValueSetCommand.

Запустите приложение и проверьте отображение строки поиска на экране выбора клиента.

Отлично!

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

Запрос на получение списка клиентов должен содержать поля:

В результате у вас должна получиться форма вида:

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

Выбор клиента

Добавим на форму параметры, в которых будем возвращать на родительскую форму данные выбранного клиента:

TemplateClientSelect.xml
<Parameter Name="ClientTitle" />
<Parameter Name="CityTitle" />
<Parameter Name="ClientEmail" />
<Parameter Name="ClientPhone" />

Добавьте команды типа ValueSetCommand, в которых всем параметрам формы, включая ClientId, присваиваются данные выбранного клиента.

Все команды объединим в одну последовательность:

TemplateClientSelect.xml
<Command Name="SetParametersSequentialCommand" Type="SequentialCommand" Assembly="Commands">
  <Commands>
    <Command Name="ClientIdValueSetCommand" />
    <Command Name="ClientTitleValueSetCommand" />
    <Command Name="CityTitleValueSetCommand" />
    <Command Name="ClientEmailValueSetCommand" />
    <Command Name="ClientPhoneValueSetCommand" />
  </Commands>
</Command>

Добавим условие на изменение выбранной записи, в котором будем проверять значение get-проперти SelectedItemChanged:

TemplateClientSelect.xml
<Condition Name="CollectionViewSelectedItemChangedEqualCondition" Type="EqualCondition" Assembly="Conditions">
  <AlwaysChange Value="True" />
  <Items>
    <Item>
      <Object Name="ClientCollectionView">
        <Property Name="SelectedItemChanged" />
      </Object>
    </Item>
    <Item>True</Item>
  </Items>
</Condition>

На это условие создадим Execution, в котором будем сохранять данные в параметры, ставить флаг изменений и уходить с экрана выбора клиента:

TemplateClientSelect.xml
<Execution>
  <ConditionExpression>
    <Condition Name="CollectionViewSelectedItemChangedEqualCondition" />
  </ConditionExpression>
  <Commands>
    <Command Name="SetParametersSequentialCommand" />
    <Command Name="UpdatedTrueValueSetCommand" />
    <Command Name="FormCloseCommand" />
  </Commands>
</Execution>

Как видите на форме используем паттерн Updated - добавьте в файл параметр Updated и создайте команду для его обновления.

Замена клиента в заказе

Вернемся в файл описания карточки заказа и реализуем обновления данных о клиенте в OrderPrimaryGetDataConnection. Самостоятельно создайте команду типа ValueSetCommand, в которой заменяйте значения в полях DataConnection соответствующих возвращаемым параметрам. Реализуйте Execution, в котором будет вызываться эта команда, используя паттерн Updated.

Запустите приложение и проверьте функциональность смены клиента в заказе.

Дополнительно: на кнопку смены клиента наложите ограничение, если заказ оплачен.

Редактирование позиций заказа

Начнем с реализации возможности добавление позиций в заказ и изменение кол-ва товара в позиции. Для этого будем использовать отдельную форму со списком ТМЦ. А затем добавим удаление позиций с помощью свайпа в карточке заказа.

Подготовка

TemplateOrderEdit.xml
<DataConnection Name="OrderPositionConvertDataConnection" Type="ConvertDataConnection" Assembly="WorkflowServer">
  <SourceDataConnection Name="OrderPositionPrimaryGetDataConnection" />
  <Fields>
    <Field Name="OrderPositionId" />
    <Field Name="MaterialId" />
    <Field Name="MaterialTitle" />
    <Field Name="MaterialCategoryTitle" />
    <Field Name="UnitPrice" />
    <Field Name="TotalPrice" />
    <Field Name="Quantity" />
    <Field Name="QuantityString" />
    <Field Name="UnitShortTitle" />
    <Field Name="ID" Type="Value">
      <Object Name="CounterVariable" />
    </Field>
    <Field Name="Updated" Type="Value" DataType="BooleanDataType">False</Field>
    <Field Name="Deleted" Type="Value" DataType="BooleanDataType">False</Field>
    <Field Name="IsNew" Type="Value" DataType="BooleanDataType">False</Field>
  </Fields>
</DataConnection>

Здесь мы добавляем поле ID, которое будет временным идентификатором, который будем передавать на форму со списком товаров, чтобы правильно выставить флаги Added, Upated и Deleted. О них подробнее позже. Так же добавили поле IsNew, флаг которым будем помечать позиции заказа не сохраненные в базе данных.

В качестве значения нового поля указали объект CounterVariable, который является переменной-счетчиком и каждый раз при обращении увеличивает текущее значение на единицу и возвращает его.

TemplateOrderEdit.xml
<MyObject Name="CounterVariable" Type="CounterVariable" Assembly="WorkflowServer" ChangeForm="False">
  <Value />
</MyObject>

Укажем OrderPositionConvertDataConnection в качестве источника данных для объекта OrderPositionCollectionView.

Создадим форму, которую будем использовать для выбора товаров из каталога. Назовем ее TemplateMaterialList.xml.

Создадим команду для открытия новой формы:

TemplateOrderEdit.xml
<Command Name="MaterialListFormShowCommand" Type="FormShowCommand" Assembly="Commands">
  <Xml Type="Path">TemplateMaterialList.xml</Xml>
  <Show Type="None" />
  <Parameters>
    <Parameter Name="MaterialArray">
      <DataConnection SourceDataConnection="OrderPositionConvertDataConnection">
        <Fields>
          <Field Name="ID" />
          <Field Name="MaterialId" />
          <Field Name="Quantity" />
          <Field Name="IsNew" />
        </Fields>
      </DataConnection>
    </Parameter>
  </Parameters>
</Command>

На форму каталога передаем параметр MaterialArray с массивом добавленных товаров в заказ.

На экране редактирования заказа добавьте кнопку Изменить, укажите для нее команду MaterialListFormShowCommand. Кнопку разместите справа над списком позиций заказов:

Список товаров

Перейдем в файл новой формы и в него добавим строку поиска, как делали на форме выбора клиента.

TemplateMaterialList.xml
<DataConnection Name="MaterialArrayGetDataConnection" Type="ArrayGetDataConnection" Assembly="DataConnections">
  <Source>
    <Parameter Name="MaterialArray" />
  </Source>
  <Fields>
    <Field Name="ID" />
    <Field Name="MaterialId" />
    <Field Name="Quantity" DataType="IntegerDataType" />
  </Fields>
</DataConnection>

Создайте самостоятельно MaterialPrimaryGetDataConnection, который будет получать данные из запроса:

Template.xml
<SqlQuery Name="AppMaterialSelectSqlQuery">
  <Text>
    SELECT
      M.material_id AS "MaterialId",
      M.title AS "Title",
      M.material_category_id AS "MaterialCategoryId",
      MC.title AS "MaterialCategoryTitle",
      U.short_title AS "UnitShortTitle",
      M.unit_price AS "UnitPrice",
      trim(to_char(M.unit_price, '999G999G999D00')) || ' руб./' || U.short_title AS "UnitPriceString",
      M.material_id != ALL({MaterialId}::bigint[]) AS "NotUse",
      M.material_id = ANY({MaterialId}::bigint[]) AS "Selected"
    FROM
      template.material M
      LEFT JOIN template.material_category MC USING(material_category_id)
      LEFT JOIN template.unit U USING(unit_id)
    WHERE
      NOT M.archive OR M.material_id = ANY({MaterialId}::bigint[])
    ORDER BY M.title;
  </Text>
</SqlQuery>

В качестве параметра в запрос передаем массив MaterialId, полученных с родительской формы.

Здесь так же возвращаем два поля с ценой за единицу товара: UnitPrice и UnitPriceString - значение из первого будем возвращать на родительскую форму и использовать для расчета итоговой стоимости, а второе для отображения в карточке товара.

Так же возвращаем два флага:

  • NotUse - true, если в заказе нет позиции с этим товаром, по этому флагу будет отображаться кнопка "Добавить" в карточке товара,

  • Selected - true, если в заказе есть позиция с этим товаром, по этому флагу будет отображаться количество товара и кнопки изменения.

Обернем загружающее соединение в ConvertDataConnection и добавим новые поля:

TemplateMaterialList.xml
<DataConnection Name="MaterialConvertDataConnection" Type="ConvertDataConnection" Assembly="WorkflowServer">
  <SourceDataConnection Name="MaterialPrimaryGetDataConnection" />
  <Fields>
    <Field Name="MaterialId" />
    <Field Name="Title" />
    <Field Name="MaterialCategoryId" />
    <Field Name="MaterialCategoryTitle" />
    <Field Name="UnitShortTitle" />
    <Field Name="UnitPrice" />
    <Field Name="UnitPriceString" />
    <Field Name="Selected" />
    <Field Name="NotUse" />
    <Field Name="TotalPrice" />
    <Field Name="Quantity" Type="Substitution" Field="MaterialId">
      <DataConnection SourceDataConnection="MaterialArrayGetDataConnection">
        <Fields>
          <Field Name="MaterialId" />
          <Field Name="Quantity" />
        </Fields>
      </DataConnection>
    </Field>
    <Field Name="ID" Type="Substitution" Field="MaterialId">
      <DataConnection SourceDataConnection="MaterialArrayGetDataConnection">
        <Fields>
          <Field Name="MaterialId" />
          <Field Name="ID" />
        </Fields>
      </DataConnection>
    </Field>
    <Field Name="Added" Type="Value" DataType="BooleanDataType">False</Field>
    <Field Name="Updated" Type="Value" DataType="BooleanDataType">False</Field>
    <Field Name="Deleted" Type="Value" DataType="BooleanDataType">False</Field>
  </Fields>
</DataConnection>

Поле TotalPrice необходимо для хранения итоговой суммы по выбранному товару.

В полях Quantity и ID подставляем соответствующие значения из массива полученного от родительской формы.

Поля Added, Updated и Deleted необходимы, чтобы при передачи на родительскую форму различать новые товары, которые нужно добавить через set-проперти AddRows, позиции в которых нужно обновить количество товара, и позиции подлежащие удалению из заказа.

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

Сразу создадим условие, в котором будем проверять наличие значения в поле ID:

TemplateMaterialList.xml
<Condition Name="SelectedIDIsNullOrEmptyCondition" Type="IsNullOrEmptyCondition" Assembly="Conditions">
  <AlwaysChange Value="True" />
  <Items>
    <Item>
      <Object Name="MaterialCollectionView">
        <Property Name="SelectedItemValueByFieldName">
          <Parameters>
            <Parameter Name="FieldName">ID</Parameter>
          </Parameters>
        </Property>
      </Object>
    </Item>
  </Items>
  <DataType Type="IntegerDataType" />
</Condition>

Отлично, теперь можем реализовывать логику изменения позиций в заказе.

Добавление товара

В шаблоне карточки списка товаров MaterialCollectionView опишем кнопку Добавить:

<TemplateItem Type="TemplateItemButton">
  <Top>50</Top>
  <Left>1</Left>
  <Width>125</Width>
  <Height>30</Height>
  <AbsoluteLayoutFlags>XProportional</AbsoluteLayoutFlags>
  <Text>Добавить</Text>
  <FontStyle>CollectionSubTitleFont</FontStyle>
  <ForeColor>WhiteSmoke</ForeColor>
  <BackColor>ColorPrimary</BackColor>
  <BorderCorner>7</BorderCorner>
  <Commands>
    <Command Name="MaterialAddValueSetCommand" />
  </Commands>
  <Visible Binding="NotUse" />
</TemplateItem>

Создадим команду MaterialAddValueSetCommand типа ValueSetCommand, используемую на кнопке:

TemplateMaterialList.xml
<Command Name="MaterialAddValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <DataConnection Name="MaterialConvertDataConnection">
    <Property Name="UpdateRow">
      <Parameters>
        <Parameter Name="RowIndex">
          <DataConnection SourceDataConnection="MaterialConvertDataConnection">
            <Property Name="RowIndexOf">
              <Parameters>
                <Parameter Name="ColumnNames">
                  <Structure Type="List">
                    <Item>MaterialId</Item>
                  </Structure>
                </Parameter>
                <Parameter Name="Values">
                  <Structure Type="List">
                    <Item>
                      <Object Name="MaterialCollectionView">
                        <Property Name="SelectedItemValueByFieldName">
                          <Parameters>
                            <Parameter Name="FieldName">MaterialId</Parameter>
                          </Parameters>
                        </Property>
                      </Object>
                    </Item>
                  </Structure>
                </Parameter>
              </Parameters>
            </Property>
          </DataConnection>
        </Parameter>
        <Parameter Name="ColumnNames">
          <Structure Type="List">
            <Item>NotUse</Item>
            <Item>Selected</Item>
            <Item>Quantity</Item>
            <Item>TotalPrice</Item>
            <Item>Added</Item>
            <Item>Updated</Item>
            <Item>Deleted</Item>
          </Structure>
        </Parameter>
        <Parameter Name="Values">
          <Structure Type="List">
            <Item>False</Item>
            <Item>True</Item>
            <Item>1</Item>
            <Item>
              <Calculate>
                <Expression>{0} * 1</Expression>
                <Items>
                  <Item>
                    <Object Name="MaterialCollectionView">
                      <Property Name="SelectedItemValueByFieldName">
                        <Parameters>
                          <Parameter Name="FieldName">UnitPrice</Parameter>
                        </Parameters>
                      </Property>
                    </Object>
                  </Item>
                </Items>
              </Calculate>
            </Item>
            <Item>
              <Condition Name="SelectedIDIsNullOrEmptyCondition" />
            </Item>
            <Item>
              <Not>
                <Condition Name="SelectedIDIsNullOrEmptyCondition" />
              </Not>
            </Item>
            <Item>False</Item>
          </Structure>
        </Parameter>
      </Parameters>
    </Property>
  </DataConnection>
</Command>

В команде находим индекс строки в MaterialConvertDataConnection по значению MaterialId элемента, по кнопке которого сделали тап, и в найденной строке меняем значения флагов и ставим количество равное единице. В результате кнопка исчезнет из карточки и вместо нее должны появиться элементы управления количеством товара в заказе.

Так же изменяем значения в полях Added, Updated и Deleted. Новые значения для Added и Updated зависят от наличия временного идентификатора на тот случай, если сначала удалили товар, и сразу нажали на кнопку Добавить. Если у товара есть временный ID, то мы должны рассматривать изменение существующего, а не добавление нового.

Управление количеством

Первой опишем кнопку уменьшения количества:

<ItemElement Type="TemplateItemButton">
  <Top>50</Top>
  <Left>0.72</Left>
  <Width>30</Width>
  <Height>30</Height>
  <AbsoluteLayoutFlags>XProportional</AbsoluteLayoutFlags>
  <BackColor>
    <Switch>
      <Case>
        <When>
          <Condition Name="AppThemeDarkEqualCondition" />
        </When>
        <Then>DarkGray70</Then>
      </Case>
      <Case>VeryLightGray</Case>
    </Switch>
  </BackColor>
  <BorderCorner>7</BorderCorner>
  <ImageSource>
    <Switch>
      <Case>
        <When>
          <Condition Name="AppThemeDarkEqualCondition" />
        </When>
        <Then>Images\minus-light.png</Then>
      </Case>
      <Case>Images\minus-dark.png</Case>
    </Switch>
  </ImageSource>
  <Commands>
    <If>
      <When>
        <Condition Name="QuantityOfSelectedMaterialGreater1Condition" />
      </When>
      <Then>
        <Command Name="MaterialDecrementQuantityValueSetCommand" />
      </Then>
      <Else>
        <Command Name="MaterialDeleteValueSetCommand" />
      </Else>
    </If>
  </Commands>
  <Visible Binding="Selected" />
</ItemElement>

В условие QuantityOfSelectedMaterialGreater1Condition проверяем количество на больше единицы, чтобы определить, какую команду следует выполнить. Если количество больше единицы, то будем его уменьшать на единицу, а если равно единицы, то меняем значения флагов NotUse и Selected на противоположные, а количество сбрасываем на 0.

Самостоятельно напишите условие и обе команды. Не забудьте менять значения в полях Added, Updated и Deleted, учитывая при этом временный ID. Если у товара нет временного идентификатора, то можно просто сбросить количество и не помечать его на удаление из карточки заказа. При уменьшении количества товара важно правильно

Текущее количество товара будем отображать в объекте:

<TemplateItem Type="TemplateItemLabel">
  <Top>50</Top>
  <Left>0.87</Left>
  <Width>45</Width>
  <Height>30</Height>
  <AbsoluteLayoutFlags>XProportional</AbsoluteLayoutFlags>
  <FontStyle>CollectionTitleFont</FontStyle>
  <ForeColor>
    <Switch>
      <Case>
        <When>
          <Condition Name="AppThemeDarkEqualCondition" />
        </When>
        <Then>WhiteSmoke</Then>
      </Case>
      <Case>VeryDarkGray</Case>
    </Switch>
  </ForeColor>
  <BorderCorner>7</BorderCorner>
  <BackColor>
    <Switch>
      <Case>
        <When>
          <Condition Name="AppThemeDarkEqualCondition" />
        </When>
        <Then>DarkGray70</Then>
      </Case>
      <Case>VeryLightGray</Case>
    </Switch>
  </BackColor>
  <TextAlignment Vertical="Center" Horizontal="Center" />
  <Padding Left="5" Top="0" Right="5" Bottom="0" />
  <Text Binding="Quantity" />
  <Visible Binding="Selected" />
</TemplateItem>

Кнопка увеличения количества товара, будет выполнять только одну команду:

<ItemElement Type="TemplateItemButton">
  <Top>50</Top>
  <Left>0.99</Left>
  <Width>30</Width>
  <Height>30</Height>
  <AbsoluteLayoutFlags>XProportional</AbsoluteLayoutFlags>
  <BackColor>
    <Switch>
      <Case>
        <When>
          <Condition Name="AppThemeDarkEqualCondition" />
        </When>
        <Then>DarkGray70</Then>
      </Case>
      <Case>VeryLightGray</Case>
    </Switch>
  </BackColor>
  <BorderCorner>7</BorderCorner>
  <ImageSource>
    <Switch>
      <Case>
        <When>
          <Condition Name="AppThemeDarkEqualCondition" />
        </When>
        <Then>Images\plus-light.png</Then>
      </Case>
      <Case>Images\plus-dark.png</Case>
    </Switch>
  </ImageSource>
  <Commands>
    <Command Name="MaterialIncrementQuantityValueSetCommand" />
  </Commands>
  <Visible Binding="Selected" />
</ItemElement>

Самостоятельно создайте команду MaterialIncrementQuantityValueSetCommand.

Добавим в файл формы описание цвета, который используется в тэгах <BackColor> созданных элементов:

TemplateMaterialList.xml
<Color Name="DarkGray70" Red="70" Green="70" Blue="70" Alpha="255" />

Запустим приложение, чтобы проверить отображение и работу новых элементов:

Отлично! Теперь можем реализовать логику передачи данных на родительский экран, где будем обновлять список позиций заказа.

Передача изменений

В заголовок формы (HeadPanel) добавим кнопку Сохранить:

TemplateMaterialList.xml
<MyObject Name="SaveButton" Type="Button" Assembly="BaseControls">
  <Top>0</Top>
  <Right>
    <Calculate>
      <Expression>{0} - 15</Expression>
      <Items>
        <Item>
          <Object Name="HeadPanel">
            <Property Name="Width" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Right>
  <Height>
    <Object Name="HeadPanel">
      <Property Name="Height" />
    </Object>
  </Height>
  <Width>100</Width>
  <TextAlign>MiddleCenter</TextAlign>
  <Text>Сохранить</Text>
  <FontStyle>ButtonFont</FontStyle>
  <ForeColor>ColorPrimary</ForeColor>
  <BackColor>
    <Object Name="HeadPanel">
      <Property Name="BackColor" />
    </Object>
  </BackColor>
  <Commands>
    <Command Name="SaveSequentialCommand" />
    <Command Name="FormCloseCommand" />
  </Commands>
</MyObject>

Добавим параметры формы, в которых будем возвращать данные по товарам:

TemplateMaterialList.xml
<Parameter Name="MaterialArrayToAdd" />
<Parameter Name="MaterialArrayToEdit" />
<Parameter Name="MaterialArrayToDelete" />

Создадим команды на запись данных в параметры.

Первой будет команда для параметра MaterialArrayToAdd. В нем будем получать из MaterialConvertDataConnection все записи, у которых в поле Added стоит значение True:

TemplateMaterialList.xml
<Command Name="MaterialArrayToAddValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <Parameter Name="MaterialArrayToAdd">
    <Array>
      <Source>
        <DataConnection SourceDataConnection="MaterialConvertDataConnection">
          <Fields>
            <Field Name="MaterialId" />
            <Field Name="Title" />
            <Field Name="MaterialCategoryTitle" />
            <Field Name="UnitShortTitle" />
            <Field Name="UnitPrice" />
            <Field Name="Quantity" />
            <Field Name="TotalPrice" />
            <Field Name="Added" />
          </Fields>
        </DataConnection>
      </Source>
      <Filter>
        <Index Value="7" />
        <DataType Type="BooleanDataType" />
        <Value>True</Value>
      </Filter>
      <Select>
        <Items>
          <Item Type="Index">0</Item>
          <Item Type="Index">1</Item>
          <Item Type="Index">2</Item>
          <Item Type="Index">3</Item>
          <Item Type="Index">4</Item>
          <Item Type="Index">5</Item>
          <Item Type="Index">6</Item>
        </Items>
      </Select>
    </Array>
  </Parameter>
</Command>

В параметр MaterialArrayToEdit будем добавлять те записи, у которых в поле Updated стоит значение True и дополнительно будем проверять значение в поле Added, чтобы оно было равно False:

TemplateMaterialList.xml
<Command Name="MaterialArrayToEditValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <Parameter Name="MaterialArrayToEdit">
    <Array>
      <Source>
        <DataConnection SourceDataConnection="MaterialConvertDataConnection">
          <Fields>
            <Field Name="ID" />
            <Field Name="MaterialId" />
            <Field Name="Title" />
            <Field Name="MaterialCategoryTitle" />
            <Field Name="UnitShortTitle" />
            <Field Name="UnitPrice" />
            <Field Name="Quantity" />
            <Field Name="TotalPrice" />
            <Field Name="Added" />
            <Field Name="Updated" />
          </Fields>
        </DataConnection>
      </Source>
      <Filter Type="Nested">
        <And>
          <Filter>
            <Index Value="8" />
            <DataType Type="BooleanDataType" />
            <Value>False</Value>
          </Filter>
          <Filter>
            <Index Value="9" />
            <DataType Type="BooleanDataType" />
            <Value>True</Value>
          </Filter>
        </And>
      </Filter>
      <Select>
        <Items>
          <Item Type="Index">0</Item>
          <Item Type="Index">1</Item>
          <Item Type="Index">2</Item>
          <Item Type="Index">3</Item>
          <Item Type="Index">4</Item>
          <Item Type="Index">5</Item>
          <Item Type="Index">6</Item>
          <Item Type="Index">7</Item>
        </Items>
      </Select>
    </Array>
  </Parameter>
</Command>

В параметре храним все поля выбранных записей, т.к. информация о ТМЦ могла быть изменена в базе данных.

В параметре MaterialArrayToDelete будем передавать массив временных идентификаторов товаром, помеченных на удаление:

TemplateMaterialList.xml
<Command Name="MaterialArrayToDeleteValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <Parameter Name="MaterialArrayToDelete">
    <Array>
      <Source>
        <DataConnection SourceDataConnection="MaterialConvertDataConnection">
          <Fields>
            <Field Name="ID" />
            <Field Name="Deleted" />
          </Fields>
        </DataConnection>
      </Source>
      <Filter>
        <Index Value="1" />
        <DataType Type="BooleanDataType" />
        <Value>True</Value>
      </Filter>
      <Select>
        <Items>
          <Item Type="Index">0</Item>
        </Items>
      </Select>
    </Array>
  </Parameter>
</Command>

В карточке заказа будем определять, какие позиции удалить только на форме, а какие пометить для удаления в базе данных.

Объединим все три команды в ранее созданную команду SaveSequentialCommand:

TemplateMaterialList.xml
<Command Name="SaveSequentialCommand" Type="SequentialCommand" Assembly="Commands">
  <Commands>
    <Command Name="MaterialArrayToAddValueSetCommand" />
    <Command Name="MaterialArrayToEditValueSetCommand" />
    <Command Name="MaterialArrayToDeleteValueSetCommand" />

    <Command Name="UpdatedTrueValueSetCommand" />
  </Commands>
</Command>

Не забываем про паттерн Updated, чтобы на родительском экране корректно отработал Execution.

Внесение изменений

Вернемся на родительскую форму чтобы добавить логику обработки полученных значений и внесения изменений в список позиций заказа.

Первым делом создайте условие MaterialListFormUpdatedTrueEqualCondition для проверки возвращаемого параметра Updated и Execution, который будет выполняться по этому условию.

Далее будет описывать команды на внесение изменений в OrderPositionConvertDataConnection и добавлять их в созданный Execution.

Команда на добавление позиций в заказ:

TemplateOrderEdit.xml
<Command Name="OrderPositionAddCommand" Type="ValueSetCommand" Assembly="Commands">
  <DataConnection Name="OrderPositionConvertDataConnection">
    <Property Name="AddRows">
      <Parameters>
        <Parameter Name="ColumnNames">
          <Structure Type="List">
            <Item>ID</Item>
            <Item>MaterialId</Item>
            <Item>MaterialTitle</Item>
            <Item>MaterialCategoryTitle</Item>
            <Item>UnitShortTitle</Item>
            <Item>UnitPrice</Item>
            <Item>Quantity</Item>
            <Item>TotalPrice</Item>
            <Item>QuantityString</Item>
            <Item>Updated</Item>
            <Item>Deleted</Item>
            <Item>IsNew</Item>
          </Structure>
        </Parameter>
        <Parameter Name="Values">
          <Array>
            <Source>
              <Command Name="MaterialListFormShowCommand" Parameter="MaterialArrayToAdd" />
            </Source>
            <Select>
              <Items>
                <Item Type="Value">
                  <Object Name="CounterVariable" />
                </Item>
                <Item Type="Index">0</Item><!-- MaterialId -->
                <Item Type="Index">1</Item><!-- Title -->
                <Item Type="Index">2</Item><!-- MaterialCategoryTitle -->
                <Item Type="Index">3</Item><!-- UnitShortTitle -->
                <Item Type="Format">{4} руб./{3}</Item><!-- UnitPrice -->
                <Item Type="Index">5</Item><!-- Quantity -->
                <Item Type="Format">{6} руб.</Item><!--TotalPrice-->
                <Item Type="Format">{5} {3}</Item><!--QuantityString-->
                <Item Type="Value">False</Item>
                <Item Type="Value">False</Item>
                <Item Type="Value">True</Item>
              </Items>
            </Select>
          </Array>
        </Parameter>
      </Parameters>
    </Property>
  </DataConnection>
</Command>

Здесь разбираем данные из параметра MaterialArrayToAdd с помощью <Array> и добавляем новые строки в OrderPositionConvertDataConnection. Новые позиции помечаем в поле IsNew значением True.

Команда на изменение позиций заказа:

TemplateOrderEdit.xml
<Command Name="OrderPositionToEditCommand" Type="ValueSetCommand" Assembly="Commands">
  <DataConnection Name="OrderPositionConvertDataConnection">
    <Property Name="UpdateRows">
      <Parameters>
        <Parameter Name="RowIndices">
          <DataConnection SourceDataConnection="OrderPositionConvertDataConnection">
            <Property Name="RowsIndicesOf">
              <Parameters>
                <Parameter Name="ColumnNames">
                  <Structure Type="List">
                    <Item>ID</Item>
                  </Structure>
                </Parameter>
                <Parameter Name="Values">
                  <Structure Type="List">
                    <Item>
                      <Array>
                        <Source>
                          <Command Name="MaterialListFormShowCommand" Parameter="MaterialArrayToEdit" />
                        </Source>
                        <Select>
                          <Items>
                            <Item Type="Index">0</Item><!-- ID -->
                          </Items>
                        </Select>
                      </Array>
                    </Item>
                  </Structure>
                </Parameter>
              </Parameters>
            </Property>
          </DataConnection>
        </Parameter>
        <Parameter Name="ColumnNames">
          <Structure Type="List">
            <Item>ID</Item>
            <Item>MaterialId</Item>
            <Item>MaterialTitle</Item>
            <Item>MaterialCategoryTitle</Item>
            <Item>UnitShortTitle</Item>
            <Item>UnitPrice</Item>
            <Item>Quantity</Item>
            <Item>TotalPrice</Item>
            <Item>QuantityString</Item>
            <Item>Updated</Item>
            <Item>Deleted</Item>
          </Structure>
        </Parameter>
        <Parameter Name="Values">
          <Array>
            <Source>
              <Command Name="MaterialListFormShowCommand" Parameter="MaterialArrayToEdit" />
            </Source>
            <Select>
              <Items>
                <Item Type="Index">0</Item><!-- ID -->
                <Item Type="Index">1</Item><!-- MaterialId -->
                <Item Type="Index">2</Item><!-- Title -->
                <Item Type="Index">3</Item><!-- MaterialCategoryTitle -->
                <Item Type="Index">4</Item><!-- UnitShortTitle -->
                <Item Type="Format">{5} руб./{4}</Item><!-- UnitPrice -->
                <Item Type="Index">6</Item><!-- Quantity -->
                <Item Type="Format">{7} руб.</Item><!--TotalPrice-->
                <Item Type="Format">{6} {4}</Item><!--QuantityString-->
                <Item Type="Value">True</Item>
                <Item Type="Value">False</Item>
              </Items>
            </Select>
          </Array>
        </Parameter>
      </Parameters>
    </Property>
  </DataConnection>
</Command>

С помощью get-проперти RowsIndicesOf у ConvertDataConnection находим индексы строк, в которых нужно обновить информацию о товарах.

Чтобы при удалении позиций различать позиции сохраненные в базе данных и недавно добавленные в заказ, создадим объект Variable вида:

TemplateOrderEdit.xml
<MyObject Name="MaterialArrayToDeleteVariable" Type="Variable" Assembly="SimpleControls">
  <Value>
    <Array>
      <Source>
        <DataConnection SourceDataConnection="OrderPositionConvertDataConnection">
          <Fields>
            <Field Name="ID" />
            <Field Name="IsNew" />
          </Fields>
        </DataConnection>
      </Source>
      <Select>
        <Items>
          <Item Type="Number" />
          <Item Type="Index">0</Item>
          <Item Type="Index">1</Item>
        </Items>
      </Select>
      <RightJoin>
        <OuterArray>
          <Command Name="MaterialListFormShowCommand" Parameter="MaterialArrayToDelete" />
        </OuterArray>
        <Keys>
          <Inner Index="1" Type="IntegerDataType" />
          <Outer Index="0" Type="IntegerDataType" />
        </Keys>
        <Result>
          <Inner Index="0" /><!-- Number = RowIndex -->
          <Inner Index="2" /><!-- IsNew -->
        </Result>
      </RightJoin>
    </Array>
  </Value>
</MyObject>

Объединяем два массива по временному идентификатору записей, и через селектор Number получаем индексы строк. Дальше в командах будем проверять значение во втором поле результирующей таблицы и по нему выделять позиции заказа сохраненные в базе данных.

Первом создадим команду для отмечания позиций на удаление из базы данных:

TemplateOrderEdit.xml
<Command Name="OrderPositionToDeleteFromDatabaseCommand" Type="ValueSetCommand" Assembly="Commands">
  <DataConnection Name="OrderPositionConvertDataConnection">
    <Property Name="UpdateRows">
      <Parameters>
        <Parameter Name="RowIndices">
          <Array>
            <Source>
              <Object Name="MaterialArrayToDeleteVariable" />
            </Source>
            <Filter>
              <Index Value="1" /><!-- IsNew -->
              <Value>False</Value>
            </Filter>
            <Select>
              <Items>
                <Item Type="Index">0</Item><!-- RowIndex -->
              </Items>
            </Select>
          </Array>
        </Parameter>
        <Parameter Name="ColumnNames">
          <Structure Type="List">
            <Item>TotalPrice</Item>
            <Item>Quantity</Item>
            <Item>Updated</Item>
            <Item>Deleted</Item>
            <Item>IsNew</Item>
          </Structure>
        </Parameter>
        <Parameter Name="Values">
          <Array>
            <Source>
              <Command Name="MaterialListFormShowCommand" Parameter="MaterialArrayToDelete" />
            </Source>
            <Select>
              <Items>
                <Item Type="Value">0</Item>
                <Item Type="Value">0</Item>
                <Item Type="Value">False</Item>
                <Item Type="Value">True</Item>
                <Item Type="Value">False</Item>
              </Items>
            </Select>
          </Array>
        </Parameter>
        <Parameter Name="ReplicateValues">False</Parameter>
      </Parameters>
    </Property>
  </DataConnection>
</Command>

В поле Deleted ставим значение True - тем самым помечаем для удаления. Дополнительно сбрасываем значения итоговой цены и количества товара в позиции.

Позиций заказа, которые еще не сохраняли в базе данных, удаляем из OrderPositionConvertDataConnection:

TemplateOrderEdit.xml
<Command Name="OrderPositionToDeleteValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <DataConnection Name="OrderPositionConvertDataConnection">
    <Property Name="DeleteRowsByIndices">
      <Parameters>
        <Parameter Name="Value">
          <Array>
            <Source>
              <Object Name="MaterialArrayToDeleteVariable" />
            </Source>
            <Filter>
              <Index Value="1" /><!-- IsNew -->
              <Value>True</Value>
            </Filter>
            <Select>
              <Items>
                <Item Type="Index">0</Item><!-- RowIndex -->
              </Items>
            </Select>
          </Array>
        </Parameter>
      </Parameters>
    </Property>
  </DataConnection>
</Command>

Отлично! Добавим Новые команды в Execution:

TemplateOrderEdit.xml
<Execution>
  <ConditionExpression>
    <Condition Name="MaterialListFormUpdatedTrueEqualCondition" />
  </ConditionExpression>
  <Commands>
    <Command Name="OrderPositionToAddCommand" />
    <Command Name="OrderPositionToEditCommand" />
    <Command Name="OrderPositionToDeleteFromDatabaseCommand" />
    <Command Name="OrderPositionToDeleteValueSetCommand" />
  </Commands>
</Execution>

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

Удаление позиции заказа

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

SwipeMenu

Свайп-меню настраивается в тэге <SwipeMenu>, определенном в CollectionView. Добавим в описание объекта OrderPositionCollectionView следующий код:

<SwipeMenu>
  <RightItems>
    <SwipeItem>
      <Text>Удалить</Text>
      <ImageSource>Images\delete.png</ImageSource>
      <BackColor>SoftRed</BackColor>
      <Commands> </Commands>
    </SwipeItem>
  </RightItems>
</SwipeMenu>

Тэг <RightItems> содержит список элементов меню <SwipeItem>, которые будут отображаться справа от карточки при свайпе влево. Если необходимо отображать меню слева от карточки (при свайпе вправо), то используем тэг <LeftItems>.

Элементы свайп-меню описываются тегом <SwipeItem>, в котором указывается текст надписи, путь до изображения, цвет фона и последовательность команд.

Добавим в файл описание используемого цвета для кнопки удаления:

TemplateOrderEdit.xml
<Color Name="SoftRed" Red="220" Green="90" Blue="90" Alpha="255" />

Запустим приложение, чтобы проверить отображение свайп-меню:

Команды удаления

Первым делом создадим команду для подтверждения у пользователя намерения удалить выбранный товар:

TemplateOrderEdit.xml
<Command Name="OrderPositionDeleteMessageBoxCommand" Type="MessageBoxCommand" Assembly="Commands">
  <Caption>Удаление</Caption>
  <Text>Вы действительно хотите удалить выбранный товар?</Text>
  <Buttons Type="OkCancel" />
</Command>

Команду OrderPositionDeleteMessageBoxCommand добавим в тэг <Commands> для <SwipeMenu> списка позиций заказа.

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

TemplateOrderEdit.xml
<Condition Name="SelectedOrderPositionIsNewEqualCondition" Type="EqualCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Object Name="OrderPositionCollectionView">
        <Property Name="SelectedItemValueByFieldName">
          <Parameters>
            <Parameter Name="FieldName">IsNew</Parameter>
          </Parameters>
        </Property>
      </Object>
    </Item>
    <Item>True</Item>
  </Items>
</Condition>

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

Создадим Execution, который будет отслеживать значение параметра Ok команды OrderPositionDeleteMessageBoxCommand, и в зависимости от условия SelectedOrderPositionIsNewEqualCondition будет вызывать одну из двух созданных выше команд.

Запустим приложение и проверим удаление позиций заказа.

Отлично! На следующем уроке рассмотрим работу с JSON на форме, для удобного сохранения

Итоги

На уроке мы рассмотрели .

Ответы

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

Архив не содержит xml-файлы форм десктопного приложения.

Первым делом создадим , который будет преобразовывать OrderPositionPrimaryGetDataConnection:

С родительской формы передаем параметр MaterialArray, добавим его на форму, а чтобы было удобно работать с передаваемым массивом данных создадим :

Создайте самостоятельно команду SaveSequentialCommand типа , оставив ее пока пустой.

ConvertDataConnection
ArrayGetDataConnection
SequentialCommand
3KB
lesson4-Images.zip
archive