Delphi окно не закрывается

Обновлено: 28.04.2024

Очень часто требуется сделать так, чтобы окно закрывалось по нажатию на клавишу Escape. Это действительно удобно. Более того, есть негласное правильно: интерфейсы ввода данных должны уметь работать и без мыши. Т.е. чтобы после ввода данных с клавиатуры можно было нажать Enter или Escape, а не тянуться за мышкой и потом целиться курсором в маленький крестик.

Возможно Вам покажется, что тема избитая и этот вопрос может волновать лишь новичков. Однако даже опытные программисты не всегда знают, как это делать правильно. В этой заметке я также расскажу, как правильно обрабатывать и другие диалоговые клавиши.

Итак. Приведу несколько вариантов.

Вариант первый.

Если у вас на форме есть кнопки (TButton), то можно у одной из кнопок выставить свойство: Cancel := True. Когда пользователь нажмёт на клавишу Escape, сработает обработчик OnClick этой кнопки, в котором можно просто вызвать метод Close формы.

Для модальной формы всё ещё проще: вместо обработчика OnClick достаточно указать свойство ModalResult := mrCancel. После попытки вызова OnClick кнопки, VCL смотрит это свойство, и если оно отлично от нуля (<> mrNone), то прописывает его в ModalResult формы, что приводит к закрытию модальной формы.

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

Вариант второй.

К этому варианту я отнесу все способы перехвата нажатия любой клавиши на уровне формы. Для этого надо у формы выставить свойство KeyPreview := True, и прописать обработчик OnKeyPress:

либо обаботчик OnKeyDown. Либо OnKeyUp.

Как видите, этот вариант довольно простой, и самый распространённый (который можно найти на просторах интернета). Но этот вариант не совсем корректный. Чтобы это показать, проделаем ещё несколько действий с формой.

Создайте на форме обычный комбобокс (TCombobox), заполните его Items произвольными значениями. Запустите приложение, откройте форму (с комбобоксом и обработчиком OnKeyPress). Теперь раскройте выпадающий список и нажмите Escape. Что произошло? Правильно, форма закрылась. Хотя я более чем уверен, что пользователь в этот момент времени ожидал другое поведение на нажатие Escape. А именно: по первому нажатию - закрытие комбобокса, а уже по второму нажатию - закрытие формы. (Замечу, что кроме комбобокса на форме могут оказаться и другие компоненты, которые по-своему обрабатывают клавишу Escape.)

Это происходит потому, что обработчик формы OnKeyPress отработал раньше, чем комбобокс получил событие о нажатии на Escape (помните, KeyPreview выставлен в True?). Если KeyPreview сбросить в False, то OnKeyPress формы вообще не обработается.

Так как же правильно обрабатывать клавишу Escape?

В этой заметке я несколько раз упомянул словосочетание "диалоговые клавиши". К этим клавишам относятся: Escape, Enter, Tab и стрелки (и ещё несколько других клавиш нестандартных клавиатур). Называются они так, потому что эти клавиши специальные. Они не предназначены для непосредственного ввода данных, а используются для управления окнами (комбобокс - это тоже окно).

Кому интересно, может поизучать исходники VCL, я же приведу третий вариант.

Вариант третий. Универсальный.

У меня программа, написанная на Delphi - defect.exe
При закрытии главной формы я не только делаю Close, но и закрываю все базы данных и т.д., а программа, будь она неладна, все равно висит в списке задач Windows, т.е. не закрывается. Вопрос, предполагаю, элементарный, но ответа никак не найду. Помогите.

Если я правильно помню, то From1.Close закрывает приложение в Delphi5, а в 7-й - только закрывает саму ФОРМУ. Вообщето надо пользоваться Application.Terminate. Ну или на худой конец Halt(1), но разработчики не рекомендуют ЭТО использовать. Рекомендуется использовать Terminate.
Р$ Смотри Help.

Originally posted by NIRS
[b]Если я правильно помню, то From1.Close закрывает приложение в Delphi5, а в 7-й - только закрывает саму ФОРМУ

