Урок 20. Пользовательские настройки
В этом уроке мы создадим карточку для редактирования пользовательских настроек, в которой каждый пользователь сможет настроить индивидуальные параметры интерфейса. К таким параметрам относится временная зона, относительно которой будут отображаться все даты со временем, и язык интерфейса.
Если хотите начать практику с этого урока, то вам необходимо развернуть учебный проект по инструкции в статье Разворачивание проекта.
При разворачивании проекта используйте backup базы данных, который можете найти в архиве из раздела Ответы прошлого урока. Скопируйте папки Forms, Workflow и Patterns в папку с развернутым проектом, например, в папку D:\WT\Projects\Template\Projects\1. Template.
Инструкция по подключению шаблонов находится по ссылке.
Начнем с настройки прав доступа к новой функциональности.
Права доступа
Перейдем в файл описания работы серверной части приложения (Template.xml) .
Добавим точку доступа UserSettingsEditAccessPoint для просмотра и редактирования пользовательских настроек.
Создадим разрешение UserSettingsPermission, в которое добавим эту точку доступа. Добавим это разрешение в таблицу template.permission и настроим права доступа для пользовательских групп:
INSERT INTO template.permission_block_item(permission_block_id, id_title, title)
SELECT
PB.permission_block_id, 'user_settings', 'Пользовательские настройки'
FROM
template.permission_block PB
WHERE
PB.id_title = 'user_action'
ON CONFLICT (id_title) DO NOTHING;
INSERT INTO template.permission(name, permission_block_item_id)
SELECT
'UserSettingsPermission', PBI.permission_block_item_id
FROM
template.permission_block_item PBI
WHERE
PBI.id_title = 'user_settings'
ON CONFLICT (name) DO NOTHING;
INSERT INTO template.group_permission(group_id, permission_id)
SELECT
group_id,
permission_id
FROM
template.permission P,
template."group" G
WHERE
(G.name = 'AdministratorGroup' OR G.name ISNULL) AND
P.name = 'UserSettingsPermission'
ON CONFLICT (group_id, permission_id) DO NOTHING;
Теперь при добавлении новой функциональности необходимо каждый раз выполнять подобные запросы.
Часовой пояс
В уроках первого блока мы затрагивали тему временных зон и дат со временем. А в этом уроке предоставим пользователю возможность менять временную зону, в которой он хотел бы работать с датами со временем на формах. Это актуально, когда бизнес-процесс предполагает распределенную систему офисов: кол-центр, обрабатывающий заявки, находится в одном часовом поясе, а исполнители заявок в других временных зонах.
В приложении "Временные зоны" собрана полная информация о работе с датами со временем в платформе WT.
Форма

Создайте форму для редактирования пользовательских настроек (TemplateUserSettings.xml):

На главной форме (TemplateStart.xml) в меню добавьте пункт Опции -> Пользовательские настройки..., по которому будет открываться новая форма. Не забудьте использовать UserSettingsEditAccessPoint, чтобы ограничить доступ к пункту меню.

