Урок 3. Форма списка

Список

SQL-запрос

На основе запроса OrderSelectSqlQuery, который описан в серверном xml-файле, создадим запрос для получения списка заказов для мобильного приложения:

Template.xml
<SqlQuery Name="AppOrderSelectSqlQuery">
  <Text>
    WITH _filter AS (
      SELECT
        date_trunc('week', CURRENT_DATE::timestamp) AS start_date,
        date_trunc('week', CURRENT_DATE::timestamp) + interval '7d - 1s' AS end_date
    ), _order AS (
      SELECT
        O.order_id,
        O.client_id,
        C.city_id,
        O.order_number,
        O.order_date
      FROM
        template.order O
        LEFT JOIN template.client C USING(client_id)
        LEFT JOIN _filter ON true
      WHERE
        O.added AND
        NOT O.deleted AND
        O.order_date BETWEEN convert_date_filter(_filter.start_date)
                         AND convert_date_filter(_filter.end_date)
    ), _order_position AS (
      SELECT
        O.order_id,
        SUM(OP.quantity * OP.unit_price) AS total_cost,
        COUNT(O.order_id) AS position_count
      FROM
        _order O
        JOIN template.order_position OP USING(order_id)
        LEFT JOIN template.material M USING(material_id)
      GROUP BY O.order_id
    ), _payment AS (
      SELECT
        O.order_id,
        SUM(C.summ) AS total_payment_summ
      FROM
        _order O
        JOIN template.order_payment OP USING(order_id)
        JOIN template.cash C USING(cash_id)
      WHERE NOT C.deleted
      GROUP BY O.order_id
    )
    
    SELECT
      O.order_id AS "OrderId",
      '№ ' || O.order_number AS "OrderNumber",
      'от ' || to_char(convert_date_to_user_timezone(O.order_date), 'dd.mm.yyyy') AS "OrderDate",
      client.title AS "ClientTitle",
      OP.position_count AS "PositionCount",
      'Сумма ' || trim(to_char(COALESCE(OP.total_cost, 0), '999G999G999D00') || ' руб.') AS "TotalOrderCost",
      (COALESCE(OP.total_cost, 0) - COALESCE(P.total_payment_summ, 0)) <![CDATA[<=]]> 0 AS "IsPaid"
    FROM
      _order O
      LEFT JOIN _order_position OP USING(order_id)
      LEFT JOIN _payment P USING(order_id)
      LEFT JOIN template.client USING(client_id)
    ORDER BY O.order_number ASC;
  </Text>
</SqlQuery>

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

Номер и дату заказа (OrderNumber и OrderDate) выводим разными строками, заранее задав им нужный формат. Обратите внимание, что дату заказа сразу приводим к часовой зоне пользователя, чтобы она отображалась корректно. Как вы помните, при передаче даты со временем в формате строки, платформа не делает автоматическое приведение временных зон. И без ручного приведения временной зоны, даты со временем останется во временной зоне сервера. Работа с датами со временем подробно рассматривается в статье Временные зоны.

В запрос добавили поле PositionCount, хранящее количество позиций заказа, и поле IsPaid - признак полной оплаты заказа. Теперь возвращаем имена клиентов вместо идентификаторов, т.к. в отличии от таблицы DatabaseTable список CollectionView не поддерживает Substitution.

Перейдем в файл главной формы (TemplateMainForm.xml).

Самостоятельно создайте соответствующий PrimaryGetDataConnection.

Теперь создадим объект типа CollectionView, который представляет данные в виде списка карточек:

TemplateMainForm.xml
<MyObject Name="CollectionView" Type="CollectionView" Assembly="WorkflowForms">
  <Top>10</Top>
  <Left>10</Left>
  <Height>
    <Calculate>
      <Expression>{0} - {1} - 10</Expression>
      <Items>
        <Item>
          <Object Name="ContentPanel">
            <Property Name="Height" />
          </Object>
        </Item>
        <Item>
          <Object Name="CollectionView">
            <Property Name="Top" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Height>
  <Width>
    <Calculate>
      <Expression>{0} - {1}*2</Expression>
      <Items>
        <Item>
          <Object Name="ContentPanel">
            <Property Name="Width" />
          </Object>
        </Item>
        <Item>
          <Object Name="CollectionView">
            <Property Name="Left" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Width>
  <EmptyText>Список заказов пуст</EmptyText>
  <ItemBorderColor>#BBDEFB</ItemBorderColor>
  <SourceDataConnection Name="OrderPrimaryGetDataConnection" />
  <TemplateItems>
  
  </TemplateItems>
