如何为ListView标题栏设置弹出菜单以及项目弹出菜单?

7
我有一个ListView,ViewStyle = vsReport,还有两个弹出菜单:
  1. 列弹出菜单,我想在用户右键单击标题栏时打开
  2. 项弹出菜单,必须在用户右键单击任何列表项/子项或项下方的空白处时打开。
什么是最正确的方法来显示这些菜单?我应该处理哪些事件?
问题在于当我设置ListView.PopupMenu属性时,弹出菜单会在单击ListView客户区域中的任何点后出现。
当我处理ListView.OnColumnRightClick事件时,它只在点击列标题之后触发,不包括头部栏的空闲空间(在列的右侧)。
事件LisView.OnMouseUp只在右键单击项下方的空白处后触发。

你想要 PopupMenuItems 菜单只在悬停在项上时弹出,还是当悬停在子项上时也弹出? - TLama
@TLama 是的,还包括子项目。 - Andrew
3个回答

8

您不必使用listview的PopupMenu属性,将其保持未设置状态,然后可以将处理程序附加到OnContextPopup事件,并根据位置启动任何弹出菜单。例如:

procedure TForm1.ListViewContextPopup(Sender: TObject; MousePos: TPoint;
  var Handled: Boolean);
var
  HeaderRect: TRect;
  Pos: TPoint;
begin
  GetWindowRect(ListView_GetHeader(ListView.Handle), HeaderRect);
  Pos := ListView.ClientToScreen(MousePos);
  if PtInRect(HeaderRect, Pos) then
    PopupMenuColumns.Popup(Pos.X, Pos.Y)
  else
    PopupMenuItems.Popup(Pos.X, Pos.Y);
end;

1
赶我一步。 :-) 我正在从IDE复制我的测试代码,以便将其粘贴到我的答案中,表达相同的意思,当“新答案发布”横幅出现时。 <g> +1。 - Ken White
@Ken - 当几个小时前的问题出现这种情况时,我觉得很奇怪,抱歉.. 谢谢。 :) - Sertac Akyuz
@Sertac,这很奇怪。我把它归咎于我的网络连接今天有延迟。 :-) - Ken White
+1 给两个。@Andrew,我给你留了一个关于 OnContextPopup 的评论,但几分钟后就删除了。但是,Handled 参数怎么样?难道你不应该将其设置为 True 吗? - TLama
@TLama - 仅当控件分配了弹出菜单并且您不想启动它时才使用。 - Sertac Akyuz
是的,这正是我提到这个的原因。如果有的话,安德鲁似乎想要覆盖分配的弹出菜单。 - TLama

3

您可以大大简化它。创建两个弹出菜单(一个用于标题行,另一个用于列)。在IDE中将 TListView.PopupMenu 指定为列弹出菜单。

使用以下内容作为 ListView 的事件处理程序:

procedure TForm1.ListView1ContextPopup(Sender: TObject; MousePos: TPoint; var Handled: Boolean);
var
  HeaderRect: TRect;
  HeaderHeight: Integer;
  Header: HWnd;
begin
  ListView1.PopupMenu := ColumnMenu;   // Default to ColumnMenu
  Header := ListView_GetHeader(ListView1.Handle);
  GetWindowRect(Header, HeaderRect);
  HeaderHeight := HeaderRect.Bottom - HeaderRect.Top;
  if MousePos.Y < HeaderHeight then
    ListView1.PopupMenu := HeaderMenu;
end;

这种方法与@Sertac的方法略有不同,不需要调用ClientToScreenPtInRect - 因为我们知道点在ListView的区域内,只需简单测试点击高度即可知道是否在标题栏或列区域。它还确保始终至少有一个弹出菜单分配给ListView


0

这是我解决问题的方法,但我不喜欢这个解决方案。如果你有更好的方法,请写下来,我会接受它作为正确的。

uses
  CommCtrl;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ListView.PopupMenu := TPopupMenu.Create(Self);
  ListView.PopupMenu.OnPopup := ListViewPopup;
end;

procedure TForm1.ListViewPopup(Sender: TObject);
var
  Pos: TPoint;
  SrcMenu: TPopupMenu;
  I: Integer;
  MenuItem: TMenuItem;
  Header: HWND;
  HeaderRect: TRect;
  HeaderHeight: Integer;
begin
  // Re-filling ListView's popup menu
  ListView.PopupMenu.Items.Clear();

  // Getting header height
  Header := ListView_GetHeader(ListView.Handle);
  GetWindowRect(Header, HeaderRect);
  HeaderHeight := HeaderRect.Bottom - HeaderRect.Top;
  Pos := ListView.ScreenToClient(ListView.PopupMenu.PopupPoint);

  // Clicked on header?
  if Pos.Y < HeaderHeight then
    SrcMenu := PopupMenuColumns
  else
    SrcMenu := PopupMenuItems;

  // Copying destired menu to ListView.PopupMenu
  for I := 0 to SrcMenu.Items.Count - 1 do
  begin
    MenuItem := TMenuItem.Create(FListViewPopupMenu);

    with SrcMenu.Items[I] do
    begin
      MenuItem.Action := Action;
      MenuItem.Caption := Caption;
      MenuItem.ShortCut := ShortCut;
      MenuItem.Checked := Checked;
      MenuItem.Enabled := Enabled;
      MenuItem.OnClick := OnClick;
      MenuItem.HelpContext := HelpContext;
      MenuItem.Name := Name;
      MenuItem.ImageIndex := ImageIndex;
    end;

    ListView.PopupMenu.Items.Add(MenuItem);
  end;

  ListView.PopupMenu.Images := SrcMenu.Images;
end;

你对这个解决方案有什么不喜欢的地方? - NGLN
@NGLN 当你需要复制菜单项时,感觉有点奇怪。我认为可以滑动到WinAPI级别并在那里做一些事情。 - Andrew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接