डेल्फी में उपयोगकर्ता इंटरफेस विकसित करने के लिए एमवीसी दृष्टिकोण। भाग 2. सूचियाँ



पिछला लेख सिर्फ एक चेकमार्क के लिए समर्पित था। यह समय कुछ और गंभीर होने का है। आज का विषय सूचियों की प्रस्तुति और आंतरिक डेटा के साथ GUI सूचियों का संबंध है। यह लेख डेल्फी डेवलपर्स के लिए है।


कहां से शुरू करें

पानी न डालने के लिए, मैं सीधे ऊपर दिए गए चित्र में दिखाए गए लाइव उदाहरण पर जाऊंगा। मान लीजिए कि आपको उपयोगकर्ता अधिकारों की स्थापना के लिए एक आदिम रूप बनाने की आवश्यकता है।
विंडो के बाएं हिस्से में, सिस्टम के सभी उपयोगकर्ताओं की एक सूची प्रदर्शित होती है, और दाईं ओर - वर्तमान में चयनित उपयोगकर्ता के अधिकारों और भूमिकाओं की एक सूची। विंडो का तर्क यह है कि जब उपयोगकर्ता को विंडो के बाएं हिस्से में चुना जाता है, तो दाएं हिस्से में अधिकारों और भूमिकाओं की सूची अपडेट की जाती है। इसके अलावा दाईं ओर "जोड़ें" / "हटाएं" बटन हैं, जो या तो उपयोगकर्ता को एक नई भूमिका जोड़ने या चयनित मौजूदा भूमिकाओं को हटाने की अनुमति देता है। नई भूमिकाएँ जोड़ते समय, भूमिका निर्देशिका की एक पॉप-अप विंडो प्रकट होती है, जिसमें आप जोड़ने के लिए भूमिकाओं का चयन कर सकते हैं। वह, वास्तव में, सब है।

आदर्श

मान लीजिए कि डेटा के आंतरिक प्रतिनिधित्व में कर्मचारी और भूमिका निर्देशिका का वर्णन करने वाले ट्यूसर वर्ग शामिल हैं, जो एक संख्यात्मक आईडी द्वारा भूमिका का नाम वापस कर सकते हैं। भूमिकाओं के लिए कक्षाएं बनाना अव्यावहारिक है क्योंकि यह बहुत आसान इकाई है:

uses Generics.Collections; //      TObjectList type TIntList = array of Integer; //        TUser = class strict private FID: Integer; FFullFio: String; FRoles: TIntList; public property ID: Integer read FID; property FullFio: String read FFullFio; property Roles: TIntList read FRoles write SetRoles; end; TUsersList = class(TObjectList<TUser>) public function UserByID(const aID: Integer): TUser; end; 


यह देखा जा सकता है कि उपयोगकर्ता भूमिकाओं को एक बहुत ही सरल तरीके से दर्शाया जाता है - आईडी की एक सूची।

प्रपत्र फ़ील्ड में उपयुक्त फ़ील्ड जोड़ें:
 TfmUserRights = class(TForm) ... lbUsers: TListBox; lbRoles: TListBox; private FUsers: TUsersList; public property Users: TUsersList read FUsers; end; 


ध्यान दें कि मैंने एक टाइप किए गए TOBjectList का उपयोग किया। डेल्फी 2009 से पहले, यह संभव नहीं था और टोबेक्लिस्ट ने हमेशा टोबिज को रखा। हर बार जब मैंने सूची आइटम को एक्सेस किया, तो मुझे इसे सही वर्ग में लाना था: फ़्यूज़र्स [i] ट्यूसर के रूप में (अच्छी तरह से, या कामिकेज़ के लिए एक विकल्प: ट्यूसर (फ़्यूज़र [i]))। यह असुविधाजनक था और गलत वर्ग में रूपांतरण करके गलती करना आसान था। जेनेरिक प्रकार (जेनेरिक) के आगमन के साथ, अब आप एक जोरदार टाइपबिजलिस्ट का उपयोग कर सकते हैं। यह अविश्वसनीय रूप से सुविधाजनक है! फ़्यूज़र्स [i] के माध्यम से ऐसी सूची के तत्वों की ओर मुड़ने पर हमें तुरंत TUser क्लास की एक वस्तु मिलती है।