Запросы
Добавим запрос на получение списка часовых поясов:
<SqlQuery Name="TimeZoneInfoSelectSqlQuery">
<Text>
SELECT
time_zone_info_id AS "TimeZoneInfoId",
string_value(id_title, {PublicUserId}) AS "Title",
by_default AS "ByDefault"
FROM
public.time_zone_info;
</Text>
</SqlQuery>
Добавим запросы на чтение и изменение значения пользовательской временной зоны:
<SqlQuery Name="UserSettingsSelectSqlQuery">
<Text>
SELECT time_zone_info_id AS "TimeZoneInfoId"
FROM
public."user"
WHERE
user_id = {PublicUserId};
</Text>
</SqlQuery>
<SqlQuery Name="UserSettingsUpdateSqlQuery">
<Text>
UPDATE public."user"
SET
time_zone_info_id = {TimeZoneInfoId}
WHERE
user_id = {PublicUserId};
</Text>
</SqlQuery>
Добавьте три новых запроса в UserSettingsPermission.
Формы
Если пользователь изменил свой часовой пояс в настройках, то необходимо перезапустить приложение, чтобы новые настройки применились.
Добавим условие проверки изменения часового пояса:
<Condition Name="TimeZoneInfoNotEqualPreviousValueCondition" Type="NotEqualCondition" Assembly="Conditions">
<Items>
<Item>
<Object Name="TimeZoneInfoComboBox" />
</Item>
<Item>
<DataConnection SourceDataConnection="UserSettingsPrimaryGetDataConnection">
<Fields>
<Field Name="TimeZoneInfoId" />
</Fields>
</DataConnection>
</Item>
</Items>
<DataType Type="IntegerDataType" />
</Condition>
Добавим команду отображения сообщения, чтобы уведомить пользователя о необходимости перезапустить программу при сохранении изменений:
<Command Name="UserTimeZoneInfoChangedMessageBoxCommand" Type="MessageBoxCommand" Assembly="Commands">
<Caption>Изменение часового пояса пользователя</Caption>
<Text>ВНИМАНИЕ!\rВыполните перезапуск программы.</Text>
<Icon Type="Information" />
<Buttons Type="Ok" />
</Command>
Скорректируем команду SaveSequentialCommand:
<Command Name="SaveSequentialCommand" Type="SequentialCommand" Assembly="Commands">
<Commands>
<Command Name="UserSettingsSaveCommand" />
<If>
<When>
<Condition Name="TimeZoneInfoNotEqualPreviousValueCondition" />
</When>
<Then>
<Command Name="UserTimeZoneInfoChangedMessageBoxCommand" />
</Then>
</If>
<Command Name="FormCloseCommand" />
</Commands>
</Command>
Запустите приложение и проверьте смену часового пояса.
Мультиязычность
Помимо выбора собственной часовой зоны пользователь может настраивать язык интерфейса своей клиентской части.
Настройка языка
Переделайте форму пользовательских настроек, добавив выпадающий список "Язык":