Ничего подобного - вызов Close для главной формы что в пятой, что в 7-й Дельфе закрывает приложение - вызывается все тот же Application.Terminate (см. реализацию TCustomForm.Close в Forms).

Для интереса набрала Halt - программа все равно висит в списке задач, т.е. так и не закрывается. Прямо какой-то полтергейст! Уже и все формы поочередно закрывала, и делала Destroy для объектов. Ничего не помогает!! Что же это такое?

Нет в Парадоксе. А с чем вообще может быть связан мой косяк? Может я что-то не то в принципе делаю? Хотя казалось бы чего проще: выйти из программы! Я думала, что Close достаточно..

У меня похожая ситуация была, когда программа была out-of-process COM-сервером, но неправильно написанным :-) : случайно удалил модуль, в котором описан co-класс, из юзов в проекте. В результате не регистрировалась фабрика COM-класса, при попытке запуска в момент, когда пытался создать главный co-класс, программа висла, а при попытке снять ее из списка задач. заново появлялась в процессах (запускалась то ли svchost'ом, то ли lmhost'ом).
Конечно, скорее всего, эта не Ваша ситуация, так как программка запускается, а виснет только на выходе, к тому же про COM ни слова ни сказано, но может кому пригодится на будущее.

А "снаружи" программа закрывается нормально? (По Alt+F4 или крестиком)

Ничего подобного - вызов Close для главной формы что в пятой, что в 7-й Дельфе закрывает приложение
Originally posted by NIRS
[b]Да. Наверное это был Form.Hide, а не Form.Close.

Действительно, Hide просто прячет главную форму (кстати, это будет делать и Close, если выставлен соответствующий Action в OnClose/DoClose), а раньше, если мне память не изменяет, Hide для главной формы тоже вызывал Application.Terminate (т.е. эти три способа презентовались как равноправные).

Спасибо за готовность помочь. Попробую все предложенные варианты. Обязательно отпишу с иллюстрацией кода как все прошло! (Если не пройдет)

procedure TForm1.FormCreate(Sender: TObject);
begin
Form1.Close;
end;

НЕ работает. Т.е. окно не закрывается! В пустом вновь созданном проекте!
Хотя с кнопки все работает.

Где можно было наплужить?
BDS 2006.

А> procedure TForm1.FormCreate(Sender: TObject);
А> begin
А> Form1.Close;
А> end;

А> НЕ работает. Т.е. окно не закрывается! В пустом вновь созданном
А> проекте! Хотя с кнопки все работает.

кто тебя учил так писать код?
Этож надо додуматься в методе класса обращаться к экземпляру этого класса.
Вот из-за этого и считается что на дельфи только глючные программы бывают.


--
С уважением
Кочмин Александр

Здравствуйте, kochmin_alexandr, Вы писали:

А>> procedure TForm1.FormCreate(Sender: TObject);
А>> begin
А>> Form1.Close;
А>> end;

А>> НЕ работает. Т.е. окно не закрывается! В пустом вновь созданном
А>> проекте! Хотя с кнопки все работает.

_>кто тебя учил так писать код?

Сам учился.

_>Этож надо додуматься в методе класса обращаться к экземпляру этого класса.
_>Вот из-за этого и считается что на дельфи только глючные программы бывают.

_>--
_>С уважением
_>Кочмин Александр

Раньше работало без проблем. Так что его в отдельную процедуру выносить?

Здравствуйте, <Аноним>, Вы писали:

А>procedure TForm1.FormCreate(Sender: TObject);
А>begin
А>Form1.Close;
А>end;

А>НЕ работает. Т.е. окно не закрывается! В пустом вновь созданном проекте!
А>Хотя с кнопки все работает.

Воспользуйся отладчиком. Зайди в метод Close. Убедись что форма не закрывается по той причине, что еще не проинициализирован Application.MainForm.

И какой цели ты собственно говоря хочешь добиться? Закрыть приложение? — Application.Terminate. Уничтожить форму? — Release. Скрыть форму? Вызови Hide в Form.OnShow

Здравствуйте, Master Yoda, Вы писали:

MY>Здравствуйте, , Вы писали:

