Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
_Delphi_1курс лекции / Тема 14 Управление формами.doc
Скачиваний:
48
Добавлен:
23.03.2015
Размер:
121.86 Кб
Скачать

Пример приложения с интерфейсом множества документов — простой многооконный редактор

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

Начнем с построения формы окна документа.

1. Откройте в Delphi новое приложение.

2. Назовите открывшуюся форму FDoc.

3. Поместите на форме компонент RichEditl типаTRichEdit. Его свойствоAlign задайте равнымalClient, чтобы окно редактирования заняло всю площадь окна. Сотрите текст, появившийся вRichEditl (свойствоLines).

4. Измените свойство формы FormStyle наfsMDIChild.

5. Сохраните проект, дав спроектированному модулю имя UDoc.

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

6. Откройте в вашем проекте новую форму (File | New Form). Назовите ее FMDI. Сохраните ее модуль с именемUMDI (File Save As).

7. Измените свойство FormStyle новой формы наfsMDIForm. Можете, если хотите, задать свойствоWindowState равнымwsMaximized, чтобы окно этой формы предъявлялось пользователю в первый момент развернутым на весь экран. В операторuses модуляUMDI введите ссылку на модуль дочерней формыUDoc (иначе вы не сможете открывать дочерние формы и управлять ими).

8. Выполните команду Project | Options и в открывшемся окне на странице Forms переведите дочернюю формуFDoc из списка автоматически создаваемых в список доступных. При этом, как вы сможете убедиться по выпадающему списку вверху окна, главной станет родительская формаFMDI — единственная, оставшаяся в списке автоматически создаваемых форм.

9. Поместите на форме список изображений ImageList и диспетчер действийАсtionList. Сошлитесь в свойствеImages компонентаActionListl наImageListl.

10. В диспетчере действий ActionList введите действие NewWindow, которое будет соответствовать созданию нового окна документа. Обработчик его событияOnExecute может иметь вид, уже рассмотренный в предыдущем разделе:

procedure TForml.NewWindowExecute(Sender: TObject);

var NewF : TFDoc;

begin

NewF := TFDoc.Create(Application);

NewF.Caption := 'Документ ' + IntToStr (MDIChildCount) ;

NewF.Show;

end;

11. Сошлитесь на этот же обработчик в событии формыOnShow, чтобы в первый момент открывалось одно окно документа.

12. В диспетчере действий ActionList введите стандартные действия раздела Window:WindowCascade,WindowTileHorizontal,WindowTileVertical,WindowArrange. Никаких обработчиков событий для них писать не надо. А если вы работаете со старыми версиями Delphi, в которых нет указанных стандартных действий, вам придется ввести аналогичные нестандартные действия. В этом случае вам надо написать обработчики их событийOnExecute. Для действия Каскад:

Cascade;

Для действия Упорядочить по горизонтали:

TileMode := tbHorizontal;

Tile;

Для действия Упорядочить по вертикали:

TileMode := tbVertical;

Tile;

Для действия Упорядочить значки:

ArrangeIcons;

13. Введите на форму компонент MainMenu. Сформируйте в нем раздел меню Окно и сошлитесь в его подразделах на введенные вами действия.

На этом первая стадия проектирования многооконного редактора завершена.

Можете сохранить проект и опробовать приложение в работе. Посмотрите, как ведет себя приложение при создании нового окна документа, если размеры родительского окна не достаточно велики. Обратите внимание на то, что ваше приложение автоматически удовлетворяет всем требованиям Windows к приложениям MDI. Например, при развертывании окна документа его заголовок автоматически перемещается в заголовок окна приложения. А если для заголовков не хватает места, они автоматически сворачиваются .

Теперь можно заняться совершенствованием нашего приложения. Мы хотим, чтобы меню содержало разделы Файл, Правка, Формат, Окно, Справка. Очевидно, что разделы Файл, Окно, Справка целесообразно отнести к главной форме. А разделы Правка и Формат лучше отнести к форме документа. Для такого решения есть два аргумента. Во-первых, если не открыт ни один документ, то править и форматировать просто нечего, так что соответствующие разделы меню должны отсутствовать. А во-вторых, для большинства разделов редактирования и форматирования целесообразно использовать стандартные действия. Они действуют только для той формы, в которой они созданы. Так что создавать объекты этих действий надо на форме документов. И представляется логичным ссылаться на эти действия из меню, расположенного на той же форме.

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

Для разделов Файл, Окно и Справка меяю главной формы надо задать соответственно значения Grouplndex, равные 0, 3, 4. Для разделов Правка и Формат меню формы документов надо задать соответственно значения GroupIndex, равные 1 и 2.

Дальнейшие действия по совершенствованию нашего редактора я изложу

кратко.

