我想使用我自己的弹出菜单(它有很多动作)替换TCustomEdit组件中的delphi显示的所有弹出菜单,如TEdit或TMemo。到目前为止,我用我自己的TPopUpMenu手动替换每个组件的PopUpMenu属性。但是我想知道如果不为我的所有表单中的每个组件手动修改此属性,我是否可以这样做。
我想要一个钩子来截取对这个系统菜单的调用并替换我自己的菜单。这有可能吗?
答案 0 :(得分:6)
如果您的表单来自共同的祖先(而不是默认的TForm
),例如TMyBaseForm
,则意味着TForm1 = class(TMyBaseForm)
这可以轻松完成。
在TMyBaseForm.OnShow
事件中,您可以迭代它的控件,如果您找到TEdit
或TMemo
,则可以动态设置其PopupMenu
属性。
另一种方法是使用Screen.OnActiveFormChange
(Screen.OnActiveControlChange
如果您右键单击活动控件 - 编辑:仅在D5 时为真main Form事件处理程序,用于捕获活动表单并遍历Screen.ActiveForm
控件并将TEdit
或TMemo
属性PopupMenu
设置为您的自定义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;
要确定哪个TCustomEdit
控件导致Popupmenu弹出,请参考MyPopupMenu.PopupComponent
(MyPopupMenu.OnPopup
事件中):
procedure TForm1.MyPopupMenuPopup(Sender: TObject);
begin
if MyPopupMenu.PopupComponent is TCustomEdit then
begin
FEditPopupControl := TCustomEdit(MyPopupMenu.PopupComponent);
Caption := FEditPopupControl.Name; // debug :-P
end;
end;
编辑: Screen.OnActiveControlChange
是我最初的想法。我在D5测试了它。如果Edit1聚焦并且我右键单击Edit2,它将首先弹出默认菜单,然后它才成为活动控件。
我终于用D7和D2009进行了测试。两者都很好。这只是 D5问题,因此Justmade's answer肯定是比使用Screen.OnActiveFormChange
更好的解决方案。
答案 1 :(得分:5)
您可以为所有修改控件分配一个OnContextPopup
事件处理程序,让它调用Popup()
的{{1}}方法,并设置事件的TPopupMenu
参数到Handled
。但这与仅直接将True
分配给所有编辑控件没有多大区别。
为了更进一步,您可以为父TPopupMenu
分配一个OnContextPopup
事件处理程序,而不是单独的编辑控件。该事件告诉您鼠标调用菜单时的鼠标坐标。您可以在这些坐标下找到子控件,如果它是您的编辑控件之一,则调用TForm
并将Popup()
设置为True。用户可以通过键盘调用菜单,在这种情况下鼠标坐标将是Handled
,因此使用{-1, -1}
属性来了解正在调用哪个控件。
答案 2 :(得分:5)
在主窗体中,添加以下代码。它应该适用于您所有表单的自定义控件。
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;
它只是kobik答案的简化版本(在编码方面),并且还将解决由代码或其他不使用表单作为所有者的复杂控件创建的任何TCustomEdit。
他关于如何确定应用哪个CustomEdit弹出窗口的说明。
编辑:添加Grid InplaceEditor支持
答案 3 :(得分:2)
您可以直接在TEdit或TMemo类'NewInstance
方法中的已安装挂钩上执行弹出窗口分配。有了这个tecnique,你只需要包含一个额外的单元来安装钩子。钩子的代码会将您的自定义TPopupMenu对象分配给应用程序中创建的类PopupMenu
和TEdit
的每个组件的TMemo
属性。
首先,将TPopupMenu对象放在全局TDatamodule或主窗体中。这里的关键点是PopupMenu父项应该始终可用,并且是应用程序初始化时创建的第一个父项,或者至少在安装该挂钩之前。
然后,创建一个空的新单元。随便打电话给你。就我而言popup_assignment.pas
。来源是:
unit popup_assignment;
interface
uses Windows, StdCtrls;
implementation
uses globaldatamodule; // Unit of global TPopupMenu parent
{------------------------------------------------------------------------------}
function TEditNewInstance(AClass: TClass): TObject;
begin
Result := TEdit.NewInstance;
TEdit(Result).PopupMenu := global_datamodule.customedit_popupmenu; // <- your global TPopupMenu component !!!
end;
function TMemoNewInstance(AClass: TClass): TObject;
begin
Result := TMemo.NewInstance;
TMemo(Result).PopupMenu := global_datamodule.customedit_popupmenu; // <- your global TPopupMenu component !!!
end;
function GetVirtualMethod(AClass: TClass; const VmtOffset: Integer): Pointer;
begin
Result := PPointer(Integer(AClass) + VmtOffset)^;
end;
procedure SetVirtualMethod(AClass: TClass; const VmtOffset: Integer; const Method: Pointer);
var
WrittenBytes: DWORD;
PatchAddress: PPointer;
begin
PatchAddress := Pointer(Integer(AClass) + VmtOffset);
WriteProcessMemory(GetCurrentProcess, PatchAddress, @Method, SizeOf(Method), WrittenBytes);
end;
{$IFOPT W+}{$DEFINE WARN}{$ENDIF}{$WARNINGS OFF} // no compiler warning
const
vmtNewInstance = System.vmtNewInstance;
{$IFDEF WARN}{$WARNINGS ON}{$ENDIF}
var
orgTEditNewInstance: Pointer;
orgTMemoNewInstance: Pointer;
initialization
orgTEditNewInstance := GetVirtualMethod(TEdit, vmtNewInstance);
orgTMemoNewInstance := GetVirtualMethod(TMemo, vmtNewInstance);
SetVirtualMethod(TEdit, vmtNewInstance, @TEditNewInstance);
SetVirtualMethod(TMemo, vmtNewInstance, @TMemoNewInstance);
finalization
SetVirtualMethod(TEdit, vmtNewInstance, OrgTEditNewInstance);
SetVirtualMethod(TMemo, vmtNewInstance, orgTMemoNewInstance);
end.
答案 4 :(得分:2)
将TApplicationEvents组件添加到delphi应用程序中。 制作自己的popupmenu(popupmenu1)? 在TApplicationEvents组件的OnMessage中,添加以下代码:
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
var
ctrl: TWincontrol;
begin
if (msg.Message = WM_RBUTTONUP) or (msg.Message = WM_KEYUP ) then begin
ctrl := FindControl(Msg.hwnd);
if ctrl <> nil then begin
if ((ctrl is TEdit)) then begin
(ctrl as TEdit).PopupMenu := Popupmenu1;
end;
if ((ctrl is TMemo)) then begin
(ctrl as TMemo).PopupMenu := Popupmenu1;
end;
end;
end;
end;
这将截取右键单击,如果当时在鼠标光标下有一个TEdit或TMemo,它会将popupmenu链接到该组件并触发它。
答案 5 :(得分:1)
其他可能性:
使用可用的专家功能:
最复杂的 - 实现TForm后代女巫design time drag and drop并修改掉落的控件PupupMenu属性。
丑陋但灵活,没有任何后代控制实施 - 请使用以下程序:
procedure CustomizePopupMenu(
const aCtrl: TWinControl;
const aClasses: array of TControlClass;
const aPopUp: TPopupMenu);
procedure Process(const aCtrl: TWinControl;
const aClasses: array of TControlClass; const aPopUp: TPopupMenu);
procedure Match(const aCtrl: TControl;
const aClasses: array of TControlClass; const aPopUp: TPopupMenu);
var
Ix: Integer;
begin
for Ix := Low(aClasses) to High(aClasses) do
begin
if aCtrl.InheritsFrom(aClasses[Ix]) then
aCtrl.PopupMenu:= aPopUp;
end;
end;
var
Ix: Integer;
Ctrl: TControl;
begin
for Ix := 0 to Pred(aCtrl.ControlCount) do
begin
if aCtrl.Controls[Ix] is TWinControl then
Process(TWinControl(aCtrl.Controls[Ix]), aClasses, aPopUp);
Match(aCtrl.Controls[Ix], aClasses, aPopUp)
end;
end;
begin
if (aCtrl <> nil) and (Length(aClasses) > 0) and (aPopUp <> nil) then
Process(aCtrl, aClasses, aPopUp)
end;