Урок 1. Форма списка и добавление записей

В предыдущем блоке "Начало работы" мы познакомились с архитектурой платформы, составом проекта и настройкой взаимосвязи его компонент. Развернули серверную и клиентскую части учебного проекта. И добавили исходники учебного проекта в редактор Workflow XML Editor.

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

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

В этом уроке мы научимся:

  • получать данные из таблицы в базе данных;

  • добавлять записи в таблицу в базе данных;

  • создавать и открывать новые формы;

  • передавать данные с дочерней формы на родительскую;

  • редактировать таблицу на форме.

Форма списка

Список будет на стартовой форме в виде таблицы с колонками: "№" (Порядковый номер записи) и "Наименование". Справа от таблицы будет размещена кнопка добавления записи в таблицу. По этой кнопке будет открываться форма карточки сущности. Под таблицей будет располагаться кнопка для сохранения изменений в базе данных.

По завершению этого этапа мы с вами получим форму вида:

Перейдем в Eclipse и откроем файл стартовой формы (TemplateStart.xml). Первым делом давайте изменим заголовок и размер формы.

Параметру формы Title зададим новое значение "Города".

TemplateStart.xml
<Parameters>
  <Parameter Name="Title">Города</Parameter>
</Parameters>

В тэге <Form> для атрибутов Width и Height зададим значение 500 и 300 соответственно. И добавим атрибут FormBorderStyle со значением Sizable. Это позволит изменять размер окна.

TemplateStart.xml
<Form Name="TemplateStartForm" Width="500" Height="300" FormBorderStyle="Sizable" StartPosition="CenterScreen" FontStyle="ThemeFontStyle" ForeColor="ThemeForeColor" BackColor="ThemeBackgroundColor" ValidationType="Flat" FlatColor="ThemeFlatColor" FlatWidth="2" StatusBar="True" MaximizeBox="True">

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

Давайте добавим таблицу (объект типа DatabaseTable) на форму. Для этого поместите курсор внутри объекта ContentPanel, после тэга <BackColor>. Начните писать, затем нажмите на сочетание клавиш Ctrl+Space. Откроется окно автозаполнения.

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

TemplateStart.xml
<MyObject Name="DatabaseTable" Type="DatabaseTable" Assembly="ComplexControls">
  <Top></Top>
  <Left></Left>
  <Height></Height>
  <Width></Width>
  <AllowResizeColumns Value="False" />
  <AllowResizeRows Value="False" />
  <AllowInsert></AllowInsert>
  <AllowUpdate></AllowUpdate>
  <AllowDelete></AllowDelete>
  <AutoSizeColumnsMode Value="Fill" />
  <SourceDataConnection Name="" />
  <Columns></Columns>
</MyObject>

Первым делом зададим нашему объекту имя. Например, CityDatabaseTable. Подробно про тип DatabaseTable можете прочитать по ссылке. А сейчас рассмотрим предложенные редактором элементы.

Элементы, общие для всех графических объектов

Тэги <Top> и <Left> задают координаты верхней левой точки объекта в пределах контейнера, в котором расположен объект. Ожидаются положительные целочисленные значения. Для таблицы CityDatabaseTable контейнером является панель ContentPanel, для которой контейнером является сама форма.

Тэги <Height>и <Width> задают высоту и ширину объекта. Ожидаются положительные целочисленные значения.

Значения этих элементов можно задать постоянными. Для <Top> и <Left> укажем значения 5 и 10 соответственно.

А можно задать гибкие значения, привязав к свойствам других объектов. Для этого можно использовать универсальные значения Calculate или Formula для вычисления числового выражения. Или напрямую привязать к свойству другого объекта. Это позволит динамически изменять размер и положение объектов на форме при изменении размеров самого окна.

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

<Height>
  <Calculate>
    <Expression>{0} - {1}*2</Expression>
    <Items>
      <Item>
        <Object Name="ContentPanel">
          <Property Name="Height" />
        </Object>
      </Item>
      <Item>
        <Object Name="CityDatabaseTable">
          <Property Name="Top" />
        </Object>
      </Item>
    </Items>
  </Calculate>
</Height>

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

Элементы, характерные только для таблицы DatabaseTable

  • <AllowResizeColumns> - признак, определяющий, может ли пользователь изменять ширину столбцов посредством графического интерфейса таблицы. Значение атрибута Value оставим по умолчанию;

  • <AllowResizeRows> - признак, определяющий, может ли пользователь изменять высоту строк посредством графического интерфейса таблицы. Оставим значение атрибута Value по умолчанию;

  • <AllowInsert>, <AllowUpdate> и <AllowDelete> - признаки, определяющие, может ли пользователь редактировать строки в таблице посредством графического интерфейса таблицы. Всем признакам укажем значение False, т.к. для редактирования таблицы будем использовать отдельную форму и набор set-проперти таблицы;

  • <AutoSizeColumnsMode> - название типа автоматического изменения ширины столбцов таблицы. Оставим значение атрибута Value по умолчанию;

  • <SourceDataConnection> - загружающее соединение с данными, данные которого будут использоваться в таблице;

  • <Columns> - содержит список столбцов таблицы.

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

