2012-03-09 4 views
11

Я хочу заменить все всплывающие меню, отображаемые delphi в компонентах TCustomEdit, таких как TEdit или TMemo, используя мое собственное всплывающее меню (в котором много действий). Пока я заменяю свойство PopUpMenu каждого компонента вручную своим собственным TPopUpMenu. но мне интересно, могу ли я сделать это без изменения этого свойства вручную для каждого компонента во всех моих формах.Заменить контекстное меню TCustomEdit с моим собственным

Я хочу что-то вроде крюка, чтобы перехватить вызовы в это системное меню и заменить для моего собственного меню. Это возможно?

enter image description here

+0

Почему бы не просто подкласс TEdit и TMemo и поделиться им с контекстным меню? –

+3

Если вы показали, как это сделать, @Warren, я ожидаю, что это попадет в сферу приемлемых ответов на этот вопрос. –

ответ

4

В вашей основной форме, пожалуйста, вводите код. Он должен применяться ко всем пользовательским элементам вашей формы.

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.

Edit: Добавление поддержки Сетка InplaceEditor

+0

Как я уже упоминал в своем ответе 'OnActiveControlChange' срабатывает слишком поздно (* после * контекстное меню всплывает), если вы щелкните правой кнопкой мыши на' TEdit', не устанавливая сначала его как активный элемент управления (вы можете проверить его на введя несколько TEdits в вашу форму). – kobik

+0

Я тестировал их, и они работали очень хорошо .... Я могу прямо щелкнуть правой кнопкой мыши эти изменения без заголовка, и это всплывающее меню. Разве это отличается от версии? – Justmade

+0

@kobik Я добавляю гораздо больше исправлений, используя код для создания TEdit, используя Panel как владельца, добавляя stringgrid, я также создаю другие формы, а затем главную форму и добавляю изменения. Все они отображают мое собственное меню независимо от того, какой контроль сосредоточен или нет, когда я нажимаю на них правой кнопкой мыши. Я не могу имитировать случай с исходным всплывающим меню. – Justmade

5

Вы можете назначить один обработчик OnContextPopup событий для всех элементов управления редактирования, у него вызвать Popup() метод TPopupMenu и установите параметр Handled мероприятия, чтобы True. Но это не сильно отличается от простого назначения TPopupMenu всем элементам управления редактирования.

Чтобы сделать это еще дальше, вы можете назначить одному обработчику событий OnContextPopup родительскому объекту TForm вместо отдельных элементов управления редактирования. Событие указывает координаты мыши, когда мышь вызывается меню. Вы можете найти дочерний элемент управления под этими координатами, и если это один из ваших править, позвоните по номеру Popup() и установите для Handled значение True. Вместо этого пользователь может вызывать меню с помощью клавиатуры, и в этом случае координаты мыши будут {-1, -1}, поэтому используйте свойство TScreen.ActiveControl, чтобы узнать, на какой элемент управления вызывается.

6

Если ваши формы получены от общего предка (а не по умолчанию TForm), например TMyBaseForm, что означает TForm1 = class(TMyBaseForm), это можно сделать легко. В мероприятии TMyBaseForm.OnShow вы можете перебирать элементы управления, и если вы найдете или TMemo, вы можете установить их свойство PopupMenu динамически.

Другой способ заключается в использовании Screen.OnActiveFormChange (Screen.OnActiveControlChange пожары слишком поздно, если вы щелкните правой кнопкой мыши на активном управлении - EDIT: Это верно только с D5) в вашей основной обработчик формы событий, чтобы захватить активную форму и перебирать в 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.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; 

EDIT:Screen.OnActiveControlChange была моя первая мысль. Я тестировал его в D5. если Edit1 сфокусирован, и я нажимаю правой кнопкой мыши на Edit2, он сначала всплывает по умолчанию, только тогда он становится активным элементом управления. Я наконец испытал это с D7 и D2009. оба работают отлично. Это выпуск D5 только, поэтому Justmade's answer, безусловно, лучшее решение, чем использование Screen.OnActiveFormChange.

2

Вы можете выполнить задание Всплывающее непосредственно на установленный крюк в TEdit или метод ТМето класса 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. 
+0

Единичный код создает крючок для каждого объекта TEdit или TMemo в вашем приложении. Вы можете легко добавить несколько классов. –

1

Другие возможности:

  1. Используйте доступные функциональные возможности: Эксперты

    • Использование CnPack Свойство Corrector и определить действия, которые предложат вам на указанных каплях компоненты.
    • Использование GExperts Rename/Заменить Components фьючерсы (обязательно выполнение пользовательских элементов управления)

  2. Самое сложное - реализовать TFORM потомок ведьму design time drag and drop и изменить упал контролирует PupupMenu свойство.

  3. некрасивым, но гибкий и без какого-либо потомка реализации элементов управления - использовать ниже процедуры:

    • CustomizePopupMenu (форма, [TEdit, TMemo], MyPopupMenu)
    • CustomizePopupMenu (AnyForm, [TEdit, TMemo], AnyPopupMenu)


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; 
2

Добавить компонент TApplicationEvents в ваше приложение delphi. Сделайте свое собственное popupmenu (popupmenu1)? В OnMessage компонента TApplicationEvents добавить следующий код:

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; 

Это будет перехватывать RightClick и если в это время существует TEdit или TMemo под MouseCursor он соединит PopupMenu к этому компоненту и огонь его.

+2

Лучше будет использовать константы сообщений, чем магические числа '517 = 0x205 = WM_RBUTTONUP', и вы можете использовать' Ctrl: = FindControl (Msg.hwnd); 'вместо поиска в конструкциях по координатам ;-) – TLama

+1

Всплывающее меню можно вызвать на клавиатуре тоже. – kobik

+0

Я реализовал 'Ctrl: = FindControl (Msg.hwnd);' который также включил для просмотра WM_KEYUP, так что теперь он также работает при вызове с клавиатуры. Я знаю, что это грязно, но нет реального чистого способа сделать это, не перестраивая приложение с нуля. –

Смежные вопросы