Урок 3. Выпадающий список

Продолжим совершенствовать наш проект. Создадим форму для списка клиентов и карточку клиента. И заменим стартовую форму проекта. Теперь это будет форма списка клиентов.

На этом уроке:

  • познакомимся со сниппетами, которые ускоряют разработку форм;

  • рассмотрим применение SetDataConnection;

  • рассмотрим возможность передавать значения параметров в момент вызова команды.

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

При разворачивании проекта используйте backup базы данных, который можете найти в архиве из раздела Ответы прошлого урока. Скопируйте папки Forms, Workflow и Patterns в папку с развернутым проектом, например, в папку D:\WT\Projects\Template\Projects\1. Template.

Инструкция по подключению шаблонов находится по ссылке.

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

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

Создание формы

Для создания формы воспользуйтесь паттерном Empty Form (пустая форма с хэдером).

После того как создали файл формы, необходимо подменить стартовую форму. В конфигурационном файле клиентской части (WorkflowForms.dll.config) в параметре StartFormFileName указан путь до стартовой формы. Его оставим без изменений. А переименуем сами xml-файлы форм. Для этого перейдем в папку форм проекта Template\Projects\1. Template\Forms\ и переименуем файлы:

  • файл TemplateStart.xml переименуем в TemplateCityList.xml, т.к. это форма списка городов;

  • а только что созданный TemplateClientList.xml переименуем в TemplateStart.xml, таким образом будущий список клиентов станет стартовой формой.

После этого перейдем в Eclipse и обновим состояние нашего проекта в окне Project Explorer, чтобы редактор увидел изменения в именах файлов. Для этого выберем наш проект и нажмем клавишу F5, либо кликнем правой кнопкой мыши на имени проекта и в открывшемся меню выберем пункт Refresh. Затем перейдем в файл формы списка городов и в тэге <Form> исправим значение атрибута Name на TemplateCityListForm. Это необходимо, чтобы в журнале событий ошибки имели правильное и уникальное имя формы, и мы могли быстро найти нужный файл.

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

Самостоятельно создайте необходимые элементы формы:

  • таблицу клиентов с колонками ClientId, № (RowNumber), ФИО (Name), Город (CityTitle) и Телефон (Phone);

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

Пока нужен только интерфейс.

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

Загрузка данных из базы данных на форму

Создание таблицы client в базе данных