Для того чтобы закомментировать часть кода, нужно выделить код и нажать сочетание клавиш Ctrl+<. Для снятия комментария с кода достаточно поставить курсор в любое место комментария и нажать эту же комбинацию клавиш.

Добавим колонки в таблицу. Нам нужно создать три колонки:

  • CityId - скрытая служебная колонка с идентификатором записи в базе;

  • RowNumber - порядковый номер отображаемой записи;

  • Title - наименование города, которое введет пользователь.

<Columns>
  <Column Name="CityId" Type="DatabaseTableColumnTextBox" Assembly="DatabaseTableColumnControls">
    <Visible>False</Visible>
  </Column>
  <Column Name="RowNumber" Type="DatabaseTableColumnTextBox" Assembly="DatabaseTableColumnControls">
    <Title>№</Title>
    <Width>30</Width>
    <AutoFill Type="RowNumber" />
    <Alignment Value="MiddleCenter" />
    <AutoSizeMode Value="None" />
  </Column>
  <Column Name="Title" Type="DatabaseTableColumnTextBox" Assembly="DatabaseTableColumnControls">
    <Title>Наименование</Title>
    <Width>150</Width>
    <AutoSizeMode Value="Fill" />
  </Column>
</Columns>

Тэг <AutoFill Type="RowNumber" /> отвечает за автоматическую генерацию порядкового номера строки в таблице.

Для автоматического форматирования xml-кода используйте сочетание клавиш Ctrl+Q

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

TemplateStart.xml
<MyObject Name="CityDatabaseTable" Type="DatabaseTable" Assembly="ComplexControls">
  <Top>5</Top>
  <Left>10</Left>
  <Height>
    <Calculate>
      <Expression>{0} - {1}*2</Expression>
      <Items>
        <Item>
          <Object Name="ContentPanel">
            <Property Name="Height" />
          </Object>
        </Item>
        <Item>
          <Object Name="CityDatabaseTable">
            <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="CityDatabaseTable">
            <Property Name="Left" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Width>
  <AllowResizeColumns Value="False" />
  <AllowResizeRows Value="False" />
  <AllowInsert>False</AllowInsert>
  <AllowUpdate>False</AllowUpdate>
  <AllowDelete>False</AllowDelete>
  <AutoSizeColumnsMode Value="Fill" />
  <!--<SourceDataConnection Name="" />-->
  <Columns>
    <Column Name="CityId" Type="DatabaseTableColumnTextBox" Assembly="DatabaseTableColumnControls">
      <Visible>False</Visible>
    </Column>
    <Column Name="RowNumber" Type="DatabaseTableColumnTextBox" Assembly="DatabaseTableColumnControls">
      <Title>№</Title>
      <Width>30</Width>
      <AutoFill Type="RowNumber" />
      <Alignment Value="MiddleCenter" />
      <AutoSizeMode Value="None" />
    </Column>
    <Column Name="Title" Type="DatabaseTableColumnTextBox" Assembly="DatabaseTableColumnControls">
      <Title>Наименование</Title>
      <Width>150</Width>
      <AutoSizeMode Value="Fill" />
    </Column>
  </Columns>
</MyObject>

Давайте посмотрим, что у нас получилось. Убедитесь, что запущен сервер. Если нет, то запустите файл _start.bat из папки, в которую разворачивали серверную часть приложения, например, D:\WorkflowEngine\Template. Запустите приложение WorkflowForms.exe из папки, в которую разворачивали клиентскую часть, например, D:\WorkflowForms\Template. Если клиентская часть уже была запущена, то лучше закрыть и запустить заново.

К сожалению, запуск WT-приложения из Eclipse не доступен.

Создание кнопок

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

Кнопка добавления записей

Добавим кнопку (объект типа Button), по которой будет открываться форма с карточкой города. Добавьте в ContentPanel вслед за CityDatabaseTable следующий код:

TemplateStart.xml
<MyObject Name="CityAddButton" Type="Button" Assembly="BaseControls">
  <Top>
    <Object Name="CityDatabaseTable">
      <Property Name="Top" />
    </Object>
  </Top>
  <Left>
    <Calculate>
      <Expression>{0} + 5</Expression>
      <Items>
        <Item>
          <Object Name="CityDatabaseTable">
            <Property Name="Right" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Left>
  <Width>40</Width>
  <Height>40</Height>
  <Hint>Добавить запись...</Hint>
  <BackgroundImage>Images\24x24\plus.png</BackgroundImage>
  <BackgroundImageLayout>Center</BackgroundImageLayout>
  <FlatStyle>Flat</FlatStyle>
  <FlatBorderSize>1</FlatBorderSize>
  <FlatBorderColor>ButtonFlatBorderColor</FlatBorderColor>
  <FlatMouseDownBackColor>ButtonFlatMouseDownBackColor</FlatMouseDownBackColor>
  <FlatMouseOverBackColor>ButtonFlatMouseOverBackColor</FlatMouseOverBackColor>
  <Commands> </Commands>