मैं कर्मचारियों की सूची प्राप्त करने के लिए कोड नहीं दूंगा, क्योंकि प्रत्येक प्रणाली में, इसकी वास्तुकला के आधार पर, यह अलग होगा। यह डेटाबेस के लिए एक SQL क्वेरी, कुछ क्लाइंट कैश तक पहुंच या एप्लिकेशन सर्वर तक पहुंच सकता है (एक बहु स्तरीय वास्तुकला में)। मान लीजिए, बस, कि आपके पास इस सूची को कहीं से प्राप्त करने का अवसर है।

सूची आइटम प्रदर्शित करें

इसलिए, हम कर्मचारियों की एक सूची प्राप्त करना चाहते हैं और इसे स्क्रीन पर प्रदर्शित करना चाहते हैं:
 procedure TfmUserRights.FormCreate(Sender: TObject); begin FillUsers; end; 


भरण विधि को केवल [पुनः] उपयोगकर्ताओं की एक सूची बनाने के लिए डिज़ाइन किया गया है:
 procedure TfmUserRights.FillUsers; var i: Integer; begin FUsers.Free; //   ,    FUsers := GetUsers; lbUsers.Items.BeginUpdate; try lbUsers.Items.Clear; for i := 0 to Users.Count-1 do lbUsers.AddItem(FUsers[i].FullFio, FUsers[i]); //         FUsers[i], //          ID' (  , ) finally lbUsers.Items.EndUpdate; end; end; 


केवल कर्मचारियों की सूची भरना ही पर्याप्त नहीं है। आपको वर्तमान में चयनित कर्मचारी की भूमिकाएँ भी दर्शानी होंगी। और इसके लिए आपको यह जानने की जरूरत है कि वर्तमान में किस कर्मचारी का चयन किया जाए? अनुभवहीन प्रोग्रामर विभिन्न स्थानों से lbUsers.Items.Objects [lbUsers.ItemIndex] से सक्रिय रूप से संपर्क करना शुरू करते हैं। हालांकि, यदि आप लेख के पिछले हिस्से को पढ़ते हैं, तो आप पहले से ही महसूस करते हैं कि हम दूसरे रास्ते पर जाएंगे। हम फॉर्म क्लास पर एक संपत्ति स्थापित करेंगे जो वर्तमान में चयनित कर्मचारी को लौटाता है और सेट करता है। आप या तो TUser ऑब्जेक्ट या स्वयं एक संख्यात्मक उपयोगकर्ता आईडी वापस कर सकते हैं। यह मुझे आईडी वापस करने के लिए अधिक सुविधाजनक लग रहा था, हालांकि यह तर्क दिया जा सकता है।

 TfmUserRights = class(TForm) private FSelUserID: Integer; public property SelUserID: Integer read FSelUserID write SetSelUserID; end; procedure TfmUserRights.SetSelUserID(const Value: Integer); begin if FSelUserID <> Value then begin FSelUserID := Value; UpdateSelUser; // !!! end; end; 


यहां मुख्य बिंदु UpdateSelUser विधि है, जो इंटरफ़ेस को उस स्थिति में लाता है जिसमें निर्दिष्ट उपयोगकर्ता का चयन किया जाता है:

 procedure TfmUserRights.UpdateSelUser; var vSelInd: Integer; i: Integer; begin vSelInd := -1; with lbUsers do for i := 0 to Items.Count-1 do if (Items.Objects[i] as TUser).ID = SelUserID then begin vSelInd := i; Break; end; lbUsers.ItemIndex := vSelInd; if SelUserID <= 0 then gbRoles.Caption := '  :' else gbRoles.Caption := '  : ' + Users.UserByID(SelUserID).FullFio FillUserRoles; // !!! end; 


