Как создать mdi приложение
Многооконная структура программы
Перетаскивание файлов в приложение
Часть урока для новичков
Приложения MDI и приложения SDI.
Прежде чем устанавливать вид и свойства вашего приложения надо подумать о том, какой вид приложения вы желаете создать. Это во многом зависит от его функционального назначения. Можно выбирать между так называемыми MDI- или SDI-приложениями.
MDI - сокращенно от Multiple Document Interface (интерфейс для одновременной работы со многими документами), а SDI - от Single Document Interface (интерфейс для работы с одним документом). В MDI приложениях два или более окон могут быть активны одновременно. В SDI-приложениях это невозможно. Здесь в каждый момент времени может быть активным только одно окно.
MDI -приложения являются удобным средством для одновременного выведения на экран текста или данных, которые хранятся в различных файлах. Такую структуру построения окон можно использовать для редактирования текстов, открывая и выводя на экран одновременно несколько различных документов. С помощь этих приложений можно также производить табличные вычисления, обрабатывая несколько таблиц одновременно, перенося или сравнивая данные из одной в другую. Пример такой работы над файлами - программа MS Word. Здесь файлы текстового и графического формата открываются в отдельных окнах, находящихся внутри главного окна программы.
Различные открываемые документы в окнах имеют общее рабочее пространство, в пределах которого они отображаются, называемое родительским окном. Всегда в MDI-приложении родительское окно является главной формой приложения. Все внутренние формы, называемые дочерними окнами отображаются в пределах рабочего пространства родительского окна и не могут быть помещены за ее пределы. Даже при свертывании. При максимизации таких окон, они занимают все рабочее пространство родительского окна, оставаясь внутри его.
Родительское окно может быть в MDI-приложениях только одно, а дочерних окон может быть теоретически бесконечно.
Большинство MDI-приложений имеют меню " Window " ("Окно") со следующими пунктами: Cascade, Arrange Icons, Tile Horizontal, Tile Vertical . С их помощью пользователь может управлять дочерними окнами приложения. Обычно нижняя часть меню отделена и представляет список всех открытых окон. Это свойство весьма удобно для быстрого перехода к тому или иному редактируемому файлу.
Все SDI-окна можно разделить по свойствам доступа друг к другу как модальные и немодальные. Они определяют, может пользователь или нет переключаться на другие окна. Когда модальное окно открыто, все другие открытые окна становятся недоступными. Пример можно увидеть практически из любой программы, вызвав пункт меню "About" ("О программе"). Отображаемое окно в приложении, как правило, не дает переключится на другое, пока не будет закрыто. Такое свойство иногда бывает очень полезным. Например, когда нужно чтобы пользователь ввел пароль, а затем получил доступ к определенному окну с данными, или окно фильтра данных, указав условия отбора, получает доступ к результату.
Итак, модальными или немодальными могут быть только SDI-окна.
С сегодняшнего урока мы с вами начинаем разрабатывать довольно сложный пример простого многооконного текстового редактора.
-
Запускаем Delphi. В меню "File" выбираем пункт "New Application".
Поскольку в этом окне создается другое, то в переименованный модуль MainUnit необходимо подключить ChildUnit . Для этого выбираем из меню " File " пункт "Use Unit: " и указываем модуль ChildUnit . Нажимаем OK.
Все, запускаем программу на выполнение. Во время работы обратите внимание на список появившихся окон в меню "Окна", по которым удобно переключаться на необходимое окно.
В следующих уроках мы подробнее остановимся на написании этого примера. Если в этом уроке у вас что-то не получается, то пример на данном этапе разработки можно скачать здесь (2 KB). Если вы в последствии открываете свой или скаченный проект, не забывайте, что нужно открывать DPR файл.
Часть урока для продвинутых программистов
Перетаскивание файлов в приложение
Иногда очень полезно избавить пользователя от лишних операций при открытии файла. Он должен нажать на кнопку "Открыть", затем найти интересующий каталог, выбрать файл. Проще перетащить мышкой файл сразу в окно приложения. Рассмотрим пример перетаскивания Drag & Drop в окно произвольного текстового файла, который сразу же открывается в компоненте Memo1.
Для начала в разделе Uses необходимо подключить модуль ShellAPI .
В private области окна нужно вставить следующую строку:
procedure TForm1.WMDropFiles(var Msg: TWMDropFiles);
var
CFileName: array[0..MAX_PATH] of Char; // переменная, хранящая имя файла
begin
try
If DragQueryFile(Msg.Drop, 0, CFileName, MAX_PATH)>0 then // получение пути файла
begin
Form1.Caption:=CFileName; // имя файла в заголовок окна
Memo1.Lines.LoadFromFile(CFileName); // открываем файл
Msg.Result := 0;
end;
finally
DragFinish(Msg.Drop); // отпустить файл
end;
end;
Для того, чтобы форма знала, что может принимать такие файлы, необходимо в процедуре создания окна указать:
procedure TForm1.FormCreate(Sender: TObject);
begin
DragAcceptFiles(Handle, True);
end;
Цель работы: Изучить основные способы разработки многооконных приложений. Получить практические навыки в создании многооконных приложений.
Создание дочерней формы
Основа Интерфейса ( MDI ) приложения - MDI родительская форма. Это - форма, которая содержит MDI дочерние окна. Дочерние окна являются "подокнами", с которыми пользователь взаимодействует в MDI приложении. Создание MDI родительской формы описано в "Создание главного меню приложения" .
Для определения главного окна ( Form1 ), как родительской формы в окне Свойств, установите IsMDIContainer свойство - true . Это определяет форму как MDI контейнер для дочерних форм. Для того чтобы родительское окно занимало весь экран необходимо свойству WindowsState установить значение Maximized .
Создайте еще одно окно, которое будет дочерним ( FormEmployee ). Для этого выберите пункт меню Project/Add Windows Form.
Это окно должно вызываться из пункта главного меню "Сотрудник". Вставьте код, подобный следующему, чтобы создать новую MDI дочернюю форму, когда пользователь щелкает на пункте меню , например "Сотрудник" - имя объекта - employeeToolStripMenuItem (В примере ниже, указатель события обращается к событию Click для employeeToolStripMenuItem_Click ).
Данный обработчик приведет к выводу на экран дочернего окна.
Создание меню в дочерней форме
Добавьте в дочернее окно пункт меню "Действие" ( actionToolStripMenuItem ) с подпунктами "Отменить" ( undoToolStripMenuItem ), "Создать" ( createToolStripMenuItem ), "Редактировать" ( editToolStripMenuItem ), "Сохранить" ( saveToolStripMenuItem ) и "Удалить" ( removeToolStripMenuItem ). Перед пунктом удалить вставьте разделитель ( Separator - name = toolStripSeparator1 ).
Добавьте в дочернее окно еще один пункт меню "Отчет" ( reportToolStripMenuItem ) c подпунктами " По сотруднику" ( reportToolStripMenuItem1 ), " По всем сотрудникам" ( reportToolStripMenuItem2 ). Дочернее окно будет иметь вид, представленный на рисунке 3.1
В главном меню родительской формы ( Form1 ) имеются пункты " Объект ", "Справочник" и "Справка". В дочерней форме ( FormEmployee ) сформированы пункты меню "Действие" и "Отчет". При загрузке дочерней формы меню родительской и дочерних форм должны были объединены и составлять следующую последовательность: " Объект ", "Действие", "Отчет", "Справочник" и "Справка". Объединение пунктов меню производится с помощью задания значений свойств MergeAction и MergeIndex для объектов ToolStripMenuItem .
Проверьте, чтобы в меню главного окна для объекта objectToolStripMenuItem свойство MergeAction было установлено Append , а MergeIndex было равно 0, а для объектов dictionaryToolStripMenuItem и helpToolStripMenuItem - соответственно 1 и 2. С учетом этого, в окне "Сотрудник" для объектов actionToolStripMenuItem (Действие) и "Отчет" ( reportToolStripMenuItem ) свойству MergeAction необходимо задать значение Insert , а свойству MergeIndex задаем порядковый номер который определяет позицию данного пункта меню обновленном главном меню , т.е. 1 (после объекта objectToolStripMenuItem ).
После компиляции программы, запуска ее на выполнение и вызова пункта меню "Сотрудник" экран должен иметь вид, представленный на рисунке 3.2.
Создание обработчиков для меню дочерней формы
Созданные пункты меню для дочернего окна должны инициировать выполнение соответствующих функций (Отменить, Создать, Редактировать, Сохранить и Удалить) приложения в отношении объектов конкретного дочернего окна. Для дочернего окна "Данные по сотруднику" эти функции должны выполнять соответственно отмену редактирования данных по сотруднику ( функция "Отменить"), создавать новые данные по сотруднику ( функция "Создать"), редактировать данные по сотруднику ( функция "Редактировать"), сохранять созданные вновь или отредактированные функция по сотруднику ( функция "Сохранить") и удалять данные по сотруднику ( функция "Удалить").
Описанную функциональность целесообразно реализовать в программе в виде методов класса созданного FormEmployee . В приложении необходимо создать следующие методы:
- Undo - отменить;
- New - создать;
- Edit - редактировать;
- Save - сохранить;
- Remove - удалить.
На начальных этапах проектирования, как правило, неясна реализация каждого метода, поэтому целесообразно их выполнять в виде методов-заглушек, которые только сообщают пользователю о своем вызове, а в дальнейшем необходимо написать реальный код.
Для создания метода Undo в коде файла FormEmployee.cs добавьте следующий метод:
Далее создаем обработчик события вызова пункта меню "Отменить". Для этого в дизайнере формы класса FormEmployee делаем двойной щелчок на пункте меню "Отменить". Инструментальная среда VS сгенерирует следующий код:
В код обработчика undoToolStripMenuItem_Click добавим вызов метода Undo :
Аналогичным образом создайте методы-заглушки для функций "Создать", "Редактировать", "Сохранить" и "Удалить".
14 декабря 2020 mob25
На этом шаге мы рассмотрим создание MDI-приложений.
Автоматическое создание форм
Динамическое создание форм
MDI-свойства TForm
MDI-события и MDI-методы TForm
Пример MDI-приложения
Термин MDI (Multiple Document Interface) дословно означает
многодокументный интерфейс и описывает приложения, способные загрузить и
использовать одновременно несколько документов или объектов. Примером
такого приложения может служить диспетчер файлов (File Manager).
Обычно MDI-приложения состоят минимум из двух форм -
родительской и
дочерней. Свойство родительской формы FormStyle
установлено равным
fsMDIForm. Стиль дочерней формы - fsMDIChild.
Родительская форма служит контейнером, содержащим дочерние формы,
которые заключены в
клиентскую область и могут перемещаться, изменять размеры,
минимизироваться или максимизироваться.
В приложении могут быть дочерние формы разных типов, например одна - для
обработки изображений, а
другая - для работы с текстом.
В MDI-приложении, как правило, требуется выводить несколько экземпляров
классов формы.
Поскольку каждая форма представляет собой объект, она должна быть создана
перед использованием
и освобождена, когда в ней больше не нуждаются. Delphi может делать
это автоматически,
а может предоставить эту работу программисту.
Автоматическое создание форм
По умолчанию при запуске приложения Delphi автоматически создает по
одному экземпляру
каждого класса форм в проекте и освобождает их при завершении программы.
Автоматическое создание
обрабатывается генерируемым Delphi кодом в трех местах.
Первое - раздел интерфейса в файле модуля формы.
В данном фрагменте кода объявляется класс TForm1.
Вторым является место, в котором описывается переменная класса:
Здесь описана переменная Form1, указывающая на экземпляр класса
TForm1
и доступная из любого модуля. Обычно она используется во время работы
программы для управления формой.
Третье место находится в исходном тексте проекта, доступ к которому можно
получить с помощью меню
View | Project Source. Этот код выглядит как:
Процесс удаления форм обрабатывается с помощью концепции владельцев
объектов: когда объект уничтожается,
автоматически уничтожаются все объекты, которыми он владеет. Созданная
описанным образом
форма принадлежит объекту Application и уничтожается при закрытии
приложения.
К оглавлению
Динамическое создание форм
Хотя автоматическое создание форм полезно при разработке однодокументных
приложений, при создании
MDI-приложении оно, как правило, неприемлемо.
Для создания нового экземпляра формы используйте конструктор
Create класса формы.
Приведенный ниже код создает новый экземпляр TForm1 во время
работы программы и
устанавливает его свойство Caption равным New Form:
Конструктор Create получает в качестве параметра-потомка объект типа
TComponent, который
будет владельцем формы. Обычно в качестве владельца выступает
Application. Это
делается для того, чтобы все формы были автоматически закрыты по окончанию
работы приложения.
Можно также в качестве параметра использовать значение Nil, создав,
тем самым,
форму без владельца. Однако в этом случае закрывать и уничтожать ее должен
программист.
В случае возникновения необрабатываемой ошибки такая форма останется в
памяти.
Примечание. При разработке MDI-приложения метод
Show не нужен, так
как Delphi автоматически показывает все вновь созданные дочерние
MDI-формы.
В случае однодокументного приложения использование метода Show
обязательно.
Даже при динамическом создании форм Delphi попытается "помочь" в
создании экземпляра
каждой формы. Чтобы отказаться от них, нужно в диалоговом окне Project
Options (вызывается
при выполнении пункта меню Project | Options) удалить классы форм из
списка Auto-create forms:
Рис.1. Окно Project | Options
Если требуется получить доступ к отдельному дочернему экземпляру класса,
используйте свойство MDIChildren, описываемое в
следующем разделе.
К оглавлению
MDI-свойства TForm
Объект TForm имеет несколько свойств, специфичных для
MDI-приложений. Перечислим
их.
В первой строке проверяется, равен ли ActiveMDIChild значению
Nil, так как
в этом случае обращение к объекту вызовет исключительную ситуацию.
Примечание. Свойство ActiveMDIChild равно Nil в том
случае,
если нет открытых дочерних форм или свойство FormStyle не равно
fsMDIForm.
Поскольку ActiveMDIChild возвращает объект TForm,
компилятор не имеет доступа
к memDailyNotes - объекту TfrmMDIChild. Вторая строка проверят
соответствие типов,
то есть действительно ли свойство ActiveMDIChild указывает на объект
TfrmMDIChild.
Третья строка выполняет преобразование типа и вызывает метод Clear
компонента memDailyNotes.
Обычно это свойство используется при выполнении какого-либо действия над
всеми открытыми дочерними формами. Вот
код сворачивания всех дочерних форм при щелчке на кнопке с именем
Button1:
К оглавлению
MDI-события и MDI-методы TForm
В MDI-приложении событие OnActivate запускается только при
переключении
между дочерними формами. Если фокус ввода передается из не
MDI-формы в MDI-форму,
генерируется событие OnActivate родительской формы, хотя ее
свойство Active никогда и
не устанавливается равным True. Эта странность на самом деле строго
логична: ведь,
если бы OnActivate генерировался только для дочерних форм, не было
бы никакой
возможности узнать о переходе фокуса ввода от другого приложения.
Специфичные для MDI-форм методы перечислены ниже:
- procedure ArrangeIcons; - выстраивает пиктограммы
минимизированных дочерних форм
в нижней части родительской формы; - procedure Cascade - располагает дочерние формы каскадом, так
что видны
все их заголовки; - procedure Next; procedure Previous; - переходит от одной дочерней
формы
к другой, как-будто нажаты клавиши Ctrl+Tab или Ctrl+Shift+Tab; - procedure Tile; - выстраивает дочерние формы так, что они не
перекрываются.
К оглавлению
Пример MDI-приложения
Создадим приложение, позволяющее просматривать графические изображения
на отдельной
форме. Для этого в новом проекте у формы зададим следующие свойства:
- Caption: Image Viewer;
- FormStyle: fsMDIForm;
- Name: frmMDIParent;
- ShowHint: True.
Поместим компонент TPanel в форму. Установим у него следующие
свойства:
Поместим компонент TSpeedButton в TPanel и зададим у него
следующие свойства:
- Name: spbtnLoad;
- Hint: Load;
- Left: 8;
- Top: 8.
Подберем рисунок для созданной кнопки.
Добавим в форму компонент TOpenDialog и установим следующие его
свойства:
- Filter: Bitmaps (*.bmp)|*.bmp;
- Name: opndlgLoad;
- Options: [ofPathMustExist,ofFileMustExist].
Теперь создадим дочернюю форму. Выполним пункт меню File | New |
Form и появится
пустая форма. Установим следующие ее свойства:
- FormStyle: fsMDIChild;
- Name: frmMDIChild;
- Position: poDefaultPosOnly.
Поместим компонент TImage во вновь созданную форму и установим
его следующие свойства:
Удалим дочернюю форму из списка автоматически создаваемых форм
следующим образом:
- выполним пункт меню Project | Options, и появится диалоговое окно
Project Options,
показанное на рисунке 1; - выберем форму frmMDIChild в списке Auto-create forms;
- щелкнем на кнопке ">". Форма frmMDIChild при этом
будет перенесена
в список Available forms.
Создав интерфейс, перейдем к написанию исходного текста приложения.
Сначала загрузим изображение. Следующий код будет размещаться в
обработчике события OnClick
компонента spbtnLoad:
После запуска диалогового окна создается новый экземпляр дочерней формы и
загружается файл
изображения. После загрузки размеры дочерней формы изменяются так, чтобы
можно было
видеть все изображение.
Поскольку модуль ссылается на тип TfrmMDIChild, находящийся в
модуле MDIChild,
то после строки implementation следует добавить еще одну строку:
Теперь можно приступить к компиляции и запуску приложения. Однако по
щелчку на кнопке Close дочерняя
форма не закрывается, а сворачивается в пиктограмму. Чтобы заставить ее
закрыться,
следует в код обработчика OnClose класса TfrmMDIChild внести
изменение свойства
Action:
Внешний вид работающего приложения изображен на рисунке 2.
Рис.2. Внешний вид приложения
На этом шаге мы рассмотрим создание MDI -приложений.
Термин MDI (Multiple Document Interface) дословно означает многодокументный интерфейс и описывает приложения, способные загрузить и использовать одновременно несколько документов или объектов. Примером такого приложения может служить диспетчер файлов (File Manager).
Обычно MDI -приложения состоят минимум из двух форм - родительской и дочерней . Свойство родительской формы FormStyle установлено равным fsMDIForm . Стиль дочерней формы - fsMDIChild .
Родительская форма служит контейнером, содержащим дочерние формы, которые заключены в клиентскую область и могут перемещаться, изменять размеры, минимизироваться или максимизироваться. В приложении могут быть дочерние формы разных типов, например одна - для обработки изображений, а другая - для работы с текстом.
В MDI-приложении, как правило, требуется выводить несколько экземпляров классов формы. Поскольку каждая форма представляет собой объект, она должна быть создана перед использованием и освобождена, когда в ней больше не нуждаются. Delphi может делать это автоматически, а может предоставить эту работу программисту.
Автоматическое создание форм
По умолчанию при запуске приложения Delphi автоматически создает по одному экземпляру каждого класса форм в проекте и освобождает их при завершении программы. Автоматическое создание обрабатывается генерируемым Delphi кодом в трех местах.
Первое - раздел интерфейса в файле модуля формы.
Вторым является место, в котором описывается переменная класса:
Здесь описана переменная Form1 , указывающая на экземпляр класса TForm1 и доступная из любого модуля. Обычно она используется во время работы программы для управления формой.
Третье место находится в исходном тексте проекта, доступ к которому можно получить с помощью меню View | Project Source . Этот код выглядит как:
Процесс удаления форм обрабатывается с помощью концепции владельцев объектов: когда объект уничтожается, автоматически уничтожаются все объекты, которыми он владеет. Созданная описанным образом форма принадлежит объекту Application и уничтожается при закрытии приложения.
Динамическое создание форм
Хотя автоматическое создание форм полезно при разработке однодокументных приложений, при создании MDI -приложении оно, как правило, неприемлемо.
Для создания нового экземпляра формы используйте конструктор Create класса формы. Приведенный ниже код создает новый экземпляр TForm1 во время работы программы и устанавливает его свойство Caption равным New Form :
Конструктор Create получает в качестве параметра-потомка объект типа TComponent , который будет владельцем формы. Обычно в качестве владельца выступает Application . Это делается для того, чтобы все формы были автоматически закрыты по окончанию работы приложения. Можно также в качестве параметра использовать значение Nil , создав, тем самым, форму без владельца. Однако в этом случае закрывать и уничтожать ее должен программист. В случае возникновения необрабатываемой ошибки такая форма останется в памяти.
Примечание . При разработке MDI -приложения метод Show не нужен, так как Delphi автоматически показывает все вновь созданные дочерние MDI -формы. В случае однодокументного приложения использование метода Show обязательно.
Даже при динамическом создании форм Delphi попытается "помочь" в создании экземпляра каждой формы. Чтобы отказаться от них, нужно в диалоговом окне Project Options (вызывается при выполнении пункта меню Project | Options ) удалить классы форм из списка Auto-create forms :
Рис.1. Окно Project | Options
Если требуется получить доступ к отдельному дочернему экземпляру класса, используйте свойство MDIChildren , описываемое в следующем разделе.
MDI-свойства TForm
Объект TForm имеет несколько свойств, специфичных для MDI -приложений. Перечислим их.
-
property ActiveMDIChild: TForm; . Это свойство возвращает дочерний объект TForm , имеющий в текущее время фокус ввода. Оно полезно, когда родительская форма содержит панель инструментов или меню, команды которых распространяются на открытую дочернюю форму. Например, представим, что проект использует дочернюю форму, содержащую элемент ТМеmо , названный memDailyNotes . Имя класса этой дочерней формы - TfrmMDIChild . Родительская форма содержит кнопку Clear в панели инструментов, которая удаляет содержимое memDailyNotes в активной дочерней форме. Вот как это реализуется:
В первой строке проверяется, равен ли ActiveMDIChild значению Nil , так как в этом случае обращение к объекту вызовет исключительную ситуацию.
Примечание . Свойство ActiveMDIChild равно Nil в том случае, если нет открытых дочерних форм или свойство FormStyle не равно fsMDIForm .
Поскольку ActiveMDIChild возвращает объект TForm , компилятор не имеет доступа к memDailyNotes - объекту TfrmMDIChild . Вторая строка проверят соответствие типов, то есть действительно ли свойство ActiveMDIChild указывает на объект TfrmMDIChild . Третья строка выполняет преобразование типа и вызывает метод Clear компонента memDailyNotes .
Обычно это свойство используется при выполнении какого-либо действия над всеми открытыми дочерними формами. Вот код сворачивания всех дочерних форм при щелчке на кнопке с именем Button1 :
MDI-события и MDI-методы TForm
В MDI -приложении событие OnActivate запускается только при переключении между дочерними формами. Если фокус ввода передается из не MDI -формы в MDI -форму, генерируется событие OnActivate родительской формы, хотя ее свойство Active никогда и не устанавливается равным True . Эта странность на самом деле строго логична: ведь, если бы OnActivate генерировался только для дочерних форм, не было бы никакой возможности узнать о переходе фокуса ввода от другого приложения.
- procedure ArrangeIcons; - выстраивает пиктограммы минимизированных дочерних форм в нижней части родительской формы;
- procedure Cascade - располагает дочерние формы каскадом, так что видны все их заголовки;
- procedure Next; procedure Previous; - переходит от одной дочерней формы к другой, как-будто нажаты клавиши Ctrl+Tab или Ctrl+Shift+Tab ;
- procedure Tile; - выстраивает дочерние формы так, что они не перекрываются.
Пример MDI-приложения
- Caption: Image Viewer;
- FormStyle: fsMDIForm;
- Name: frmMDIParent;
- ShowHint: True.
- Name: spbtnLoad;
- Hint: Load;
- Left: 8;
- Top: 8.
- Filter: Bitmaps (*.bmp)|*.bmp;
- Name: opndlgLoad;
- Options: [ofPathMustExist,ofFileMustExist].
- FormStyle: fsMDIChild;
- Name: frmMDIChild;
- Position: poDefaultPosOnly.
- выполним пункт меню Project | Options , и появится диалоговое окно Project Options , показанное на рисунке 1;
- выберем форму frmMDIChild в списке Auto-create forms ;
- щелкнем на кнопке ">" . Форма frmMDIChild при этом будет перенесена в список Available forms .
Создав интерфейс, перейдем к написанию исходного текста приложения.
Сначала загрузим изображение. Следующий код будет размещаться в обработчике события OnClick компонента spbtnLoad :
После запуска диалогового окна создается новый экземпляр дочерней формы и загружается файл изображения. После загрузки размеры дочерней формы изменяются так, чтобы можно было видеть все изображение.
Поскольку модуль ссылается на тип TfrmMDIChild , находящийся в модуле MDIChild , то после строки implementation следует добавить еще одну строку:
Теперь можно приступить к компиляции и запуску приложения. Однако по щелчку на кнопке Close дочерняя форма не закрывается, а сворачивается в пиктограмму. Чтобы заставить ее закрыться, следует в код обработчика OnClose класса TfrmMDIChild внести изменение свойства Action :
Читайте также: