Delphi notes як правильно закривати форму по escape

Дуже часто потрібно зробити так, щоб вікно закривалося після натискання на клавішу 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 використовується повідомлення CM_DIALOGKEY. Це повідомлення спочатку приходить поточного контролю (тобто того, який в даний момент знаходиться в фокусі), а потім (до тих пір, поки воно не буде опрацьовано, тобто поки Result = 0) - Батьківський контроль (від поточного до рівня форми). Якщо CM_DIALOGKEY не було оброблено, то спрацьовує OnKeyDown поточного контрола.

У другому прикладі, щоб відловити Escape на рівні форми, ми виставили KeyPreview. Це властивість ламає описану логіку: всі повідомлення від клавіатури спочатку обробляються формою, а потім вже приходять до котроль.

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

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

CM_DIALOGKEY також слід обробляти і для інших діалогових клавіш. Наведу типовий приклад: на формі є поле введення (SomeEdit: TEdit) і таблиця. При натисканні на Enter в SomeEdit, користувач очікує якоїсь реакції (наприклад фільтрація даних в таблиці). Однак, якщо форма модальна і на ній є кнопка "OK" (у якій виставлено властивість Default: = True і ModalResult: = mrOk), то повідомлення про натискання на Enter до SomeEdit дійти не встигне (спрацює Click кнопки і модальна форма закриється). В цьому випадку можна написати такий обробник:

Ну вобщем-то і все ...







Схожі статті