Продолжается подписка на наши издания! Вы не забыли подписаться?

Применение фреймов в Baikonur SuperServer

А. Дементьев
adem@demo.ru

Baikonur SuperServer 1.5 (BSS) был выпущен компанией Epsylon Technologies вслед за выпуском Baikonur Web App Server (BWAS) и Baikonur Enterprise Web App Server (BEWAS). Во все эти продукты с самого начала были заложены несколько основных идей - универсальный многопротокольный сервер приложений, умеющий работать с internet-протоколами и исполняемыми модулями .exe, коммутация потоков информации между различными пользователями и запускаемыми приложениями, поддержка различных internet-клиентов, в том числе специализированных, созданных разработчиком, визуальная сборка приложений серверного слоя. Еще в BWAS можно было визуально собирать приложения серверного слоя из HTML-компонент Delphi или С++ Builder. При этом собранное разработчиком и скомпилированное приложение (возможно, многопользовательское) работает под управлением сервера Baikonur, а общается с пользователем при помощи динамически, "на лету", формируемых HTML страничек. В BEWAS эти возможности были усилены, а в BSS появился и альтернативный путь развития - через JAT-компоненты. А расширением стандартных возможностей HTML-компонент в BSS стала поддержка фреймов.


Несмотря на то, что фреймы широко применяются довольно давно, в стандарт HTML они были введены только в версии HTML 4.0, которую можно найти в Интернете по адресу: http://www.w3.org/TR/REC-html40

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

Фреймы в HTML являются средством, позволяющим создавать сложные документы. Такие документы могут быть представлены в виде нескольких, возможно, взаимосвязанных, окон. Фреймы дают разработчику множество дополнительных возможностей по построению интерфейса между Web-приложением и пользователем, а также позволяют реализовать некоторые проекты, которые без фреймов сделать крайне трудно.

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

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

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

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

Строение фреймов

Приведем пример простого документа, содержащего фреймы:

<HTML>
<HEAD>
<TITLE>A simple frameset document</TITLE></HEAD>
<FRAMESET cols="20%, 80%">
    <FRAMESET rows="100, 200">
        <FRAME src="frame1.html">
        <FRAME src="frame2.gif">
    </FRAMESET>
    <FRAME src="frame3.html">
</FRAMESET>
<NOFRAMES>
<P>Этот документ содержит:
<UL>
<LI><A href="frame1.html">Текстовая страница</A>
<LI><IMG src="frame2.gif" alt="Картинка">
<LI><A href="frame3.html">Еще одна страница</A>
</UL>
</NOFRAMES>
</HTML>

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

Если браузер не может отображать фреймы или эта возможность в нем отключена, он будет показывать содержимое элемента <NOFRAMES>. Именно так достигается совместимость для разных браузеров.
Внимательно исследовав приведенный текст, мы увидим, что в различные части окна браузера выводится содержимое разных файлов frame1.html, frame2.gif, frame3.html.

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

Рассмотрим, какие возможности для этого предоставляет BSS 1.5.

Компоненты для создания фреймов

В состав поставки библиотек BSS 1.5 входит три довольно сложных компонента для поддержки фреймов.

Для создания приложений с фреймами применяется компонент HTMLFrameContainer – контейнер фреймов. Он находится на странице HTML Add страницы компонентов.

Кроме того, при формировании структуры фреймов на форме используются компоненты HMLFrameSet и HTMLFrame, которые применяются только внутри контейнера фреймов.

При проектировании роли между этими тремя компонентами распределяются следующим образом:
HTMLFrameContainer обеспечивает всю логику обработки приложения с фреймами и логику проектирования такого приложения. Контейнер содержит все остальные фреймы (HTMLFrame) и наборы фреймов(HMLFrameSet). Он одновременно является и самым старшим набором фреймов. К контейнеру применимы те же операции, что и к набору фреймов. При любых обстоятельствах контейнер фреймов должен содержать хотя бы один компонент HTMLFrame. Для того, чтобы это требование соблюсти, при
создании контейнера внутри него автоматически создается компонент HTMLFrame. Аналогичное требование существует по отношению к приложению, содержащему несколько фреймов. Несколько фреймов описывается компонентом HTMLFrameSet. Поэтому, как только создается второй фрейм, правила фреймов требуют, чтобы набор из двух фреймов был описан в компоненте HTMLFrameSet. Именно так и происходит автоматически во время дизайна. При попытке разделить существующий фрейм на несколько частей автоматически создается FrameSet, описывающий получившийся набор фреймов.

