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

时间:2012-03-09 00:52:49

标签: windows delphi

我想使用我自己的弹出菜单(它有很多动作)替换TCustomEdit组件中的delphi显示的所有弹出菜单,如TEdit或TMemo。到目前为止,我用我自己的TPopUpMenu手动替换每个组件的PopUpMenu属性。但是我想知道如果不为我的所有表单中的每个组件手动修改此属性,我是否可以这样做。

我想要一个钩子来截取对这个系统菜单的调用并替换我自己的菜单。这有可能吗?

enter image description here

6 个答案:

答案 0 :(得分:6)

如果您的表单来自共同的祖先(而不是默认的TForm),例如TMyBaseForm,则意味着TForm1 = class(TMyBaseForm)这可以轻松完成。 在TMyBaseForm.OnShow事件中,您可以迭代它的控件,如果您找到TEditTMemo,则可以动态设置其PopupMenu属性。

另一种方法是使用Screen.OnActiveFormChangeScreen.OnActiveControlChange如果您右键单击活动控件 - 编辑:仅在D5 时为真main Form事件处理程序,用于捕获活动表单并遍历Screen.ActiveForm控件并将TEditTMemo属性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.PopupComponentMyPopupMenu.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对象分配给应用程序中创建的类PopupMenuTEdit的每个组件的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)

其他可能性:

  1. 使用可用的专家功能:

    • 使用CnPack属性更正器并定义提示您指定组件丢弃的操作。
    • 使用GExperts重命名/替换组件期货(需要实施自定义控件)

  2. 最复杂的 - 实现TForm后代女巫design time drag and drop并修改掉落的控件PupupMenu属性。

  3. 丑陋但灵活,没有任何后代控制实施 - 请使用以下程序:

    • CustomizePopupMenu(Form,[TEdit,TMemo],MyPopupMenu)
    • CustomizePopupMenu(AnyForm,[TEdit,TMemo],AnyPopupMenu)


  4. 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;