</MyObject>

Основные моменты:

  • тэг <EmptyText> содержит текст-заглушку, который будет отображаться, если в списке нет записей;

  • В тэге <SourceDataConnection> указывается источник данных, каждой записи (строке) которого будет соответствовать отдельная карточка;

  • В тэге <TemplateItems> описывается набор элементов шаблона карточки

Шаблон карточки

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

Номер заказа - это простое текстовое поле, для него будем использовать элемент типа TemplateItemLabel:

<TemplateItem Type="TemplateItemLabel">
  <Top>0</Top>
  <Left>0</Left>
  <Text Binding="OrderNumber" />
  <FontStyle>CollectionTitleFont</FontStyle>
  <TextColor>#536DFE</TextColor>
</TemplateItem>

В тэгах <Top> и <Left> задаются абсолютные значения отступов от границ контейнера, с учетом внутренних отступов контейнера для его содержимого.

В тэге <Text> указывается текст, отображаемый в поле на карточке. При этом можно указать текст значением тэга, тогда такой текст будет одинаков на всех карточках. А можно указать атрибут Binding с именем одного из полей источника данных из тэга <SourceDataConnection>, в таком случае значение будет индивидуально для каждой карточки. Значение тэга <Text> и атрибут Binding вместе не работают - предпочтение отдается атрибуту.

В тэгах <FontStyle> и <TextColor> настраиваем стиль текста, его цвет и размер.

Для дата заказа также будем использовать элемент типа TemplateItemLabel:

<TemplateItem Type="TemplateItemLabel">
  <Top>0</Top>
  <Left>1</Left>
  <Text Binding="OrderDate" />
  <AbsoluteLayoutFlags>XProportional</AbsoluteLayoutFlags>
</TemplateItem>

Так как поле должно отображаться в правом верхнем углу, то координату <Left> переведем из абсолютного значения в пропорциональное. Для этого используем тэг <AbsoluteLayoutFlags> со значением XProportional. Теперь в тэге <Left> указываем не величину сдвига, а коэффициент, который будет использоваться для расчета координаты по формуле:

координата_Х = (ширина_контейнера - ширина_элемента) * пропорциональное_значение_Х

Аналогичным образом можно задавать пропорциональное значение для координаты <Top> - YProportional, а так же одновременно для координат <Top> и <Left> - PositionProportional. Пропорции можно задавать для ширины и высоты элемента - WidthProportional и HeightProportional соответственно, а так же одновременно для высоты и ширины - SizeProportional. Если необходимо для всех четырех значений использовать пропорциональные, то следует указывать All.

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

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

<TemplateItem Type="TemplateItemLabel">
  <Top>25</Top>
  <Left>0</Left>
  <Text Binding="ClientTitle" />
  <FontStyle>CollectionSubTitleFont</FontStyle>
</TemplateItem>

<TemplateItem Type="TemplateItemLabel">
  <Top>50</Top>
  <Left>0</Left>
  <Text Binding="TotalOrderCost" />
  <FontStyle>CollectionSubTitleFont</FontStyle>
</TemplateItem>

Значение координаты <Top> придется подобрать вручную.

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

<TemplateItem Type="TemplateItemLabel">
  <Top>40</Top>
  <Left>1</Left>
  <Height>25</Height>
  <AbsoluteLayoutFlags>XProportional</AbsoluteLayoutFlags>
  <Visible Binding="IsPaid"/>
  <Text>Оплачен</Text>
  <TextColor>#2042fe</TextColor>
  <FontStyle>CollectionStatusFont</FontStyle>
  <BackgroundColor>#BBDEFB</BackgroundColor>
  <CornerRadius>7</CornerRadius>
  <TextAlignment Vertical="Center" Horizontal="Center" />
  <Padding Left="15" Top="0" Right="15" Bottom="0" />
</TemplateItem>

В качестве условия видимости элемента в атрибуте Binding тэга <Visible> укажем поле IsPaid.

Чтобы статус визуально выделялся, зададим цвет фона текста в тэге <BackgroundColor> и радиус скругления углов фона в тэге <CornerRadius>. Зададим высоту элемента в 25 единиц.

Чтобы текст был по центру блока, используем тэг <TextAlignment>, с указанием правил выравнивания для вертикали и горизонтали.

Тэг <Padding> добавит внутренние отступы текста от границ фона. слева и справа укажем - так блок будет выглядеть симпатичнее.

Стоит помнить, что по умолчанию текст всегда выравнивается по верхней части своего блока. Если высота элемента будет больше, например, 40, то внутреннего отступа в 2е единицы будет недостаточно. В таком случае лучше :

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