</MyObject>

Так как кнопка графически находится справа от таблицы, что и указано в тэге <Left>, то необходимо скорректировать ширину таблицы. Для этого в <Calculate> для тэга <Width> объекта CityDatabaseTable необходимо учесть ширину кнопки и отступ в 5 пикселей между таблицей и кнопкой. Заменим описание тэга <Width>объекта CityDatabaseTable следующим кодом:

<Width>
  <Calculate>
    <Expression>{0} - {1}*2 - {2} - 5</Expression>
    <Items>
      <Item>
        <Object Name="ContentPanel">
          <Property Name="Width" />
        </Object>
      </Item>
      <Item>
        <Object Name="CityDatabaseTable">
          <Property Name="Left" />
        </Object>
      </Item>
      <Item>
        <Object Name="CityAddButton">
          <Property Name="Width" />
        </Object>
      </Item>
    </Items>
  </Calculate>
</Width>

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

Кнопка сохранения

Кнопка "Сохранить" будет располагаться в нижней части формы (Footer). Давайте создадим новую панель, которую назовем FootPanel. Так же визуально отделим FootPanel от ContentPanel с помощью разделителя, в качестве которого будет выступать Panel с высотой в один пиксель. Добавьте следующий код следом за ContentPanel.

TemplateStart.xml
<MyObject Name="FootSeparatePanel" Type="Panel" Assembly="BaseControls">
  <Top>
    <Object Name="ContentPanel">
      <Property Name="Bottom" />
    </Object>
  </Top>
  <Left>5</Left>
  <Height>1</Height>
  <Width>
    <Calculate>
      <Expression>{0} - 10</Expression>
      <Items>
        <Item>
          <Form>
            <Property Name="Width" />
          </Form>
        </Item>
      </Items>
    </Calculate>
  </Width>
  <BackColor>ThemeSeparateBackgroundColor</BackColor>
</MyObject>

<MyObject Name="FootPanel" Type="Panel" Assembly="BaseControls">
  <Top>
    <Object Name="FootSeparatePanel">
      <Property Name="Bottom" />
    </Object>
  </Top>
  <Left>0</Left>
  <Height>40</Height>
  <Width>
    <Form>
      <Property Name="Width" />
    </Form>
  </Width>
  <TabIndex>2</TabIndex>
  <BackColor>ThemeBackgroundColor</BackColor>
  
</MyObject>

Теперь нам необходимо скорректировать высоту ContentPanel, чтобы FootPanel вместе с сепаратором поместились на форме. Заменим описание тэга <Height> объекта ContentPanel следующим кодом:

<Height>
  <Calculate>
    <Expression>{0} - {1} - {2} - {3}</Expression>
    <Items>
      <Item>
        <Form>
          <Property Name="Height" />
        </Form>
      </Item>
      <Item>
        <Object Name="ContentPanel">
          <Property Name="Top" />
        </Object>
      </Item>
      <Item>
        <Object Name="FootSeparatePanel">
          <Property Name="Height" />
        </Object>
      </Item>
      <Item>
        <Object Name="FootPanel">
          <Property Name="Height" />
        </Object>
      </Item>
    </Items>
  </Calculate>
</Height>

Добавим в FootPanel кнопку "Сохранить", скопировав код:

TemplateStart.xml
<MyObject Name="SaveButton" Type="Button" Assembly="BaseControls">
  <Top>5</Top>
  <Right>
    <Formula>
      <Minus DataType="IntegerDataType">
        <Item>
          <Object Name="FootPanel">
            <Property Name="Right" />
          </Object>
        </Item>
        <Item>5</Item>
      </Minus>
    </Formula>
  </Right>
  <Height>30</Height>
  <Width>200</Width>
  <TextAlign>MiddleCenter</TextAlign>
  <FontStyle>ButtonFontStyle</FontStyle>
  <ImageAlign>MiddleLeft</ImageAlign>
  <Image>Images\24x24\content-save.png</Image>
  <FlatStyle>Flat</FlatStyle>
  <FlatBorderSize>1</FlatBorderSize>
  <FlatBorderColor>ButtonFlatBorderColor</FlatBorderColor>
  <FlatMouseDownBackColor>ButtonFlatMouseDownBackColor</FlatMouseDownBackColor>
  <FlatMouseOverBackColor>ButtonFlatMouseOverBackColor</FlatMouseOverBackColor>
  <Text>Сохранить</Text>
  <Commands> </Commands>
</MyObject>

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

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

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

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

CREATE SEQUENCE template.city_id_seq;

CREATE TABLE template.city
(
  city_id smallint NOT NULL DEFAULT nextval('template.city_id_seq'::regclass),
  title character varying,
  CONSTRAINT pk_city_id PRIMARY KEY (city_id)
);

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

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

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

Вернемся в Eclipse и перейдем в файл описания работы серверной части приложения (Template.xml).

