Windows 用我自己的上下文菜单替换TCustomEdit上下文菜单

Windows 用我自己的上下文菜单替换TCustomEdit上下文菜单,windows,delphi,Windows,Delphi,我想用我自己的弹出菜单(有很多操作)替换TCustomEdit组件(如TEdit或TMemo)中delphi显示的所有弹出菜单。到目前为止,我用自己的tpopumenu手动替换每个组件的PopUpMenu属性。但是我想知道我是否可以在不手动修改所有表单中每个组件的属性的情况下完成这项工作 我想要一个类似钩子的东西来拦截对这个系统菜单的调用,并替换为我自己的菜单。这可能吗 您可以为所有编辑控件分配一个OnContextPopup事件处理程序,让它调用tpopumenu的Popup()方法,并将事件

我想用我自己的弹出菜单(有很多操作)替换TCustomEdit组件(如TEdit或TMemo)中delphi显示的所有弹出菜单。到目前为止,我用自己的tpopumenu手动替换每个组件的PopUpMenu属性。但是我想知道我是否可以在不手动修改所有表单中每个组件的属性的情况下完成这项工作

我想要一个类似钩子的东西来拦截对这个系统菜单的调用,并替换为我自己的菜单。这可能吗


您可以为所有编辑控件分配一个
OnContextPopup
事件处理程序,让它调用
tpopumenu
Popup()
方法,并将事件的
Handled
参数设置为
True
。但这与直接将
TPopupMenu
分配给所有编辑控件没有太大区别


为了更进一步,您可以将单个
OnContextPopup
事件处理程序分配给您的父
TForm
,而不是单独的编辑控件。当鼠标调用菜单时,事件会告诉您鼠标坐标。您可以在这些坐标下找到子控件,如果它是您的编辑控件之一,则调用
Popup()
并将
Handled
设置为True。用户可以通过键盘调用菜单,在这种情况下,鼠标坐标将为
{-1,-1}
,因此使用
TScreen.ActiveControl
属性来了解正在调用哪个控件。

如果您的表单来自一个共同的祖先(而不是默认的
TForm
),例如
TMyBaseForm
,意思是
TForm1=class(TMyBaseForm)
这很容易做到。 在
TMyBaseForm.OnShow
事件中,您可以迭代它的控件,如果您找到
TEdit
TMemo
则可以动态设置它们的
弹出菜单
属性

另一种方法是使用
Screen.OnActiveFormChange
Screen.OnActiveControlChange
如果右键单击活动控件-编辑,则触发太迟:这仅适用于D5)在主窗体中,事件处理程序捕获活动窗体并在
屏幕中迭代。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
控件导致弹出菜单,请参阅
MyPopupMenu.popupmonent
(在
MyPopupMenu.OnPopup
事件中):


编辑:
屏幕。OnActiveControlChange
是我最初的想法。我已经在D5测试过了。如果Edit1处于焦点状态,我右键单击Edit2,它将首先弹出默认菜单,然后才成为活动控件。
我最终用D7和D2009测试了这个。两者都很好。这只是D5的一个问题,因此肯定是比使用
屏幕更好的解决方案。在主窗体中的活动表单更改中,添加以下代码。它应该应用于表单的所有自定义控件

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弹出窗口的说明


编辑:在PlaceEditor支持中添加网格

您可以直接在TEdit或TMemo类“
NewInstance
方法中安装的挂钩上执行弹出指定。使用这项技术,您只需在安装挂钩时添加一个附加装置。钩子的代码将为应用程序中创建的类
TEdit
TMemo
的每个组件的
PopupMenu
属性分配自定义tpopumenu对象

首先,在全局TDatamodule或主窗体中删除TPopupMenu对象。这里的关键点是,PopupMenu父级应该始终可用,并且应该是在应用程序初始化时创建的第一个父级,或者至少是在安装挂钩之前创建的父级

然后,创建一个空的新单元。随便你叫什么都行。在我的例子中,
popup\u 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.
unit popup\u赋值;
接口
使用Windows、stdctrl;
实施
使用globaldatamodule;//全局tpopumenu父项的单位
{------------------------------------------------------------------------------}
函数TEditNewInstance(AClass:TClass):TObject;
开始
结果:=TEdit.NewInstance;
TEdit(结果).PopupMenu:=global_datamodule.customedit_PopupMenu;// 其他可能性:

  • 使用可用的专家功能:

    • 使用CnPack属性校正器并定义在指定组件放置时提示您的操作
    • 使用GExperts重命名/替换组件(自定义控件的必需实现)

  • 最复杂-实现TForm子代开关和修改丢弃的控件PupupMenu属性

  • 丑陋但灵活且没有任何子代控件实现-使用以下过程:

    • 自定义弹出菜单(格式[TEdit,TMemo],MyPopupMenu)
    • 自定义弹出菜单(任意形式,[TEdit,TMemo],任意弹出菜单)


  • 过程自定义菜单(
    const-aCtrl:TWinControl;
    const aClasses:TControlClass的数组;
    const aPopUp:TPopupMenu);
    程序过程(const aCtrl:TWinControl;
    const aClasses:TControlClass的数组;const aPopUp:TPopupMenu);
    程序匹配(const aCtrl:t控制;
    const aClasses:TControlClass的数组;const aPopUp:TPopupMenu);
    变量
    Ix:整数;
    开始
    对于Ix:=低(aClasses)到高(aClasses)do
    开始
    如果aCtrl.InheritsFrom(aClasses[Ix]),则
    aCtrl.PopupMenu:=aPopUp;
    结束;
    结束;
    变量
    
    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.
    
    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;
    
    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;