Ответ 1
Поведение, которое вы наблюдаете, контролируется обработкой сообщения WM_GETDLGCODE
. Для напоминания, которое выглядит так:
procedure TCustomMemo.WMGetDlgCode(var Message: TWMGetDlgCode);
begin
inherited;
if FWantTabs then Message.Result := Message.Result or DLGC_WANTTAB
else Message.Result := Message.Result and not DLGC_WANTTAB;
if not FWantReturns then
Message.Result := Message.Result and not DLGC_WANTALLKEYS;
end;
Для управления редактированием VCL не реализует специальную обработку для WM_GETDLGCODE
, а основной элемент управления Windows обрабатывает ее.
В стандартном приложении Win32 диспетчер диалоговых окон Windows отправляет сообщения WM_GETDLGCODE
. Но Delphi не построен поверх диспетчера диалогов, поэтому VCL отвечает за отправку WM_GETDLGCODE
. Это делается в обработчике CN_KEYDOWN
. Код выглядит следующим образом:
Mask := 0;
case CharCode of
VK_TAB:
Mask := DLGC_WANTTAB;
VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN:
Mask := DLGC_WANTARROWS;
VK_RETURN, VK_EXECUTE, VK_ESCAPE, VK_CANCEL:
Mask := DLGC_WANTALLKEYS;
end;
if (Mask <> 0) and
(Perform(CM_WANTSPECIALKEY, CharCode, 0) = 0) and
(Perform(WM_GETDLGCODE, 0, 0) and Mask = 0) and
(GetParentForm(Self).Perform(CM_DIALOGKEY,
CharCode, KeyData) <> 0) then Exit;
Обратите внимание, что VK_RETURN
, VK_EXECUTE
, VK_ESCAPE
и VK_CANCEL
все сгруппированы. Это означает, что элемент управления VCL должен решить, обрабатывать ли эти ключи самостоятельно или позволить форме обрабатывать их в своем обработчике CM_DIALOGKEY
.
Как вы можете видеть из TCustomMemo.WMGetDlgCode
, вы можете повлиять на этот выбор с помощью свойства WantReturns
. Итак, вы можете убедить VCL, чтобы форма дескриптора формы ESC просто установила WantReturns
в записке на False
. Но это также останавливает нажатие клавиши ENTER на заметку и делает довольно сложным для пользователя заметки ввод новых строк. Они должны сделать это с помощью CTRL + ENTER.
Фактически WantReturns
действительно должен был называться WantReturnsAndEscapesAndExecutesAndCtrlBreaks
. Дизайнеры VCL могли реализовать свойство WantEscapes
, но его просто нет.
Таким образом, вы сами так или иначе обрабатываете его. Лично я делаю это с помощью собственного производного контроля над записью. Он переопределяет метод KeyDown
и делает следующее:
procedure TMyMemo.KeyDown(var Key: Word; Shift: TShiftState);
var
Form: TCustomForm;
Message: TCMDialogKey;
begin
inherited;
if (Key=VK_ESCAPE) and (Shift*[ssShift..ssCtrl])=[]) then begin
Form := GetParentForm(Self);
if Assigned(Form) then begin
// we need to dispatch this key press to the form so that it can 'press'
// any buttons with Cancel=True
Message.Msg := CM_DIALOGKEY;
Message.CharCode := VK_ESCAPE;
Message.KeyData := 0;
Message.Result := 0;
Form.Dispatch(Message);
end;
end;
end;
Другой способ добиться этого - обработать CM_WANTSPECIALKEY
и WM_GETDLGCODE
. Здесь грубый мешанин, который иллюстрирует технику:
type
TMemo = class(StdCtrls.TMemo)
protected
procedure CMWantSpecialKey(var Msg: TCMWantSpecialKey); message CM_WANTSPECIALKEY;
procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
end;
procedure TMemo.CMWantSpecialKey(var Msg: TCMWantSpecialKey);
begin
case Msg.CharCode of
VK_ESCAPE:
Msg.Result := 0;
VK_RETURN, VK_EXECUTE, VK_CANCEL:
Msg.Result := 1;
else
inherited;
end;
end;
procedure TMemo.WMGetDlgCode(var Message: TWMGetDlgCode);
begin
inherited;
Message.Result := Message.Result and not DLGC_WANTALLKEYS;
end;