हम देखते हैं कि वर्तमान उपयोगकर्ता की स्थापना विधि हमेशा भूमिकाओं की सूची (FillUserRoles) अतिप्रवाह का कारण बनती है।

पिछले लेख की तरह, जब से हमने सिंक्रोनाइज़ेशन दिशा को लागू किया है मॉडल-> प्रस्तुति, हमें रिवर्स सिंक्रोनाइज़ेशन की भी आवश्यकता है। इसलिए, lbUser सूची के OnClick इवेंट में, निम्न कोड जोड़ें:
 procedure TfmUserRights.lbUsersClick(Sender: TObject); begin SelUserID := (lbUsers.Items.Objects[lbUsers.ItemIndex] as TUser).ID; end; 


SelUserID को निर्दिष्ट करते समय, यदि कोई अन्य उपयोगकर्ता पहले चुना गया था, तो सेट विधि UpdateSelUser को कॉल करेगा, जो बदले में मॉडल के साथ दृश्य को पूरी तरह से सिंक्रनाइज़ करेगा, अर्थात्, यह भूमिकाओं की सूची को अपडेट करेगा। यानी मुझे अब lbUsers के भीतर से भूमिकाओं की सूची को अपडेट करने की विधि को कॉल करने की आवश्यकता नहीं है, सब कुछ अपने आप हो जाएगा।

मैं भूमिकाओं की सूची भरने के लिए एक विधि दूंगा (यह तुच्छ है):
 procedure TfmUserRights.FillUserRoles; var i: Integer; vSelUser: TUser; begin lbRoles.BeginUpdate; try lbRoles.Clear; if SelUserID <= 0 then Exit; vSelUser := Users.UserByID(SelUserID); for i := 0 to High(vSelUser.Roles) do lbRoles.AddItem(DictRoles.NameByID(vSelUser.Roles[i]), TObject(vSelUser.Roles[i])); //            ,   ID',     TObject' ( ) finally SomeList.EndUpdate; end; end; 


मैं सूची में पहले उपयोगकर्ता को आरंभ करने के साथ फॉर्म आरंभीकरण कोड को पूरक करूंगा:
 procedure TfmUserRights.FormCreate(Sender: TObject); begin FillUsers; FSelUserID := -2; // ,   Set- SelUserID := -1; //       end; 


हमें क्या मिला? अब आप SelUserID के माध्यम से वर्तमान चयनित उपयोगकर्ता तक पहुँच सकते हैं। इसके अलावा, दोनों जब प्रोग्रामर संपत्ति के मूल्य को प्रोग्रामेटिक रूप से सेट करते हैं, और जब कोई उपयोगकर्ता GUI सूची के माध्यम से चुना जाता है, तो भूमिकाओं की सूची स्वचालित रूप से अपडेट हो जाएगी।

भूमिकाओं (ऐड, डिलीट) के साथ काम करने के लिए, आप क्लास की सेलरॉल्स प्रॉपर्टी बना सकते हैं। इसे पूरी तरह से आभासी बनाना आसान है (इसके लिए अलग फ़ील्ड नहीं है):
 property SelRoles: TIntList read GetSelRoles write SetSelRoles; function TfmUserRights.GetSelRoles: TIntList; var i: Integer; begin Result := nil; for i := 0 to lbRoles.Items.Count-1 do if lbRoles.Selected[i] then AddIntToList(Integer(lbRoles.Items.Objects[i]), Result); //    ?     Objects' //   ,  ID' ,      Integer end; procedure TfmReportMain.SetSelRoles(const aSelRoles: TIntList); var i: Integer; begin lbRoles.Items.BeginUpdate; try for i := 0 to lbRoles.Items.Count-1 do lbRoles.Selected[i] := IntInList(Integer(lbRoles.Items.Objects[i]), aSelRoles); finally lbRoles.Items.EndUpdate; end; UpdateSelRoles; //      .     ,  ,   " N "     -  end; 


IntInList और AddIntToList क्रमशः एक तत्व के प्रवेश को एक सरणी में जाँचते हैं और सरणी में एक नया तत्व जोड़ते हैं।