Добавим запрос для чтения данных из таблицы template.city в процесс. Для этого расположим в тэге <SqlQueries> вложенный тэг <SqlQuery> со следующими параметрами:

Template.xml
<SqlQuery Name="CitySelectSqlQuery">
  <Text>
    SELECT
      city_id AS "CityId",
      title AS "Title"
    FROM
      template.city
    ORDER BY title;
  </Text>
</SqlQuery>

Далее этот запрос следует добавить в разрешение. Для этого расположим в тэге <Permissions> вложенный тэг <Permission> со следующими параметрами:

Template.xml
<Permission Name="CityViewSqlQueryPermission" Type="SqlQueryPermission">
  <SqlQueries>
    <SqlQuery Name="CitySelectSqlQuery" />
  </SqlQueries>
</Permission>

После чего данное разрешение следует добавить в роль для просмотра списка. Для этого расположим в тэге <Roles> вложенный тэг <Role> со следующими параметрами:

Template.xml
<Role Name="CityViewRole">
  <Permissions>
    <Permission Name="CityViewSqlQueryPermission" />
  </Permissions>
</Role>

Затем роли необходимо распределить по группам. В тэге <Groups> вложенные тэги <Group> должны иметь названия, которые совпадают с названиями групп в таблице group. Т.к. в проекте пока не реализована авторизация, то в программе мы будем работать под анонимным пользователем (WS. Гость). Этот пользователь состоит в группе GuestGroup. Добавим в тэг <Groups>вложенный тэг <Group>для этой группы. И назначим группе роль для просмотра списка городов.

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

Получение данных с сервера на форме

Перейдем в файл описания стартовой формы (TemplateStart.xml).

Добавим загружающее соединение с данными (объект типа PrimaryGetDataConnection). Для этого расположим в тэге <DataConnections> вложенный тэг <DataConnection> со следующими параметрами:

TemplateStart.xml
<DataConnection Name="CityPrimaryGetDataConnection" Type="PrimaryGetDataConnection" Assembly="DataConnections">
  <SqlQuery Name="CitySelectSqlQuery" Type="Select">
    <Workflow Name="Template" />
    <Fields>
      <Field Name="CityId" />
      <Field Name="Title" />
    </Fields>
  </SqlQuery>
</DataConnection>
  • <SqlQuery> - имя запроса для получения данных. Ожидается название одного из запросов, описанных в серверном XML-файле;

  • <Workflow> - имя процесса, в рамках которого происходит запрос;

  • <Field> - название поля запроса, которое будет использоваться на форме. Ожидается название одного из полей, возвращаемых запросом.

С помощью CityPrimaryGetDataConnection мы получим на форме виртуальную таблицу (двумерный массив данных с именованными столбцами) с определенными полями, которая будет соответствовать результатам запроса CitySelectSqlQuery, описанного в серверном XML-файле.

Названия полей в DataConnection должны полностью (вплоть до регистра!) совпадать с полями или их алиасами в запросе.

Теперь необходимо таблице CityDatabaseTable в качестве источника данных указать только что созданный CityPrimaryGetDataConnection. Для этого раскомментируем тэг <SourceDataConnection>, и в качестве значения аргумента Name укажем имя DataConnection'а.

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

TemplateStart.xml
<MyObject Name="CityDatabaseTable" Type="DatabaseTable" Assembly="ComplexControls">
  <Top>5</Top>
  <Left>10</Left>
  <Height>
    <Calculate>
      <Expression>{0} - {1}*2</Expression>
      <Items>
        <Item>
          <Object Name="ContentPanel">
            <Property Name="Height" />
          </Object>
        </Item>
        <Item>
          <Object Name="CityDatabaseTable">
            <Property Name="Top" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Height>
  <Width>
    <Calculate>
      <Expression>{0} - {1}*2 - {2} - 5</Expression>
      <Items>
        <Item>
          <Object Name="ContentPanel">
            <Property Name="Width" />
          </Object>
        </Item>
        <Item>
          <Object Name="CityDatabaseTable">
            <Property Name="Left" />
          </Object>
        </Item>
        <Item>
          <Object Name="CityAddButton">
            <Property Name="Width" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Width>
  <AllowResizeColumns Value="False" />
  <AllowResizeRows Value="False" />
  <AllowInsert>False</AllowInsert>
  <AllowUpdate>False</AllowUpdate>
  <AllowDelete>False</AllowDelete>
  <AutoSizeColumnsMode Value="Fill" />
  <SourceDataConnection Name="CityPrimaryGetDataConnection" />
  <Columns>
    <Column Name="CityId" Type="DatabaseTableColumnTextBox" Assembly="DatabaseTableColumnControls">
      <Visible>False</Visible>
    </Column>
    <Column Name="RowNumber" Type="DatabaseTableColumnTextBox" Assembly="DatabaseTableColumnControls">
      <Title>№</Title>
      <Width>30</Width>
      <AutoFill Type="RowNumber" />
      <Alignment Value="MiddleCenter" />
      <AutoSizeMode Value="None" />
    </Column>
    <Column Name="Title" Type="DatabaseTableColumnTextBox" Assembly="DatabaseTableColumnControls">
      <Title>Наименование</Title>
      <Width>150</Width>
      <AutoSizeMode Value="Fill" />
    </Column>
  </Columns>
