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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

17KB
Open
Шаблоны форм

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

И сделаем ClientEditRole:

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

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

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

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

По заданию поле "Город" должно быть сделано в виде выпадающего списка (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.

338B
Open
Иконка для кнопки

Создайте самостоятельно команду на открытие формы 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 у нас уже есть. Давайте сделаем для них два SetDataConnection:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Итоги

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

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

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

Ответы

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

315KB
Open

Last updated