好的,首先:这与模态或非模态形式无关,它是 Delphi 动作组件工作方式的限制(如果你想这样称呼它)。
让我通过一个简单的例子来证明这一点:用一个新表单创建一个新应用程序,将 aTMemo
和 aTComboBox
放到它上面,然后运行该应用程序。这两个控件都将具有系统提供的带有编辑命令的上下文菜单,并将正确地对它们做出反应。他们将对菜单快捷方式执行相同的操作,但组合框不支持Ctrl+除外。A
现在添加一个TActionList
具有剪切、复制和粘贴三个标准操作的组件。事情仍然会奏效,行为不会改变。
现在添加一个主菜单,并从模板中添加编辑菜单。删除除剪切、复制和粘贴之外的所有命令。为菜单项设置相应的动作组件,并运行应用程序。观察组合框如何仍然具有上下文菜单和那里的命令仍然有效,但快捷方式不再有效。
问题是标准编辑操作被设计为TCustomEdit
仅与控件一起使用。看看TEditAction.HandlesTarget()
StdActns.pas 中的方法。由于组合框中的编辑控件、树控件中的就地编辑器或本机对话框中的编辑控件不会被此捕获,因此它们将不会被处理。当这些控件之一具有焦点时,菜单命令将始终被禁用。至于仅在某些时候工作的快捷方式 - 这取决于 VCL 是否在某些时候将快捷方式映射到操作命令。如果没有,那么他们最终将到达本机窗口过程并启动编辑命令。在这种情况下,快捷方式仍然有效。我假设对于模态对话框,动作处理被暂停,因此模态对话框和非模态对话框之间的行为是不同的。
要解决此问题,您可以为OnExecute
这些标准操作提供处理程序。例如粘贴命令:
procedure TMainForm.EditPaste1Execute(Sender: TObject);
var
FocusWnd: HWND;
begin
FocusWnd := GetFocus;
if IsWindow(FocusWnd) then
SendMessage(FocusWnd, WM_PASTE, 0, 0);
end;
以及用于剪切命令 ( WM_CUT
) 和复制命令 ( WM_COPY
) 的类似处理程序。在小演示应用程序中执行此操作会使组合框再次工作。您应该在您的应用程序中尝试,但我认为这会有所帮助。正确启用和禁用所有本机编辑控件的主菜单命令是一项艰巨的任务。也许您可以发送EM_GETSEL
消息以检查焦点编辑控件是否有选择。
编辑:
更多信息为什么模态对话框和非模态对话框上的组合框的行为不同(在 Delphi 2009 上完成的分析):有趣的代码在TWinControl.IsMenuKey()
- 它试图在父表单的动作列表之一中找到一个动作组件处理快捷方式的焦点控件。如果失败,它会发送一条CM_APPKEYDOWN
消息,最终导致对应用程序主窗体的操作列表执行相同的检查。但事情是这样的:只有在启用应用程序主窗体的窗口句柄时才会这样做(参见TApplication.IsShortCut()
代码)。现在调用ShowModal()
一个表单将禁用所有其他表单,因此除非模态对话框本身包含一个具有相同快捷方式的操作,否则本机快捷方式处理将起作用。
编辑:
我可以重现这个问题——关键是要以某种方式让编辑操作被禁用。回想起来,这很明显,Enabled
动作的属性当然也需要更新。
请尝试使用这个额外的事件处理程序:
procedure TForm1.ActionList1Update(Action: TBasicAction; var Handled: Boolean);
var
IsEditCtrl, HasSelection, IsReadOnly: boolean;
FocusCtrl: TWinControl;
FocusWnd: HWND;
WndClassName: string;
SelStart, SelEnd: integer;
MsgRes: LRESULT;
begin
if (Action = EditCut1) or (Action = EditCopy1) or (Action = EditPaste1) then
begin
IsEditCtrl := False;
HasSelection := False;
IsReadOnly := False;
FocusCtrl := Screen.ActiveControl;
if (FocusCtrl <> nil) and (FocusCtrl is TCustomEdit) then begin
IsEditCtrl := True;
HasSelection := TCustomEdit(FocusCtrl).SelLength > 0;
IsReadOnly := TCustomEdit(FocusCtrl).ReadOnly;
end else begin
FocusWnd := GetFocus;
if IsWindow(FocusWnd) then begin
SetLength(WndClassName, 64);
GetClassName(FocusWnd, PChar(WndClassName), 64);
WndClassName := PChar(WndClassName);
if AnsiCompareText(WndClassName, 'EDIT') = 0 then begin
IsEditCtrl := True;
SelStart := 0;
SelEnd := 0;
MsgRes := SendMessage(FocusWnd, EM_GETSEL, WPARAM(@SelStart),
LPARAM(@SelEnd));
HasSelection := (MsgRes <> 0) and (SelEnd > SelStart);
end;
end;
end;
EditCut1.Enabled := IsEditCtrl and HasSelection and not IsReadOnly;
EditCopy1.Enabled := IsEditCtrl and HasSelection;
// don't hit the clipboard three times
if Action = EditPaste1 then begin
EditPaste1.Enabled := IsEditCtrl and not IsReadOnly
and Clipboard.HasFormat(CF_TEXT);
end;
Handled := TRUE;
end;
end;
我没有检查本机编辑控件是否为只读,这可以通过添加以下内容来完成:
IsReadOnly := GetWindowLong(FocusWnd, GWL_STYLE) and ES_READONLY <> 0;
注意:我已经给了 mghie 答案,因为他做了很多工作并且他的答案是正确的,但是我已经实现了一个更简单的解决方案,我自己添加了作为答案