</MyObject>

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

Карточка города

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

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

Но для начала подключим к проекту готовые шаблоны форм. Они ускорят процесс создания новых форм.

Подключение шаблонов к проекту

Скачаем архив с шаблонами и распакуем его в удобное место. Например, в папку проекта \Template\Projects\1. Template\Patterns.

Вернемся в Eclipse и перейдем в свойства проекта, используя контекстное меню. Кликнем правой кнопкой мыши по имени проекта в окне Project Explorer и в появившемся меню выберем пункт Properties. И в открывшемся окне свойств проекта в левой части перейдем к секции Patterns.

В правой части окна в поле Project patterns path укажем путь до папки с паттернами. Жмем OK.

Чтобы Eclipse корректно подтянул изменения паттернов, воспользуемся кнопкой Reload patterns, расположенной на панели инструментов.

Создание формы с помощью шаблона

Для создания формы с помощью шаблона используем пункт меню File -> New -> Apply Workflow Pattern.

В открывшемся диалоговом окне Workflow Editor отображается список шаблонов форм, в котором есть два шаблона: Empty Form (пустая форма с хэдером) и Empty Form With Footer (пустая форма с хэдером и футером).

Для задачи создания формы карточки города выберем шаблон Empty Form with Footer. Этот шаблон уже содержит код формы с основными элементами. И нам останется только добавить нужное поле и реализовать логику передачи данных на родительскую форму. Жмем Finish.

Появилось окно настроек паттерна:

  • Имя формы: это постфикс для имени файла, пишем "CityEdit". И т.к. процесс имеет название Template, то имя файла созданной формы будет TemplateCityEdit.xml;

  • Заголовок формы: текст, который будет отображаться в хэдере формы, пишем "Добавление города";

  • Имя кнопки в футере: имя объекта, которое будет использоваться на форме. Пишем SaveButton;

  • Текст кнопки в футере: видимый текст на кнопке. Оставим без изменения;

  • Иконка на кнопке в футере: путь до файла с графическим содержанием относительно папки \Forms\Images\24x24, которое будет отображаться на кнопке. Оставим без изменения. Значение по умолчанию соответствует ранее скачанному и размещенному в проекте файлу.

Жмем Finish.

Редактор создаст форму и сразу же ее откроет. В окне Project Explorer в ветке проекта увидим только что созданную форму TemplateCityEdit.xml:

Открытие формы по кнопке

Перейдем в файл стартовой формы (TemplateStart.xml) и реализуем команду открытия формы будущей карточки города. Для этого в тэг <Commands> самой формы добавим команду FormShowCommand вида:

TemplateStart.xml
<Command Name="CityAddFormShowCommand" Type="FormShowCommand" Assembly="Commands">
  <Xml Type="Path">TemplateCityEdit.xml</Xml>
  <Show Type="Modal" />
</Command>

В качестве значения тэга <Xml> укажем имя файла карточки редактирования города. В атрибуте Type тэга <Show> стоит значение Modal, это означает, что форма будет открываться в модальном режиме. Особенности работы с модальными формами рассмотрим на следующем уроке.

Теперь нужно на кнопке CityAddButton в тэге <Commands> прописать вызов этой команды. Таким образом, синтаксис кнопки добавления города будет иметь вид:

TemplateStart.xml
<MyObject Name="CityAddButton" Type="Button" Assembly="BaseControls">
  <Top>
    <Object Name="CityDatabaseTable">
      <Property Name="Top" />
    </Object>
  </Top>
  <Left>
    <Calculate>
      <Expression>{0} + 5</Expression>
      <Items>
        <Item>
          <Object Name="CityDatabaseTable">
            <Property Name="Right" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Left>
  <Width>40</Width>
  <Height>40</Height>
  <Hint>Добавить запись...</Hint>
  <BackgroundImage>Images\24x24\plus.png</BackgroundImage>
  <BackgroundImageLayout>Center</BackgroundImageLayout>
  <FlatStyle>Flat</FlatStyle>
  <FlatBorderSize>1</FlatBorderSize>
  <FlatBorderColor>ButtonFlatBorderColor</FlatBorderColor>
  <FlatMouseDownBackColor>ButtonFlatMouseDownBackColor</FlatMouseDownBackColor>
  <FlatMouseOverBackColor>ButtonFlatMouseOverBackColor</FlatMouseOverBackColor>
  <Commands>
    <Command Name="CityAddFormShowCommand" />
  </Commands>
</MyObject>

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

Заполнение формы объектами

Вернемся в файл новой формы (TemplateCityEdit.xml).

Для быстрого перемещения к описанию объекта зажмите клавишу Ctrl и кликните левой кнопкой мыши по имени объекта, в месте его использования. Этот трюк работает с объектами MyObject, командами, файлами форм и именами SQL-запросов.

Добавим на форму в панель ContentPanel текстовое поле TextBox для ввода значения и Label с текстом "Наименование" для описания текстового поля.

TemplateCityEdit.xml
<MyObject Name="TitleLabel" Type="Label" Assembly="BaseControls">
  <Top>5</Top>
  <Left>10</Left>
  <Width>
    <Calculate>
      <Expression>{0} - {1} - 10</Expression>
      <Items>
        <Item>
          <Object Name="ContentPanel">
            <Property Name="Width" />
          </Object>
        </Item>
        <Item>
          <Object Name="TitleLabel">
            <Property Name="Left" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Width>
  <Height>20</Height>
  <TextAlign>MiddleLeft</TextAlign>
  <FontStyle>LabelFontStyle</FontStyle>
  <ForeColor>LabelForeColor</ForeColor>
  <Text>Наименование</Text>
</MyObject>

<MyObject Name="TitleTextBox" Type="TextBox" Assembly="BaseControls">
  <Top>
    <Object Name="TitleLabel">
      <Property Name="Bottom" />
    </Object>
  </Top>
  <Left>
    <Object Name="TitleLabel">
      <Property Name="Left" />
    </Object>
  </Left>
  <Width>
    <Calculate>
      <Expression>{0} - {1} - 10</Expression>
      <Items>
        <Item>
          <Object Name="ContentPanel">
            <Property Name="Width" />
          </Object>
        </Item>
        <Item>
          <Object Name="TitleTextBox">
            <Property Name="Left" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Width>
  <Text> </Text>
</MyObject>

Скорректируйте высоту и ширину формы.

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

Передача параметров на родительскую форму

По кнопке "Сохранить" мы должны передавать на родительскую форму наименование города, которое ввел пользователь. При этом дочерняя форма должна закрываться. Для передачи данных между форм используем параметры формы, описанные в тэге <Parameters>.

Создадим новый параметр:

TemplateCityEdit.xml
<Parameter Name="CityTitle" />

Добавим на форму команду типа ValueSetCommand, которая будет параметру CityTitle присваивать значение из текстового поля TitleTextBox:

TemplateCityEdit.xml
<Command Name="CityTitleValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <Parameter Name="CityTitle">
    <Object Name="TitleTextBox" />
  </Parameter>
</Command>

В файле уже описана команда FormCloseCommand, которая будет закрывать окно. Нам нужно лишь объединить ее с нашей командой в одну последовательность SequentialCommand. Добавим следующий код в тэг <Commands>:

TemplateCityEdit.xml
<Command Name="SaveSequentialCommand" Type="SequentialCommand" Assembly="Commands">
  <Commands>
    <Command Name="CityTitleValueSetCommand" />
    <Command Name="FormCloseCommand" />
  </Commands>
</Command>

И затем на кнопке SaveButton пропишем вызов этой последовательности.

Получение данных от дочерней формы

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

У таблицы DatabaseTable есть set-проперти AddRow, AddRows, UpdateRow, UpdateRows и DeleteRowsByIndices. Они позволяют редактировать строки в таблице. Чтобы воспользоваться этими свойствами необходима команда ValueSetCommand.

Ряд соединений с данными (например, PrimaryGetDataConnection, SecondaryGetDataConnection и ConvertDataConnection) так же обладают набором set-проперти (AddRow, AddRows, UpdateRow, UpdateRows и DeleteRowsByIndices) для редактирования своих записей.

Давайте создадим такую команду для добавления записи в таблицу:

TemplateStart.xml
<Command Name="CityDatabaseTableAddRowValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <Object Name="CityDatabaseTable">
    <Property Name="AddRow">
      <Parameters>
        <Parameter Name="ColumnNames"> </Parameter>
        <Parameter Name="Values"> </Parameter>
        <Parameter Name="SelectAfterAdd"> </Parameter>
      </Parameters>
    </Property>
  </Object>
</Command>

Параметр ColumnNames ожидает линейный массив названий столбцов таблицы. Чтобы создать такой линейный массив используем структуру данных Structure типа List. Т.к. дочерняя форма возвращает только наименование города, то в списке колонок будет лишь одно имя колонки с наименованием:

<Parameter Name="ColumnNames">
  <Structure Type="List">
    <Item>Title</Item>
  </Structure>
</Parameter>

Параметр Values так же ожидает линейный массив значений, которые будут добавляться в колонки, указанные в параметре ColumnNames. Нам нужно значение, которое мы сохранили в параметр формы CityTitle. К параметрам формы, которую открывали через команду FormShowCommand, можно обратиться через атрибут Parameter тэга <Command>:

<Parameter Name="Values">
  <Structure Type="List">
    <Item>
      <Command Name="CityAddFormShowCommand" Parameter="CityTitle" />
    </Item>
  </Structure>
</Parameter>

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

В итоге общий синтаксис команды будет иметь вид:

TemplateStart.xml
<Command Name="CityDatabaseTableAddRowValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <Object Name="CityDatabaseTable">
    <Property Name="AddRow">
      <Parameters>
        <Parameter Name="ColumnNames">
          <Structure Type="List">
            <Item>Title</Item>
          </Structure>
        </Parameter>
        <Parameter Name="Values">
          <Structure Type="List">
            <Item>
              <Command Name="CityAddFormShowCommand" Parameter="CityTitle" />
            </Item>
          </Structure>
        </Parameter>
        <Parameter Name="SelectAfterAdd">True</Parameter>
      </Parameters>
    </Property>
  </Object>
</Command>

Теперь нужно на кнопке CityAddButton в тэге <Commands> добавить вызов этой команды сразу за вызовом команды CityAddFormShowCommand. Таким образом, синтаксис кнопки добавления записи в таблицу будет выглядеть так:

TemplateStart.xml
<MyObject Name="CityAddButton" Type="Button" Assembly="BaseControls">
  <Top>
    <Object Name="CityDatabaseTable">
      <Property Name="Top" />
    </Object>
  </Top>
  <Left>
    <Calculate>
      <Expression>{0} + 5</Expression>
      <Items>
        <Item>
          <Object Name="CityDatabaseTable">
            <Property Name="Right" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Left>
  <Width>40</Width>
  <Height>40</Height>
  <Hint>Добавить запись...</Hint>
  <BackgroundImage>Images\24x24\plus.png</BackgroundImage>
  <BackgroundImageLayout>Center</BackgroundImageLayout>
  <FlatStyle>Flat</FlatStyle>
  <FlatBorderSize>1</FlatBorderSize>
  <FlatBorderColor>ButtonFlatBorderColor</FlatBorderColor>
  <FlatMouseDownBackColor>ButtonFlatMouseDownBackColor</FlatMouseDownBackColor>
  <FlatMouseOverBackColor>ButtonFlatMouseOverBackColor</FlatMouseOverBackColor>
  <Commands>
    <Command Name="CityAddFormShowCommand" />
    <Command Name="CityDatabaseTableAddRowValueSetCommand" />
  </Commands>
</MyObject>

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

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

Запрос на вставку данных

Перейдем в файл серверной xml (Template.xml). И добавим запрос на вставку данных в таблицу template.city в базе данных:

Template.xml
<SqlQuery Name="CityInsertSqlQuery">
  <Text>
    INSERT INTO template.city (
      title
    )
    VALUES (
      {Title}
    );
  </Text>
</SqlQuery>

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

Обратите внимание, что параметры, передаваемые с формы, указываются в фигурных скобках, например, {Title}. Причем названия этих параметров должны совпадать с названиями параметров, указанных в DatabaseTableSetDataConnection.

Создадим новое разрешение для этого запроса. Т.к. разрешение связано с редактированием записей в таблице, то отметим этот момент в имени разрешения, указав после имени сущности "Edit":

Template.xml
<Permission Name="CityEditSqlQueryPermission" Type="SqlQueryPermission">
  <SqlQueries>
    <SqlQuery Name="CityInsertSqlQuery" />
  </SqlQueries>
</Permission>

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

Template.xml
<Role Name="CityEditRole">
  <Permissions>
    <Permission Name="CityViewSqlQueryPermission" />
    <Permission Name="CityEditSqlQueryPermission" />
  </Permissions>
</Role>

Затем у группы GuestGroup заменим роль:

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

Отправка данных на сервер

Перейдем в файл описания стартовой формы (TemplateStart.xml).

Реализация отправки данных делится на два этапа:

  • описание соединения с данными для отправки: SetDataConnection или DatabaseTableSetDataConnection;

  • описание и вызов команды, которая будет обращаться к соединению с данными для отправки: SaveCommand.

Загрузка данных через соединение с данными для загрузки (PrimaryGetDataConnection) происходит автоматически при открытии формы, а соединения с данными для отправки (SetDataConnection или DatabaseTableSetDataConnection) выполнятся при вызове. Поэтому и нужна команда SaveCommand, которая будет обращаться к соединению с данными для отправки, чтобы последнее осуществило отправку.

Описание соединения с данными для отправки на сервер

Создадим сохраняющее соединение типа DatabaseTableSetDataConnection, которое напрямую из таблицы берет измененные строки и отправляет на сервер:

TemplateStart.xml
<DataConnection Name="CityDatabaseTableSetDataConnection" Type="DatabaseTableSetDataConnection" Assembly="ComplexDataConnections">
  <Workflow Name="Template" />
  <DatabaseTable Name="" />
  <Parameters>
    <Parameter NativeName="">
      <Column Name="" />
    </Parameter>
  </Parameters>
  <SqlQueries>
    <SqlQuery Name="" Type="" />
  </SqlQueries>
</DataConnection>

Тэг <Workflow> описывает имя процесса, в рамках которого происходит запрос.