Таким образом, HTMLFrameContainer обеспечивает логику обработки фреймов во время дизайна и runtime

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

Стандартный подход, который используется в библиотеках компонентов для Baikonur, состоит в том, что в набор свойств HTML компонента вводятся свойства описания реакции на события, обрабатываемые на стороне клиента в браузере при помощи интерпретатора JavaScript. Все такие свойства обладают префиксом JS.

Такие свойства имеются и в компонентах поддержки фреймов. К примеру, в компоненте HTMLFrameContainer используются свойства JSOnLoad – функции JavaScript, исполняющиеся при загрузке контейнера фреймов в браузер – при открытии страницы, содержащей контейнер; и JSOnUnload – функции JavaScript, исполняющиеся при выгрузке контейнера фреймов из браузера (при переходе пользователя на другую страницу или при закрывании окна браузера). 


Примеры приложений с фреймами

Пример Simple

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

Создадим форму и поместим на нее компоненты THTMLControl, THTMLPage и THTMLFrameContainer. При этом следует учесть, что любые другие визуальные HTML-компоненты, находившиеся до этого на форме, не будут участвовать в построении HTML-скрипта. Если такие компоненты есть, то рекомендуется перенести эти компоненты внутрь контейнера фрейма, используя буфер обмена.

При создании компонента типа THTMLFrameContainer в него автоматически вставляется пустой фрейм - компонент типа THTMLFrame, занимающий всю поверхность контейнера. Этот фрейм можно разделить на несколько столбцов или строк. При этом в контейнер добавляется компонент типа THTMLFrameSet, содержащий новые фреймы, а все компоненты, находившиеся в старом фрейме, переносятся в один из новых. 

Для разделения фрейма необходимо выбрать пункт всплывающего меню "Split Frame…" этого фрейма, нажав правую кнопку мыши или комбинацию клавиш Alt-F10.

В диалоговом окне Split Frame можно указать, на сколько столбцов или строк нужно разделить фрейм, а также уточнить, будут ли новые фреймы вставляться в выбранный или же они будут помещаться в родительский фрейм. 
Все полученные фреймы будут автоматически получать уникальные имена. Имя фрейма находится в свойстве Name соответствующего компонента. Вы можете изменять эти названия, но следует помнить, что стандарт HTML не позволяет давать фреймам имена, начинающиеся не с буквы латинского алфавита. Кроме того, для возможности однозначной идентификации фреймов во время работы приложения фрейм должен иметь уникальное непустое имя.

Структура HTML-страницы получается последовательным разделением фреймов на строки и столбцы. Перед разделением фреймов рекомендуется тщательно разработать структуру будущей страницы, так как потом для ее изменения потребуется довольно много времени.

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

 
Порядок разделения страницы на фреймы


Вид полученной формы в дизайнере

Следующим шагом является определение правил взаимодействия фреймов во время выполнения программы.
Прежде всего, определим, что при нажатии кнопки “Add Item” во фрейме HTMLFrame1 должен обновляться фрейм HTMLFrame2, в котором находится список.
Для этого присвоим значение “HTMLFrame2” свойству Target фрейма HTMLFrame1.
Это свойство предназначено для указания фрейма, в который будет направляться результат нажатия кнопок типа Submit и вызова функции submit() языка JavaScript браузера.
При получении таких данных от клиента производится обновление компонентов, находящихся на фрейме. Перед этим вызывается событие OnBeforeUpdate соответствующего фрейма или, если оно не установлено, OnUpdate компонента HTMLPage. После обновления компонентов вызывается событие OnAfterUpdate соответствующего фрейма. Для формирования ответной HTML-страницы выбирается фрейм, указанный в свойстве Target. При этом перед формированием страницы вызывается событие OnBeforeScript соответствующего фрейма или, если оно не установлено, OnScript компонента HTMLPage.

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