Перейдем в программу для управления СУБД PostgreSQL. И для нашей базы данных template_project выполним следующий скрипт:

  CREATE SEQUENCE template.client_id_seq;
  
  CREATE TABLE template.client
  (
    client_id bigint NOT NULL DEFAULT nextval('template.client_id_seq'::regclass),
    city_id smallint,
    date_of_birth date,
    title character varying,
    email character varying,
    phone character varying,
    CONSTRAINT pk_client_id PRIMARY KEY (client_id),
    CONSTRAINT fk_city_id FOREIGN KEY (city_id)
      REFERENCES template.city (city_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
  );

Обратите внимание, что для колонки client_id используем тип bigint, а для city_id используем тип smallint. Это связано с тем, что таблица template.client потенциально может иметь количество записей, которое превысит максимальное значение типа smallint, равное 32767. В то время, как для таблицы template.city тип bigint будет избыточным.

Описание запроса для чтения таблицы client базы данных

Перейдем в файл описания работы серверной части приложения (Template.xml). Добавим запрос для получения списка клиентов:

Template.xml
<SqlQuery Name="ClientSelectSqlQuery">
  <Text>
    SELECT
      client.client_id AS "ClientId",
      client.title AS "Name",
      city.title AS "CityTitle",
      client.phone AS "Phone"
    FROM
      template.client
      LEFT JOIN template.city USING(city_id)
    ORDER BY client.title;
  </Text>
</SqlQuery>

Создайте ClientViewSqlQueryPermission с этим запросом и дайте права доступа группе GuestGroup.

На форме списка клиентов (TemplateStart.xml) создайте PrimaryGetDataConnection на этот запрос и укажите его в тэге <SourceDataConnection>для таблицы ClientDatabaseTable.

Перезапустите проект. Убедитесь, что форма успешно загружена, и в журнале нет никаких сообщений об ошибках.

Отлично! Теперь можем заняться формой для карточки клиента.

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

Карточка клиента должна содержать поля: ФИО, Дата рождения, Город, Контактный телефон и E-mail. Поле "Город" будет представлено в виде выпадающего списка. Справа от поля "Город" будет кнопка, по которой будет открываться существующая форма со списком городов.

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

С помощью шаблона Entity Form создайте форму для карточки клиента со следующими параметрами:

Одна команда на открытие формы

Вернемся в файл стартовой формы (TemplateStart.xml). Создадим команду FormShowCommand для формы карточки клиента, в которой сразу укажем два параметра:

TemplateStart.xml
<Command Name="ClientEditFormShowCommand" Type="FormShowCommand" Assembly="Commands">
  <Xml Type="Path">TemplateClientEdit.xml</Xml>
  <Show Type="None" />
  <Parameters>
    <Parameter Name="Edit"> </Parameter>
    <Parameter Name="ClientId"> </Parameter>
  </Parameters>
</Command>

Форма списка клиентов в таблице содержит колонки не для всех полей, которые описаны в задании на карточку клиента. Нет смысла перегружать sql-запрос для списка клиентов лишними полями, которые понадобятся на другой форме. В карточке клиента будем использовать дополнительный sql-запрос, в котором для конкретного клиента будем получать все необходимые данные. Для этого нам и понадобится параметр ClientId. В файле карточки клиента (TemplateClientEdit.xml) эти параметры уже есть.

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

Для параметра Edit в качестве значения укажем <Input> со значением по умолчанию False. А для параметра ClientId укажем <Input> без значения.

TemplateStart.xml
<Command Name="ClientEditFormShowCommand" Type="FormShowCommand" Assembly="Commands">
  <Xml Type="Path">TemplateClientEdit.xml</Xml>
  <Show Type="None" />
  <Parameters>
    <Parameter Name="Edit">
      <Input Name="Edit">False</Input>
    </Parameter>
    <Parameter Name="ClientId">
      <Input Name="ClientId" />
    </Parameter>
  </Parameters>
</Command>

Для открытия формы в режиме добавления записи нам не нужно передавать ClientId. А для параметра Edit мы уже указали значение по умолчанию. Таким образом, на кнопке ClientAddButton в тэге <Commands> пропишем простой вызов команды по имени:

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

Для открытия формы в режиме редактирования в команду ClientEditFormShowCommand через Input будем передавать нужные значения параметров. И так как открыть форму карточки клиента можно либо по кнопке "Редактировать запись...", либо по двойному клику по строке в таблице, мы создадим SequentialCommand, в которой будем вызывать ClientEditFormShowCommand:

TemplateStart.xml
<Command Name="ClientEditSequentialCommand" Type="SequentialCommand" Assembly="Commands">
  <Commands>
    <Command Name="ClientEditFormShowCommand">
      <Input Name="Edit">True</Input>
      <Input Name="ClientId">
        <Object Name="ClientDatabaseTable">
          <Property Name="SelectedRowCellValueByColumnName">
            <Parameters>
              <Parameter Name="ColumnName">ClientId</Parameter>
            </Parameters>
          </Property>
        </Object>
      </Input>
    </Command>
  </Commands>
</Command>

Укажем команду ClientEditSequentialCommand в тэге <Commands> кнопки "Редактировать запись...".

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

Также самостоятельно создайте команду типа DataConnectionRefreshCommand на ClientPrimaryGetDataConnection, которая будет выполняться в Execution по изменению параметра Updated у команды ClientEditFormShowCommand.

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

Отлично! Теперь можем приступить к наполнению формы карточки клиента объектами. Но для начала реализуем запрос и загружающее соединение с данными для получения информации по клиенту на форме.

Запросы к базе данных

Перейдем в файл описания работы серверной части приложения (Template.xml). Добавим запрос для получения данных по конкретному клиенту:

Template.xml
<SqlQuery Name="ClientByIdSelectSqlQuery">
  <Text>
    SELECT
      client.title AS "Name",
      client.city_id AS "CityId",
      client.date_of_birth AS "DateOfBirth",
      client.email AS "Email",
      client.phone AS "Phone"
    FROM
      template.client
    WHERE
      client.client_id = {ClientId};
  </Text>
</SqlQuery>

Добавим его в ранее созданный ClientViewSqlQueryPermission.

Давайте сразу сделаем запросы на сохранение изменений в таблицу template.client:

Template.xml
<SqlQuery Name="ClientInsertSqlQuery">
  <Text>
    INSERT INTO template.client(
      city_id,
      date_of_birth,
      title,
      email,
      phone
    )
    VALUES (
      {CityId},
      {DateOfBirth},
      {Name},
      {Email},
      {Phone}
    )
    RETURNING client_id;
  </Text>
</SqlQuery>
    
<SqlQuery Name="ClientUpdateSqlQuery">
  <Text>
    UPDATE template.client
    SET
      city_id = {CityId},
      date_of_birth = {DateOfBirth},
      title = {Name},
      email = {Email},
      phone = {Phone}
    WHERE
      client_id = {ClientId};
  </Text>
</SqlQuery>
    
<SqlQuery Name="ClientDeleteSqlQuery">
  <Text>
    DELETE FROM template.client
    WHERE
      client_id = {ClientId};
  </Text>
</SqlQuery>

Запрос ClientInsertSqlQuery возвращает идентификатор client_id новой записи. Этот идентификатор будем использовать на формах, как результат выполнения команды SaveCommand. Но об этом позже, а пока настроим права доступа к новым запросам.

Соберем новые запросы в ClientEditSqlQueryPermission:

Template.xml
<Permission Name="ClientEditSqlQueryPermission" Type="SqlQueryPermission">
  <SqlQueries>
    <SqlQuery Name="ClientInsertSqlQuery" />
    <SqlQuery Name="ClientUpdateSqlQuery" />
    <SqlQuery Name="ClientDeleteSqlQuery" />
  </SqlQueries>
</Permission>

И сделаем ClientEditRole:

Template.xml
<Role Name="ClientEditRole">
  <Permissions>
    <Permission Name="ClientViewSqlQueryPermission" />
    <Permission Name="ClientEditSqlQueryPermission" />
  </Permissions>
</Role>

Скорректируем права доступа для GuestGroup:

Template.xml
<Group Name="GuestGroup">
  <Roles>
    <Role Name="CityEditRole" />
    <Role Name="ClientEditRole" />
  </Roles>
</Group>

Создание объектов формы

Перейдем в файл карточки клиента (TemplateClientEdit.xml) и первым делом создадим PrimaryGetDataConnection:

TemplateClientEdit.xml
<DataConnection Name="ClientPrimaryGetDataConnection" Type="PrimaryGetDataConnection" Assembly="DataConnections">
  <SqlQuery Name="ClientByIdSelectSqlQuery" Type="Select">
    <Workflow Name="Template" />
    <Fields>
      <Field Name="Name" />
      <Field Name="CityId" />
      <Field Name="DateOfBirth" />
      <Field Name="Email" />
      <Field Name="Phone" />
    </Fields>
    <Parameters>
      <Parameter NativeName="ClientId">
        <Value>
          <Parameter Name="ClientId" />
        </Value>
      </Parameter>
    </Parameters>
  </SqlQuery>
</DataConnection>

Создавать объекты на форме можно по одному: отдельно Label, отдельно связанный с ним TextBox. А можно воспользоваться сниппетом, который вставит пару объектов с корректной привязкой координат относительно друг друга и контейнера, в котором они располагаются.

Вставить сниппет можно из окна автозаполнения. Разместим курсор внутри ContentPanel и начнем писать "Lab". Нажмите на сочетание клавиш Ctrl+Space, чтобы открылось окно автозаполнения.

Редактор подсказывает нам три сниппета: "Label + ComboBox", "Label + DateTimePicker" и "Label + TextBox". Справа от имени сниппета указывается его короткое имя. Например, lcombox. По этому имени также можно искать конкретный сниппет. Эти сниппеты пригодятся нам при создании объектов на форме.

Выберем сниппет "Label + TextBox" и нажмем Enter. Откроется окно настроек:

Последние два поля оставим пустыми, т.к. в ContentPanel у нас еще нет никаких объектов, к которым можно привязаться. Жмем Finish.

Редактор на место курсора подставит код наших объектов NameLabel и NameTextBox. Также откроет блокнот с текстом подсказки необходимых изменений. Сейчас эта подсказка нас не интересует - все необходимые доработки уже существуют.

Давайте смотреть, что добавил редактор. В объекте NameLabel редактор подсвечивает ошибки:

Это из-за того, что последние два поля в настройках сниппета мы оставили пустыми. Давайте заменим значения для тэгов <Top> и <Left> на 5 и 10 соответственно.

Вторым интересным моментом является то, что редактор для объекта NameTextBox в тэг <Text> сразу подставил наше соединение с данными ClientPrimaryGetDataConnection и указал нужное поле.

<Text>
  <DataConnection SourceDataConnection="ClientPrimaryGetDataConnection">
    <Fields>
      <Field Name="Name" />
    </Fields>
  </DataConnection>
</Text>

Перейдем в приложение и нажмем кнопку "Добавить запись...". Убедимся, что карточка клиента успешно загружена, и проверим расположение объектов.

Сначала доделаем все поля на форме, а затем скорректируем ее размеры.

Для создания поля "Дата рождения" воспользуемся сниппетом "Label + DateTimePicker":

Теперь заполним два последних поля. И укажем в них ранее созданные поля.

Жмем Finish.

Обратите внимание на значения тэгов <Top> у DateOfBirthLabel и DateOfBirthDateTimePicker. Отступ между объектами NameTextBox и DateOfBirthLabel равен 5 пикселей. А отступ между DateOfBirthLabel и DateOfBirthDateTimePicker - нулевой. Такие значения отступов позволяют визуально отделить поля одного свойства от полей другого свойства.

Редактор для объекта DateOfBirthDateTimePicker в тэг <Value> сразу подставил наше соединение с данными ClientPrimaryGetDataConnection и указал нужное поле.

Перейдем в приложение и убедимся, что карточка клиента успешно загружена, и проверим расположение объектов.

Внесем небольшие правки в описание объекта DateOfBirthDateTimePicker. Во-первых, изменим его ширину, прописав в тэг <Width> значение 150, чтобы поле выглядело симпатичнее. Во-вторых, изменим значение атрибута Show у тэга <NullValue> на True. Атрибут Show определяет, может ли дата иметь значение NULL.

В итоге синтаксис объекта DateOfBirthDateTimePicker будет выглядеть так:

TemplateClientEdit.xml
<MyObject Name="DateOfBirthDateTimePicker" Type="DateTimePicker" Assembly="BaseControls">
  <Top>
    <Object Name="DateOfBirthLabel">
      <Property Name="Bottom" />
    </Object>
  </Top>
  <Left>
    <Object Name="DateOfBirthLabel">
      <Property Name="Left" />
    </Object>
  </Left>
  <Width>150</Width>
  <Format>dd MMMM yyyy</Format>
  <ShowCalendar>True</ShowCalendar>
  <NullValue Show="True" />
  <Value>
    <DataConnection SourceDataConnection="ClientPrimaryGetDataConnection">
      <Fields>
        <Field Name="DateOfBirth" />
      </Fields>
    </DataConnection>
  </Value>
</MyObject>

По заданию поле "Город" должно быть сделано в виде выпадающего списка (ComboBox). Поэтому для его создания воспользуемся сниппетом "Label + ComboBox":

Помимо знакомых уже полей настроек, этот сниппет имеет две специальных настройки: Имя схемы БД и Имя таблицы БД. Они необходимы для автоматического создания SqlQuery, на основе которого будет заполняться выпадающий список.

Жмем Finish.

Как можем видеть, редактор в файле описания работы серверной части приложения (Template.xml) создал запрос CityShortSelectSqlQuery. Осталось добавить этот запрос в какой-нибудь SqlQueryPermission. Например, в CityViewSqlQueryPermission.

Вы могли заметить, что текст запроса в созданном CityShortSelectSqlQuery полностью совпадает с текстом запроса в CitySelectSqlQuery. Такое дублирование кода допустимо. Лучше иметь отдельный запрос на заполнение выпадающего списка. Забегая вперед, назову две причины такого разделения. Во-первых, в будущем это упростит настраивание прав доступа. А, во-вторых, текст обоих запросов будет дополняться и изменяться по-разному.

Вернемся в файл карточки клиента (TemplateClientEdit.xml) и посмотрим, что добавилось там.

Редактор создал CityShortPrimaryGetDataConnection, который прописал в тэг <ValueList> созданного объекта CityComboBox. А в тэг <Value> того же объекта подставил ClientPrimaryGetDataConnection и указал нужное поле.

Перейдем в приложение и убедимся, что карточка клиента успешно загружена, и проверим расположение объектов.

Самостоятельно создайте поля "Контактный телефон" и "E-mail", использую нужные сниппеты. И скорректируйте размеры формы.

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

Редактирование выпадающего списка

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

Для начала скачайте архив с изображением для этой кнопки и распакуйте его в папку \Template\Projects\1. Template\Forms\Images\16x16.

Создайте самостоятельно команду на открытие формы TemplateCityList.xml.

Создадим саму кнопку, разместив код, представленный ниже, сразу после описания объекта CityComboBox.

TemplateClientEdit.xml
<MyObject Name="CityListButton" Type="Button" Assembly="BaseControls">
  <Top>
    <Object Name="CityComboBox">
      <Property Name="Top" />
    </Object>
  </Top>
  <Left>
    <Formula>
      <Plus DataType="IntegerDataType">
        <Item>
          <Object Name="CityComboBox">
            <Property Name="Right" />
          </Object>
        </Item>
        <Item>5</Item>
      </Plus>
    </Formula>
  </Left>
  <Height>22</Height>
  <Width>22</Width>
  <Hint>Список городов...</Hint>
  <BackgroundImage>Images\16x16\list.png</BackgroundImage>
  <BackgroundImageLayout>Center</BackgroundImageLayout>
  <FlatStyle>Flat</FlatStyle>
  <FlatBorderSize>1</FlatBorderSize>
  <FlatBorderColor>ButtonFlatBorderColor</FlatBorderColor>
  <FlatMouseDownBackColor>ButtonFlatMouseDownBackColor</FlatMouseDownBackColor>
  <FlatMouseOverBackColor>ButtonFlatMouseOverBackColor</FlatMouseOverBackColor>
  <Commands>
    <Command Name="CityListFormShowCommand" />
  </Commands>
</MyObject>

Не забудьте поправить ширину CityComboBox с учетом ширины созданной кнопки и отступа в 5 пикселей между объектами.

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

Осталось реализовать обновление CityShortPrimaryGetDataConnection, который предоставляет список для CityComboBox, если на форме были сохранены изменения.

Для этого самостоятельно выполните следующие пункты:

  • Создайте DataConnectionRefreshCommand на CityShortPrimaryGetDataConnection;

  • Создайте условие для проверки параметра Updated у команды открытия формы TemplateCityList.xml;

  • Создайте Execution, который по условию из предыдущего пункта будет вызывать команду на обновление CityShortPrimaryGetDataConnection;

  • На форме TemplateCityList.xml создайте параметр Updated и команду UpdatedTrueValueSetCommand;

  • Там же на форме TemplateCityList.xml в тэге <Commands> кнопки SaveButton исправьте список выполняемых команд. Удалите вызов команды CityDataConnectionRefreshCommand. Вместо нее добавьте вызовы команд UpdatedTrueValueSetCommand и FormCloseCommand;

  • Так же на форме TemplateCityList.xml удалите описание команды CityDataConnectionRefreshCommand. Эта команда нам больше не понадобится.

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

Сохранение изменений в таблицу в базе данных

Запросы ClientInsertSqlQuery, ClientUpdateSqlQuery у нас уже есть. Давайте сделаем для них два SetDataConnection:

TemplateClientEdit.xml
<DataConnection Name="ClientInsertSetDataConnection" Type="SetDataConnection" Assembly="DataConnections">
  <Workflow Name="Template" />
  <Parameters>
    <Parameter NativeName="Name">
      <Value>
        <Object Name="NameTextBox" />
      </Value>
    </Parameter>
    <Parameter NativeName="DateOfBirth">
      <Value>
        <Object Name="DateOfBirthDateTimePicker"/>
      </Value>
    </Parameter>
    <Parameter NativeName="CityId">
      <Value>
        <Object Name="CityComboBox" />
      </Value>
    </Parameter>
    <Parameter NativeName="Phone">
      <Value>
        <Object Name="PhoneTextBox" />
      </Value>
    </Parameter>
    <Parameter NativeName="Email">
      <Value>
        <Object Name="EmailTextBox" />
      </Value>
    </Parameter>
  </Parameters>
  <SqlQueries>
    <SqlQuery Name="ClientInsertSqlQuery" Type="Insert" />
  </SqlQueries>
</DataConnection>

<DataConnection Name="ClientUpdateSetDataConnection" Type="SetDataConnection" Assembly="DataConnections">
  <Workflow Name="Template" />
  <Parameters>
    <Parameter NativeName="ClientId">
      <Value>
        <Parameter Name="ClientId" />
      </Value>
    </Parameter>
    <Parameter NativeName="Name">
      <Value>
        <Object Name="NameTextBox" />
      </Value>
    </Parameter>
    <Parameter NativeName="DateOfBirth">
      <Value>
        <Object Name="DateOfBirthDateTimePicker"/>
      </Value>
    </Parameter>
    <Parameter NativeName="CityId">
      <Value>
        <Object Name="CityComboBox" />
      </Value>
    </Parameter>
    <Parameter NativeName="Phone">
      <Value>
        <Object Name="PhoneTextBox" />
      </Value>
    </Parameter>
    <Parameter NativeName="Email">
      <Value>
        <Object Name="EmailTextBox" />
      </Value>
    </Parameter>
  </Parameters>
  <SqlQueries>
    <SqlQuery Name="ClientUpdateSqlQuery" Type="Update" />
  </SqlQueries>
</DataConnection>

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

TemplateClientEdit.xml
<Command Name="ClientInsertSaveCommand" Type="SaveCommand" Assembly="Commands">
  <DataConnections>
    <DataConnection Name="ClientInsertSetDataConnection" />
  </DataConnections>
</Command>

<Command Name="ClientUpdateSaveCommand" Type="SaveCommand" Assembly="Commands">
  <DataConnections>
    <DataConnection Name="ClientUpdateSetDataConnection" />
  </DataConnections>
</Command>

Как вы помните, наш запрос ClientInsertSqlQuery возвращает идентификатор client_id новой записи. Команда ClientInsertSaveCommand получает этот идентификатор от ClientInsertSetDataConnection и записывает его в свой результат выполнения. Давайте создадим команду типа ValueSetCommand, которая запишет этот результат в параметр формы ClientId:

TemplateClientEdit.xml
<Command Name="ClientIdValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <Parameter Name="ClientId">
    <Command Name="ClientInsertSaveCommand" />
  </Parameter>
</Command>

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

Скорректируем синтаксис команды SaveSequentialCommand, которая уже есть в коде:

TemplateClientEdit.xml
<Command Name="SaveSequentialCommand" Type="SequentialCommand" Assembly="Commands">
  <Commands>
    <If>
      <When>
        <Parameter Name="Edit" />
      </When>
      <Then>
        <Command Name="ClientUpdateSaveCommand" />
      </Then>
      <Else>
        <Command Name="ClientInsertSaveCommand" />
        <Command Name="ClientIdValueSetCommand" />
      </Else>
    </If>
    <Command Name="UpdatedTrueValueSetCommand" />
    <Command Name="FormCloseCommand" />
  </Commands>
</Command>

Вернемся в файл стартовой формы (TemplateStart.xml). Здесь мы получим значение параметра ClientId из команды ClientEditFormShowCommand, которое будем использовать для выделение строки в таблице ClientDatabaseTable. Это позволит пользователю убедиться в том, что новая запись успешно сохранилась. Для выделения строки в таблице создадим следующую команду:

TemplateStart.xml
<Command Name="ClientSelectInTableValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <Object Name="ClientDatabaseTable">
    <Property Name="SelectRowByFieldValue">
      <Parameters>
        <Parameter Name="ColumnName">ClientId</Parameter>
        <Parameter Name="Value">
          <Command Name="ClientEditFormShowCommand" Parameter="ClientId" />
        </Parameter>
      </Parameters>
    </Property>
  </Object>
</Command>

Добавьте вызов этой команды в Execution по изменению параметра Updated у команды ClientEditFormShowCommand после вызова команды типа DataConnectionRefreshCommand.

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

Удаление записей

Вернемся в файл стартовой формы (TemplateStart.xml) и реализуем функционал удаления записей из таблицы клиентов.

У нас уже есть sql-запрос на удаление записи в таблице template.client в базе данных. Давайте создадим SetDataConnection для этого запроса:

TemplateStart.xml
<DataConnection Name="ClientDeleteSetDataConnection" Type="SetDataConnection" Assembly="DataConnections">
  <Workflow Name="Template" />
  <Parameters>
    <Parameter NativeName="ClientId">
      <Value>
        <Object Name="ClientDatabaseTable">
          <Property Name="SelectedRowCellValueByColumnName">
            <Parameters>
              <Parameter Name="ColumnName">ClientId</Parameter>
            </Parameters>
          </Property>
        </Object>
      </Value>
    </Parameter>
  </Parameters>
  <SqlQueries>
    <SqlQuery Name="ClientDeleteSqlQuery" Type="Delete" />
  </SqlQueries>
</DataConnection>

Sql-запрос ожидает только один параметр ClientId. В качестве значения в этот параметр передадим значение из одноименной колонки выделенной строки таблицы ClientDatabaseTable.

После удаления записи из таблицы в базе данных нам нужно будет обновить данные, получаемые из ClientPrimaryGetDataConnection. Это можно сделать используя знакомую нам команду DataConnectionRefreshCommand. А можно использовать более компактный способ обновить DataConnection.

У SetDataConnection и DatabaseTableSetDataConnection есть необязательный тэг <Refresh>, в качестве значения которого указывается список тэгов <DataConnection> c именами загружающих соединений с данными, которые будут обновлены после выполнения сохранения.

Давайте добавим такой тэг в наш ClientDeleteSetDataConnection. Таким образом, общий синтаксис соединения с данными для отправки будет выглядеть так:

TemplateStart.xml
<DataConnection Name="ClientDeleteSetDataConnection" Type="SetDataConnection" Assembly="DataConnections">
  <Workflow Name="Template" />
  <Parameters>
    <Parameter NativeName="ClientId">
      <Value>
        <Object Name="ClientDatabaseTable">
          <Property Name="SelectedRowCellValueByColumnName">
            <Parameters>
              <Parameter Name="ColumnName">ClientId</Parameter>
            </Parameters>
          </Property>
        </Object>
      </Value>
    </Parameter>
  </Parameters>
  <SqlQueries>
    <SqlQuery Name="ClientDeleteSqlQuery" Type="Delete" />
  </SqlQueries>
  <Refresh>
    <DataConnection Name="ClientPrimaryGetDataConnection" />
  </Refresh>
</DataConnection>

Самостоятельно создайте команду SaveCommand для нового DataConnection и пропишите ее в тэге <Commands> кнопки "Удалить запись...".

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

Итоги

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

Также рассмотрели использование универсального значения Input, которое позволяет передавать в команду значения параметров в месте вызова команды, а не при ее описании.

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

Ответы

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

Last updated