Добавим запрос на получения списка поддерживаемых языков:
<SqlQuery Name="LanguageSelectSqlQuery">
<Text>
SELECT
language_id AS "LanguageId",
title AS "Title",
by_default AS "ByDefault"
FROM
public.language;
</Text>
</SqlQuery>
Не забудьте добавить этот запрос в UserSettingsPermission.
В запросы UserSettingsSelectSqlQuery и UserSettingsUpdateSqlQuery добавим поле LanguageId:
<SqlQuery Name="UserSettingsSelectSqlQuery">
<Text>
SELECT
time_zone_info_id AS "TimeZoneInfoId",
language_id AS "LanguageId"
FROM
public."user"
WHERE
user_id = {PublicUserId};
</Text>
</SqlQuery>
<SqlQuery Name="UserSettingsUpdateSqlQuery">
<Text>
UPDATE public."user"
SET
language_id = {LanguageId},
time_zone_info_id = {TimeZoneInfoId}
WHERE
user_id = {PublicUserId};
</Text>
</SqlQuery>
Чтобы стартовая форма узнала об изменении языка пользователя, необходимо внести доработки.
Заменим параметр Updated на параметр LanguageUpdated - так будет более наглядно:
<Parameter Name="LanguageUpdated">False</Parameter>
Скорректируйте команду типа ValueSetCommand, которая будет присваивать этому параметру значение True, если значение языка изменилось.
Добавим условие проверки изменения зыка:
<Condition Name="LanguageNotEqualPreviousValueCondition" Type="NotEqualCondition" Assembly="Conditions">
<Items>
<Item>
<Object Name="LanguageComboBox" />
</Item>
<Item>
<DataConnection SourceDataConnection="UserSettingsPrimaryGetDataConnection">
<Fields>
<Field Name="LanguageId" />
</Fields>
</DataConnection>
</Item>
</Items>
<DataType Type="IntegerDataType" />
</Condition>
Скорректируем команду SaveSequentialCommand:
<Command Name="SaveSequentialCommand" Type="SequentialCommand" Assembly="Commands">
<Commands>
<Command Name="UserSettingsSaveCommand" />
<If>
<When>
<Condition Name="TimeZoneInfoNotEqualPreviousValueCondition" />
</When>
<Then>
<Command Name="UserTimeZoneInfoChangedMessageBoxCommand" />
</Then>
</If>
<If>
<When>
<Condition Name="LanguageNotEqualPreviousValueCondition" />
</When>
<Then>
<Command Name="LanguageUpdatedSetCommand" />
</Then>
</If>
<Command Name="FormCloseCommand" />
</Commands>
</Command>
Изменение языка приложения
Скорректируем представление template.user_info, расширив его новыми полями language_id и language_code:
CREATE OR REPLACE VIEW template.user_info AS
SELECT
au.user_id,
pu.user_name,
pu.user_full_name,
pu.person,
NOT pu.enabled AS archive,
pu.user_id AS public_user_id,
pu.language_id,
l.code AS language_code
FROM
template."user" au
JOIN "user" pu ON au.public_user_id = pu.user_id
JOIN public.language l ON l.language_id = pu.language_id;
В серверном xml-файле скорректируем запрос UserCurrentSelectSqlQuery, добавив поле LanguageCode:
<SqlQuery Name="UserCurrentSelectSqlQuery">
<Text>
SELECT
user_id AS "UserId",
user_name AS "UserName",
user_full_name AS "UserFullName",
language_code AS "LanguageCode"
FROM
template.user_info UI
WHERE
user_id = {UserId};
</Text>
</SqlQuery>
Перейдем в файл стартовой формы и в UserCurrentPrimaryGetDataConnection добавим новое поле LanguageCode:
<DataConnection Name="UserCurrentPrimaryGetDataConnection" Type="PrimaryGetDataConnection" Assembly="DataConnections">
<ManualLoad>True</ManualLoad>
<SqlQuery Name="UserCurrentSelectSqlQuery" Type="Select">
<Workflow Name="Template" />
<Fields>
<Field Name="UserId" />
<Field Name="LanguageCode" />
</Fields>
</SqlQuery>
</DataConnection>
Создадим команду обновления для UserCurrentPrimaryGetDataConnection, удалив его из команды AllPrimaryGetDataConnectionRefreshCommand:
<Command Name="UserCurrentDataConnectionRefreshCommand" Type="DataConnectionRefreshCommand" Assembly="Commands">
<DataConnections>
<DataConnection Name="UserCurrentPrimaryGetDataConnection" />
</DataConnections>
</Command>
Создадим команду типа LocaleSetCommand, с помощью которой будем устанавливать локаль и язык перевода для всего приложения:
<Command Name="LocaleSetCommand" Type="LocaleSetCommand" Assembly="Commands">
<Locale>
<DataConnection SourceDataConnection="UserCurrentPrimaryGetDataConnection">
<Fields>
<Field Name="LanguageCode" />
</Fields>
</DataConnection>
</Locale>
</Command>
Соберем обе команды в одну последовательность:
<Command Name="LocaleSequentialCommand" Type="SequentialCommand" Assembly="Commands">
<Commands>
<Command Name="UserCurrentDataConnectionRefreshCommand" />
<Command Name="LocaleSetCommand" />
</Commands>
</Command>
Добавим Execution на параметр LanguageUpdated команды UserSettingsFormShowCommand:
<Execution>
<ConditionExpression>
<Command Name="UserSettingsFormShowCommand" Parameter="LanguageUpdated" />
</ConditionExpression>
<Commands>
<Command Name="LocaleSequentialCommand" />
</Commands>
</Execution>
Добавим вызов команды LocaleSequentialCommand в Execution, которые срабатывают на команды LoginFormShowCommand и ReloginFormShowCommand - тем самым мы будем задавать настройки текущего пользователя при его аутентификации в программе.
Доработка форм
Чтобы интерфейс приложения поддерживал различные языки, необходимо организовать файлы строковых ресурсов и на формах указать привязку к нужным ресурсам.
Файлы строковых ресурсов
Скачайте архив с файлами строковых ресурсов и распакуйте его в папку с формами проекта (\Template\Projects\1. Template\Forms).
В папке Language вы найдете файлы:

По договоренности внутри компании: файлы ru.wxlf и en.wxlf используем для задания строковых ресурсов для форм, а файлы object-ru.wxlf и object-en.wxlf - для кастомных элементов (команд, объектов и т.д.), которые рассмотрим в одном из следующих уроков. На самом деле платформа считывает из папки Forms\Language все файлы одной локали и записывает их в один словарь. И файлов по одной локали может быть сколько угодно - отбор происходит по имени файла, оно должно оканчиваться именем локали (ru/en).
Структура файлов одинакова:
<Wxliff xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Context>
<Text Id="">
<Target></Target>
</Text>
<Text Id="">
<Source></Source>
<Target></Target>
</Text>
</Context>
<Context File="">
<Text Id="">
<Target></Target>
</Text>
<Text Id="">
<Source></Source>
<Target></Target>
</Text>
</Context>
</Wxliff>
Тэг <Wxliff>
содержит список вложенных тэгов <Context>
. Каждый такой тэг привязывается к конкретному файлу, имя которого указывается в атрибуте File
. Если атрибут отсутствует, то такой контекст считается общим для всех форм.
Каждый контекст содержит набор строковых ресурсов, представленных тэгом <Text>
, у которого есть атрибуты:
Id
- уникальный (в рамках контекста) идентификатор ресурса, по которому форма будет его искать. Обязательный атрибут.
Тэг <Text>
содержит тэги:
<Source>
- оригинальный текст, который выступает в качестве подсказки в файле перевода. Необязательный тэг;<Target>
- целевая строка, которая будет использоваться на форме.
В файлах ru.wxlf и en.wxlf есть начальные данные, и указаны два тэга <Context>
: общий контекст с ресурсами, которые используются на многих формах, и контекст для формы TemplateUserSettings.xml.
Использование строкового ресурса на форме
Давайте перейдем в файл формы пользовательских настроек (TemplateUserSettings.xml) и на его примере разберем использование строковых ресурсов.
В файле формы необходимо все строковые константы заменить тэгом <Text>
, значением которого будет строка:
<Text Id=""></Text>
Например, на форме есть параметр Title:
<Parameter Name="Title">Пользовательские настройки</Parameter>
Заменим его описание:
<Parameter Name="Title">
<Text Id="FormTitle">Пользовательские настройки</Text>
</Parameter>
В файлах ru.wxlf и en.wxlf в соответствующем контексте есть строковые ресурсы:
<!-- ru.wxlf -->
<Text Id="FormTitle">
<Target>Пользовательские настройки</Target>
</Text>
<!-- en.wxlf -->
<Text Id="FormTitle">
<Source>Пользовательские настройки</Source>
<Target>User settings</Target>
</Text>
Замените текст объектов LanguageLabel и TimeZoneInfoLabel, используя идентификатор строковых ресурсов из контекста данной формы.
А для текста кнопки SaveButton используйте идентификатор строкового ресурса из общего контекста:
<Text Id="Save">Сохранить</Text>
Запустите приложение и проверьте смену языка.