Полный код CollectionView
<MyObject Name="CollectionView" Type="CollectionView" Assembly="WorkflowForms">
  <Top>10</Top>
  <Left>10</Left>
  <Height>
    <Calculate>
      <Expression>{0} - {1} - 10</Expression>
      <Items>
        <Item>
          <Object Name="ContentPanel">
            <Property Name="Height" />
          </Object>
        </Item>
        <Item>
          <Object Name="CollectionView">
            <Property Name="Top" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Height>
  <Width>
    <Calculate>
      <Expression>{0} - {1}*2</Expression>
      <Items>
        <Item>
          <Object Name="ContentPanel">
            <Property Name="Width" />
          </Object>
        </Item>
        <Item>
          <Object Name="CollectionView">
            <Property Name="Left" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Width>
  <EmptyText>Список заказов пуст</EmptyText>
  <RefreshCommand>
    <Command Name="DataConnectionRefreshCommand" />
  </RefreshCommand>
  <SourceDataConnection Name="OrderPrimaryGetDataConnection" />
  <TemplateItems>
    <TemplateItem Type="TemplateItemLabel">
      <Top>0</Top>
      <Left>0</Left>
      <Text Binding="OrderNumber" />
      <FontStyle>CollectionTitleFont</FontStyle>
      <TextColor>#536DFE</TextColor>
    </TemplateItem>
    <TemplateItem Type="TemplateItemLabel">
      <Top>0</Top>
      <Left>1</Left>
      <Text Binding="OrderDate" />
      <AbsoluteLayoutFlags>XProportional</AbsoluteLayoutFlags>
    </TemplateItem>
    <TemplateItem Type="TemplateItemLabel">
      <Top>25</Top>
      <Left>0</Left>
      <Text Binding="ClientTitle" />
      <FontStyle>CollectionSubTitleFont</FontStyle>
    </TemplateItem>
    <TemplateItem Type="TemplateItemLabel">
      <Top>50</Top>
      <Left>0</Left>
      <Text Binding="TotalOrderCost" />
      <FontStyle>CollectionSubTitleFont</FontStyle>
      <AbsoluteLayoutFlags>XProportional</AbsoluteLayoutFlags>
    </TemplateItem>
    <TemplateItem Type="TemplateItemLabel">
      <Top>40</Top>
      <Left>1</Left>
      <Height>25</Height>
      <AbsoluteLayoutFlags>XProportional</AbsoluteLayoutFlags>
      <Visible Binding="IsPaid" />
      <Text>Оплачен</Text>
      <TextColor>#2042fe</TextColor>
      <FontStyle>CollectionStatusFont</FontStyle>
      <BackgroundColor>#BBDEFB</BackgroundColor>
      <CornerRadius>7</CornerRadius>
      <Padding Left="15" Top="2" Right="15" Bottom="2" />
    </TemplateItem>
  </TemplateItems>
</MyObject>

Клик по карточке

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

Чтобы получить значение поля из строки источника данных, соответствующей выделенной карточке, будем использовать get-проперти SelectedItemValueByFieldName, которое принимает один параметр FieldName с именем этого поля. Например, получим значение из поля OrderId:

<Object Name="CollectionView">
  <Property Name="SelectedItemValueByFieldName">
    <Parameters>
      <Parameter Name="FieldName">OrderId</Parameter>
    </Parameters>
  </Property>
</Object>

При этом в шаблоне карточки может не быть определен элемент привязанный к этому полю.

Создадим команду на открытие формы редактирования заказа и передадим в ней параметр со значением OrderId выбранного заказа:

<Command Name="OrderEditFormShowCommand" Type="FormShowCommand" Assembly="Commands">
  <Xml Type="Path">TemplateOrderEdit.xml</Xml>
  <Show Type="None" />
  <Parameters>
    <Parameter Name="OrderId">
      <Object Name="CollectionView">
        <Property Name="SelectedItemValueByFieldName">
          <Parameters>
            <Parameter Name="FieldName">OrderId</Parameter>
          </Parameters>
        </Property>
      </Object>
    </Parameter>
  </Parameters>
</Command>

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

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

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

<Execution>
  <ConditionExpression>
    <Condition Name="CollectionViewSelectedItemChangedEqualCondition" />
  </ConditionExpression>
  <Commands>
    <Command Name="OrderEditFormShowCommand" />
  </Commands>
</Execution>

Карточка заказа

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

Итоги

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

Ответы

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

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

Last updated