В тэге <DatabaseTable> в атрибуте Name ожидается имя таблицы, описанной на форме. Для этой таблицы и будет происходит сохранение изменений. Укажем в этом тэге имя нашей таблицы:

<DatabaseTable Name="CityDatabaseTable" />

Тэг <Parameters> содержит список параметров, передаваемых в запросы. Каждый параметр представлен тэгом <Parameter>. Имя параметра, указанное в атрибуте NativeName, должно полностью совпадать с именем параметра, указанного в фигурных скобках в тексте SQL-запроса. В качестве значения тэга указывается колонка сохраняемой таблицы, из которой будут подставляться значения в соответствующий параметр запроса.

<Parameter NativeName="Title">
  <Column Name="Title" />
</Parameter>

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

В тэге <SqlQueries> описываются запросы для отправки данных. В атрибуте Name тэга <SqlQuery> указывается имя запроса, описанного в серверном файле. В качестве значения атрибута Type ожидается значение Insert, Update или Delete. В insert-запросы передаются параметры новых строк таблицы, в update-запросы - измененных строк, в delete-запросы - удаленных строк. DatabaseTable сразу разделяет свои строки по соответствующим коллекциям. Потому DatabaseTableSetDataConnection знает для каких строк таблицы какой запрос использовать.

В серверном XML-файле ранее описали запрос на вставку новых данных в таблицу базы данных - CityInsertSqlQuery. Укажем его в соединении с данными:

<SqlQueries>
  <SqlQuery Name="CityInsertSqlQuery" Type="Insert" />
</SqlQueries>

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

TemplateStart.xml
<DataConnection Name="CityDatabaseTableSetDataConnection" Type="DatabaseTableSetDataConnection" Assembly="ComplexDataConnections">
  <Workflow Name="Template" />
  <DatabaseTable Name="CityDatabaseTable" />
  <Parameters>
    <Parameter NativeName="Title">
      <Column Name="Title" />
    </Parameter>
  </Parameters>
  <SqlQueries>
    <SqlQuery Name="CityInsertSqlQuery" Type="Insert" />
  </SqlQueries>
</DataConnection>

Команда для отправки данных на сервер

Как было сказано выше, для отправки данных на сервер используется команда типа SaveCommand. Создадим такую команду в тэге <Commands> и укажем в ней описанное ранее соединение с данными:

TemplateStart.xml
<Command Name="CityDatabaseTableSaveCommand" Type="SaveCommand" Assembly="Commands">
  <DataConnections>
    <DataConnection Name="CityDatabaseTableSetDataConnection" />
  </DataConnections>
</Command>

А теперь вызов этой команды следует разместить в описании кнопки сохранения SaveButton в тэге <Commands>. Таким образом, общий синтаксис кнопки сохранения изменений на форме в таблицу city в базе данных будет выглядеть так:

TemplateStart.xml
<MyObject Name="SaveButton" Type="Button" Assembly="BaseControls">
  <Top>5</Top>
  <Right>
    <Formula>
      <Minus DataType="IntegerDataType">
        <Item>
          <Object Name="FootPanel">
            <Property Name="Right" />
          </Object>
        </Item>
        <Item>5</Item>
      </Minus>
    </Formula>
  </Right>
  <Height>30</Height>
  <Width>200</Width>
  <TextAlign>MiddleCenter</TextAlign>
  <FontStyle>ButtonFontStyle</FontStyle>
  <ImageAlign>MiddleLeft</ImageAlign>
  <Image>Images\24x24\content-save.png</Image>
  <FlatStyle>Flat</FlatStyle>
  <FlatBorderSize>1</FlatBorderSize>
  <FlatBorderColor>ButtonFlatBorderColor</FlatBorderColor>
  <FlatMouseDownBackColor>ButtonFlatMouseDownBackColor</FlatMouseDownBackColor>
  <FlatMouseOverBackColor>ButtonFlatMouseOverBackColor</FlatMouseOverBackColor>
  <Text>Сохранить</Text>
  <Commands>
    <Command Name="CityDatabaseTableSaveCommand" />
  </Commands>
</MyObject>

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

Итоги

Как вы могли заметить, на данный момент есть несколько недочетов:

  • При закрытии карточки города по крестику (без сохранения), в таблицу добавляются пустые строки. Происходит лишнее срабатывание команды CityDatabaseTableAddRowValueSetCommand;

  • Нет проверки корректности заполнения текстового поля "Наименование".

Эти недочеты мы поправим в последующих уроках.

А пока подведем итог того, что мы узнали:

  • для загрузки данных из базы данных используем PrimaryGetDataConnection, который выполняется автоматически;

  • для сохранения данных из таблицы на форме в таблицу в базе данных используем DatabaseTableSetDataConnection. Для его выполнения нужно использовать команду SaveCommand;

  • как подключать и использовать паттерны форм;

  • как на родительской форме получать данные с дочерней формы;

  • как в таблицу на форме добавлять новые строки.

На следующем уроке мы:

  • добавим возможность редактировать и удалять записи в таблице на форме;

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

Ответы

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

Last updated