Как это работает
В процессе загрузки форма считывает файл строковых ресурсов, соответствующий установленной локали приложения, и ищет в нем ресурс с указанным идентификатором.
Сначала форма ищет в общем контексте, если там нет нужного ресурса, то продолжает искать в собственном контексте.
Когда форма находит подходящий ресурс, то берет из него значение тэга <Target> и использует это значение в своих элементах.
Если форма не находит подходящего ресурса в файле строковых ресурсов, то использует текст, который указали в качестве значения тэга <Text>
.
Другие замены
Продолжим работать с формой пользовательских настроек (TemplateUserSettings.xml).
На форме есть команды SaveOnCloseMessageBoxCommand и CloseOnCloseMessageBoxCommand - в них тоже необходимо подобным образом заменить значения тэгов <Caption>
и <Text>
:
<Command Name="SaveOnCloseMessageBoxCommand" Type="MessageBoxCommand" Assembly="Commands">
<Caption>
<Text Id="SaveOnCloseMessageBox.Caption">Сохранение</Text>
</Caption>
<Text>
<Text Id="SaveOnCloseMessageBox.Text">Форма содержит несохраненные изменения.\rСохранить их перед закрытием?</Text>
</Text>
<Icon Type="Question" />
<Buttons Type="YesNoCancel" />
</Command>
<Command Name="CloseOnCloseMessageBoxCommand" Type="MessageBoxCommand" Assembly="Commands">
<Caption>
<Text Id="CloseOnCloseMessageBox.Caption">Закрытие</Text>
</Caption>
<Text>
<Text Id="CloseOnCloseMessageBox.Text">При закрытии все несохраненные изменения будут утеряны.\rВы уверены, что хотите закрыть форму?</Text>
</Text>
<Icon Type="Question" />
<Buttons Type="YesNo" />
</Command>

Текст кнопок в диалоговом окне отображается на том языке, который задан в настройках операционной системы.
Аналогичным образом сделайте с командой UserTimeZoneInfoChangedMessageBoxCommand.
Замена констант
На самом деле подобные подстановки ресурсов мы можем делать с любыми константами на форме.
В качестве примера мы можем привязать ширину кнопки SaveButton к локали.
Добавим в файлы строковых ресурсов значения:
<!-- ru.wxlf -->
<Text Id="SaveButton.Width">
<Source>200</Source>
<Target>200</Target>
</Text>
<!-- en.wxlf -->
<Text Id="SaveButton.Width">
<Source>200</Source>
<Target>100</Target>
</Text>
Запустите приложение и проверьте отображение кнопки при использовании разных языков:

Эти изменения можно убрать - они носили демонстрационный характер.
ComboBox
Перейдем в файл карточки назначения платежа (TemplateOperationEdit.xml) и найдем там описание объекта CategoryComboBox.
В объекте используется вложенный тэг <NullValue>
для настройки отображения NULL-значения выпадающего списка:
<NullValue Show="True" Title="[Не выбрано]" />
Использование атрибута Title
в тэге <NullValue>
не позволяет заменять его значение на строковый ресурс. Поэтому заменим атрибут Title
на тэг <NullValueTitle>
. Это позволит динамически задавать отображение NULL-значения объекта, в том числе для поддержки мультиязычности.
В файлах строковых ресурсов в общем контексте уже есть нужное значение. Его и будем использовать:
<NullValue Show="True" />
<NullValueTitle>
<Text Id="NotChosen">[Не выбрано]</Text>
</NullValueTitle>
На некоторых формах у нас есть фильтр архивных актуальных записей (ArchiveFilterComboBox):
<MyObject Name="ArchiveFilterComboBox" Type="ComboBox" Assembly="BaseControls">
<Top>...</Top>
<Right>...</Right>
<Width>100</Width>
<TabIndex>1</TabIndex>
<NullValue Show="True" Title="Все" />
<AutoCompleteMode>SmartSuggest</AutoCompleteMode>
<ValueList>
<Structure Type="Table">
<Row>
<Item>False</Item>
<Item>Актуальные</Item>
</Row>
<Row>
<Item>True</Item>
<Item>Архивные</Item>
</Row>
</Structure>
</ValueList>
<Value>False</Value>
</MyObject>
При поддержке мультиязычности он примет вид:
<MyObject Name="ArchiveFilterComboBox" Type="ComboBox" Assembly="BaseControls">
<Top>...</Top>
<Right>...</Right>
<Width>100</Width>
<TabIndex>1</TabIndex>
<NullValue Show="True" />
<NullValueTitle>
<Text Id="ArchiveFilterComboBox.All">Все</Text>
</NullValueTitle>
<AutoCompleteMode>SmartSuggest</AutoCompleteMode>
<ValueList>
<Structure Type="Table">
<Row>
<Item>False</Item>
<Item>
<Text Id="ArchiveFilterComboBox.Actual">Актуальные</Text>
</Item>
</Row>
<Row>
<Item>True</Item>
<Item>
<Text Id="ArchiveFilterComboBox.Archival">Архивные</Text>
</Item>
</Row>
</Structure>
</ValueList>
<Value>False</Value>
</MyObject>
Поддержка языков в SQL-запросах
В некоторых запросах мы уже сталкивались с функцией public.string_value(character varying, integer), которая относительно настроек языка пользователя возвращала по ключу из таблицы public.strings соответствующую строку.
В этой таблице хранятся системные строки, редактировать которые пользователи не могут.
Например, в запросе LoadModeSelectSqlQuery для получения списка доступных режимов загрузки данных мы использовали эту функцию. В таблице уже были необходимые строковые ресурсы:

Самостоятельно
Переведите все формы на поддержку языков.
Не забудьте перевести строковые константы в запросах для списка назначений платежей и отчета по бюджету. Добавьте строковые ресурсы в таблицу public.strings и используйте функцию public.string_value(character varying, integer) для получения нужного значения, как это сделано в запросе TimeZoneInfoSelectSqlQuery и LoadModeSelectSqlQuery.
Также добавьте перевод для описания динамических прав доступа - запрос PermissionBlockItemSelectSqlQuery. Используйте значения в колонках id_title таблиц template.permission_block и template.permission_block_item в качестве ключа для строковых ресурсов.
На форму настроек добавьте возможность редактировать временную зону, выбранную по умолчанию для новых пользователей.

Пользовательские настройки форм
Дополнительно к пользовательским настройкам можно отнести настройки видимости, порядка и ширины колонок таблицы DatabaseTable, сортировку строк и фильтры в таблице. Также можно запоминать состояние формы и значения переменных, а при следующем открытии формы восстанавливать их.
Все это можно сохранять для каждого пользователя отдельно.
Настройки таблиц
Для сохранения настроек в таблице DatabaseTable реализован вложенный тэг <SaveOnFormClose>
:
<SaveOnFormClose Columns="True" Filter="True" Sort="True" />
Информация о настройках пишется в таблицу public.user_form_info. Для каждой формы и каждого пользователя отдельная запись.
Давайте перейдем в файл списка клиентов (TemplateClientList.xml) и в таблицу ClientDatabaseTable добавим тэг <SaveOnFormClose>
.
Когда откроем и закроем окно списка клиентов, в базе данных в таблице появится запись:

Значение в колонке name совпадает со значение в атрибуте Name
тэга <Form>
.
Так как мы просто открыли и закрыли форму, то в колонке tables_info хранится информация только о колонках:
{
"(
ClientDatabaseTable,
\"{
\"\"(ClientId,0,0,f)\"\",
\"\"(RowNumber,30,1,t)\"\",
\"\"(Name,0,2,t)\"\",
\"\"(CityTitle,0,3,t)\"\",
\"\"(Phone,150,4,t)\"\",
\"\"(Archive,0,5,f)\"\"
}\",
{},
{}
)"
}
Чтобы можно было менять размер колонок, необходимо в таблице атрибуту Value
тэга <AllowResizeColumns>
поставить значение True, а для колонки атрибуту Value
тэга <AutoSizeMode>
поставить значение None.
Давайте это сделаем для колонки CityTitle (Город). Откроем форму списка клиентов, изменим ширину колонки "Город" и закроем окно. При повторном открытии формы мы увидим, что размер колонки сохранился, а в базе будет запись вида:
{
"(
ClientDatabaseTable,
\"{
\"\"(ClientId,0,0,f)\"\",
\"\"(RowNumber,30,1,t)\"\",
\"\"(Name,0,2,t)\"\",
\"\"(CityTitle,200,3,t)\"\",
\"\"(Phone,150,4,t)\"\",
\"\"(Archive,0,5,f)\"\"
}\",
{},
{}
)"
}
Добавим настраиваемый фильтр в таблицу через контекстное меню, кликнув ПКМ по заголовку таблицы:

