ГЛАВА 2. ВЗАИМОДЕЙСТВИЕ ВИДИМЫХ ЭЛЕМЕНТОВ.

Программа на TurboVision инкапсулирована в объекты и код пишется только внутри объектов. Что делать, если объектам требуется обмен информацией с другими объектами программы? В традиционной программе, вероятно, информация будет копироваться из одной структуры данных в другую. В объектно - ориентированной программе это может быть непросто, поскольку объекты могут не знать, где находятся другие объекты. Если возникла необходимость во взаимодействии видимых элементов, то первый вопрос - правильно ли разделены задачи между двумя видимыми элементами. Проблема может возникать из - за неправильного проектирования программы. Вероятно, два видимых элемента должны быть объединены в один видимый элемент, или часть одного видимого элемента должна быть перемещена в другой видимый элемент.

Посредники.

Если программа спроектирована правильно и видимые элементы требуют взаимодействия между собой, один из способов - создать промежуточный видимый элемент. Например, предположим, что вы имеете объект электронной таблицы и объект текстового процессора и хотите иметь возможность вставлять что-либо из электронной таблицы в текстовый процессор и наоборот. Вы можете выполнить это прямым взаимодействием видимых элементов. Но если позднее вам понадобится добавить к этой группе объектов, например, базу данных и вставлять в базу и из базы данных, вам потребуется дублировать связь, установленную между первыми двумя объектами, на все три объекта. Лучшее решение - это установить промежуточный видимый элемент. В этом случае, скажем, "карман". Объект должен знать только, как копировать что-либо в этот карман и как вставить что-либо из кармана. Вне зависимости от того, сколько новых объектов вы добавите в группу, взаимодействие никогда не станет более сложным, чем сейчас.

Сообщения между видимыми элементами.

Если вы тщательно проанализировали вашу ситуацию, решили, что программа спроектирована правильно и что вам не требуется создавать промежуточные элементы, вы можете реализовать простое взаимодействие между двумя видимыми элементами. До того, как один видимый элемент сможет взаимодействовать с другим, вы можете определить, где находится другой видимый элемент и вероятно даже убедиться, что другой видимый элемент существует в данное время. Сначала пример. Модуль StdDlg содержит диалоговое окнo TFileDialog (этот видимый элемент открывается в интегрированной среде, когда вы хотите загрузить новый файл). TFileDialog имеет TFileList, который показывает список файлов и директорий, а TFileInputLine отображает текущий файл для загрузки. Каждый раз, когда пользователь выбирает другой файл в TFileList, TFileList должен сообщить об этом объекту TFileInputLine, чтобы тот вывел новое имя файла. В этом случае TFileList может быть уверен, что TFileInputLine существует, поскольку оба инициализированы внутри одного объекта TFileDialog. Как TFileList сможет сказать TFileInputLine, что пользователь выбрал новое имя файла? TFileList создает и посылает сообщение внутри метода TFileList.FocusItem, a TFileInputLine.HandleEvent получает его (Item - номер файла в списке): procedure TFileList.FocusItem(Item: Integer); var Event: TEvent: begin TSortedListBox.FocusItem(Item); { Вызывает наследуемый метод. } Message( TopView, evBroadcast, cmFileFocused, List^.At(Item)); { TopView указывает на текущий модальный видимый элемент; List^.At(Item) указывает на строку списка, содержащую имя выбранного файла. } end; procedure TFileInputLine.HandleEvent (var Event: TEvent); var Name: NameStr; begin TInputLine.HandleEvent(Event);{Вызывает наследуемый HandleEvent.} if (Event.What = evBroadcast) and (Event.Command = cmFileFocused) and (State and sfSelected = 0) then begin if PSearchRec(Event.InfoPtr)^.Attr and Directory < > 0 then Data^ := PSearchRec(Event.InfoPtr)^.Name + '\' + PFileDialog(Owner)^.WildCard else Data^ := PSearchRec(Event.InfoPtr)^.Name; {Записывает новое имя файла и путь в свое поле данных Data.} DrawView; {Перерисовка для отображения нового имени файла.} ClearEvent(Event); end; end; . Message - это функция ( модуль Views ), которая генерирует событие - сообщение и возвращает указатель на объект (если есть), который обработал это событие. Объявление функции Message следующее: function Message(Receiver: PView; What, Command: Word; InfoPtr: Pointer): Pointer;. Message создает запись события - сообщения со значениями полей What, Command и InfoPtr, заданными в одноименных параметрах при вызове функции. Затем, если возможно, вызывается Receiver^.HandleEvent для обработки этого события. Message возвращает nil, если Receiver = nil, или событие не обработано. Если же событие обработано (т.е. Receiver^.HandleEvent возвратил запись события с полем What, установленным в evNothing), то результатом вызова функции Message будет содержимое поля Event.InfoPtr. TFileList.FocusItem использует расширенный синтаксис TurboPascal (директива компилятора $Х+), чтобы использовать функцию Message как процедуру, поскольку результат, возвращаемый Message, в данном случае не нужен.

3. Кто обрабатывает общие сообщения.

Предположим, вам требуется определить, находится ли на панели экрана открытое окно, прежде чем выполнить некоторые действия. Как это сделать? Ваш код должен послать общее событие, на которое окна знают как ответить. "Подпись", оставленная объектом, который обработал это событие, будет говорить, кто (если есть) обработал его. Конкретный пример: в IDE TurboPascal (Интегрированная Среда Разработки TurboPascal), если пользователь запрашивает открыть окно просмотра, код, который открывает окно просмотра, должен проверить, не открыто ли оно. Если нет, то открывает его; если есть, то переносит наверх. Передача общего сообщения проста: AreYouThere := Message(DeskTop, evBroadcast, cmFindWindow, nil); В методе HandleEvent окна просмотра есть проверка на о тклик (очистка события) на команду cmFindWindow: case Event.What of ... cmFindWindow : ClearEvent(Event); ... end;. Вспомним, что ClearEvent не только устанавливает поле What записи события в evNothing, no также устанавливает поле InfoPtr в @Self. Message читает эти поля и, если событие было обработано, возвращает указатель на объект, обработавший событие - сообщение. В данном случае это окно просмотра. Так, за строкой, которая посылала сообщение, следует: if AreYouThere = nil then CreateWatchWindow else AreYouThere^.Select;. Поскольку окно просмотра - это единственный объект, который знает, как отвечать на общее сообщение cmFindWindow, можно быть уверенным, что когда код выполнится, будет одно и только одно окно просмотра н вершине всех видимых элементов на панели экрана. Используя технику, описанную ранее, вы также можете, например, определить, какое окно является верхним из видимых элементов его типа на панели экрана. Поскольку общее сообщение посылается каждому подэлементу модального видимого элемента в Z-порядке, первым получит сообщение верхний видимый элемент на панели экрана. Рассмотрим ситуацию, возникающую в IDE, когда пользователь имеет окно просмотра, открытое на вершине панели экрана во время пошагового выполнения кода в окне редактора. Окно просмотра может быть активным окном (двойная рамка), но курсор выполнения кода в окне требует сохранения трассы выполняемого кода. Если на панели экрана открыто несколько окон редактора, они могут не перекрываться вообще, но IDE должен знать, какое из окон редактора предназначено для трассировки. Ответ: конечно, самое верхнее окно редактора. Для того, чтобы определить, какое из окон "верхнее", IDE посылает общее сообщение, отклик на которое знают только окна редактора. Первое окно редактора, которое получает общее сообщение, и будет верхним; оно обработает событие, очищая его, и IDE узнает, какое окно использовать для трассировки кода, читая результат, возвращенный Message.

Вызов процедуры HandleEvent.

Bы также можете создать или модифицировать событие, а затем вызвать HandleEvent напрямую. Можно сделать три типа вызовов: 1. Bы можете иметь видимый элемент, вызывающий HandleEvent равного подэлемента прямо ("равные" видимые элементы - это подэлементы с одним владельцем). Сообщение не передается другим в идимым элементам. Оно идет прямо к этому HandleEvent, затем управление возвращается к вам. 2. Вы можете вызвать HandleEvent владельца. Событие будет затем распространяться вниз по цепочке видимых элементов. (Если вы вызываете HandleEvent из вашего собственного HandleEvent, ваш HandleEvent будет вызываться рекурсивно.) Управление передается вам после обработки события. 3. Bы можете вызвать HandleEvent видимого элемента из другой цепочки видимых элементов. Событие будет передаваться вниз по этой цепочке видимых элементов. Управление передается вам после обработки события.