我正试图阻止TMemo(和TRichEdit)控制吃Escape钥匙.
如果用户聚焦于a TEdit,按下Escape将触发表单以执行用户按下escape时表单执行的操作.如果用户专注于a TMemo,按下逃脱就被吃了TMemo.
当然我可以做黑客:
procedure TForm1.Memo1KeyPress(Sender: TObject; var Key: Char);
begin
if Key = #27 then
begin
//figure out how to send a key to the form
end;
end;
Run Code Online (Sandbox Code Playgroud)
但这并不理想(我必须处理转义键,而不是让表单处理它).
当然我可以做黑客:
Form1.KeyPreview := True;
procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
if Key = #27 then
begin
//Figure out how to invoke what the form was going to do when the user presses escape
end;
end;
Run Code Online (Sandbox Code Playgroud)
但这并不理想(我必须处理转义键,而不是让表单处理它).
相反,我们将借此机会学习一些东西.如果a 不是:a TMemo甚至接收与转义键关联的keyPress事件,那么它是怎么回事TEdit:
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if Key = #27 then
begin
//never happens
end;
end;
Run Code Online (Sandbox Code Playgroud)
该TEdit和TMemo是相同的Windows EDIT共同控制.
如果我打开表单KeyPreview,并且用户Escape在聚焦于TEdit框中时按下,并且设置了按钮的Cancel属性,则表单将关闭,并且:
Edit1.KeyPress事件没有被触发Form1.KeyPress事件没有被触发如果创建了一个动作,其Shortcut是Esc,则不会KeyPress引发事件,不管是什么控制用户集中英寸
tl;博士:TMemo.WantEscape财产在哪里?
Dav*_*nan 13
您观察到的行为由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;
Run Code Online (Sandbox Code Playgroud)
对于编辑控件,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;
Run Code Online (Sandbox Code Playgroud)
请注意VK_RETURN,VK_EXECUTE,VK_ESCAPE并且VK_CANCEL都集中在一起.这意味着VCL控件必须决定是否自己处理这些键,或让表单在其CM_DIALOGKEY处理程序中处理它们.
从您可以看到TCustomMemo.WMGetDlgCode您可以影响该WantReturns属性的选择.因此,您可以ESC通过简单地设置WantReturns备忘录来说服VCL让表单处理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;
Run Code Online (Sandbox Code Playgroud)
实现这一目标的另一种方法是处理CM_WANTSPECIALKEY和WM_GETDLGCODE.这是一个粗略的插入器,说明了这种技术:
type
TMemo = class(StdCtrls.TMemo)
protected
procedure CMWantSpecialKey(var Msg: TCMWantSpecialKey); message CM_WANTSPECIALKEY;
procedure WMGetDlgCode(var Msg: 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 Msg: TWMGetDlgCode);
begin
inherited;
Msg.Result := Msg.Result and not DLGC_WANTALLKEYS;
end;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1255 次 |
| 最近记录: |