2012-03-09 21 views
11

के साथ TCustomEdit संदर्भ मेनू को बदलें, मैं अपने स्वयं के पॉपअप मेनू (जिसमें बहुत अधिक क्रियाएं हैं) का उपयोग कर टीडीटीटी या टीडीएमओ जैसे टीसी कस्टमएडिट घटकों में डेल्फी द्वारा प्रदर्शित सभी पॉपअप मेनू को प्रतिस्थापित करना चाहते हैं। अब तक मैं अपने स्वयं के TPopUpMenu के साथ मैन्युअल रूप से प्रत्येक घटक की PopUpMenu प्रॉपर्टी को प्रतिस्थापित करता हूं। लेकिन मुझे आश्चर्य है कि क्या मैं अपने सभी रूपों में प्रत्येक घटक के लिए मैन्युअल रूप से इस संपत्ति को संशोधित किए बिना ऐसा कर सकता हूं।अपने स्वयं के

मुझे इस सिस्टम मेनू में कॉल को अवरुद्ध करने और अपने मेनू के लिए प्रतिस्थापित करने के लिए एक हुक की तरह कुछ चाहिए। क्या यह संभव है?

enter image description here

+0

क्यों न केवल TEdit और TMemo subclass और उन्हें एक संदर्भ मेनू साझा करें? –

+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 संबोधित करेंगे।

यह निर्धारित करने के लिए कि कौन से कस्टमएडिट पॉपअप लागू होते हैं, उनके निर्देश।

संपादित करें: जोड़े ग्रिड InplaceEditor समर्थन

+0

जैसा कि मैंने अपने उत्तर 'ऑनएक्टिव कंट्रोल चेंज' में उल्लेख किया है, बहुत देर हो चुकी है (* के बाद * संदर्भ मेनू पॉप-अप हो गया है), यदि आप पहले 'सक्रिय' के रूप में इसे सेट किए बिना 'TEDIT' पर राइट-क्लिक करें (आप इसे जांच सकते हैं अपने फॉर्म पर कई टीडीट डालें)। – kobik

+0

मैंने उनका परीक्षण किया है और उन्होंने बहुत अच्छी तरह से काम किया .... मैं बिना किसी कैप्शन के उन संपादन को राइट क्लिक कर सकता हूं और यह मेरे मेनू को पॉपअप करता है। क्या यह संस्करण के बीच भिन्न है? – Justmade

+0

@kobik मैं कोड का उपयोग करके टीडीआईटी बनाने के लिए कोड का उपयोग करके कोड का उपयोग करके, और अधिक संपादन जोड़ता हूं, स्ट्रिंगग्रिड जोड़ता हूं, मैं अन्य फॉर्म भी बना देता हूं, फिर मुख्य रूप और संपादन जोड़ता हूं। वे सभी अपना खुद का मेनू दिखाते हैं चाहे कोई नियंत्रण केंद्रित न हो या नहीं, जब मैं उन्हें राइट-क्लिक करता हूं। मैं मूल मेनू पॉपअप के साथ एक मामला अनुकरण नहीं कर सकता। – Justmade

5

आप संपादन नियंत्रण के सभी के लिए एक एकल OnContextPopup ईवेंट हैंडलर आवंटित कर सकते हैं, यह TPopupMenu की Popup() विधि कहते हैं, और True को घटना के Handled पैरामीटर सेट है। लेकिन यह सभी संपादन नियंत्रणों को सीधे TPopupMenu असाइन करने से बहुत अलग नहीं है।

इसे एक कदम आगे ले जाने के लिए, आप अलग-अलग संपादन नियंत्रणों के बजाय अपने माता-पिता TForm पर एकल OnContextPopup ईवेंट हैंडलर असाइन कर सकते हैं। जब माउस माउस द्वारा मेनू लगाया जाता है तो ईवेंट आपको माउस निर्देशांक बताता है। आप उन निर्देशांक के नीचे बाल नियंत्रण का पता लगा सकते हैं, और यदि यह आपके संपादन नियंत्रणों में से एक है तो Popup() पर कॉल करें और Handled को True पर सेट करें। उपयोगकर्ता कीबोर्ड के बजाय मेन्यू का आह्वान कर सकता है, इस मामले में माउस निर्देशांक {-1, -1} होगा, इसलिए TScreen.ActiveControl संपत्ति का उपयोग यह जानने के लिए करें कि कौन सा नियंत्रण लागू किया जा रहा है।

6