А>>procedure TForm1.FormCreate(Sender: TObject);
А>>begin
А>>Form1.Close;
А>>end;

А>>НЕ работает. Т.е. окно не закрывается! В пустом вновь созданном проекте!
А>>Хотя с кнопки все работает.

MY>Воспользуйся отладчиком. Зайди в метод Close. Убедись что форма не закрывается по той причине, что еще не проинициализирован Application.MainForm.

MY>И какой цели ты собственно говоря хочешь добиться? Закрыть приложение? — Application.Terminate. Уничтожить форму? — Release. Скрыть форму? Вызови Hide в Form.OnShow

Спасибо. Application.Terminate — работает.

А почему Form1.Close внезапно перестал работать?

Здравствуйте, <Аноним>, Вы писали:

А>А почему Form1.Close внезапно перестал работать?

Он и не должен работать. В обработчике OnFormCreate ещё нет созданной формы Form1. Кстати, относительно первой ветки этой темы. Обращаться к экземпляру класса в методах класса нельзя, поскольку может быть несколько экземпляров одного и того же класса. Если ты в методе класс хочешь сослаться на экземпляр, для которого этот метод вызван, то используй специальное свойство Self. Например:

При нажатии на кнопку ты будешь закрывать ту форму, на которой расположена кнопка, даже если в приложении форм TForm1 несколько. Что произойдёт, если ты напишешь Form1.Close догадайся сам, в качестве упражнения.

Здравствуйте, Аноним, Вы писали:

А>А почему Form1.Close внезапно перестал работать?
А с чего вы взяли что он у вас работал? срабатывание и работа это не есть эквивалент. Случайно может быть как-то приводило к тем ожидаемым вами результатам.

Здравствуйте, SeLarin, Вы писали:
SL>

А этот код не тоже самое сделает?

А>А почему Form1.Close внезапно перестал работать?

PS: метод Close можно вызывать для созданных, существующих форм. Поэтому он работает в Button1Click и как повезёт — в OnCreate.

Аналогично метод Hide работает для видимых окон и не работает в OnShow, который вызывается в процессе ShowModal

Неудобно — но так оно есть.

Здравствуйте, Аноним, Вы писали:

А>А этот код не тоже самое сделает?
А>

То же самое. Self ещё и неявно проставляться может . Я просто объяснял "на пальцах".

Здравствуйте, Аноним, Вы писали:

А>procedure TForm1.FormCreate(Sender: TObject);
А>begin
А>Form1.Close;
А>end;

А>НЕ работает. Т.е. окно не закрывается! В пустом вновь созданном проекте!
А>Хотя с кнопки все работает.

А>Где можно было наплужить?
А>BDS 2006.

← →
PZ ( 2006-12-25 12:51 ) [0]

У меня модальная форма без заголовка:
BorderStyle = bsNone
KeyPreviw = True

Всю площадь формы закрывает компонент Bevel1, а его
полностью закрывает метка Label1
Пробую два варианта закрыть форму - не закрывается.
Как же её можно закрыть? Подскажите, пожалуйста.

procedure TForm1.FormDblClick(Sender: TObject);
begin
Form1.Close;
end;

procedure TForm1.Label1DblClick(Sender: TObject);
begin
Form1.Close;
end;

← →
Anatoly Podgoretsky © ( 2006-12-25 13:06 ) [1]

> PZ (25.12.2006 12:51:00) [0]

А чей у тебя Form1 ?

← →
novill © ( 2006-12-25 13:12 ) [2]

[0] PZ (25.12.06 12:51)
второй вариант работает.

← →
PZ ( 2006-12-25 13:14 ) [3]

> [1] Anatoly Podgoretsky © (25.12.06 13:06)

Form1 - это та самая модальная форма, которую пытаюсь закрыть

← →
Ega23 © ( 2006-12-25 13:16 ) [4]


> Form1 - это та самая модальная форма, которую пытаюсь закрыть
>

У меня почему-то не работает. Проверю еще раз.

← →
Anatoly Podgoretsky © ( 2006-12-25 13:23 ) [6]