Свойствам Base и Target можно присвоить любое значение или выбрать из выпадающего списка.
Следует особо отметить зарезервированные значения, начинающиеся на “_” и имеющие специальное назначение:
_blank
Браузер направит результат в новое пустое окно, не имеющее имени фрейма. В дальнейшем приложение никак не сможет влиять на это окно, и, в том числе, перенаправлять туда вывод.

_self
Результат будет загружен в тот же фрейм, где находится элемент, вызвавший запрос на сервер – кнопка или гиперссылка. Данное значение для фрейма полезно лишь в том случае, если установлено значение свойства Base, а результат нажатия кнопок следует направлять в этот же фрейм.

_parent
Браузер направит результат в окно, являющееся родительским для фрейма, откуда поступил запрос. Это значение эквивалентно _self, если текущий фрейм не имеет родительского фрейма. Следует применять это значение с осторожностью, так как при этом может наступить рассогласование в отображении фреймов между приложением и браузером.

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

Установим для фрейма, в котором находится кнопка “Exit”, значение свойства Target в “_top”. При этом финальная страница приложения будет выведена в полное окно браузера, игнорируя любые фреймы.

Код для обработки нажатия кнопки “Exit”:

procedure TForm1.CloseBtnClick(Sender: TObject);
begin
  {Close application}
  HTMLControl1.UserClose;
end;

Рассмотрим, каким образом можно создать кнопку “Refresh”, при нажатии на которую будут обновляться все фреймы в окне браузера. Прежде всего, ее нужно поместить во фрейм, свойство Target которого установлено в “_top”. Затем при обработке запроса нужно указать, что отправляемый браузеру ответ должен содержать не фреймы, а всю форму. Для этого необходимо воспользоваться свойством ActiveFrame, содержащимся в записи типа TUserInfo о текущем пользователе, которую можно получить из свойств CurrentUser или Users, а также метода GetUserInfo компонента HTMLControl приложения. 

Свойство ActiveFrame предназначено для указания активного в данный момент фрейма. Это свойство можно изменить во время обработки запроса от пользователя. При этом ответ пользователю будет формироваться на основе этого фрейма или, если установленное значение – nil, или активная форма не является владельцем активного фрейма, то на основе активной для данного пользователя формы. 

Код для обработки нажатия кнопки “Refresh”: 

procedure TForm1.RefreshBtnClick(Sender: TObject);  
begin
  {Refresh all frames in browser window}  
  HTMLControl1.CurrentUser.ActiveFrame := nil;  
end;

Пример DBFrames

В этом примере показано, как применяется свойство ActiveForm компонента THTMLFrame.

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

Если ActiveForm установлено в nil, то выводятся компоненты, содержащиеся внутри фрейма, иначе - компоненты, содержащиеся на указанной форме. Свойство ActiveForm можно изменять в момент обработки запроса от клиента или перед генерацией ответа. Не рекомендуется изменять значение свойства после отправки скрипта и до начала обработки пришедшего запроса, так как приложение не сможет правильно сопоставить пришедший запрос и соответствующую форму для обработки.

Свойство URL фрейма имеет приоритет над ActiveForm, но только при формировании страницы, описывающей структуру фреймов в окне браузера.

Пример Dynamic

Одной из возможностей использования компонента THTMLFrameContainer является динамическое изменение структуры фреймов на странице во время выполнения программы.
При этом с фреймами можно осуществлять те же операции, что и во время design-time: создание, удаление, перемещение, изменение свойств.
Пример Dynamic показывает, как можно осуществлять такие действия.

Доступ к фреймам во время исполнения производится с помощью свойства Frames компонентов HTMLFrameContainer и HTMLFrameSet. Frames – это список, наследник класса TList. К нему применимы все те же операции, что и к обычному списку. Следует только заметить, что для обеспечения необходимого условия существования непустого уникального имени у фрейма при вставке нового элемента в список производится проверка его имени на уникальность, или, если оно пустое, для него автоматически генерируется уникальное значение.
Кроме прямого изменения списка Frames, можно воспользоваться методами компонента THTMLFrame, предназначенными для разделения фрейма на несколько частей:

function SplitFrame(ASplitType: TSplitType;
    FrameCount: Integer;
    UseParentFrameSet: Boolean;
    const FrameWidths: array of String): TCustomHTMLFrameSet;  

Параметры функции SplitFrame: 
ASplitType – указывает, как разделяется фрейм – на столбцы - stVertical - или на строки – stHorizontal.
FrameCount – задает количество частей, на которые разделяется фрейм. 
UseParentFrameSet – параметр, указывающий, какой из фреймов будет содержать создаваемые фреймы: исходный фрейм, для которого была вызвана функция, или его родительский фрейм. На рисунке показан результат разбиения левого фрейма на два равных столбца при разных значения параметра UseParentFrameSet.

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

FrameWidths – массив строк-констант, описывающих размеры новых фреймов. Данные размеры задаются по тем же правилам, что и свойство FrameWidth отдельного фрейма. Количество этих размеров может не совпадать со значением параметра FrameCount. При этом лишние размеры игнорируются, а вместо недостающих берется размер фрейма по умолчанию - “*”.

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

function SplitFrameEx(ASplitType: TSplitType;
    FrameCount: Integer;
    UseParentFrameSet: Boolean;
    FrameWidths: TStrings): TCustomHTMLFrameSet;

Здесь параметр FrameWidths – динамический список строк. Следует заметить, что функция не освобождает память, выделенную под передаваемый список. Об этом должно заботиться вызывающее приложение.

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

Для объединения двух соседних фреймов можно воспользоваться функцией:

function MergeFrames(Frame1, Frame2: THTMLFrame;
    NewFrameName, NewFrameWidth: String): THTMLFrame;

Параметры Frame1 и Frame2 задают фреймы, которые нужно объединить.

Функция возвращает полученный фрейм с именем NewFrameName, если оно задано. Если имя не задано, используется имя фрейма Frame1. Новый фрейм имеет размер, указанный в параметре NewFrameWidth. Если параметр не задан, размер нового фрейма вычисляется как сумма размеров объединяемых фреймов. Компоненты, принадлежавшие этим фреймам, переносятся в новый фрейм. При этом не гарантируется, что получаемый после этого HTML-скрипт для нового фрейма будет иметь какой-либо практический смысл из-за неправильного расположения компонентов.

В том случае, если фреймы не могут быть объединены, возвращается nil.

Для определения того, являются ли два фрейма соседними и возможно ли их объединить, предназначена функция:

function IsFramesSibling(Frame1, Frame2: THTMLFrame): Boolean;

Параметры Frame1 и Frame2 задают фреймы для проверки. Функция возвращает True, если фреймы являются соседними и их можно объединять.

Особенности применения фреймов

Следует всегда помнить, что фреймы – это независимые документы. Введенные значения в одном из окон не станут доступны в другом, если не произвести отсылку данных на сервер. Также типичной ошибкой является представление о том, что значения, введенные в одном из фреймов, будут отосланы на сервер при нажатии кнопки типа Submit в любом другом фрейме. 

Одним из самых больших ограничений технологии фреймов является то, что при запросе из браузера приложение не может изменить содержимое более чем одного фрейма. Это ограничение можно обойти только с использованием языка JavaScript, однако не все браузеры, поддерживающие фреймы, поддерживают также и JavaScript. Кроме того, в большинстве браузеров его поддержку можно отключить. У компонента THTMLFrameContainer есть несколько функций, позволяющих перезапросить определенные фреймы у приложения с использованием JavaScript, при этом программисту не нужно ничего писать на этом языке.

Кроме того, не существует стандартного способа изменить по инициативе приложения фрейм, в который будет загружена отсылаемая страница. Для этого может применяться специальный нестандартный параметр “Window-target” HTTP-заголовка отсылаемой страницы. Однако его поддерживают только браузеры Netscape версий 3.0 и выше. Компонент THTMLFrameContainer содержит функции, перенаправляющие страницу в нужный фрейм с помощью этого метода, однако применять их нужно с большой осторожностью.

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


Copyright © 1994-2016 ООО "К-Пресс"