В таблицу сохранится запись вида:
{
"(
ClientDatabaseTable,
\"{
\"\"(ClientId,0,0,f)\"\",
\"\"(RowNumber,30,1,t)\"\",
\"\"(Name,0,2,t)\"\",
\"\"(CityTitle,200,3,t)\"\",
\"\"(Phone,150,4,t)\"\",
\"\"(Archive,0,5,f)\"\"
}\",
\"{
\"\"ClassName\"\": \"\"WorkflowForms.Controls.DatabaseTableFilterGroupOperation\"\",
\"\"Condition\"\": \"\"AND\"\",
\"\"OperationList\"\": [{
\"\"Value\"\": \"\"че\"\",
\"\"ClassName\"\": \"\"WorkflowForms.Controls.DatabaseTableFilterStartWithOperation\"\",
\"\"ColumnName\"\": \"\"CityTitle\"\"
}]
}\",
{}
)"
}
Давайте сбросим фильтрацию и добавим настраиваемую сортировку:

Теперь в таблицу сохранится запись вида:
{
"(
ClientDatabaseTable,
\"{
\"\"(ClientId,0,0,f)\"\",
\"\"(RowNumber,30,1,t)\"\",
\"\"(Name,0,2,t)\"\",
\"\"(CityTitle,200,3,t)\"\",
\"\"(Phone,150,4,t)\"\",
\"\"(Archive,0,5,f)\"\"
}\",
{},
\"[
{
\"\"Order\"\": 0,
\"\"SortOrder\"\": \"\"ASC\"\",
\"\"ColumnName\"\": \"\"CityTitle\"\"
},
{
\"\"Order\"\": 1,
\"\"SortOrder\"\": \"\"DESC\"\",
\"\"ColumnName\"\": \"\"Name\"\"
}
]\"
)"
}
Состояние формы
Чтобы сохранять состояние формы, у тэга <Form>
есть атрибут RestoreLastFormState
. Если значение атрибута равно True, то состояние формы будет запоминаться в колонку form_state таблицы public.user_form_info.
Поддерживается три состояния:
0 - окно с размерами по умолчанию (Normal);
1 - свернутое окно (Minimized);
2 - развернутое окно (Maximized).
Значение Variable
Для сохранения значения переменной Variable реализован вложенный тэг <SaveOnFormClose>
:
<SaveOnFormClose Value="True" />
Значения переменных сохраняются в колонку variables_info таблицы public.user_form_info.
На форме списка клиентов создайте тестовую переменную, которой укажите тэг <SaveOnFormClose>
со значением True и каким-нибудь значением. Откройте и закройте форму списка, а затем проверьте содержимое таблицы public.user_form_info.

Итоги
В этом уроке мы познакомились с поддержкой языков в платформе WT и реализовали пользовательские настройки. Узнали, что на формах можно сохранять настройки видимости, ширину и порядок столбцов таблицы, фильтры и сортировку строк в DatabaseTable, а также состояние самой формы.
Ответы
В архиве присутствуют xml-файлы форм и серверный xml-файл, также лежит бэкап базы данных и файл с запросами на изменение структуры базы данных - с помощью файлов можете проверить себя.
Last updated