Перейдите в модуль UDoc. Сошлитесь в нем оператором uses на модульUMDI, так как нам надо будет иметь доступ к его списку изображений. Перенесите на форму компонент ActionList. Сошлитесь в его свойстве Images на ImageListl, расположенный на главной форме (если вы не забыли сделать указанную выше ссылку на главный модуль, его компонент ImageListl должен появиться в выпадающем списке свойства Images). Введите в диспетчер ActionList стандартные действия, связанные с редактированием текстов и форматированием. Перенесите на форму компонент MainMenu и сформируйте в нем разделы меню Правка и Формат, ссылающиеся на введенные действия. Не забудьте установить в разделах указанные выше значения свойства Grouplndex.

Введите также в раздел public класса формы объявление переменной FName, в которой будет храниться имя файла, загруженного в окно:

FName:string;

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

Теперь перейдите в модуль главной формы. Перенесите на форму диалоги открытия и сохранения файла. Введите в диспетчере ActionList нестандартные действия Open (открыть файл), CloseDoc (закрыть окно документа). Save (сохранить в файле), SaveAs (сохранить как ...), Можете также ввести действия печати, установки принтера, задания параметров страницы и т.д. Но на этих вспомогательных действиях мы останавливаться не будем.

Напишите обработчики событий OnExecute введенных действий. Обработчик

для действия Open может иметь вид:

procedure TFMDI.OpenExecute(Sender: TObject);

begin

if OpenDialog1.Execute

then begin

(ActiveMDIChild.ActiveControl as TRichEdit).

Lines.LoadFromFile(OpenDialog1.FileName);

// (ActiveMDIChild as TFDoc).RichEdit1.Lines.LoadFromFile(OpenDialog1.FileName);

(ActiveMDIChild as TFDoc).FName := OpenDialog1.FileName;

ActiveMDIChild.Caption := ExtractFileName(OpenDialog1.FileName);

end;

end;

В приведенном обработчике сначала вызывается диалог открытия файла. Выбранный пользователем файл нам надо загрузить в окно RithEdit формы активного документа. Доступ к этой форме обеспечивается свойством ActiveMDIChild. Поскольку активным на этой форме является окно RichEdit (больше там, фактически, ничего нет), то доступ к нему можно получить через свойствоActiveControl. Но так как это свойство имеет типTWinControl, а нам надо рассматривать его как объект типаTRichEdit, то окончательно доступ к окну редактирования обеспечивается конструкцией(ActiveMDIChild.ActiveControl as TRichEdit). А далее обычным образом в окно загружается выбранный пользователем файл.

Рассматриваемый оператор можно было бы изменить следующим образом:

(ActiveMDIChild as TFDoc).RichEditl.Lines.LoadFromFile(OpenDialogl.FileName);

Здесь форма активного окна документа рассматривается как форма типа TFDoc, и обращение следует непосредственно к расположенному на ней компонентуRichEditl. Подобное обращение позволяет в общем случае получить доступ к любому компоненту дочерней формы. Но зато предыдущий вариант обеспечивает доступ именно к активному компоненту, что имело бы смысл, если бы на дочерней форме располагалось несколько оконRichEdit.

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

(ActiveMDIChild as TFDoc). Последний оператор обработчика заносит в заголовок

активного окна документа имя файла, извлеченное из его полного имени функцией ExtractFileName.

Обработчик события OnExecute для действия SaveAs может иметь вид:

procedure TFMDI.SaveAsExecute(Sender: TObject);

begin

SaveDialog1.FileName := ActiveMDIChild.Caption;

if(SaveDialog1.Execute)

then begin

(ActiveMDIChild as TFDoc).FName := SaveDialog1.FileName;

(ActiveMDIChild.ActiveControl as TRichEdit).Lines.SaveToFile(SaveDialog1.FileName);

ActiveMDIChild.Caption := ExtractFileName(SaveDialog1.FileName);

end;

end;

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

Обработчик события OnExecute для действия Save может иметь вид:

procedure TFMDI.SaveExecute(Sender: TObject);

begin

if((ActiveMDIChild as TFDoc).FName = '') then SaveAsExecute(Sender)

else

(ActiveMDIChild.ActiveControl as TRichEdit).Lines.

SaveToFile((ActiveMDIChild as TFDoc).FName);

end;;

Оператор if проверяет, является ли переменная FName активного окна документа пустой строкой. Поскольку при загрузке документа из файла значение переменнойFName делается равным имени файла, то пустая строка означает, что пока неизвестно, в каком файле надо сохранять текст документа. В этом случае производится вызов описанной выше процедурыSaveAsExecute, чтобы пользователь мог указать имя файла. А если имя файла уже известно, то документ сразу сохраняется в этом файле без вызова диалога.

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

NewF.FName:=’’;

Осталось рассмотреть обработчик события OnExecute для действия CloseDoc.

Он может состоять всего из одного оператора:

ActiveMDIChild.Free;

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

обработчике закрытия дочерней формы можно написать

procedure TFDoc.FormClose(Sender: TObject; var Action: TCloseAction);

begin

Action:=caFree;

end;