> PZ (25.12.2006 13:14:03) [3]

> [4] Ega23 © (25.12.06 13:16)

procedure TForm1.Label1DblClick(Sender: TObject);
begin
Form1.ModalResult := mpOk; // Error Undeclared Identifier
Form1.Close;
end;

← →
PZ ( 2006-12-25 13:35 ) [8]

> [6] Anatoly Podgoretsky © (25.12.06 13:23)

У меня другой формы с тким именем нет

← →
Германн © ( 2006-12-25 13:43 ) [9]


> Form1.ModalResult := mpOk; // Error Undeclared Identifier

mrOK
И Close после этого не нужен

Не верю. При том я не про форму спрашивал, а про переменную.

← →
novill © ( 2006-12-25 13:46 ) [12]

> Form1.ModalResult := mpOk; // Error Undeclared Identifier

это наверное от того что правильно писать mrOk

← →
Anatoly Podgoretsky © ( 2006-12-25 13:59 ) [13]


> а у меня все равно работает

У тебя наверно правильный Form1
Только нафиг он совсем нужен?

← →
PZ ( 2006-12-25 14:21 ) [14]

Исправил mpOk -> mrOK

> [11] Anatoly Podgoretsky © (25.12.06 13:43)
> [1] Anatoly Podgoretsky © (25.12.06 13:06)
> А чей у тебя Form1 ?

Не понял, о какой переменной идет речь?
А Form1 - действительно модальная форма, которую мне надо закрыть, но она не включена в проект, а создается динамически в главной форме, по таймеру:

If then
Try
Form1 := TForm1.Create(Owner);
.
Form1.ShowModal;
Finally
FreeAndNil(Form1);
End;

← →
PZ ( 2006-12-25 14:23 ) [15]

Может быть таймер не дает закрывать эту форму?

← →
alex_*** © ( 2006-12-25 14:25 ) [16]

а нажатие на крестик в верх. правом углу не закрывает форму? (таймер не влияет на закрытие)

← →
PZ ( 2006-12-25 14:28 ) [17]

> [16] alex_*** © (25.12.06 14:25)

Вы не внимательно прочитали мой вопрос: форма без заголовка

← →
alex_*** © ( 2006-12-25 14:30 ) [18]

тогда dblclick по лабелу решит проблему

← →
PZ ( 2006-12-25 14:37 ) [19]

> [18] alex_*** © (25.12.06 14:30)

Я тоже так думаю, но не решает .

← →
Anatoly Podgoretsky © ( 2006-12-25 14:38 ) [20]

> PZ (25.12.2006 14:21:14) [14]

О переменной с именем Form1
Нормальный код ты категорически отказываешься приводить, а гадать уже я не хочу.

← →
alex_*** © ( 2006-12-25 14:52 ) [21]


> Я тоже так думаю, но не решает .

ты туда попадаешь вообще?

← →
PZ ( 2006-12-25 14:55 ) [22]

> [20] Anatoly Podgoretsky © (25.12.06 14:38)

Я, честное слово, не понимаю. что от меня требуется. Весь код проекта - он довольно большой, приводить нет возможности. А все, что касается моего вопроса я, кажется, сообщил.

← →
Anatoly Podgoretsky © ( 2006-12-25 14:56 ) [23]

Не понимаешь и ладно, а понимаешь ли ты то, что указано ранее, что переменная Form1 не нужна?

← →
novill © ( 2006-12-25 14:57 ) [24]

> [22] PZ (25.12.06 14:55)

создай новый проект с двумя формами - одна будет основная с кнопкой модального вызова второй, а вторая без границ с бевелом и лейблом, назначь соотв. методы. и пробуй!

← →
PZ ( 2006-12-25 15:00 ) [25]

> [21] alex_*** © (25.12.06 14:52)

Label растянут по всей форме allClient. Ну как не попасть?

← →
alex_*** © ( 2006-12-25 15:07 ) [26]

ну так скажи, попадаешь или нет? до брейк поинта доходишь?

← →
alex_*** © ( 2006-12-25 15:10 ) [27]

