5

我在主菜单上使用标准的剪切、复制、粘贴操作。他们有快捷方式Ctrl-XCtrl-CCtrl-V

当我打开一个模态表单(例如 FindFilesForm.ShowModal)时,所有的快捷方式都可以在表单中使用。

但是当我打开一个非模态表单(例如 FindFilesForm.Show)时,快捷方式不起作用。

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

无论如何,我怎样才能获得在非模态表单上工作的快捷方式?


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

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

  1. TComboBox 中的文本
  2. TFindDialog 中的文本
  3. TElTreeInplaceEdit 控件,LMD 的 ElPack 的一部分

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

这些都是我程序中重要的非模态形式。

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


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

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

正如我在其中一个评论中提到的,这里似乎讨论了一个相关问题

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

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

只是要小心,似乎有一些东西可以让它有时工作,但有时不能工作。如果我可以更详细地隔离它,我会在这里报告。

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


Mghie 非常努力地寻找解决方案,他的 OnExecute 处理程序与他的 ActionListUpdate 处理程序相结合就可以解决问题。因此,为了他的努力,我给了他公认的解决方案和赏金积分。

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

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

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

4

3 回答 3

3

好的,首先:这与模态或非模态形式无关,它是 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 答案,因为他做了很多工作并且他的答案是正确的,但是我已经实现了一个更简单的解决方案,我自己添加了作为答案

于 2009-12-08T09:33:38.110 回答
3

我在我的博客上发布了这个问题的链接,并从不在 StackOverflow 上的 Uwe Molzhan 那里得到了建议。Uwe 曾经运行DelphiPool。他向我指出了 borland.public.delphi.objectpascal 上的这个线程:

动作列表(错误)行为。

在这个线程中提出原始问题的汤姆亚历山大甚至说:

这种行为通常会发生,但并非总是如此。有时在上述一系列错误之后,行为开始按我的预期行事。

这正是我一直有的奇怪行为,使这个问题几乎无法追踪。

Peter 下面在该线程中回复说,如果有碰撞的快捷方式,您必须采取措施确保活动控件首先破解快捷方式。

拿他的代码(这是为框架问题编写的),我只需将“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 对他的回答所做的编辑,看看这是否也是一个解决方案。

于 2009-12-11T02:55:05.520 回答
1

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

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

因此,我在表单上放置了一个 TMainMenu,并添加了一个 TActionList。我创建了一个编辑菜单项,并添加了复制、剪切、粘贴子菜单项。我将这些连接到标准操作 EditCopy、EditCut 和 EditPaste。尽管如此,一切都像以前一样正常。我可以使用菜单项,也可以使用Ctrl-CCtrl-XCtrl-V组合键。

这里一定有其他事情发生。

于 2009-12-05T20:09:05.040 回答