यदि आपके फॉर्म एक सामान्य पूर्वजों (डिफ़ॉल्ट TForm के बजाय) से निकले हैं उदाहरण के लिए TMyBaseForm, जिसका अर्थ है TForm1 = class(TMyBaseForm) यह आसान किया जा सकता है। TMyBaseForm.OnShow ईवेंट में आप इसके नियंत्रणों के माध्यम से पुन: प्रयास कर सकते हैं, और यदि आपको TEdit या TMemo मिलते हैं तो आप अपनी PopupMenu गतिशील रूप से संपत्ति सेट करते हैं। अपने मुख्य फार्म ईवेंट हैंडलर में सक्रिय रूप को पकड़ने और के माध्यम से पुनरावृति करने के लिए: -

एक और तरीका है (यह केवल D5 साथ यह सच है है संपादित करें यदि आप सक्रिय नियंत्रण पर राइट क्लिक करें बहुत देर हो चुकी Screen.OnActiveControlChange आग) Screen.OnActiveFormChange उपयोग करने के लिए है Screen.ActiveForm नियंत्रण और अपने कस्टम MyPopupMenu करने के लिए सेट TEdit या TMemo संपत्ति PopupMenu:

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 नियंत्रण की वजह से Popu pmenu पॉप-अप करने (MyPopupMenu.OnPopup घटना में) MyPopupMenu.PopupComponent का संदर्भ लें:

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 मेरी प्रारंभिक सोचा था। मैंने इसे डी 5 में परीक्षण किया है। अगर संपादन 1 केंद्रित है और मैं संपादन 2 पर राइट-क्लिक करता हूं, तो यह पहले डिफ़ॉल्ट मेनू पॉप-अप करेगा, केवल तभी यह सक्रिय नियंत्रण बन जाएगा। मैंने अंततः डी 7 और डी 200 9 के साथ इसका परीक्षण किया। दोनों ठीक काम करता है। यह डी 5 अंक केवल है इसलिए Justmade's answer निश्चित रूप से Screen.OnActiveFormChange का उपयोग करने से बेहतर समाधान है।

2

आप TEdit या TMemo क्लास 'NewInstance विधि में एक स्थापित हुक पर सीधे पॉपअप काम कर सकते हैं। इस tecnique के साथ आपको हुक स्थापित करने के साथ केवल एक अतिरिक्त इकाई शामिल करना होगा। हुक का कोड आपके कस्टम TPopupMenu ऑब्जेक्ट को PopupMenu क्लास TEdit और TMemo के प्रत्येक घटक की संपत्ति को आपके एप्लिकेशन में बनाएगा।

सबसे पहले, एक वैश्विक TDatamodule या अपने मुख्य फॉर्म में एक TPopupMenu ऑब्जेक्ट ड्रॉप करें। यहां मुख्य बिंदु यह है कि पॉपअपमेनू माता-पिता हमेशा उपलब्ध होना चाहिए और आपके आवेदन प्रारंभिकरण में बनाए गए पहले व्यक्ति बनें, या कम से कम हुक स्थापित होने से पहले।

फिर, एक खाली नई इकाई बनाएं। आप उसे जो चाहें कहें। मेरे मामले में 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 संपत्ति पढ़नेवाला और कार्रवाई आपके द्वारा निर्दिष्ट घटक ड्रॉप पर संकेत परिभाषित स्रोत यह है।
    • उपयोग GExperts नाम बदलें/अवयव वायदा (अपने कस्टम नियंत्रण के लिए जरूरी कार्यान्वयन) की जगह

  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 घटक जोड़ें। अपना खुद का पॉपअपमेनू (popupmenu1) बनाएं?

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 लिंक और यह सक्रिय होगा: TApplicationEvents घटक के OnMessage में निम्न कोड जोड़ें।

+2

एक जादुई संख्या '517 = 0x205 = WM_RBUTTONUP' की तुलना में संदेश स्थिरांक का उपयोग करना बेहतर होगा और आप निर्देशांक द्वारा कंसोल खोजने के बजाय' Ctrl: = FindControl (Msg.hwnd); 'का उपयोग कर सकते हैं ;-) – TLama

+1

पॉपअप मेनू को बुलाया जा सकता है कीबोर्ड द्वारा भी। – kobik

+0

मैंने 'Ctrl: = FindControl (Msg.hwnd) लागू किया; 'जो WM_KEYUP के लिए भी सक्षम है, इसलिए अब यह कीबोर्ड से आने पर भी काम करता है। मुझे पता है कि यह गंदा है लेकिन ग्राउंड अप से आपके आवेदन को पुनर्निर्माण किए बिना इसे करने का कोई वास्तविक साफ तरीका नहीं है। –

संबंधित मुद्दे