Сделать правильно порой означает сделать единственно возможным образом.
Процесс перехода на фрейм-архитектуру оказался увлекательным и драматичным. Отсутствие у меня практического опыта использования компонентов FireMonkey вызывает желание экспериментировать и оптимизировать на этапе прототипирования. И это приводит к удивительным (для меня, привыкшему к архитектуре VCL) открытиям, которыми я готов с вами поделиться.
Базовый фрейм
Задача: создать базовый фрейм, на котором будет располагаться метка с заголовком и две кнопки: для возврата назад и для вызова помощи. На кнопках должны отображаться картинки, обозначающие действия.
TDataModule
Начать нужно с модуля для хранения не визуальных компонентов. Здесь мы будем размещать всё, что связано с графическими ресурсами, и то, что необходимо для подключения к базе данных. Из настроек поменяем только имя на damUIData. Напомню, что за имя отвечает свойство Name – общее свойство для всех компонентов.
TImageList
Для хранения изображений добавим на damUIData компонент класса TImageList – продвинутый инструмент, позволяющий формировать изображения в различном разрешении.
![](https://k245.ru/wp-content/uploads/2024/06/image-8.png)
Чуть позже я расскажу о том, как этим пользоваться, а пока переименую его в imlMain и загружу в него картинки, которые можно получить из приложения Pichon for Windows. Само приложение бесплатное, скачать его можно на сайте стоковой графики icons8.com.
![](https://k245.ru/wp-content/uploads/2024/06/image-10.png)
TFrame
Тут особых сюрпризов нет. Вместительный контейнер для всего, что душе угодно разместить на форме, но без создания формы. По сути используется для задания списка компонентов и определения их свойств в disign-time, которые потом будут создаваться и отображаться на главной форме. Все остальные визуальные компоненты я буду размешать на этом фрейме, который назову fraBaseFrame. Для того, чтобы из фрейма можно было дотянуться до картинок модуля damUIData, его нужно добавить в команду uses секции implementation. Это можно сделать вручную или с помощью меню “File” – “Use unit…”.
TToolBar
Из названия следует, что именно он нужен, чтобы создать контейнер в верхней части экрана, на котором будут находиться кнопки и метка. По умолчанию он располагается в верхней части экрана ( Align = Top ). Я переименую его в tbrTop и проверил высоту ( Height = 40 ).
❓ Чем TToolBar отличается от TPanel? Предок у них общий, назначение тоже похоже. Возможно, дело в некоторых специфичных свойствах и методах, с которыми мне ещё предстоит столкнуться.
![](https://k245.ru/wp-content/uploads/2024/06/TToolBar.png)
![](https://k245.ru/wp-content/uploads/2024/06/TPanel.png)
TActionList
Все совершаемые действия удобно хранить в виде объектов в предназначенном для этого месте – в списке действий TActionList. Это традиционный для VCL компонент, но с расширенными возможностями по хранению не только обычных действий (TAction), но и действий, специфичных для различных компонентов/платформ.
![](https://k245.ru/wp-content/uploads/2024/06/image-9.png)
Я переименовал компонент в alsMain, настроил свойство, отвечающее за картинки ( Images = damUIData.imlMain ), и добавил два действия : actBack и actHelp,
TSpeedButton
На панели TToolBar рекомендуется размещать кнопки класса TSpeedButton, хотя допустимы и другие компоненты. Не будем искушать судьбу и добавим на панель инструментов пару кнопок данного класса. Одна должна быть прижата к правому краю, другая – к левому.
❓ Немного сбило с толку свойство StyleLookup и его значения toolbuttonleft и toolbuttonrigth. На выравнивание кнопок оно не повлияло, по крайней мере в design-time. Но помогло проверенное годами свойство Anchors. Я не стал использовать Align, так как кроме кнопок на панели инструментов будет отцентрированная надпись, а видимость кнопок может меняться в зависимости от функциональности наследника базового фрейма.
Имена новых кнопок – sbtBack и sbtHelp, а их свойства Anchors установлены в [akLeft,akTop] и [akRight,akTop] соответственно. Не забываем выставить размеры в соответствии с размерами панели ( Width = 40, Height = 40 ).
❗ Попытка пойти проверенным путём, установив свойство Action у кнопки, окончилась неудачей: картинка на кнопке отображается криво – мелкая и придавлена к левому краю. Прочесывание форумов привело к тому, что самым правильным решением является размещение поверх кнопки элемента для отображения картинки, так как кнопка в FireMonkey также является контейнером для других компонентов.
TGlyph
Так как я решил использовать хранилище картинок, то мне понадобился экземпляр класса TGlyph. Принцип работы классического TImage предполагает, что изображение загружается в каждый экземпляр, что не соответствует изначальной архитектурной концепции. А TGlyph прекрасно работает со списком изображений TImageList. Более того, у него есть свойство Action, которое я тут же связал с ранее созданными действиями. И даже подумал, что кнопка-то теперь как бы и нужна вовсе – ведь при клике (нажатии) на картинку вызовется нужное действие. Но не тут то было: y TGlyph нет свойства Anchors, которое я решил использовать для привязки положения, есть только Align.
❓ И вот новая дилемма (порожденная, как выяснилось в последствии, недостаточным владением матчастью): либо отказаться от свойства Align у метки, задействовав там Anchors для центрирования, и не использовать кнопки вовсе, либо на каждую кнопку набрасывать сверху картинку. При этом картинка перекроет всю область взаимодействия и обработчики кнопки не будут срабатывать, а будет срабатывать Action, привязанный к изображению. К слову, у TImage имеется возможность становиться “прозрачной” для кликов ( HitTest = False ), а у TGlyph такого свойства нет.
Прежде чем принимать решение, я решил поэкспериментировать с различными вариантами. При этом история с дизайном и компонентами FireMonkey получила новый неожиданный поворот. Вот что мне удалось экспериментально установить:
- Свойство Action у TGlyph не связывает картинку с действием TAction, а только устанавливает картинку из аналогичных настроек действия.
- TGlyph является прозрачной для кликов, которые обрабатываются родительской кнопкой.
- Если у TSpeedButton указан Action, то картинка из TActionList подтягивается в run-time даже если очистить свойство Images у кнопки в design-time
Таким образом единственное правильное решение состоит в том, чтобы:
- На каждую кнопку накладывать картинку, картинку привязывать к списку изображений imlMain.
- Убрать привязку списка действий alsMain к списку изображений imlMain.
- Каждую кнопку привязывать к действию из списка alsMain.
Возможно, что все это можно понять из документации, и не нужно было строить предположений насчет поведения и свойств новых для меня компонентов по их названиям и аналогиям с VCL. Но все же остался неприятный осадок от несоответствия ожиданий и действительности.
TLabel
Тут все прошло без сюрпризов, разве что вместо свойства Caption необходимо использовать Text – именно оно отвечает за отображаемый текст у компонентов FireMonkey. Для того, чтобы метка labCaption располагалась строго посредине, использовал привычные якоря ( Anchors = [akLeft, akTop, akRight, akBottom] ).
Последний штрих
Поразмыслив немного о возможных наследниках TfraBaseFrame, я решил добавить ещё одну панель инструментов, на которой будут располагаться кнопки совершения различных действий с отображаемыми данными и кнопки дополнительной навигации по фреймам. Эта панель присутствует на всех фреймах, кроме фрейма с помощью, но как знать – может и там она тоже пригодится в будущем. Переименовываем её в tbrBottom и меняем расположение ( Align = Bottom ). Базовый фрейм готов!
![](https://k245.ru/wp-content/uploads/2024/06/image-11.png)