【问题标题】:Drop down menu for TButtonTButton 的下拉菜单
【发布时间】:2012-05-15 09:28:24
【问题描述】:

我正在尝试模拟一个TButton的下拉菜单,如下图:

procedure DropMenuDown(Control: TControl; PopupMenu: TPopupMenu);
var
  APoint: TPoint;
begin
  APoint := Control.ClientToScreen(Point(0, Control.ClientHeight));
  PopupMenu.Popup(APoint.X, APoint.Y);
end;

procedure TForm1.Button1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
  begin
    DropMenuDown(Button1, PopupMenu1);
    // ReleaseCapture;
  end;
end;

问题是,当菜单被下拉时,如果我再次单击该按钮,我希望菜单关闭,但它却再次下拉。

我正在为通用 Delphi TButton 寻找解决方案专门,而不是任何第 3 方等价物。

【问题讨论】:

    标签: delphi button drop-down-menu delphi-7


    【解决方案1】:

    在查看了 Whiler & Vlad 提供的解决方案,并将其与 WinSCP 实现相同事物的方式进行比较后,我目前正在使用以下代码:

    unit ButtonMenus;
    interface
    uses
      Vcl.Controls, Vcl.Menus;
    
    procedure ButtonMenu(Control: TControl; PopupMenu: TPopupMenu);
    
    implementation
    
    uses
      System.Classes, WinApi.Windows;
    
    var
      LastClose: DWord;
      LastPopupControl: TControl;
      LastPopupMenu: TPopupMenu;
    
    procedure ButtonMenu(Control: TControl; PopupMenu: TPopupMenu);
    var
      Pt: TPoint;
    begin
      if (Control = LastPopupControl) and (PopupMenu = LastPopupMenu) and (GetTickCount - LastClose < 100) then begin
        LastPopupControl := nil;
        LastPopupMenu := nil;
      end else begin
        PopupMenu.PopupComponent := Control;
        Pt := Control.ClientToScreen(Point(0, Control.ClientHeight));
        PopupMenu.Popup(Pt.X, Pt.Y);
        { Note: PopupMenu.Popup does not return until the menu is closed }
        LastClose := GetTickCount;
        LastPopupControl := Control;
        LastPopupMenu := PopupMenu;
      end;
    end;
    
    end.
    

    它的优点是不需要对 from 进行任何代码更改,除了在 onClick 处理程序中调用 ButtonMenu()

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      ButtonMenu(Button1, PopupMenu1);
    end;
    

    【讨论】:

    • 这是更好、更通用的解决方案。另见this answer。 +1
    【解决方案2】:

    根据我们 (Vlad & I) 的讨论,您可以使用一个变量来了解上次打开弹出窗口的时间,以选择是显示弹出菜单还是取消鼠标事件:

    unit Unit4;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Menus, Vcl.StdCtrls;
    
    type
      TForm4 = class(TForm)
        PopupMenu1: TPopupMenu;
        Button1: TButton;
        fgddfg1: TMenuItem;
        fdgdfg1: TMenuItem;
        procedure FormCreate(Sender: TObject);
        procedure Button1Click(Sender: TObject);
        procedure Button1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
      private
        { Private declarations }
        cMenuClosed: Cardinal;
    
      public
        { Public declarations }
      end;
    
    var
      Form4: TForm4;
    
    implementation
    
    {$R *.dfm}
    
    procedure DropMenuDown(Control: TControl; PopupMenu: TPopupMenu);
    var
      APoint: TPoint;
    begin
      APoint := Control.ClientToScreen(Point(0, Control.ClientHeight));
      PopupMenu.Popup(APoint.X, APoint.Y);
    end;
    
    procedure TForm4.Button1Click(Sender: TObject);
    begin
      DropMenuDown(Button1, PopupMenu1);
      cMenuClosed := GetTickCount;
    end;
    
    procedure TForm4.Button1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    begin
      if (Button = mbLeft) and not ((cMenuClosed + 100) < GetTickCount) then
      begin
        ReleaseCapture;
      end;
    end;
    
    procedure TForm4.FormCreate(Sender: TObject);
    begin
      cMenuClosed := 0;
    end;
    
    end.
    

    【讨论】:

    • 这里的 PopupListEx 不是矫枉过正吗?我们知道菜单在 DropMenuDown 行之后立即关闭(因为弹出窗口是同步的),还是我错过了什么?
    • 如果您单击按钮...然后,您等待 n 秒而不做任何事情...然后...您决定再次按下按钮...在按下它之前,如你什么都没做...弹出窗口仍然打开?所以,如果你 cMenuClosed := GetTickCount; 就在 DropMenuDown(Button1, PopupMenu1); 之后,我刚才解释的情况应该不起作用......
    • 我的意思是:procedure TForm1.Button1Click(Sender: TObject); begin DropMenuDown(Button1, PopupMenu1); cMenuClosed := GetTickCount; end; procedure TForm1.Button1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if (Button = mbLeft) and not ((cMenuClosed + 100) &lt; GetTickCount) then begin ReleaseCapture; end; end;
    • 你的回答给了我正确的想法,所以我会接受它:) 谢谢。
    猜你喜欢
    • 2018-01-27
    • 2012-07-05
    • 1970-01-01
    • 2019-12-05
    • 2015-08-08
    • 2012-01-14
    • 2015-03-15
    • 1970-01-01
    • 2021-10-14
    相关资源
    最近更新 更多