There are TPopupMenu and three buttons on the form named "AddButton", "EditButton", and "DestroyButton" and added OnClick events to all three buttons. The TPopupMenu in the PopupMenu property of the form. I have created the PopupMenuItemsClick procedure in the TForm1 type declaration so that it can be used as the method call for the menu item OnClick event.
type
TForm1 = class(TForm)
AddButton: TButton;
EditButton: TButton;
DestroyButton: TButton;
PopupMenu1: TPopupMenu;
procedure AddButtonClick(Sender: TObject);
procedure EditButtonClick(Sender: TObject);
procedure DestroyButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
procedure PopupMenuItemsClick(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.AddButtonClick(Sender: TObject);
var
index: Integer;
NewItem: TMenuItem;
begin
// The owner (PopupMenu1) will clean up this menu item.
NewItem := TMenuItem.Create(PopupMenu1); // Create the new item.
index := PopupMenu1.Items.Count;
PopupMenu1.Items.Add(NewItem);// Add it to the pop-up menu.
NewItem.Caption := 'Menu Item ' + IntToStr(index);
NewItem.Tag := index;
NewItem.OnClick :=
PopupMenuItemsClick; // Assign it an event handler.
end;
procedure TForm1.PopupMenuItemsClick(Sender: TObject);
begin
with Sender as TMenuItem do
begin
case Tag of
0: ShowMessage('first item clicked');
1: ShowMessage('second item clicked');
2: ShowMessage('third item clicked');
3: ShowMessage('fourth item clicked');
end;
end;
end;
{
To edit or destroy an item, grab its pointer
using the Items property.
procedure TForm1.EditButtonClick(Sender: TObject);
var
ItemToEdit: TMenuItem;
begin
ItemToEdit := PopupMenu.Items[1];
ItemToEdit.Caption := 'Changed Caption';
end;
procedure TForm1.DestroyButtonClick(Sender: TObject);
var
ItemToDelete: TMenuItem;
begin
ItemToDelete := PopupMenu.Items[2];
ItemToDelete.Free;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
index: Integer;
NewItem: TMenuItem;
begin
for index := 0 to 3 do
begin
// The owner (PopupMenu1) will clean up this menu item.
NewItem := TMenuItem.Create(PopupMenu1); // Create the new item.
PopupMenu1.Items.Add(NewItem);// Add it to the pop-up menu.
NewItem.Caption := 'Menu Item ' + IntToStr(index);
NewItem.Tag := index;
NewItem.OnClick :=
PopupMenuItemsClick; // Assign it an event handler.
end;
end;
But PopupMenu is not appearing when I clicked on addmenu button. Anyone can find what is the reason why Popupmenu is not appearing when form is loaded or any button clicked.
your code not what you really need
use this code and it will work perfectly
procedure TForm1.PopupMenuItemsClick(Sender: TObject);
var ICount : Integer;
begin
ICount := TMenuItem(Sender).MenuIndex;
ShowMessage('Item Number '+ IntToStr(ICount+1) + ' Selected');
end;
procedure TForm1.AddClick(Sender: TObject);
var
Index: Integer;
NewItem: TMenuItem;
begin
NewItem := TMenuItem.Create(PopupMenu);
Index := PopupMenu.Items.Count;
PopupMenu.Items.Add(NewItem);
NewItem.Caption := 'Menu Item ' + IntToStr(Index);
NewItem.Tag := Index;
NewItem.OnClick := PopupMenuItemsClick;
PopupMenu.Popup(Mouse.CursorPos.X, Mouse.CursorPos.Y);
end;
I tested it with Delphi7, XE2 and XE3 its working
Add this line in the FormCreate or set this property in Object Inspector.
self.PopupMenu:=PopupMenu1;
This comments may help to solve the problem (i was having similar in old Delphi versions, i do not have XE to test on).
Never, ever create a component and let empty its .Name, allways
assign it a unique value (i see a lot of faulty interna code when let empty, since they can not be empty).
And allways assign properties and events to the componet prior to add
them onto their parent.
See this suggestions in the comments:
procedure TForm1.AddClick(Sender: TObject);
var
Index: Integer;
NewItem: TMenuItem;
begin
NewItem := TMenuItem.Create(PopupMenu);
Index := PopupMenu.Items.Count;
//PopupMenu.Items.Add(NewItem); // Not the correct place, see below
NewItem.Name : = 'SomeText' + IntToStr(Index); // Name them, with a unique name not starting with a number (also there is no need to put a number)
NewItem.Caption := 'Menu Item ' + IntToStr(Index);
NewItem.Tag := Index;
NewItem.OnClick := PopupMenuItemsClick;
PopupMenu.Items.Add(NewItem); // After properties has been set, never before
PopupMenu.Popup(Mouse.CursorPos.X, Mouse.CursorPos.Y);
// Do not forget to free such menu item somewhere on your code, obviously not here
end;
And with menus, remember to free the items created, they do not free by them selfs and names will be in use next time.
Related
i have an application with 2 TButton, 1 TListView. I would like display the value or content(Text) of TListViewItem inside the TButton(s) in a way that the content of the first TButton can't be the same with the 2nd one.
Steps =>>
When I click on the 1st TButton, I can select the Item text in the TListView and save it as new TButton text.
When I click on the 2nd TButton, I can select another item text in the same TListView, and it is saved as Text in the 2nd TButton.
My code:
....
ListView1: TListView;
Base: TButton;
Hypo: TButton;
....
procedure TMainForm.BaseClick(Sender: TObject);
begin
ListView1.Visible := True;
end;
procedure TMainForm.HypoClick(Sender: TObject);
begin
ListView1.Visible := True;
end;
procedure TMainForm.ListView1ItemClick(const Sender: TObject;
const AItem: TListViewItem);
begin
if Assigned(ListView1.Selected) and Assigned(Base.OnClick) then
begin
Base.Text := TListViewItem(ListView1.Selected).Text;
end else
if Assigned(ListView1.Selected) and Assigned(Hypo.OnClick) then
begin
Hypo.Text := TListViewItem(ListView1.Selected).Text;
end;
ListView1.Visible := False;
end;
I used LiveBindings to fill the TListView; when i run the app and select one item it works but it's displaying the same value/content in both TLabels
If you really have 2 selected items, then you have to iterate through the whole list view
procedure TForm3.ListView1ItemClick(const Sender: TObject;
const AItem: TListViewItem);
var elvitem : TListViewItem;
i,n : integer;
begin
n:=0;
for i:=0 to ListView1.ItemCount-1 do
begin
if ListView1.Items[i].Purpose=TListItemPurpose.None then // it's an item
begin
if ListView1.Items[i].Checked then
begin
inc(n);
case n of
1 : base.text:=ListView1.Items[i].Text;
2 : begin
hypo.text:=ListView1.Items[i].Text;
break; // don't search more
end;
end;
end;
end;
end;
Here item 2 and 8 are selected with this code
procedure TForm3.FormCreate(Sender: TObject);
begin
Listview1.Items[2].Checked:=True;
Listview1.Items[8].Checked:=True;
end;
My first reaction, if your listview is livebinded then why don't you use livebindings to link your 2 labels ?
Second one is your code, you use Selected when you have the AItem parameter
so
procedure TMainForm.ListView1ItemClick(const Sender: TObject;
const AItem: TListViewItem);
begin
Base.Text:= AItem.text;
Hypo.Text:= AItem.detail;
ListView1.Visible := False;
end;
should be sufficient if it is not a DynamicAppearance type.
In a 32-bit Delphi 11 VCL Application on Windows 10, when RIGHT-CLICKING any menu item, I need to get the name of the clicked MenuItem.
I use a TApplicationEvents component and this code to get notified when I click on any menu item:
procedure TformMain.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
begin
case Msg.message of
Winapi.Messages.WM_COMMAND:
begin
CodeSite.Send('TformMain.ApplicationEvents1Message: WM_COMMAND');
end;
end;
end;
However:
How to get notified only when RIGHT-clicking the menu item?
How to get the NAME of the clicked MenuItem?
Each TMenu (i.e. TMainMenu or TPopupMenu) offers a method FindItem, which allows you to find an item by varying criteria. In your case the correct call for the form's main menu would be
TheMenuItem := Menu.FindItem(Msg.wParam, fkCommand);
Since I have several forms in my application and several (popup) menus on each of these forms, a special solution is needed here:
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
begin
case Msg.message of
Winapi.Messages.WM_COMMAND:
begin
// Todo: Check HERE for RightMouseButtonDown - BUT HOW? (Or how to check HERE for modifier keys?)
var ThisMenuItem := GetMenuItem(Msg.wParam);
if Assigned(ThisMenuItem) then
begin
CodeSite.Send('TForm1.ApplicationEvents1Message: Clicked MenuItem Name', ThisMenuItem.Name);
end;
end;
end;
end;
function TForm1.GetMenuItem(const aWParam: NativeUInt): TMenuItem;
var
ThisMenuItem: TMenuItem;
begin
Result := nil;
var ThisForm := Screen.ActiveForm; // works on any form in the application
for var i := 0 to ThisForm.ComponentCount - 1 do
begin
if ThisForm.Components[i] is TMenu then
begin
ThisMenuItem := TMenu(ThisForm.Components[i]).FindItem(aWParam, fkCommand);
if Assigned(ThisMenuItem) then
begin
Result := ThisMenuItem;
EXIT;
end;
end;
end;
end;
I use a custom listview component and I need it to have a popupmenu item "copy data to clipboard". If there is no assigned popup, I create one and add the menuitem, if there is already a menu assigned, add the item to the current popup. Tried to put the code in the constructor, but then I realized, that popupmenu is still not created or associated to my listview. So any idea when to create my default item?
constructor TMyListView.Create(AOwner: TComponent);
var
FpopupMenu: TPopupMenu;
begin
inherited;
.....
FPopUpMenuItem := TMenuItem.Create(self);
FPopUpMenuItem.Caption := 'Copy data to clipboard';
FPopUpMenuItem.OnClick := PopupMenuItemClick;
if assigned(PopupMenu) then begin
popupMenu.Items.Add(FPopUpMenuItem);
end
else begin
FpopupMenu := TPopupMenu.Create(self);
FpopupMenu.Items.Add(FPopUpMenuItem);
PopupMenu := FpopupMenu;
end;
...
end;
Override the virtual TControl.DoContextPopup() method, eg:
type
TMyListView = class(TListView)
protected
...
procedure DoContextPopup(MousePos: TPoint; var Handled: Boolean); override;
...
end;
procedure TMyListView.DoContextPopup(MousePos: TPoint; var Handled: Boolean);
var
LPopupMenu: TPopupMenu;
LItem: TMenuItem;
function IsSameEvent(const E1, E2: TNotifyEvent): Boolean;
begin
Result := (TMethod(E1).Code = TMethod(E2).Code) and
(TMethod(E1).Data = TMethod(E2).Data);
end;
begin
inherited DoContextPopup(MousePos, Handled);
if Handled then Exit;
LPopupMenu := PopupMenu;
if not Assigned(LPopupMenu) then
begin
LPopupMenu := TPopupMenu.Create(Self);
PopupMenu := LPopupMenu;
end;
for I := 0 to LPopupMenu.Items.Count-1 do
begin
LItem := LPopupMenu.Items[I];
if IsSameEvent(LItem.OnClick, PopupMenuItemClick) then
Exit;
end;
LItem := TMenuItem.Create(Self);
LItem.Caption := 'Copy data to clipboard';
LItem.OnClick := PopupMenuItemClick;
LPopupMenu.Items.Add(LItem);
end;
The accepted answer indeed works perfectly - unless you add keyboard shortcuts to your menu item. If you do, these won't work before the popup menu has been accessed in some other way, because the items will not have been created.
If you need shortcuts, it may therefore be preferable to move the code from DoContextPopup to Loaded. Most simply,
procedure Loaded; override;
...
procedure Loaded;
var
MI: TMenuItem;
ItemCovered: boolean;
i: integer;
begin
inherited;
if not Assigned(PopupMenu) then
PopupMenu:=TPopupMenu.Create(self);
ItemCovered:=false;
for i := 0 to PopupMenu.Items.Count-1 do
if IsSameEvent(PopupMenu.Items[I].OnClick, CopyDataToClipboardClick) then begin
ItemCovered:=true;
break;
end;
if not ItemCovered then begin
MI:=TMenuItem.Create(PopupMenu);
MI.Caption:='Copy data to clipboard';
MI.OnClick:=CopyDataToClipboardClick;
MI.ShortCut:=ShortCut(Ord('C'),[ssShift,ssCtrl]);
PopupMenu.Items.Add(MI);
end;
end;
This won't check for popup menus added on runtime, but probably serve most cases better.
I am using Delphi 10 Seattle to create multi device software (Win32) (Firemonkey).
How can I show a SearchBox only when the list is shown in a ComboBox.
I fill the ComboBox in code with ListBoxItems. See for an example below.
Now the SearchBox is displayed over a closed ComboBox.
procedure AddItems;
var
SearchBox: TSearchBox;
Item: TListBoxItem;
begin
ComboBox.Items.Clear;
SearchBox := TSearchBox.Create(ComboBox);
SearchBox.Align := TAlignLayout.Contents;
SearchBox.Parent := ComboBox;
SearchBox.Visible:=True;
Item := TListBoxItem.Create(ComboBox);
Item.Parent := ComboBox;
Item.Text := 'Item 1';
Item := TListBoxItem.Create(ComboBox);
Item.Parent := ComboBox;
Item.Text := 'Item 2';
end;
Use TComboBox events OnPopup & OnClosePopup.
Move ScrollBox declaration to private form (or frame) section and create it in OnCreate form event (or in frame constructor).
type
THeaderFooterForm = class(TForm)
procedure FormCreate(Sender: TObject);
private
FSearchBox: TSearchBox;
end;
procedure THeaderFooterForm.FormCreate(Sender: TObject);
begin
FSearchBox := TSearchBox.Create(nil);
FSearchBox.Align := TAlignLayout.Contents;
FSearchBox.Parent := ComboBox;
FSearchBox.Visible:=False;
end;
procedure THeaderFooterForm.ComboBoxClosePopup(Sender: TObject);
begin
FSearchBox.Visible:=False;
end;
procedure THeaderFooterForm.ComboBoxPopup(Sender: TObject);
begin
FSearchBox.Visible:=True;
end;
I have a form that has a list of useful procedures that I have created, that I often use in every project. I am adding a procedure that makes it simple to add a click-able image over where would be the TAccessory of a TListBoxItem. The procedure intakes the ListBox currently, but I would also need it to intake which procedure to call for the OnClick Event for the image.. Here is my existing code:
function ListBoxAddClick(ListBox:TListBox{assuming I need to add another parameter here!! but what????}):TListBox;
var
i : Integer;
Box : TListBox;
BoxItem : TListBoxItem;
Click : TImage;
begin
i := 0;
Box := ListBox;
while i <> Box.Items.Count do begin
BoxItem := Box.ListItems[0];
BoxItem.Selectable := False;
Click := Timage.Create(nil);
Click.Parent := BoxItem;
Click.Height := BoxItem.Height;
Click.Width := 50;
Click.Align := TAlignLayout.alRight;
Click.TouchTargetExpansion.Left := -5;
Click.TouchTargetExpansion.Bottom := -5;
Click.TouchTargetExpansion.Right := -5;
Click.TouchTargetExpansion.Top := -5;
Click.OnClick := // this is where I need help
i := +1;
end;
Result := Box;
end;
The desired procedure would be defined in the form that is calling this function.
Since the OnClick event is of type TNotifyEvent you should define a parameter of that type. Look at this (I hope self-explaining) example:
type
TForm1 = class(TForm)
Button1: TButton;
ListBox1: TListBox;
procedure Button1Click(Sender: TObject);
private
procedure TheClickEvent(Sender: TObject);
end;
implementation
procedure ListBoxAddClick(ListBox: TListBox; OnClickMethod: TNotifyEvent);
var
Image: TImage;
begin
Image := TImage.Create(nil);
// here is assigned the passed event method to the OnClick event
Image.OnClick := OnClickMethod;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
// here the TheClickEvent event method is passed
ListBoxAddClick(ListBox1, TheClickEvent);
end;
procedure TForm1.TheClickEvent(Sender: TObject);
begin
// do something here
end;