Да, кстати обрати внимание на [23] - убери указание Form1 для методов и никогда так не делай. (замени на self, если так хочется класс указать)

← →
Anatoly Podgoretsky © ( 2006-12-25 15:18 ) [28]

> alex_*** (25.12.2006 15:10:27) [27]

self тоже лишнее

← →
PZ ( 2006-12-25 15:32 ) [29]

> [23] Anatoly Podgoretsky © (25.12.06 14:56)
> [27] alex_*** © (25.12.06 15:10)

Form1 я убрал. Может это не прилично то, что я пишу Form1, но работе-то это не мешает.

> [26] alex_*** © (25.12.06 15:07)

ну так скажи, попадаешь или нет? до брейк поинта доходишь?

Давайте приостановим обсуждение. Я уже получил от вас много рекомендаций. В спокойной обстановке поэксперименитую, возможно найду решение.
Спасибо всем, принявшим участие в этом трудном диалоге.
С уважением, PZ

← →
evvcom © ( 2006-12-26 09:06 ) [30]

> [29] PZ (25.12.06 15:32)
> Может это не прилично то, что я пишу Form1, но работе-то
> это не мешает

Судя по тому, что ты здесь, видимо, мешает.

← →
PZ ( 2006-12-26 12:50 ) [31]

> [30] evvcom © (26.12.06 09:06)

Возможно мешает. Я больше так не пишу.

Итак, что я выяснил. У меня есть процедура, чтобы можно было перемещать окно при нажатой левой кнопке мыши:

procedure TForm1.WMNCHitTest(var M: TWMNCHitTest);
begin
inherited;
if M.Result = htClient then M.Result := htCaption;
end;

Так вот, если закомментировать эту процедуру, то окошко закрывется по следующему обработчику, иначе нет:

procedure TForm1.Label1DblClick(Sender: TObject);
begin
ModalResult := mrOk;
end;

Теперь я в полном тупике. Не знаю, как сохранить возможность перемещать окно и закрывать его по ненадобности.
Если кто-то может, пожалуйста помогите.

← →
Anatoly Podgoretsky © ( 2006-12-26 12:53 ) [32]

> PZ (26.12.2006 12:50:31) [31]

А чего ты хочешь, сам же навесил на кнопку две взаимоисключающие вещи.

← →
novill © ( 2006-12-26 12:54 ) [33]

повесь свою процедуру только на wm_mousemove

← →
novill © ( 2006-12-26 13:00 ) [34]

> [33] novill © (26.12.06 12:54)

не, не пойдет

← →
palva © ( 2006-12-26 13:03 ) [35]

Alt-F4 разве не срабатывает?

← →
PZ ( 2006-12-26 13:06 ) [36]

> [35] palva © (26.12.06 13:03)

Попробовал - срабатывает. Но я этми кнопками никогда не пользуюсь, не люблю.
В крайнем случае придется.

← →
novill © ( 2006-12-26 13:10 ) [38]

как насчет того чтобы перенести закрытие в обработчик WM_LBUTTONDBLCLK

← →
PZ ( 2006-12-26 13:20 ) [39]

> [38] novill © (26.12.06 13:10)

Я, к сожалению, с собщениями Windows не очень в дружбе.
Может быть Вы напишите примером.

Я попробовал PostMessage(Handle, WM_SYSCOMMAND, SC_CLOSE, 0), тоже никакой реакции.

← →
novill © ( 2006-12-26 13:47 ) [40]

> [39] PZ (26.12.06 13:20)

как пробовал? кинь код. я не знаю, где и что ты еще успел перехватить

← →
PZ ( 2006-12-26 13:51 ) [41]

> [40] novill © (26.12.06 13:47)

procedure TForm1.Label1DblClick(Sender: TObject);
begin
PostMessage(Handle, WM_SYSCOMMAND, SC_CLOSE, 0);
end;

← →
PZ ( 2006-12-26 13:56 ) [42]

Т. е. у меня сейчас два обработчикка:

procedure TForm1.WMNCHitTest(var M: TWMNCHitTest);
begin
inherited;
if M.Result = htClient then M.Result := htCaption;
end;

