Урок 6. Главный экран
Фильтры
В мобильном приложение сделаем фильтры заказов на конкретный день, который пользователь будет выбирать в календаре. По умолчанию будут отображаться заказы на текущий день.
В конце раздела главная форма будет иметь вид:
Первым делом скорректируем запрос AppOrderSelectSqlQuery для получение списка заказов. Ранее мы делали заглушку, чтобы запрос возвращал все заказы за текущую неделю. Теперь удалим ограничения по дате и будем возвращать все не удаленные заказы. В рамках учебного проекта такое допущение оправдано небольшим количеством заказов. В реальном приложении нужно использовать более гибкую систему фильтрации.
В запросе AppOrderSelectSqlQuery получаем дату заказа в виде строки, для фильтра понадобится исходное значение даты, для этого внесем в запрос изменения:
Переименуем поле OrderDate в OrderDateString;
Добавим в запрос поле OrderDate, в котором будет храниться исходное значение даты заказа.
Внесите соответствующие правки в xml-файл формы, а именно в OrderPrimaryGetDataConnection и OrderCollectionView.
Для фильтрации заказов создадим объект Calendar, добавим в ContentPanel его описание:
<MyObject Name="Calendar" Type="Calendar" Assembly="BaseControls">
<Top>0</Top>
<Left>0</Left>
<Height>330</Height>
<Width>
<Object Name="ContentPanel">
<Property Name="Width" />
</Object>
</Width>
<BackColor>
<Switch>
<Case>
<When>
<Condition Name="AppThemeDarkEqualCondition" />
</When>
<Then>Black</Then>
</Case>
<Case>White</Case>
</Switch>
</BackColor>
<DaysTitleHeight>15</DaysTitleHeight>
<DayViewSize>35</DayViewSize>
<CalendarSectionShown>False</CalendarSectionShown>
<TodayOutlineColor>SoftBlue</TodayOutlineColor>
<EventIndicatorColor>SoftBlue</EventIndicatorColor>
<SelectedDayBackgroundColor>SoftBlue</SelectedDayBackgroundColor>
<EventList>
<DataConnection SourceDataConnection="OrderPrimaryGetDataConnection">
<Fields>
<Field Name="OrderDate"/>
<Field Name="OrderId" />
</Fields>
</DataConnection>
</EventList>
</MyObject>
Объект Calendar имеет свойство CalendarSectionShown, которое отвечает за формат отображения объекта. Если свойство имеет значение False, то объект отображается в виде выбранной даты, иначе - в виде календаря. Режимы отображения можно переключать жестами. Если тапнуть по дате, то объект развернется в календарь. А если сделать свайп по календарю снизу вверх, то календарь свернется в режим отображения даты. По умолчанию для календаря задали режим отображения только даты, т.к. при запуске приложения пользователю важно видеть заказы на текущую дату.
В тэге EventList передаем таблицу из двух колонок: дата и идентификатор заказа. Эти данные будут использоваться для отметки дней в календаре, на которые есть события. Такие дни будут помечаться небольшой точкой под датой.
Для объекта OrderCollectionPanel скорректируем координаты Top и Height с учетом календаря.
Теперь необходимо создать SecondaryGetDataConnection, в котором будем фильтровать исходный список заказов по выбранной в календаре дате:
<DataConnection Name="OrderSecondaryGetDataConnection" Type="SecondaryGetDataConnection" Assembly="DataConnections">
<SourceDataConnection Name="OrderPrimaryGetDataConnection" />
<Filter>
<Field NativeName="OrderDate" />
<Value>
<Object Name="Calendar">
<Property Name="SelectedDate" />
</Object>
</Value>
<DataType Type="DateDataType" />
</Filter>
</DataConnection>
У объекта Calendar используем get-проперти SelectedDate, которое возвращает значение выбранной даты.
В качестве источника данных для OrderCollectionView укажем новый DataConnection.
Запустим приложение и проверим работу фильтра по дате:
Автоматическое обновление данных
UpdateInterval
PrimaryGetDataConnection имеет необязательный вложенный тэг <UpdateInterval>
, который включает механизм автоматического обновления данных и задает интервал обновлений.
Перейдем в файл стартовой формы и в описание OrderPrimaryGetDataConnection добавим вложенный тэг:
<UpdateInterval Seconds="20" />
Таким образом, мы задаем интервал обновления в 20 секунд, и DataConnection уже самостоятельно следит за выполнением обновлений.
Запустите экземпляр клиентской части на десктопе и мобильное приложение. На десктопе добавьте заказ и проверьте, что в мобильном приложении на главной форме появился этот заказ без необходимости вручную обновлять список.
Минус такого обновления данных в том, что клиентская часть будет всегда обращаться на сервер за данными, даже если данные не изменились.
Уберем внесенные изменения.
Timer
В WT-платформе реализован объект Timer, выполняющий последовательность команд с заданным интервалом. Этот объект можно использовать для обновления данных в таблице. Например, вместо подписки главной формы на событие, можно с помощью объекта Timer задать интервал выполнения команды OrderDataConnectionRefreshCommand.
Давайте посмотрим, как это можно реализовать.
Вернемся в файл главного экрана и создадим объект типа Timer:
<MyObject Name="Timer" Type="Timer" Assembly="SimpleControls">
<ManualStart>True</ManualStart>
<Interval>20000</Interval>
<Commands>
<Command Name="OrderDataConnectionRefreshCommand" />
</Commands>
</MyObject>
В тэге <Commands>
прописали вызов команды на обновление данных о заказах. А в качестве интервала для тестов указали 20 секунды.
Запустите экземпляр клиентской части на десктопе и мобильное приложение. На десктопе добавьте заказ и проверьте, что в мобильном приложении на главной форме появился этот заказ без необходимости вручную обновлять список.
Минус такого обновления данных тот же: клиентская часть будет всегда обращаться на сервер за данными, даже если данные не изменились.
Уберем внесенные изменения.
Рассылка событий
Самым лучшим способом обновления данных является механизм подписки форм на событие, которое генерирует серверная часть при выполнении запроса на изменении данных в базе. Механизм состоит из двух частей: генерация события сервером и подписка формы на событие.
Генерация события
Для тэга <SqlQuery>
существует вложенный тэг <Events>
, содержащий список событий, которые будут переданы на все клиентские приложения после выполнения запроса.
Таким образом, сервер может генерировать несколько событий одновременно, и на каждое событие могут быть подписаны разные формы.
Каждое событие, описываемое тэгом <Event>
, содержит набор параметров, передаваемых на форму, подписанную на это событие.
В серверном xml-файле есть запрос OrderUpdateSqlQuery, который генерирует событие ChangedNumberOfOrders:
<SqlQuery Name="OrderUpdateSqlQuery">
<Events>
<Event Name="ChangedNumberOfOrders">
<Parameters>
<Parameter Name="NumberOfOrders">
SELECT
count(*)
FROM
template.order O
WHERE
O.added AND
NOT O.deleted;
</Parameter>
</Parameters>
</Event>
</Events>
<Text>
UPDATE template.order
SET
order_number = {OrderNumber},
order_date = {OrderDate},
client_id = {ClientId},
description = {Description},
added = true
WHERE
order_id = {OrderId};
</Text>
</SqlQuery>
Каждый раз, когда любой пользователь будет сохранять изменения в заказе, сервер будет рассылать это событие всем клиентским частям и мобильным приложениям.
Подписание формы на событие
Чтобы подписать форму на какое-либо событие, необходимо создать объект типа Subscriber, в котором указать имя события или имена нескольких событий.
Давайте перейдем в файл главной формы (TemplateMainForm.xml) и подпишем ее на событие ChangedNumberOfOrders:
<MyObject Name="NumberOfOrdersSubscriber" Type="Subscriber" Assembly="SimpleControls">
<Subscribes>
<Subscribe>ChangedNumberOfOrders</Subscribe>
</Subscribes>
</MyObject>
Теперь необходимо проверять, что событие сработало. Для этого воспользуемся get-проперти Subscribe, которое возвращает название события последнего принятого сообщения:
<Condition Name="NumberOfOrdersSubscriberChangedCondition" Type="ChangedCondition" Assembly="Conditions">
<Items>
<Item>
<Object Name="NumberOfOrdersSubscriber">
<Property Name="Subscribe" />
</Object>
</Item>
</Items>
</Condition>
Так как у нас только одно событие, на которое подписана главная форма, то достаточно использовать условие ChangedCondition.
Если бы главная форма была подписана на несколько событий, то мы бы использовали EqualCondition, чтобы сопоставлять значение get-проперти с именами событий:
<Condition Name="ChangedNumberOfOrdersSubscriberEqualCondition" Type="EqualCondition" Assembly="Conditions">
<Items>
<Item>
<Object Name="Subscriber">
<Property Name="Subscribe" />
</Object>
</Item>
<Item>ChangedNumberOfOrders</Item>
</Items>
</Condition>
<Condition Name="SecondEventNameSubscriberEqualCondition" Type="EqualCondition" Assembly="Conditions">
<Items>
<Item>
<Object Name="Subscriber">
<Property Name="Subscribe" />
</Object>
</Item>
<Item>SecondEventName</Item>
</Items>
</Condition>
А еще можно использовать get-проперти Parameter, чтобы получать значение конкретного параметра и применять его в условии.
Создадим Execution, который по условию срабатывания события будет вызывать команду на обновление:
<Execution>
<ConditionExpression>
<Condition Name="NumberOfOrdersSubscriberChangedCondition" />
</ConditionExpression>
<Commands>
<Command Name="OrderDataConnectionRefreshCommand" />
</Commands>
</Execution>
Запустите экземпляр клиентской части на десктопе и мобильное приложение. На десктопе добавьте заказ и проверьте, что в мобильном приложении на главной форме появился этот заказ без необходимости вручную обновлять список.
Итоги
На уроке мы рассмотрели
Ответы
В архиве присутствуют xml-файлы форм для мобильного приложения и серверный xml-файл, также лежит бэкап базы данных и файл с запросами на изменение структуры базы данных - с помощью файлов можете проверить себя.
Архив не содержит xml-файлы форм десктопного приложения.
Last updated