Урок 14. Постраничный просмотр

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

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

Постраничный просмотр

Рассмотрим паттерн Pagination на примере формы списка клиентов.

Подготовка формы

Перейдем в файл списка клиентов (TemplateClientList.xml) и в ContentPanel добавим описание панели PagePanel с объектами, необходимыми для постраничного просмотра списка клиентов:

PagePanel

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

2KB
Open
Иконки для кнопок

Количество записей в ответе от сервера

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

Создадим условие для проверки количества строк в ClientPrimaryGetDataConnection, используя для этого get-проперти Count:

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

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

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

Для объекта TotalItemsLabel скорректируем значение тэга <Text>, указав количество строк которые вернул сервер:

TotalItemsLabel

Переход между страницами

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

Укажем эту переменную в тэге <Text> объекта CurrentPageTextBox.

CurrentPageTextBox

Создадим переменную для расчета количества страниц:

Где условие ItemsPerPageComboBoxIsNullCondition имеет вид:

Укажем переменную TotalPagesVariable в тэге <Text> объекта TotalPagesLabel.

TotalPagesLabel

Команда изменения номера текущей страницы на произвольное значение:

Команда для перехода на предыдущую страницу:

Команда для перехода на следующую страницу:

Условие проверки, является ли текущая страница первой из доступных:

Условие проверки, является ли текущая страница последней из доступных:

Теперь можем скорректировать описание кнопок перехода по страницам:

FirstPageButton
PrevPageButton
NextPageButton
LastPageButton

Но переход между страницами возможен и при ручном вводе значения в поле CurrentPageTextBox. Для этого создадим условие для проверки события нажатия клавиши Enter при редактировании текстового поля:

Создадим условия проверки введенного значение на удовлетворение минимального и максимального количества страниц:

Создадим Execution, в котором будет отрабатывать событие нажатия клавиши Enter в текстовом поле CurrentPageTextBox:

Запрос на просмотр

В ClientPrimaryGetDataConnection добавим параметры Limit (количество записей на странице) и Page (номер текущей страницы).

Так же необходимо переделать фильтрацию архивных и актуальных записей. Для этого из колонки Archive в таблице ClientDatabaseTable удалим тэг <Filter>, и добавим в ClientPrimaryGetDataConnection одноименный параметр.

В запрос ClientSelectSqlQuery добавим переменные для новых параметров:

Запустите проект и проверьте загрузку формы и работу постраничного просмотра списка клиентов.

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

Реализуйте постраничный просмотр на главной форме.

Загрузка файлов

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

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

База данных

В таблицу template.client добавим колонку photo_file_id, которую через внешний ключ привяжем к таблице public.file:

В таблице public.file хранится вся необходимая информация о загруженном файле: имя файла без расширения (name), полный путь до файла на сервере (path), дата загрузки (date) и уникальный guid-идентификатор.

Подготовка формы

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

Перейдем в файл TemplateClientEdit.xml

Внутри панели ContentPanel создадим две панели: MainPanel, в которую перенесем все имеющиеся поля на форме, и PhotoPanel, в которой будут располагаться объекты для работы с изображением:

Скачайте архив с фото, которое будем использовать в качестве заглушки, и распакуйте его в папку \Template\Projects\1. Template\Forms\Images.

3KB
Open

Добавим в PhotoPanel объект типа PictureBox:

В качестве значения тэга <Image> мы будем передавать ссылку с GUID файла, расположенного на сервере. По этой ссылке объект PictureBox самостоятельно скачает файл с сервера.

В тэгах <NullImage> и <ErrorImage> указан относительный путь до изображения, которое будет отображаться пользователю, если тэг <Image> имеет значение NULL, или изображение загружено с ошибкой.

Скорректируем запрос ClientByIdSelectSqlQuery, добавив в него поля PhotoGuid и PhotoGuidPath. Первое поле будет содержать чистый guid файла, который нам понадобится для скачивания изображения перед открытием на клиентской машине. Второе поле - guid-ссылка на файл, которую будем передавать в PhotoPictureBox.

Добавим новые поля в ClientPrimaryGetDataConnection и укажем PhotoGuidPath в качестве значения тэга <Image> PhotoPictureBox.

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

Запустите проект и проверьте расположение объектов на форме.

Редактирование изображения

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

Добавьте вызов этой команды на кнопку PhotoEditButton.

Обращаясь к параметру FullPath результата выполнения команды FileDialogShowCommand, мы получим полный путь до выбранного файла, который и передадим в наш объект PhotoPictureBox через set-проперти Image:

Команда FileDialogShowCommand возвращает словарь со следующими элементами:

  • OK - признак того, что в диалоговом окне пользователем был выбран хотя бы 1 файл;

  • Path - массив путей до папок, в которых расположены выбранные файлы, или путь до папки, в котором расположен 1 выбранный файл;

  • FileName - массив имен выбранных файлов (только имена, без расширения) или имя 1 выбранного файла;

  • FileNameExtension - массив расширений выбранных файлов (с точкой в начале) или расширение 1 выбранного файла;

  • FullFileName - массив полных имен выбранных файлов (имя и расширение) или полное имя 1 выбранного файла;

  • FullPath - массив полных путей до выбранных файлов (путь и полное имя) или полный путь до 1 выбранного файла.

Создадим Execution для обработки результата выполнения команды выбора файла:

FormChangedValueSetCommand

Удаление изображения

Создадим условия для проверки наличия пользовательского изображения в поле PhotoPictureBox. Для этого через get-проперти CurrentImageSource будем получать источник отображаемого изображения и сравнивать его со значениями, возвращаемыми get-проперти NullImage и ErrorImage.

Для удобства использования объединили обе проверки в одно условие PhotoPictureBoxIsNotEmptyCondition, которое укажем на кнопке PhotoDeleteButton в тэге <Enabled>.

Создадим команду для удаления пользовательского изображения из PhotoPictureBox.

Добавьте вызов этой команды на кнопку PhotoDeleteButton.

Для удаления файла с сервера используйте команду DeleteFileCommand.

Сохранение изображения

Для отправки файла на сервер создадим команда типа UploadFileCommand:

Серверная часть сохранит файл в хранилище, имя которого указывается в тэге <Storage>. Так как у нас этот тэг отсутствует, то в качестве каталога для сохранения файлов будет использоваться хранилище с именем Default.

Имена хранилищ и пути до их папок указываются в файле настроек серверной части (appsettings.json):

Хранилище Default привязано к папке Upload в каталоге, куда была развернута серверная часть приложения. В поле "Format" задается формат вложенных папок.

При успешном сохранении сервер сгенерирует уникальный guid-идентификатор, который вернется клиенту результатом команды UploadFileCommand.

Необходимая информация о загруженном файле будет сохранена в таблицу public.file.

Скорректируем команду SaveSequentialCommand, добавив вызов команды на передачу файла на сервер:

Добавим в ClientInsertSetDataConnection и ClientUpdateSetDataConnection параметр PhotoGuid, в котором будем передавать guid-идентификатор загруженного файла:

Скорректируем запросы:

ClientInsertSqlQuery
ClientUpdateSqlQuery

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

В таком сохранении есть один большой недочет: при сохранении изменений в других полях (например, изменили дату рождения), если у клиента ранее была сохранена фотография, то команда PhotoPictureBoxUploadFileCommand будет выполняться повторно. Это приведет к очередному сохранению файла на сервер, а значит добавлению копии файла и перезаписи photo_file_id у записи в template.client. Придумайте и реализуйте механизм, блокирующий перезапись файла, если файл не изменялся.

Учтите один момент: сейчас в качестве параметра PhotoGuid передается результат команды PhotoPictureBoxUploadFileCommand, если она не будет вызываться, то параметре будет передаваться пустое значение, что приведет к удалению ссылки на фото у записи в template.client.

Просмотр изображения

Теперь нам необходимо реализовать просмотр изображения по двойному клику по объекту PhotoPictureBox. При этом будем различать две ситуации в зависимости от источника изображения:

  • из каталога на локальной машине;

  • по guid-ссылке файла на сервере.

Путь до файла на локальной машине

Когда мы добавляем новое изображение в PhotoPictureBox, get-проперти CurrentImageSource возвращает полный путь до файла на локальной машине. В таком случае мы можем открыть изображение с помощью команды типа ApplicationRunCommand, которая будет запускать подходящее приложение относительно переданного в тэг <Application> файла.

Создадим такую команду:

Условие PhotoPictureBoxIsNotEmptyCondition, указанное во вложенном тэге <Condition>, ограничит выполнение команды, если в объекте нет пользовательского изображения.

По guid-ссылке файла

Если в PhotoPictureBox передается guid-ссылка на файл, расположенный на сервере, то мы не можем воспользоваться командой ApplicationRunCommand - она не распознает эту ссылку. В этом случае нам нужно скачать файл, воспользовавшись командой типа DownloadFileCommand.

Создадим такую команду:

После скачивания файла с сервера команда сама откроет этот файл в подходящем приложении.

Реализация

Создадим условие типа StartsWithCondition, чтобы различать guid-ссылки на файла:

Создадим Execution для обработки двойного клика по PhotoPictureBox для просмотра изображения в стороннем приложении:

Итоги

В этом уроке мы рассмотрели паттерн Pagination, применение которого позволяет просматривать большой объем записей, разбив их на страницы. Познакомились с объектом PictureBox для отображения изображений на форме, а также рассмотрели команды UploadFileCommand и DownloadFileCommand для загрузки файлов на сервер и скачивания их на клиентскую машину соответственно.

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

Ответы

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

527KB
Open

Last updated