procedure TForm1.Label1DblClick(Sender: TObject);
begin
PostMessage(Handle, WM_SYSCOMMAND, SC_CLOSE, 0);
end;

← →
novill © ( 2006-12-26 14:22 ) [43]

Учитывая
> Я, к сожалению, с собщениями Windows не очень в дружбе.

рекомендую воспользоваться обычными событиями, возььмите соседния пример в Дельфиворлд:

Draging - для обозначение того периода времени когда пользователь перемещает мышь с зажатой кнопкой мыши,
X0 и Y0 - координаты точки, над которой была зажата кнопка мыши
Далее описываем события формы OnMouseDown, OnMouseMove и OnMouseUp:

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Draging := true;
x0 := x;
y0 := y;
end;

procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Draging := false;
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if Draging = true then
begin
Form1.Left := Form1.Left + X - X0;
Form1.top := Form1.top + Y - Y0;
end;
end;

тогда у вас ничего не будет перехватыватсья лишнего

← →
novill © ( 2006-12-26 14:33 ) [44]

естесственно события вешать на события не на формы, а лейбла.

← →
PZ ( 2006-12-26 14:46 ) [45]

> [43] novill © (26.12.06 14:22)

Как обычно, все гениальное просто!
Нормально работает.
Смутила меня эта процедура Орлика для перемещения окна без заголовка. Почему-то я думал, что без нее ни чем сдвинуть с места окно невозможно.

Спасибо, novill, еще раз спасибо всем-всем, принимавшим участие в обсуждении.
С уважением, PZ.

Есть второе окно, которое должно блокировать все, пока пользователь не ответит на вопрос. Оно вызывается из главного таким кодом:

В некоторых случаях второе окно "проваливается" под главное и не блокирует его, т.е. ведет себя не как модальное, а как обычное. Иногда то же самое происходит с диалогами, вызванными посредством ShowMessage .

Предполагаю, что у меня какие-то проблемы с главным окном, но вот какие - не могу понять.

Что можете посоветовать, куда копать, что проверять?

Был уже почти правильный ответ, только одно но, не то свойство задели. при динамическом создании элементов не присваивается родитель Задайте его и будет вам счастье

Что можете посоветовать, куда копать, что проверять?

зыы. еще не указана версия среды, в D7\xe-5\7\8 только с такой проблемой сталкивался. (не считая описанного в другом ответе - ухода модальной формы "под" родительское окно, но блок на ввод при этом остается не тронутым).

Во всех непонятных случаях нужно начать с воспроизведения ошибки/неадекватного поведения в новом пустом проекте. FormStyle здесь не играет никакой роли.

  1. Создаем новый проект VCL Forms Application.
  2. Добавляем новую форму в проект (Правой кнопкой мыши на проект в менеджере проектов: Add New -> VCL Form). На новой форме ничего не меняем.
  3. В свойствах проекта (Project -> Options -> Forms) убираем вторую форму из списка Auto-create forms.
  4. На первой форме добавляем обработчик OnClick.
  5. В раздел uses модуля с первой формой добавляем Unit2 и пишем обработчик OnClick.

@kot-da-vinci Похоже, что Вы мало сталкивались с трудно-воспроизводимыми/плавающими багами, раз даёте ответ в стиле "у меня всё работает". В том, что проблема реально существует можете убедиться, немного погуглив по фразе modal form behind. Аналогичный вопрос есть и на enSO: Form is hidden behind other forms when ShowModal is called

Вот тут вы ошибаетесь, мне часто приходится искать причины трудновоспроизводимых багов. Я нигде не говорил, что проблемы нет. И я не сомневаюсь, что она существует. Проблемы подобного рода обычно возникают при попытках манипулирования порядком отображения окон на экране или работой с визуальными компонентами не из основного потока.

Можете и дальше искать что не так с вашими окнами, только дело не в окнах. И try пишут после Create , а не до, как в вашем вопросе. Если Create не сработает, то код в секции finally бесполезен, т.к. освобождать нечего. Если в конструкторе случается exception, то компилятор сам вызовет деструктор.

Читайте также: