WT. Практика (Desktop)
Платформа WTПрактикаСинтаксисБаза знаний
  • Приветствие
  • Основной
    • Урок 1. Форма списка и добавление записей
    • Урок 2. Редактирование таблицы
    • Урок 3. Выпадающий список
    • Урок 4. Паттерн onClose
    • Урок 5. Удаление связанных данных
    • Урок 6. Главная форма
    • Урок 7. Фильтры
    • Урок 8. Редактирование выпадающего списка
    • Урок 9. Список категорий
    • Урок 10. Паттерн Add/Edit
    • Урок 11. Экспорт данных в документ
    • Урок 12. Дерево в таблице
    • Урок 13. Самостоятельная
    • Урок 14. Постраничный просмотр
    • Дополнительно
      • Array
      • ArrayGetDataConnection
      • ConvertDataConnection
  • Загрузка данных
    • Урок 15. Режимы загрузки данных
    • Урок 16. Режим блокировки форм (Lock)
  • Многопользовательский режим
    • Урок 17. Аутентификация пользователей в программе
    • Урок 18. Права доступа
    • Урок 19. Динамические права доступа
    • Урок 20. Пользовательские настройки
    • Урок 21. Автоматическое обновление данных
  • Кастомизация
    • Урок 22. Создание кастомных команд для форм
    • Урок 23. Создание кастомных команд для серверной части
    • Урок 24. Планировщик задач
  • Продвинутый уровень
    • Урок 25. Создание API-запросов
    • Урок 26. Работа с JSON на форме
    • Урок 27. Разделение формы на несколько файлов
Powered by GitBook
On this page
  • Права доступа
  • Часовой пояс
  • Форма
  • Мультиязычность
  • Настройка языка
  • Изменение языка приложения
  • Доработка форм
  • Как это работает
  • Другие замены
  • Поддержка языков в SQL-запросах
  • Самостоятельно
  • Пользовательские настройки форм
  • Настройки таблиц
  • Состояние формы
  • Значение Variable
  • Итоги
  • Ответы
  1. Многопользовательский режим

Урок 20. Пользовательские настройки

Last updated 1 year ago

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

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

При разворачивании проекта используйте 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;

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

Часовой пояс

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

Форма

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

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

Запросы

Добавим запрос на получение списка часовых поясов:

Template.xml
<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>

Добавим запросы на чтение и изменение значения пользовательской временной зоны:

Template.xml
<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.

Формы

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

Добавим условие проверки изменения часового пояса:

TemplateUserSettings.xml
<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>

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

TemplateUserSettings.xml
<Command Name="UserTimeZoneInfoChangedMessageBoxCommand" Type="MessageBoxCommand" Assembly="Commands">
  <Caption>Изменение часового пояса пользователя</Caption>
  <Text>ВНИМАНИЕ!\rВыполните перезапуск программы.</Text>
  <Icon Type="Information" />
  <Buttons Type="Ok" />
</Command>

Скорректируем команду SaveSequentialCommand:

TemplateUserSettings.xml
<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>

Запустите приложение и проверьте смену часового пояса.

Мультиязычность

Помимо выбора собственной часовой зоны пользователь может настраивать язык интерфейса своей клиентской части.

Настройка языка

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

Добавим запрос на получения списка поддерживаемых языков:

Template.xml
<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:

Template.xml
<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 - так будет более наглядно:

TemplateUserSettings.xml
<Parameter Name="LanguageUpdated">False</Parameter>

Скорректируйте команду типа ValueSetCommand, которая будет присваивать этому параметру значение True, если значение языка изменилось.

Добавим условие проверки изменения зыка:

TemplateUserSettings.xml
<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:

TemplateUserSettings.xml
<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:

Template.xml
<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:

TemplateStart.xml
<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:

TemplateStart.xml
<Command Name="UserCurrentDataConnectionRefreshCommand" Type="DataConnectionRefreshCommand" Assembly="Commands">
  <DataConnections>
    <DataConnection Name="UserCurrentPrimaryGetDataConnection" />
  </DataConnections>
</Command>
TemplateStart.xml
<Command Name="LocaleSetCommand" Type="LocaleSetCommand" Assembly="Commands">
  <Locale>
    <DataConnection SourceDataConnection="UserCurrentPrimaryGetDataConnection">
      <Fields>
        <Field Name="LanguageCode" />
      </Fields>
    </DataConnection>
  </Locale>
</Command>

Соберем обе команды в одну последовательность:

TemplateStart.xml
<Command Name="LocaleSequentialCommand" Type="SequentialCommand" Assembly="Commands">
  <Commands>
    <Command Name="UserCurrentDataConnectionRefreshCommand" />
    <Command Name="LocaleSetCommand" />
  </Commands>
</Command>