भूमिका जोड़ना और हटाना

रोल्स जोड़ना:
 procedure TfmUserRights.btAddRoleClick(Sender: TObject); var vSelUser: TUser; vRoles: TIntList; vAddRoles: TIntList; i: Integer; begin vAddRoles := nil; vAddRoles := TfmDictionary.GelDictIDs(DictRoles); //   ID'       vSelUser := Users.UserByID(SelUserID); vRoles := vSelUser.Roles; for i := 0 to High(vAddRoles) do AddIntToList(vAddRoles[i], vRoles); vSelUser.Roles := vRoles; //           (  ) SelRoles := vAddRoles; end; 

हटाने भूमिकाएँ:
 procedure TfmUserRights.btDelRoleClick(Sender: TObject); var vSelUser: TUser; vDelRoles: TIntList; vRoles: TIntList; vNewRoles: TIntList; i, vInd: Integer; begin if lbAllowRightsRoles.SelCount = 0 then raise Exception.Create('     .'); vDelRoles := SelRoles; vSelUser := Users.UserByID(SelUserID); vRoles := vSelUser.Roles; SetLength(vNewRoles, Length(vRoles)); //     //  vNewRoles    ,       vInd := 0; for i := 0 to High(vRoles) do begin if IntInList(vRoles[i], vDelRoles) then Continue; vNewRoles[vInd] := vRoles[i]; inc(vInd); end; SetLength(vNewRoles, vInd); //     vSelUser.Roles := vNewRoles; end; 


यह तय करना है कि डेटाबेस में TUser ऑब्जेक्ट में परिवर्तनों को कहाँ सहेजना है। कोई इसे तुरंत करना चाह सकता है, ठीक TUser वर्ग के सेटरॉल्स के अंदर (ताकि सभी परिवर्तन तुरंत डेटाबेस में दिखाई दें)। किसी ने विंडो में ओके बटन पर क्लिक करके टीयूजर ऑब्जेक्ट्स को संशोधित किया है। तीसरा विकल्प ओके बटन द्वारा सहेजना है, और उपयोगकर्ताओं के बीच स्विच करने की कोशिश करते समय भी, यदि वर्तमान उपयोगकर्ता की भूमिकाएं बदल गई हैं (क्योंकि उपरोक्त विंडो इंटरफ़ेस आपको नेत्रहीन ट्रैक करने की अनुमति नहीं देता है कि कौन से कर्मचारी भूमिकाएं बदल चुके हैं और जो स्विच नहीं करते हैं। एक कर्मचारी से दूसरे में, जो एक त्रुटि हो सकती है)।

परिणाम

परिणाम उपयोगकर्ता अधिकार प्रबंधन विंडो थी। विंडो निम्नलिखित तर्क को लागू करती है:
1) कर्मचारियों की एक सूची का अनुरोध करें।
2) कर्मचारियों की एक सूची प्रदर्शित करें।
3) सेल्यूसरिड के माध्यम से वर्तमान में चयनित कर्मचारी की आईडी प्राप्त करना।
4) आईडी'यू द्वारा चयनित कर्मचारियों की स्थापना उनकी भूमिकाओं की सूची के स्वचालित अपडेट के साथ।
5) सेलरोल्स के माध्यम से चयनित कर्मचारी भूमिकाओं की एक सूची प्राप्त करना।
6) भूमिकाओं को जोड़ना और हटाना।

अनुपूरक। चयनित आइटम को सहेजने के साथ सूची को अद्यतन करना

यहां रहना संभव होगा, लेकिन फिर भी मैं यह दिखाना चाहता हूं कि आप मौजूदा चयनित कर्मचारी को खोए बिना खुद कर्मचारियों की सूची को कैसे अपडेट कर सकते हैं। कर्मचारियों की सूची को मैन्युअल रूप से अपडेट करने की कार्यक्षमता उपयोगी हो सकती है यदि कर्मचारियों को किसी अन्य विंडो के माध्यम से जोड़ा जाता है, और नए कर्मचारी को जोड़ने के अधिकारों को बदलने के लिए विंडो को स्वचालित रूप से सूचित करने के लिए तंत्र लागू नहीं किया जाता है। सिस्टम का एक अन्य उपयोगकर्ता किसी अन्य मशीन पर एक नया कर्मचारी जोड़ सकता है, लेकिन आप अधिकार सेटिंग विंडो में नहीं जाना चाहते हैं ताकि सूची में जोड़ा गया उपयोगकर्ता दिखाई दे।

तो, मान लें कि आपने अधिकार सेटिंग विंडो में "कर्मचारियों की सूची ताज़ा करें" एक और बटन जोड़ा है। जाहिर है, इसे फिलूजर्स विधि के लिए एक सरल कॉल करना चाहिए। लेकिन फिर वर्तमान चयनित कर्मचारी खो जाएगा (क्योंकि जीयूआई सूची को फिर से साफ़ किया जाएगा और फिर से भरा जाएगा), जो उपयोगकर्ता के लिए बहुत असुविधाजनक और अजीब होगा।

 procedure TfmUserRights.FillUsers; var i: Integer; vSavedSelUserID: Integer; begin //        if SelUserID > 0 then vSavedSelUserID := SelUserID else vSavedSelUserID := -1; ... //   FUsers    ... //     if vSavedSelUserID > 0 then begin FSelUserID := -1; SelUserID := vSavedSelUserID; end else SelUserID := -1; end; 


भविष्य में, और भी अधिक आवश्यक हो सकता है: अधिकार सेटिंग्स विंडो में बार-बार प्रविष्टियों के बीच या एप्लिकेशन सत्रों के बीच चुने गए अंतिम कर्मचारी को याद करना। इस स्थिति में, आप FillUsers में एक पैरामीटर जोड़ सकते हैं जो यह निर्धारित करता है कि सूची के पुनर्निर्माण के बाद किस उपयोगकर्ता को तैनात किया जाना चाहिए। इस मामले में, वर्तमान उपयोगकर्ता को याद रखने का तर्क थोड़ा जटिल होगा:
 procedure TfmUserRights.FillUsers(const aSelUserID: Integer = -1); var i: Integer; vNeedSelUserID: Integer; begin if aSelUserID > 0 then //     ,    vNeedSelUserID := aSelUserID else if SelUserID > 0 then //     -    vNeedSelUserID := SelUserID else vNeedSelUserID := -1; ... //   FUsers    ... if vNeedSelUserID > 0 then begin FSelUserID := -1; SelUserID := vNeedSelUserID; end else SelUserID := -1; end; 


इस स्थिति में, FormCreate बदल जाएगा
 procedure TfmUserRights.FormCreate(Sender: TObject); begin FillUsers(Config.RightsFormSavedUserID); end; 


और FormDestroy पर
 procedure TfmUserRights.FormCreate(Sender: TObject); begin Config.RightsFormSavedUserID := SelUserID; end; 


उपरोक्त कोड में से अधिकांश का आविष्कार सिर से किया गया था, टाइपोस और अशुद्धियों के लिए बहुत मुश्किल का न्याय नहीं करते हैं। यह एक वास्तविक परियोजना के समान है, लेकिन वास्तव में एक वास्तविक परियोजना में बहुत अधिक विवरण हैं जो मैं अभी के बारे में बात नहीं करना चाहता हूं।

धीरे-धीरे, मैं जीयूआई नियंत्रण के साथ आंतरिक डेटा की कक्षाओं को जोड़ने के करीब पहुंच रहा हूं। मैंने अभी तक ऐसा नहीं किया है। लेख के अगले भाग में, मैं अधिसूचना सदस्यता टेम्प्लेट को देखूंगा और दिखाऊंगा कि कैसे जीयूआई वस्तुओं में परिवर्तन का जवाब दे सकता है।

सौभाग्य है

पुनश्च

लेख का पहला भाग
लेख का तीसरा भाग

Source: https://habr.com/ru/post/In147198/


All Articles