如何防止Delphi中的快捷方式冲突/交互?

时间:2009-12-05 18:17:22

标签: delphi keyboard-shortcuts modal-dialog non-modal

我在主菜单上使用标准剪切,复制,粘贴操作。他们有快捷键 Ctrl-X Ctrl-C Ctrl-V

当我打开模态表格时,例如FindFilesForm.ShowModal,然后所有快捷方式都从表单中工作。

但是当我打开非模态表格时,例如FindFilesForm.Show,然后快捷方式不起作用。

如果FindFilesForm是活动表单,我认为这些操作应该有效。它的形态应该与它无关,还是我的思维错了?

从来没有,我怎样才能让快捷方式在非模态表格上工作?


在Cary的回应之后,我进一步研究了它。某些控件不是问题,例如TMemo或TEdit。

但它适用于其他一些人。具体来说,它发生的地方包括:

  1. TComboBox中的文字
  2. TFindDialog中的文字
  3. 一个TElTreeInplaceEdit控件,是LMD的ElPack
  4. 的一部分

    我会看看是否有其他人并将其添加到列表中。

    这些都是我程序中重要的非模态表格。

    所以我仍然需要一个解决方案。


    好。我真的需要帮助。所以这成为我给予赏金的第一个问题。

    我与Cary的讨论通过他的回答和那里的评论更详细地描述了我的问题。

    正如我在其中一条评论中所提到的,似乎讨论了一个相关的问题here.

    我需要的是一个解决方案或解决方法,它将允许 Ctrl-X Ctrl-C Ctrl-V 始终在非模态窗口中的TComboBox和TFindDialog中工作。如果这两个问题得到解决,我确信我的TElTreeInplaceEdit也能正常工作。

    如Cary所述,设置一个简单的测试程序只需要几分钟。希望有人能够解决这个问题。

    要小心谨慎,似乎有些东西可以让它有时工作但不能在其他时间工作。如果我可以更详细地分析它,我会在这里报告。

    感谢您提供给我的任何帮助。


    Mghie努力寻找解决方案,他的OnExecute处理程序与他的ActionListUpdate处理程序相结合。所以对于他的努力,我给了他接受的解决方案和赏金点。

    但是他的动作列表更新处理程序并不简单,您需要在其中指定要处理的所有案例。假设还有 Ctrl + A 用于全选或 Ctrl-Y 用于撤消您可能需要的内容。一般程序会更好。

    因此,如果您在搜索答案时遇到此问题,请首先尝试我提供的添加IsShortcut处理程序的答案。它对我有用,应该处理每个案例,不需要OnExecute处理程序,所以更简单。 Peter Below写了这段代码,Uwe Molzhan得到了发现者的费用。

    感谢Cary,mghie,Uwe和Peter帮助我解决这个问题。没有你,不可能做到。 (也许我可以,但它可能需要我6个月。)

3 个答案:

答案 0 :(得分:3)

我发布了a link to this question on my blog,并得到了不在StackOverflow上的Uwe Molzhan的建议。 Uwe曾经运行DelphiPool。他在borland.public.delphi.objectpascal指出了这个主题:

  

Action List (mis)behavior.

汤姆亚历山大在这个帖子中问了原问题,甚至说:

  

通常会发生此行为,但不会发生   每时每刻。有时候经过一系列的   以上错误,行为   开始按照我的预期行事。

这正是我一直以来的奇怪行为,这使得这个问题几乎无法追查。

Peter Below在该主题中回复说,如果存在碰撞的快捷方式,则必须采取措施确保活动控件首先在快捷方式处获得破解。

拿他的代码(这是为一个框架问题编写的),我只需修改“ctrl is TCustomFrame”到“ctrl is TControl”,它就完美了。所以这就是我们所需要的:

public
Function IsShortcut( var Message: TWMKey): Boolean; override;

Function TMyform.IsShortcut( var Message: TWMKey): Boolean; 
Var 
  ctrl: TWinControl; 
  comp: TComponent; 
  i: Integer; 
Begin 
  ctrl := ActiveControl; 
  If ctrl <> Nil Then Begin 
    Repeat 
      ctrl := ctrl.Parent 
    Until (ctrl = nil) or (ctrl Is TControl); 
    If ctrl <> nil Then Begin 
      For i:= 0 To ctrl.componentcount-1 Do Begin 
        comp:= ctrl.Components[i]; 
        If comp Is TCustomActionList Then Begin 
          result := TCustomActionList(comp).IsShortcut( message ); 
          If result Then 
            Exit; 
        End; 
      End;   
    End; 
  End; 
//  inherited; { Originally I had this, but it caused multiple executions }
End;   

到目前为止,这似乎适用于所有情况。

具有讽刺意味的是,它对原问题提问者汤姆亚历山大不起作用。他所做的是向FrameEnter事件添加一个过程,该过程将焦点设置为帧的适当网格。这可能意味着我的问题的另一个替代解决方案,但我没有必要探索,因为彼得的解决方案适合我。

另请注意,彼得在答案中包含了一个值得了解的关键处理复杂步骤的精彩摘要。

但我现在想检查mghie对他的回答的编辑,看看这是否也是一个解决方案。

答案 1 :(得分:2)

好的,首先要做的是:这与模态或非模态形式无关,它限制了Delphi动作组件的工作方式(如果你想称之为)。

让我通过一个简单的示例来证明这一点:使用新表单创建一个新应用程序,将TMemoTComboBox放到其上,然后运行该应用程序。两个控件都将具有系统提供的上下文菜单和编辑命令,并将对它们做出正确的反应。他们将对菜单快捷方式执行相同操作,但 Ctrl + A 除外,组合框不支持。

现在添加一个TActionList组件,其中包含剪切,复制和粘贴三个标准操作。事情仍然有效,行为没有变化。

现在添加一个主菜单,然后从模板中添加“编辑菜单”。删除除剪切,复制和粘贴之外的所有命令。为菜单项设置相应的操作组件,然后运行该应用程序。观察组合框如何仍然具有上下文菜单,并且那里的命令仍然有效,但快捷方式不再有效。


问题在于标准编辑操作仅适用于TCustomEdit控件。看看StdActns.pas中的TEditAction.HandlesTarget()方法。由于组合框中的编辑控件,树控件中的原位编辑器或本机对话框中的编辑控件不会被捕获,因此不会处理它们。当其中一个控件具有焦点时,将始终禁用菜单命令。至于快捷方式只在某些时候工作 - 这取决于VCL是否在某些时候将快捷方式映射到动作命令。如果没有,那么它们最终将到达本机窗口过程并启动编辑命令。在这种情况下,快捷方式仍然有效。我假设对于模态对话框,动作处理被暂停,因此模态和非模态对话框之间的行为是不同的。

要解决此问题,您可以为OnExecute这些标准操作提供处理程序。例如,粘贴命令:

procedure TMainForm.EditPaste1Execute(Sender: TObject);
var
  FocusWnd: HWND;
begin
  FocusWnd := GetFocus;
  if IsWindow(FocusWnd) then
    SendMessage(FocusWnd, WM_PASTE, 0, 0);
end;

以及Cut命令(WM_CUT)和Copy命令(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答案,因为他做了很多工作并且答案是正确的,但我已经实施了a simpler solution that I added as an answer myself

答案 2 :(得分:1)

我创建了一个非常简单的示例,其中包含在Vista 64位上运行的Delphi 2009(安装了Update 3和Update 4)中的两种表单。第二种形式,Form2以非模态方式显示(Form2.Show;)。我在Form2上有一个TMemo。 Ctrl-X Ctrl-V Ctrl-C 工作正常。

这是在我在Form2上放置一个TMainMenu之前。

所以,我在表单上放了一个TMainMenu,并添加了一个TActionList。我创建了一个Edit菜单项,并添加了Copy,Cut,Paste子菜单项。我将它们连接到标准操作EditCopy,EditCut和EditPaste。不过,一切都和以前一样好。我可以使用菜单项,或 Ctrl-C Ctrl-X Ctrl-V 组合键。

这里肯定会有其他事情发生。