I want to replace all the popup menus displayed by delphi in the TCustomEdit components like TEdit or TMemo using my own popup menu (which has a lot of more actions). So far I replace the PopUpMenu property of each component manually with my own TPopUpMenu. but I wondering if I can do this without modify this property manually for each component in all my forms.
I want something like a hook to intercept the calls to this system menu and replace for my own menu. is this possible?
If your Forms derives from a common ancestor (rather than the default TForm
) for example TMyBaseForm
, meaning TForm1 = class(TMyBaseForm)
this could be done easy.
In the TMyBaseForm.OnShow
event you could iterate through it's controls, and if you find a TEdit
or TMemo
you set their PopupMenu
property dynamically.
Another way is to use Screen.OnActiveFormChange
(Screen.OnActiveControlChange
fires too late if you right-click on the active control - EDIT: This is True only with D5) in your main Form event handler to capture the active form and iterate through the Screen.ActiveForm
controls and set TEdit
or TMemo
property PopupMenu
to your custom MyPopupMenu
:
procedure TForm1.FormCreate(Sender: TObject);
begin
Screen.OnActiveFormChange := ActiveFormChange;
end;
procedure TForm1.ActiveFormChange(Sender: TObject);
begin
CustomEditControlsNormalize(Screen.ActiveForm);
end;
type
TCustomEditAccess = class(TCustomEdit);
procedure TForm1.CustomEditControlsNormalize(F: TForm);
var
I: Integer;
begin
if not Assigned(F) then Exit;
for I := 0 to F.ComponentCount - 1 do
if F.Components[I] is TCustomEdit then
TCustomEditAccess(F.Components[I]).Popupmenu := MyPopupMenu;
end;
To determine which TCustomEdit
control caused the Popupmenu to pop-up refer to the MyPopupMenu.PopupComponent
(in the MyPopupMenu.OnPopup
event):
procedure TForm1.MyPopupMenuPopup(Sender: TObject);
begin
if MyPopupMenu.PopupComponent is TCustomEdit then
begin
FEditPopupControl := TCustomEdit(MyPopupMenu.PopupComponent);
Caption := FEditPopupControl.Name; // debug :-P
end;
end;
EDIT: Screen.OnActiveControlChange
was my initial thought. I have tested it in D5. if Edit1 is focused and I right-click on Edit2, it will first pop-up the default menu, only then it becomes the active control.
I finally tested this with D7 and D2009. both works just fine. This is a D5 issue only so Justmade's answer is surely a better solution than using Screen.OnActiveFormChange
.
In your main form, add the following code. It should apply to all your form's custom control.
TForm2 = class(TForm)
procedure FormCreate(Sender: TObject);
private
procedure ActiveControlChanged(Sender: TObject);
end;
implementation
type
TCustomEditAccess = class(TCustomEdit);
TCustomGridAccess = class(TCustomGrid);
procedure TForm2.ActiveControlChanged(Sender: TObject);
begin
if (Screen.ActiveControl is TCustomEdit) and not Assigned(TCustomEditAccess(Screen.ActiveControl).PopupMenu) then
TCustomEditAccess(Screen.ActiveControl).PopupMenu := MyPopupMenu
else if (Screen.ActiveControl is TCustomGrid) then
begin
TCustomGridAccess(Screen.ActiveControl).ShowEditor;
if Assigned(TCustomGridAccess(Screen.ActiveControl).InplaceEditor) then
TCustomEditAccess(TCustomGridAccess(Screen.ActiveControl).InplaceEditor).PopupMenu := MyPopupMenu;
end;
end;
procedure TForm2.FormCreate(Sender: TObject);
begin
Screen.OnActiveControlChange := ActiveControlChanged;
end;
It is just a simplified version (in the point of view of coding) of kobik's answer and will also address any TCustomEdit that are created by code or other complex Controls which do not use the Form as Owner.
His instruction on how to determine which CustomEdit popup apply.
Edit : Add Grid InplaceEditor Support
You could assign a single OnContextPopup
event handler to all of the edit controls, have it call the Popup()
method of the TPopupMenu
, and set the event's Handled
parameter to True
. But that is not much different than just assigning the TPopupMenu
to all of the edit controls directly.
To take it a step further, you could assign a single OnContextPopup
event handler to your parent TForm
instead of the individual edit controls. The event tells you the mouse coordinates when the menu is being invoked by mouse. You can locate the child control underneath those coordinates, and if it is one of your edit conrols then call Popup()
and set Handled
to True. The user can invoke menus by keyboard instead, in which case the mouse coordinates will be {-1, -1}
, so use the TScreen.ActiveControl
property to know which control is being invoked on.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With