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

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

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

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

  • рассмотрим применение SetDataConnectionarrow-up-right;

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

circle-check

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

Форма списка клиентов должна содержать таблицу и три кнопки редактирования списка. Сохранение данных в таблицу в базе данных будет происходить на форме карточки клиента. Поэтому на форме списка кнопка "Сохранить" и 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 выполним следующий скрипт:

circle-info

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

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

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

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

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

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

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

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

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

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

file-archive
17KB
Шаблоны форм

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

И сделаем ClientEditRole:

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

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

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

Создавать объекты на форме можно по одному: отдельно 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 и указал нужное поле.

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

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

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

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

Жмем Finish.

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

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

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

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

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

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

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

Жмем Finish.

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

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

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

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

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

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

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

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

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

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

file-archive
338B
Иконка для кнопки

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

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

Не забудьте поправить ширину 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 у нас уже есть. Давайте сделаем для них два SetDataConnectionarrow-up-right:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Итоги

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

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

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

Ответы

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

file-archive
315KB

Last updated