| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Delphi: Общие вопросы > Несколько окон одного класса |
| Автор: Чучмек 24.10.2012, 02:04 | ||||||||||
| Если требуется создать более одного экземпляра окна - почему бы и нет? TFormN.Create - и все ок. Но если программа уже готова(почти), и вдруг возникает необходимость в использования более чем одного экземпляра главного окна. Могут возникнуть проблемы. http://forum.vingrad.ru/forum/topic-357029/kw-paramstr-newwindow-ppdisp.html Часть проблем примитивные и легко решаемые(если представлять что и зачем), а некоторые - требуют отдельного решения. Поэтому пришлось создавать эту длинную и возможно, никому не нужную тему. Пример. Есть почти готовая рисовалка. Главное окно: Unit1.TForm1 (Form1.AutoSize=true) Image1 - поле для рисования (AutoSize=true) MainMenu1 - меню N1 - "Файл" N2 - "Info" - вызов окна с информацией о программе N3 - Выбор цвета "пера" - (открывается отдельное окно) N4 - вызов окна изменения размера N5 - сохранить художество N6 - открывает окно настроек Unit1.pas
Окно выбора цвета Unit2.TForm2 содержит один ColorBox. Вынес в отдельное окно для примера. unit2.pas
Окно изменения размера Unit3.TForm3 Edit1 - ширина Edit2 - высота Button1 - "Ok" unit3.pas
Окно с информацией о программе Unit4.Form4 Содержит одну лейбу с текстом. Окно настроек (Unit5.Form5) - Два чекбокса CheckBox1- изменение размеров рисунка при растягивании окна мышью CheckBox2- жирное перо Unit5.pas
Project1.dpr
... И захотелось нам... Чтобы... Сразу несколько рисовальных окон было. Стерео картинку рисовать будем. Преобразование разобью на несколько этапов. Описание чуть позже. |
| Автор: Rennigth 24.10.2012, 08:44 |
| Чучмек, если я правильно понял проблему(т.е. настройки и т.д. должны применяться ко всем экземплярам окна?), то мне кажется и менюшку и все эвенты в динамике присваивать при создании окна. Другого варианта не вижу... Хотя можно использовать что-то глобальное, реестр или файл на жестком. |
| Автор: Чучмек 25.10.2012, 10:39 | ||||||||||
| Нужно еще одно окно - пожалуйста TForm1.Create(Application).Visible:=true; Почти полноценное окно и рисовать в нем можно, и сохранить нарисованное. Только не совсем то - размеры изменяются только у первого, цвет "пера" - меняется сразу в двух окнах. При закрытии первого окна завершается приложение. Завершение приложения происходит потому что первое окно является Application.MainForm А при закрытии MainForm происходит завершение приложения. Отсюда - нужно либо в OnClose делать проверку if Self = application.MainForm, и скрывать MainForm вместо закрытия. Либо заменить MainForm. Мне подуше второй вариант. File->New->Form; Form.Name=StartForm; Save->StartFormUnit.pas В project.dpr строку Application.CreateForm(TStartForm, StartForm) делаем первой из Application.CreateForm Строку Application.CreateForm(TForm1, Form1) - убираем В StartForm можно показать заставку при запуске приложения. Но нам это не надо. StartForm показывать не будем. в dpr прописываем Application.ShowMainForm:=false; Создавать первый экземпляр TForm1 можно в StartForm.OnCreate При создании окна, возможно, необходимо передать ему начальные параметры. Для этого можно написать отдельную функцию NewForm. А можно переопределить конструктор TForm1
[b]StartFormUnit.pas[b]
В MainMenu1 добавляем пункт N7 - "новое окно"
При закрытии окна TForm1 необходимо делать проверку - если закрывается последнее окно TForm1 - завершить приложение. Можно держать количество открытых TForm1 в отдельной переменной. Можно создать список открытых TForm1. А можно использовать список Application.Components
Одну проблему устранили... |
| Автор: Чучмек 25.10.2012, 11:13 | ||||||
| Теперь вторая проблема - на все окна TForm1 в панели задач одна кнопка "Project1" Это происходит потому, что при создании нового окна (используется CreateWindowEx) в параметре hWndParent передается Application.Handle. И все окна приложения являются дочерними окнами Application. (Кто не знал - Application тоже окно, причем главное окно приложения). Дочерние окна своей кнопки в панели задач не имеют, а кнопка "Project1" - кнопка от Application. Отсюда решение - скрыть Application а в качестве hWndParent, при создании окна TForm1, передавать 0 или GetDesktopWindow() Переопределяем CreateParams
И в dpr добавляем строчку ShowWindow(Application.Handle,SW_HIDE)
|
| Автор: Чучмек 26.10.2012, 15:36 | ||||||||||||||||||||||||||
Идем дальше. "Причесывание" кода. Все глобальные переменные, которые используются для сохранения промежуточных состояний и связи межу методами/событиями TForm1 вносим внутрь класса TForm1. В методах TForm1 везде где встречается Form1 - меняем на self или вообще убираем. Стоку var Form1: TForm1; - удаляем. (Переменную Form1, для удобства, можно перенести внутрь TForm1)
Если Form1 встречается в функциях/процедурах - делаем их либо методами TForm1, либо добавляем в функции дополнительный параметр. Было
стало
или
Определяемся с дополнительными окнами. Какие из дополнительных окон будут существовать в единственном экземпляре,а какие - в нескольких(по одному на каждый экземпляр TForm1) Так в моем примере окно с информацией о программе Form4 и окно настроек Form5 будут в единственном экземпляре. А окно выбора цвета TForm2 и окно изменения размера TForm3 - для каждого экземпляра TForm1. Для "многоэкземплярных" окон(форм) - аналогично "причесываем" код. Из юнита формы удаляем переменную var FormN:TFormN. Из dpr удаляем строку создания формы Application.CreateForm(TFormN, FormN); В TForm1 добавляем переменную на каждую "многоэкземплярную" форму. При создании экземпляра TForm1 создаем и связанные "многоэкземплярные" формы.
Если в методах "многоэкземплярных" форм было обращение к Form1 или к глобальной переменной из Unit1(мы ее уже перенесли в TForm1) то возможны варианты: a) Добавить в TFormN переменную Form1:TForm; инициализировать ее в переопределенном конструкторе, или при создании из TForm1, или при первом вызове ( if not Assigned(form1) then form1:=self.Owner as tform);использовать как (form1 as TForm1). б) сразу использовать (Owner as TForm1) Если из одной вспомогательной "многоэкземплярной" формы было обращение к другой - заменить на обращение через TForm1 - (Owner as TForm1).Form3 Для "одноэкземплярных", если использовалось обращение к TForm1, необходимо разделить обращения на общие(ко всем экземплярам главного/рабочего окна) и частные(к одному). Так в окне настроек было два пункта -"Изменение размера мышью" и "Жирное перо" Пусть "Изменение размера мышью" - настройка для всех окон, а "Жирное перо" - только для окна вызвавшего окно настроек. Было
Стало
Для опции "Жирное перо" Добавим в TForm5 переменную Form1
Вызов окна настроек
При закрытии экземпляра TForm1 открытые связанные "многоэкземплярные" окна закроются сами (передаем Self в TFormN.Create), а вот "одноэкземплярные" остаются открытыми (родитель - Application) . Иногда это не нужно. Если мы, в окне настроек, чекним "Жирное перо", после закрытия окна вызвавшего настройки, - получим "Invalid class typecast". Окно настроек нужно закрыть
|
| Автор: Чучмек 27.10.2012, 16:27 |
| Теперь собственно самое интересное. Потребность в псевдомодальном режиме. Если мы из экземпляра TForm1 вызовем окно в модальном режиме (MessageBox/ShowMessage/TCommonDialog.Execute/FormN.ShowModal мы получим блокировку всех остальных окон до закрытия модального окна. Это не есть гуд. Остальные экземпляры TForm1 и связанные с ними окна должны оставаться свободными. Если бы каждый экземпляр TForm1 имел свой цикл обработки сообщений и свой поток - нет проблем. Однако,в Delphi, по крайней мере в 7-ой, такого нет. |