【问题标题】:How to trigger action when use mouse middle click on TPopupMenu's items?使用鼠标中键单击 TPopupMenu 项目时如何触发动作?
【发布时间】:2011-10-21 07:58:03
【问题描述】:

我们在TPopupMenu的菜单项中使用鼠标左键触发动作。如何在这些菜单项中触发鼠标中键单击的不同动作?也就是说,鼠标左键和中键点击TPopupmenu的菜单项都是不同的动作。

【问题讨论】:

  • 我认为这很难(以及其他人尚未做出回应的原因)是因为这是一件非常不标准且违反直觉的事情......您的用户如何知道这一点他们需要按菜单项上的中间按钮吗?他们如何知道中按在哪个菜单项上起作用?
  • 必须是中间按钮吗?并非所有鼠标都有三个按钮。 (我在微软商店看到过只有 one 按钮的笔记本电脑,比如 Mac,这意味着即使是右键也需要使用特殊的修饰键。)也许你可以 use the right button 而不是中间一个。 popup-menu API 直接支持这一点。
  • 即使鼠标有一个中键,也很少有人知道它可以被点击。大多数人只知道滚动。更何况鼠标中键被按下时有一个预定义的含义,即开始一个滚动循环。
  • 如果中间点击不是一个合适的选择,不如使用一些组合键和鼠标点击,比如Ctrl-Click,来触发另一个动作? TPopupMenu 没有任何自定义点击相关的事件。

标签: delphi


【解决方案1】:

全局 Menus.PopupList 变量跟踪所有 PopupMenus 并处理发送给它们的所有消息。您可以使用自己的实例覆盖此 PopupList,如下所示:

type
  TMyPopupList = class(TPopupList)
  private
    FMenuItem: TMenuItem;
  protected
    procedure WndProc(var Message: TMessage); override;
  end;

{ TMyPopupList }

procedure TMyPopupList.WndProc(var Message: TMessage);
var
  FindKind: TFindItemKind;
  I: Integer;
  Item: Integer;
  Action: TBasicAction;
  Menu: TMenu;
begin
  case Message.Msg of
    WM_MENUSELECT:
      with TWMMenuSelect(Message) do
      begin
        FindKind := fkCommand;
        if MenuFlag and MF_POPUP <> 0 then
          FindKind := fkHandle;
        for I := 0 to Count - 1 do
        begin
          if FindKind = fkHandle then
          begin
            if Menu <> 0 then
              Item := GetSubMenu(Menu, IDItem)
            else
              Item := -1;
          end
          else
            Item := IDItem;
          FMenuItem := TPopupMenu(Items[I]).FindItem(Item, FindKind);
          if FMenuItem <> nil then
            Break;
        end;
      end;
    WM_MBUTTONUP:
      if FMenuItem <> nil then
      begin
        GetMenuItemSecondAction(FMenuItem, Action);
        Menu := FMenuItem.GetParentMenu;
        if Action <> nil then
        begin
          Menu := FMenuItem.GetParentMenu;
          SendMessage(Menu.WindowHandle, WM_IME_KEYDOWN, VK_ESCAPE, 0);
          Action.Execute;
          Exit;
        end;
      end;
  end;
  inherited WndProc(Message);
end;

initialization
  PopupList.Free;
  PopupList := TMyPopupList.Create;

GetMenuItemSecondAction 例程您必须自己编写。也许this answer 提供了一些关于将您自己的操作添加到组件的帮助。

请注意WM_MENUSELECT 下的代码只是从Menus.TPopupList.WndProc 复制而来。您还可以使用MenuItemFromPoint 检索WM_MBUTTONUP 处理中的MenuItem。

但正如许多 cmets 已经说过的:在实现此 UI 功能之前要三思(或更多)。

【讨论】:

    【解决方案2】:

    您不会收到此类事件的通知。如果你在list of menu notifications 中会有一个鼠标中键单击条目。

    因此,如果您真的想这样做,也许您可​​以在菜单系统的背后使用某种技巧。但是,正如 cmets 中所讨论的,我们有充分的理由认为您提议的 UI 可能不太合适。

    【讨论】:

      【解决方案3】:

      如果中间单击不是一个合适的选择,那么如何使用一些组合键和鼠标单击(如 Ctrl-Click)来触发另一个动作? TPopupMenu 没有任何自定义点击相关的事件。

      这比单击鼠标中键更可取。

      然后就简单多了。如果按下 CTRL 按钮,只需检查您的操作执行处理程序:

      procedure TForm1.Action1Execute(Sender: TObject);
      begin
        if (GetKeyState(VK_CONTROL) and $8000 = 0) then
          // process normal click
        else
          // process ctrl click
      end;
      

      【讨论】:

      • 是的,这更直观,但仍然需要一些解释。动作的长提示文本就足够了。
      • 我的意图是如果表单不存在,则使用鼠标左键单击打开一个新表单,无论表单是否存在,单击鼠标中键打开一个新表单。中键是模仿 Windows 7 任务栏按钮,使用中键打开程序的新实例。
      【解决方案4】:

      我尝试结合作者 NGLN 的 2 个答案并得出以下结论。

      定义一个继承自TPopupList的新类:

      TMyPopupList = class(TPopupList)
      protected
        procedure WndProc(var Message: TMessage); override;
      end;
      
      procedure TMyPopupList.WndProc(var Message: TMessage);
      var H: HWND;
      begin
        case Message.Msg of
          WM_MBUTTONDOWN: begin
            H := FindWindow(PChar('#32768'), nil);
            SendMessage(H, WM_IME_KEYDOWN, VK_RETURN, 0);
          end;
        end;
        inherited WndProc(Message);
      end;
      
      initialization
        PopupList.Free;
        PopupList := TMyPopupList.Create;
      end.
      

      Item1Click 是 TMenuItem 的 OnClick 事件处理程序,它基于鼠标单击执行:

      procedure TForm1.Item1Click(Sender: TObject);
      begin
        if (GetKeyState(VK_MBUTTON) and $80 > 0) then
          Caption := 'Middle Click'
        else
          Caption := 'Normal Click';
      end;
      

      注意:#32768 是弹出菜单的默认窗口类名,请参阅MSDN documentation。

      【讨论】:

      • +1 用于使用主窗体的标题栏作为测试输出媒体。而且弹出菜单使用默认的窗口类名也很好找!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-03
      • 1970-01-01
      • 1970-01-01
      • 2018-03-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多