Show the default right-click menu - Delphi

2019-02-05 01:51发布

I have a Listbox which contain a list of files . can i access the Windows right-click menu in the listbox to access the open , properties , delete and rename items ?

4条回答
萌系小妹纸
2楼-- · 2019-02-05 02:31

Kermia check the JclShell unit from the JEDI JCL library, inside of this unit exist a function called DisplayContextMenu which show the context menu associated to a file. this function encapsulate the calls to the IContextMenu interface and makes your work more easy.

function DisplayContextMenu(const Handle: HWND; const FileName: string;
  Pos: TPoint): Boolean;
查看更多
仙女界的扛把子
3楼-- · 2019-02-05 02:33

I'd recommend looking at something like tpShellShock when you want to show shell like controls in your Delphi app. It offers tree views, list views etc. that can be connected together much like an Explorer Windows. It will display the appropriate icons for the files. I'm sure it offers the facilities you talk about too.

It might need some porting work if you are on a modern Unicode Delphi, but when I have done that it proved relatively straightforward.

No doubt there are other libraries that offer shell controls, this is just the one I am familiar with.

Otherwise if you want to stick with your current solution it's easiest to implement your own menu actions. Open and Properties are just simple calls to ShellExecute with the appropriate verb. Delete is a call to DeleteFile and Rename is a call to MoveFile.

查看更多
够拽才男人
4楼-- · 2019-02-05 02:39

Here is an implementation example using the 'OnContextPopup' event of a list box, which is populated with file names in the project directory, to launch the shortcut menu of a file when right-clicked on its name:

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure ListBox1ContextPopup(Sender: TObject; MousePos: TPoint;
      var Handled: Boolean);
  private
  protected
    procedure WndProc(var Msg: TMessage); override;
  public
  end;

var
  Form1: TForm1;

implementation

uses
  shlobj, comobj;

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  SearchRec: TSearchRec;
begin
  ListBox1.Clear;

  // populate list box with files in the project folder
  if FindFirst(ExtractFilePath(Application.ExeName) + '*.*',
               0, SearchRec) = 0 then
    repeat
      ListBox1.Items.Add(SearchRec.Name);
    until FindNext(SearchRec) <> 0;
  FindClose(SearchRec);
end;

var
  // Required to handle messages for owner drawn items, as in 'SendTo' menu.
  // Also used as a flag in WndProc to know if we're tracking a shortcut menu.
  ContextMenu2: IContextMenu2 = nil;

procedure TForm1.ListBox1ContextPopup(Sender: TObject; MousePos: TPoint;
  var Handled: Boolean);
var
  Item: Integer;
  DeskFolder, Folder: IShellFolder;
  Eaten, Attributes: ULONG;
  pIdl, FolderpIdl: PItemIDList;
  ContextMenu: IContextMenu;
  Menu: HMENU;
  Pos: TPoint;
  Cmd: DWORD;
  CommandInfo: TCMInvokeCommandInfo;
begin
  Item := (Sender as TListBox).ItemAtPos(MousePos, True);
  Handled := Item <> -1;
  if not Handled then
    Exit;
  TListBox(Sender).ItemIndex := Item;

  // IShellFolder for Desktop folder (root)
  OleCheck(SHGetDesktopFolder(DeskFolder));

  // Item ID List for the folder that the file is in
  Attributes := 0;
  OleCheck(DeskFolder.ParseDisplayName(Handle, nil,
                    PWideChar(WideString(ExtractFilePath(Application.ExeName))),
                    Eaten, FolderpIdl, Attributes));

  // IShellFolder for the folder the file is in
  OleCheck(DeskFolder.BindToObject(FolderpIdl, nil, IID_IShellFolder, Folder));
  CoTaskMemFree(FolderpIdl);

  // Item ID List for the file, relative to the folder it is in
  Attributes := 0;
  OleCheck(Folder.ParseDisplayName(Handle, nil,
           PWideChar(WideString(ExtractFileName(TListBox(Sender).Items[Item]))),
           Eaten, pIdl, Attributes));

  // IContextMenu for the relative Item ID List
  OleCheck(Folder.GetUIObjectOf(Handle, 1, pIdl, IID_IContextMenu,
                                  nil, ContextMenu));
  CoTaskMemFree(pIdl);

  Menu := CreatePopupMenu;
  try
    // Populate our menu with shortcut items
    OleCheck(ContextMenu.QueryContextMenu(Menu, 0, 1, $7FFF, CMF_EXPLORE));

    // ContextMenu2 used in WndProc
    ContextMenu.QueryInterface(IID_IContextMenu2, ContextMenu2);
    try
      Pos := TWinControl(Sender).ClientToScreen(MousePos);
      // launch the menu
      Bool(Cmd) := TrackPopupMenu(Menu,
                            TPM_LEFTBUTTON or TPM_RIGHTBUTTON or TPM_RETURNCMD,
                            Pos.X, Pos.Y, 0, Handle, nil);
    finally
      // clear so that we don't intervene every owner drawn menu item message in
      // WndProc
      ContextMenu2 := nil;
    end;

    // Invoke command if we have one
    if Bool(Cmd) then begin
      FillChar(CommandInfo, SizeOf(CommandInfo), 0);
      CommandInfo.cbSize := SizeOf(CommandInfo);
      CommandInfo.hwnd := Handle;
      CommandInfo.lpVerb := MakeIntResource(Cmd - 1);
      CommandInfo.nShow := SW_SHOWNORMAL;

      OleCheck(ContextMenu.InvokeCommand(CommandInfo));
    end;

  finally
    DestroyMenu(Menu);
  end;
end;

procedure TForm1.WndProc(var Msg: TMessage);
begin
  if ((Msg.Msg = WM_INITMENUPOPUP) or (Msg.Msg = WM_DRAWITEM)
              or (Msg.Msg = WM_MEASUREITEM)) and Assigned(ContextMenu2) then
    ContextMenu2.HandleMenuMsg(Msg.Msg, Msg.WParam, Msg.LParam)
  else
    inherited;
end;
查看更多
女痞
5楼-- · 2019-02-05 02:40

Check the IContextMenu interface. But be aware that Windows shell doesn't identify its objects by filename - actually they could not be files. It uses a concatenation of ids, and you may need to get what item id list a file is assigend to before invoking some shell functions on it.

查看更多
登录 后发表回答