Добавим Execution на параметр LanguageUpdated команды UserSettingsFormShowCommand:

TemplateStart.xml
<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:

TemplateUserSettings.xml
<Parameter Name="Title">Пользовательские настройки</Parameter>

Заменим его описание:

TemplateUserSettings.xml
<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>:

TemplateUserSettings.xml
<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 Show="True" Title="[Не выбрано]" />

В файлах строковых ресурсов в общем контексте уже есть нужное значение. Его и будем использовать:

<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.check_strings(), которая проверяет наличие переводов для всех языков и вернет список ключей, у которых отсутствует перевод для какого-либо языка.

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

CREATE OR REPLACE FUNCTION public.check_strings()
  RETURNS character varying AS
$BODY$
DECLARE
  _row record;
  _result varchar[];
BEGIN
  FOR _row IN
    (WITH min_value AS
      (SELECT MIN(language_id) AS val FROM language)
      SELECT
        val,
        language_id
      FROM
        min_value
        CROSS JOIN
        language
       WHERE
        language_id != val
       ORDER BY 2)
  LOOP
    EXECUTE 'SELECT
      array_agg(strings_id)
    FROM
      (SELECT strings_id FROM strings WHERE language_id = ' || _row.val || ') S1
      FULL JOIN
      (SELECT strings_id FROM strings WHERE language_id = ' || _row.language_id || ') S2
      USING(strings_id)
    WHERE
      S1.strings_id IS NULL OR S2.strings_id IS NULL'
    INTO
      _result;

    IF (_result IS NOT NULL) THEN
      RETURN _result::varchar;
    END IF;
  END LOOP;
  
  RETURN NULL;
END;  
$BODY$
  LANGUAGE plpgsql;

Самостоятельно

Переведите все формы на поддержку языков.

Не забудьте перевести строковые константы в запросах для списка назначений платежей и отчета по бюджету. Добавьте строковые ресурсы в таблицу public.strings и используйте функцию public.string_value(character varying, integer) для получения нужного значения, как это сделано в запросе TimeZoneInfoSelectSqlQuery и LoadModeSelectSqlQuery.

Также добавьте перевод для описания динамических прав доступа - запрос PermissionBlockItemSelectSqlQuery. Используйте значения в колонках id_title таблиц template.permission_block и template.permission_block_item в качестве ключа для строковых ресурсов.

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

Пользовательские настройки форм

Все это можно сохранять для каждого пользователя отдельно.

Настройки таблиц

<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)\"\"
    }\",
    {},
    {}
  )"
}

Давайте это сделаем для колонки 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\"\"
      }
    ]\"
  )"
}

Состояние формы

Поддерживается три состояния:

  • 0 - окно с размерами по умолчанию (Normal);

  • 1 - свернутое окно (Minimized);

  • 2 - развернутое окно (Maximized).

Значение Variable

<SaveOnFormClose Value="True" />

Значения переменных сохраняются в колонку variables_info таблицы public.user_form_info.

На форме списка клиентов создайте тестовую переменную, которой укажите тэг <SaveOnFormClose> со значением True и каким-нибудь значением. Откройте и закройте форму списка, а затем проверьте содержимое таблицы public.user_form_info.

Итоги

В этом уроке мы познакомились с поддержкой языков в платформе WT и реализовали пользовательские настройки. Узнали, что на формах можно сохранять настройки видимости, ширину и порядок столбцов таблицы, фильтры и сортировку строк в DatabaseTable, а также состояние самой формы.

Ответы

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

В приложении собрана полная информация о работе с датами со временем в платформе WT.

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

В объекте используется вложенный тэг для настройки отображения NULL-значения выпадающего списка:

Использование атрибута Title в тэге <NullValue>не позволяет заменять его значение на строковый ресурс. Поэтому заменим атрибут Title на тэг . Это позволит динамически задавать отображение NULL-значения объекта, в том числе для поддержки мультиязычности.

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

Для сохранения настроек в таблице DatabaseTable реализован вложенный тэг :

Чтобы можно было менять размер колонок, необходимо в таблице атрибуту Value тэга поставить значение True, а для колонки атрибуту Value тэга поставить значение None.

Чтобы сохранять состояние формы, у тэга есть атрибут . Если значение атрибута равно True, то состояние формы будет запоминаться в колонку form_state таблицы public.user_form_info.

Для сохранения значения переменной Variable реализован вложенный тэг :

"Временные зоны"
LocaleSetCommand
<NullValue>
<NullValueTitle>
DatabaseTable
<SaveOnFormClose>
<AllowResizeColumns>
<AutoSizeMode>
<Form>
RestoreLastFormState
<SaveOnFormClose>
Разворачивание проекта
ссылке
5KB
Language.zip
archive
587KB
lesson20-answer.zip
archive
Ответы