2

I've added a TPopupMenu to a TControlList, on right-click the ItemIndex isn't updated to reflect the item clicked on. Is there a way to make a right-click respond in a similar way to a left-click?

This would be nice so that the user right-clicks on a particular item, the PopupMenu is associated with that item, not the currently focused item.

Alister
  • 6,527
  • 4
  • 46
  • 70
  • You would probably need to introduce new property similar to [`TTreeView.RightClickSelect`](http://docwiki.embarcadero.com/Libraries/en/Vcl.ComCtrls.TTreeView.RightClickSelect). To implement that just handle selection change in `WM_CONTEXTMENU`. – Peter Wolf Mar 30 '21 at 06:33

3 Answers3

2

You can use HotItemIndex property from OnPopupMenu event and save it to a variable. Then for the popup menu items events you can use it.

Example code:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Menus, Vcl.ControlList, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    ControlList1: TControlList;
    PopupMenu1: TPopupMenu;
    TestPopupMnu: TMenuItem;
    Label1: TLabel;
    Memo1: TMemo;
    procedure ControlList1BeforeDrawItem(AIndex: Integer; ACanvas: TCanvas; ARect:
        TRect; AState: TOwnerDrawState);
    procedure ControlList1Click(Sender: TObject);
    procedure TestPopupMnuClick(Sender: TObject);
    procedure PopupMenu1Popup(Sender: TObject);
  private
    FPopupItemIndex : Integer;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.ControlList1BeforeDrawItem(AIndex: Integer; ACanvas: TCanvas;
    ARect: TRect; AState: TOwnerDrawState);
begin
    Label1.Caption := AIndex.ToString;
end;

procedure TForm1.ControlList1Click(Sender: TObject);
begin
    Memo1.Lines.Add('Clicked item ' + ControlList1.ItemIndex.ToString);
end;

procedure TForm1.TestPopupMnuClick(Sender: TObject);
begin
    Memo1.Lines.Add('Test PopupMenu item ' + FPopupItemIndex.ToString);
end;

procedure TForm1.PopupMenu1Popup(Sender: TObject);
begin
    FPopupItemIndex := ControlList1.HotItemIndex;
end;

end.

The form itself:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 299
  ClientWidth = 635
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object ControlList1: TControlList
    Left = 24
    Top = 20
    Width = 317
    Height = 200
    ItemCount = 5
    ItemMargins.Left = 0
    ItemMargins.Top = 0
    ItemMargins.Right = 0
    ItemMargins.Bottom = 0
    ParentColor = False
    PopupMenu = PopupMenu1
    TabOrder = 0
    OnBeforeDrawItem = ControlList1BeforeDrawItem
    OnClick = ControlList1Click
    object Label1: TLabel
      Left = 92
      Top = 20
      Width = 31
      Height = 13
      Caption = 'Label1'
    end
  end
  object Memo1: TMemo
    Left = 368
    Top = 24
    Width = 241
    Height = 201
    Lines.Strings = (
      'Memo1')
    TabOrder = 1
  end
  object PopupMenu1: TPopupMenu
    OnPopup = PopupMenu1Popup
    Left = 436
    Top = 128
    object TestPopupMnu: TMenuItem
      Caption = 'Test'
      OnClick = TestPopupMnuClick
    end
  end
end
fpiette
  • 11,983
  • 1
  • 24
  • 46
2

In order to handle Right Clicks you will have to make use of OnMouseDown or OnMouseUp events instead of OnClick event.

Unlike OnClick event that only detects left clicks OnMouseDown and OnMouseUp events are able to detect all mouse clicks (left, right, middle).

procedure TForm1.ControlList1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbRight then
  begin
    //Do what needs to be done on right click
  end
  else if Button = mbMiddle then
  begin
    //Do what needs to be done on middle click
  end
end;
Alister
  • 6,527
  • 4
  • 46
  • 70
SilverWarior
  • 7,372
  • 2
  • 16
  • 22
0

The HotItemIndex property can be used to detect which item has been right-clicked by using the following

procedure TListingList.clListingsMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  if Button <> mbRight then
    Exit;

  if clListings.HotItemIndex <> -1 then
    clListings.ItemIndex := clListings.HotItemIndex;
end;

This mostly works, but if you already have a TPopupmenu visible already, then the HotItemIndex is -1. This means that consecutive right-clicks don't popup for the correct item - but I can live with this. I think a MousePosToItemIndex or ItemIndexUnderMouse method would be required to fix this properly.

Alister
  • 6,527
  • 4
  • 46
  • 70