Главная » Уроки по ООП » Урок 19. Создание приложений с MDI-интерфейсом |
ПРАКТИЧЕСКАЯ РАБОТА |
Урок 19. Создание приложений с MDI-интерфейсом
Уровень 1.
Задание 1. Создать многооконный текстовый редактор, в каждое окно которого можно загрузить содержимое заданного входного текстового файла, что-то в нем изменить и сохранить текст в заданном выходном файле.
Создать форму окна документа с использованием компонентов RichEdit.
Создание дочерней формы
- Создайте приложение. Настройте форму (рис.1):
- Name=FDoc
- FormStyle = fsMDIChild
Рисунок 1.
- На этом закончим пока создание формы документа. Потом мы к нему вернемся и наполним необходимым сервисом.
Создание родительской формы
- Создайте новую форму.
- Name=FMDI.
- FormStyle = fsMDIForm.
- В оператор uses родительского модуля введите ссылку на модуль дочерней формы (иначе вы не сможете открывать дочерние формы и управлять ими).
- Выполните команду Project \ Options и в открывшемся окне на странице Forms переведите дочернюю форму FDoc из списка автоматически создаваемых в список доступных.
- Поместите на форме список изображений ImageList и диспетчер действий АсtionList. Сошлитесь в свойстве Images компонента ActionList1 на ImageList1.
- Дважды щелкните по ActionList и в открывшемся диспетчере действий щелкните по кнопке NewAction. Созданному вами действию дайте имя NewWindow, которое будет соответствовать созданию нового окна документа (рис.2).
Рисунок 2. Новое действие NewWindow
- Создайте обработчик события OnExecute и внесите в него следующий код:
var NewF : TFDoc;
begin
NewF := TFDoc.Create(Application);
NewF.Caption:='Документ '+IntToStr (MDIChildCount);
NewF.Show;
end;
- Сошлитесь на этот же обработчик в событии формы OnShow, чтобы в первый момент открывалось одно окно документа.
- Щелкните дважды по компоненту ActionList и в окне инспектора действий нажмите на кнопку со стрелочкой. В раскрывшемся списке выберите NewStandartAction. В появившемся окне выберите стандартные действия раздела Window: WindowCascade, WindowTileHorizontal, WindowTileVertical, WindowArrange. Никаких обработчиков событий для них писать не надо.
- Установите на форму компонент ToolBar (свойство ShowCaptions=True). Создайте несколько кнопок и с помощью свойств кнопок Action, свяжите их с действиями, созданными вами ранее (рис.3).
Рисунок 3.
Можете сохранить проект и опробовать приложение в работе. Посмотрите, как ведет себя приложение при создании нового окна документа, если размеры родительского окна не достаточно велики. Обратите внимание на то, что ваше приложение автоматически удовлетворяет всем требованиям Windows к приложениям MDI.
Например, при развертывании окна документа его заголовок автоматически перемещается в заголовок окна приложения. А если для заголовков не хватает места, они автоматически сворачиваются.
Сохраните проект. Мы к нему вернемся в третьем уровне.
Уровень 2.
Задание 2: Создать программу просмотра изображений с использованием интерфейса MDI.
Рисунок 4. Родительская форма (Name=frmMDIParent)
У кнопок Растянуть и По центру установите свойство Style= tbsCheck.
У компонента OpenDialog установите следующие свойства:
Filter=Bitmaps (*.bmp)]*.bmp
Options=[ofPathMustExist, ofFileMustExist]
Рисунок 5. Дочерняя форма (Name=frmMDIChild)
Интерфейс MDI настройте самостоятельно.
Кнопка «Загрузить»
if OpenDialog1.Execute then
with TfrmMDIChild.Create(Application) do
begin
Caption:= OpenDialog1.FileName;
Image1.Picture.LoadFromFile(OpenDialog1.FileName);
ClientWidth:= Image1.Picture.Width;
ClientHeight:= Image1.Picture.Height;
end;
После запуска диалогового окна создается новый экземпляр дочерней формы и загружается файл изображения. После загрузки размеры дочерней формы изменяются так, чтобы можно было видеть все изображение.
Заметьте, что, когда вы щелкаете на кнопке Close, дочерняя форма не закрывается, а сворачивается в пиктограмму. Чтобы заставить ее закрыться, следует добавить в код обработчика OnClose класса TfrmMDIChild маленькую деталь— изменить свойство Action:
Action:= caFree;
Кнопка «Растянуть»
if not (ActiveMDIChild = Nil) then
if ActiveMDIChild is TfrmMDIChild then
TfrmMDIChild(ActiveMDIChild).Image1.Stretch:= ToolButton2.Down;
Кнопка «По центру»
if not (ActiveMDIChild = Nil) then
if ActiveMDIChild is TfrmMDIChild then
TfrmMDIChild(ActiveMDIChild).Image1.Center:= ToolButton3.Down;
Остается последняя проблема — состояния кнопок Stretch и Center одинаковы для всех дочерних форм. Для решения этой задачи добавьте в обработчик события OnActivate класса TfrmMDIChild строки:
frmMDIParent.ToolButton2.Down:= Image1.Stretch;
frmMDIParent.ToolButton3.Down:= Image1.Center;
Уровень 3. Продолжение программы из уровня 1.
Мы хотим, чтобы меню содержало разделы Файл, Правка, Формат, Окно, Справка. Очевидно, что разделы Файл, Окно, Справка целесообразно отнести к главной форме. А разделы Правка и Формат лучше отнести к форме документа. Для такого решения есть два аргумента. Во-первых, если не открыт ни один документ, то править и форматировать просто нечего, так что соответствующие разделы меню должны отсутствовать. А во-вторых, для большинства разделов редактирования и форматирования целесообразно использовать стандартные действия. Они действуют только для той формы, в которой они созданы. Так что создавать объекты этих действий надо на форме документов. И представляется логичным ссылаться на эти действия из меню, расположенного на той же форме.
Если мы приняли такое решение, надо позаботиться, чтобы разделы меню наших двух форм не стирали друг друга. Это обеспечивается заданием соответствующих значений свойств GroupIndex разделов меню.
По умолчанию все разделы меню имеют одинаковое значение GroupIndex, равное нулю. Если требуется объединение меню, то разделам надо задать неубывающие номера свойств GroupIndex. Тогда, если разделы встраиваемого меню имеют те же значения GroupIndex, что и какие-то разделы меню основной формы, то эти разделы заменяют соответствующие разделы основного меню. В противном случае разделы вспомогательного меню встраиваются между элементами основного меню в соответствии с номерами GroupIndex. Если встраиваемый раздел имеет GroupIndex меньший, чем любой из разделов основного меню, то разделы встраиваются в начало.
Для разделов Файл, Окно и Справка меню главной формы надо задать соответственно значения Grouplndex, равные 0, 3, 4. Для разделов Правка и Формат меню формы документов надо задать соответственно значения GroupIndex, равные 1 и 2.
Перейдите в модуль UDoc. Сошлитесь в нем оператором uses на модуль UMDI, так как нам надо будет иметь доступ к его списку изображений. Перенесите на форму компонент ActionList. Сошлитесь в его свойстве Images на ImageList1, расположенный на главной форме. Введите в диспетчер ActionList стандартные действия, связанные с редактированием текстов и форматированием. Перенесите на форму компонент MainMenu и сформируйте в нем разделы меню Правка и Формат, ссылающиеся на введенные действия. Не забудьте установить в разделах указанные выше значения свойства GroupIndex.
Введите также в раздел public класса формы объявление переменной FName, в которой будет храниться имя файла, загруженного в окно:
FName:string;
Это имя, которое нам потребуется для сохранения текста, будет в каждом окне разным. Так что его надо независимо помнить в каждом объекте формы.
Теперь перейдите в модуль главной формы. Перенесите на форму диалоги открытия и сохранения файла. Введите в диспетчере ActionList нестандартные действия Open (открыть файл), CloseDoc (закрыть окно документа), Save (сохранить в файле), SaveAs (сохранить как ...).
Напишите обработчики событий OnExecute введенных действий.
«Открыть»
Обработчик для действия Open может иметь вид:
procedure TForm1.OpenExecute(Sender: TObject);
begin
if OpenDialog1.Execute
then begin
(ActiveMDIChild.ActiveControl as TRichEdit).Lines.
LoadFromFile(OpenDialogl.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).RichEdit1.Lines.LoadFromFile(OpenDialog1.FileName);
Здесь форма активного окна документа рассматривается как форма типа TFDoc, и обращение следует непосредственно к расположенному на ней компоненту RichEdit1. Подобное обращение позволяет в общем случае получить доступ к любому компоненту дочерней формы. Но зато предыдущий вариант обеспечивает доступ именно к активному компоненту, что имело бы смысл, если бы на дочерней форме располагалось несколько окон RichEdit.
После загрузки файла в переменную FName активного окна документа заносится имя файла, чтобы в дальнейшем отредактированный текст можно было сохранить в том же файле. Чтобы компилятор понял введенную нами переменную FName, активная форма документа рассматривается как форма типа TFDoc: (ActiveMDIChild as TFDoc). Последний оператор обработчика заносит в заголовок активного окна документа имя файла, извлеченное из его полного имени функцией ExtractFileName.
«Сохранить как»
Обработчик события OnExecute для действия SaveAs может иметь вид:
procedure TForm1.SaveAsExecute(Sender: TObject);
begin
SaveDialog1.FileName:= ActiveMDIChild.Caption;
if(SaveDialog1.Execute)
then begin
(ActiveMDIChild.ActiveControl as TRichEdit).
Lines.SaveToFile(SaveDialog1.FileName);
(ActiveMDIChild as TFDoc).FName := SaveDialog1.FileName;
ActiveMDIChild.Caption:=ExtractFileName(SaveDialog1.FileName);
end;
end;
Первый оператор предлагает пользователю имя файла, хранящееся в заголовке окна активного документа, как имя но умолчанию. Далее вызывается диалог сохранения файла. Оператор сохранения текста в файле почти не отличается от рассмотренного ранее оператора загрузки из файла. Последние два оператора запоминают имя файла.
«Сохранить»
Обработчик события OnExecute для действия Save может иметь вид:
procedure TForm1.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 надо добавить оператор
KewF.FKame := '';
Осталось рассмотреть обработчик события OnExecute для действия CloseDoc.
Он может состоять всего из одного оператора:
ActiveMDIChild.Free;
Этот оператор удаляет из памяти объект активной формы документа. Здесь нельзя было бы применить метод Close, так как для дочернего окна приложения MDI закрыть форму означает свернуть окно.
Составитель: Салий Н.А.