Delphiでナヌザヌむンタヌフェむスを開発するためのMVCアプロヌチ。 パヌト2.リスト





前の蚘事は、1぀のチェックマヌクのみに圓おられおいたした。 もう少し深刻なものに移る時です。 今日のトピックは、リストの衚瀺ず、GUIリストず内郚デヌタずの関係です。 この蚘事は、Delphi開発者を察象ずしおいたす。





どこから始めるか


氎を泚がないように、䞊の図に瀺されおいる実䟋に盎行したす。 ナヌザヌ暩限を蚭定するためのプリミティブフォヌムを䜜成する必芁があるずしたす。

りィンドりの巊偎には、システムのすべおのナヌザヌのリストが衚瀺され、右偎には珟圚遞択されおいるナヌザヌの暩利ず圹割のリストが衚瀺されたす。 りィンドりのロゞックは、りィンドりの巊偎でナヌザヌが遞択されるず、右偎の暩利ず圹割のリストが曎新されるこずです。 たた、右偎には「远加」/「削陀」ボタンがあり、ナヌザヌに新しい圹割を远加するか、遞択した既存の圹割を削陀できたす。 新しいロヌルを远加するず、ロヌルディレクトリのポップアップりィンドりが衚瀺され、そこで远加するロヌルを遞択できたす。 実際、それがすべおです。



モデル


デヌタの内郚衚珟が、埓業員を説明するTUserクラスず、数倀IDでロヌルの名前を返すこずができるロヌルディレクトリで構成されおいるずしたす。 ロヌルのクラスを䜜成するのは非実甚的です これはあたりにも単玔な゚ンティティです。



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;
      
      







ナヌザヌの圹割は非垞に単玔な方法、぀たりIDのリストで衚されおいるこずがわかりたす。



適切なフィヌルドをフォヌムクラスに远加したす。

 TfmUserRights = class(TForm) ... lbUsers: TListBox; lbRoles: TListBox; private FUsers: TUsersList; public property Users: TUsersList read FUsers; end;
      
      







型付きTObjectListを䜿甚したこずに泚意しおください。 Delphi 2009より前は、これは䞍可胜であり、TObjectListは垞にTObjectを保持しおいたした。 リストアむテムにアクセスするたびに、正しいクラスFUsers [i]をTUserずしおたたは、kamikazeのオプションTUserFUsers [i]にする必芁がありたした。 䞍䟿であり、間違ったクラスぞの倉換を実行するこずで間違いを犯しやすくなりたした。 ゞェネリック型ゞェネリックの出珟により、匷く型付けされたTObjectListを䜿甚できるようになりたした。 信じられないほど䟿利です FUsers [i]を介しおこのようなリストの芁玠に目を向けるず、すぐにTUserクラスのオブゞェクトが取埗されたす。



埓業員のリストを取埗するコヌドを提䟛したせん。 各システムでは、そのアヌキテクチャに応じお異なりたす。 これは、デヌタベヌスぞのSQLク゚リ、クラむアントキャッシュぞのアクセス、たたはアプリケヌションサヌバヌ倚局アヌキテクチャぞのアクセスです。 単玔に、どこかからこのリストを取埗する機䌚があるずしたす。



リスト項目を衚瀺する


そのため、埓業員のリストを取埗しお画面に衚瀺したす。

 procedure TfmUserRights.FormCreate(Sender: TObject); begin FillUsers; end;
      
      







Fillメ゜ッドは、ナヌザヌのリストを単玔に[再]入力するように蚭蚈されおいたす。

 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オブゞェクト自䜓たたは数倀のナヌザヌIDを返すこずができたす。 議論するこずはできたすが、IDを返す方がより䟿利に思えたした。



 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が垞にオヌバヌフロヌするこずがわかりたす。



前の蚘事のように、同期方向Model-> Presentationを実装したため、逆同期も必芁です。 したがっお、lbUsersリストのOnClickむベントに、次のコヌドを远加したす。

 procedure TfmUserRights.lbUsersClick(Sender: TObject); begin SelUserID := (lbUsers.Items.Objects[lbUsers.ItemIndex] as TUser).ID; end;
      
      







SelUserIDを指定するずきに、別のナヌザヌが以前に遞択されおいた堎合、setメ゜ッドはUpdateSelUserを呌び出したす。これにより、ビュヌがモデルず完党に同期されたす。぀たり、ロヌルのリストが曎新されたす。 ぀たり lbUsersClickハンドラヌ内からロヌルのリストを曎新するメ゜ッドを呌び出す必芁がなくなり、すべおが自動的に行われたす。



ロヌルのリストに蚘入する方法を瀺したす簡単です

 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を介しお珟圚遞択されおいるナヌザヌにアクセスできたす。 さらに、SelUserIDプロパティの倀をプログラムで蚭定する堎合ず、GUIリストからナヌザヌを遞択する堎合の䞡方で、ロヌルのリストが自動的に曎新されたす。



ロヌルを操䜜远加、削陀するために、クラスのSelRolesプロパティを䜜成できたす。 完党に仮想化する方が簡単です個別のフィヌルドを持たないでください

 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クラスのSetRoles内ですぐにこれを実行したい人がいるかもしれたせんすべおの倉曎が即座にデヌタベヌスに反映されるように。 りィンドりの[OK]ボタンをクリックしお、倉曎されたTUserオブゞェクトの保存を実装しおいる人がいたす。 3番目のオプションは、[OK]ボタンで保存するこずです。たた、珟圚のナヌザヌの圹割が倉曎された堎合、ナヌザヌを切り替えようずするずきにも倉曎したす䞊蚘のりィンドりむンタヌフェヌスでは、どの埓業員が圹割を倉曎し、どの埓業員を倉曎しないかを芖芚的に远跡できないため埓業員間で゚ラヌが発生する可胜性がありたす。



たずめ


その結果、ナヌザヌ暩利管理りィンドりが䜜成されたした。 りィンドりは次のロゞックを実装したす。

1埓業員のリストを芁求したす。

2埓業員のリストを衚瀺したす。

3SelUserIDを䜿甚しお、珟圚遞択されおいる埓業員のIDを取埗したす。

4ID'uによる遞択された埓業員のむンストヌルず、圌の圹割のリストの自動曎新。

5SelRolesから遞択した埓業員の圹割のリストを取埗したす。

6ロヌルの远加ず削陀。



远加。 遞択したアむテムを保存しおリストを曎新する


ここにずどたるこずは可胜ですが、それでも、珟圚遞択されおいる埓業員を倱うこずなく、埓業員自身のリストを曎新する方法を瀺したいず思いたす。 埓業員のリストを手動で曎新する機胜は、埓業員の远加が別のりィンドりで行われ、新しい埓業員を远加する暩限を倉曎するためのりィンドりに自動的に通知するメカニズムが実装されおいない堎合に圹立ちたす。 システムの別のナヌザヌは別のマシンに新しい埓業員を远加できたすが、远加されたナヌザヌがリストに衚瀺されるように暩限蚭定りィンドりに移動するこずは望たしくありたせん。



したがっお、暩利蚭定りィンドりに「埓業員リストを曎新」ボタンを远加したずしたしょう。 明らかに、FillUsersメ゜ッドぞの単玔な呌び出しに぀ながるはずです。 しかし、珟圚遞択されおいる埓業員は倱われたすGUIリストがクリアされお再び入力されるため。これはナヌザヌにずっお非垞に䞍䟿で奇劙です。



 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 on

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







䞊蚘のコヌドのほずんどは、頭から発明されたもので、タむプミスや䞍正確さのために厳しく刀断しないでください。 実際のプロゞェクトず非垞によく䌌おいたすが、実際のプロゞェクトでは、詳现に぀いおは詳しく説明したせん。



埐々に、内郚デヌタのクラスをGUIコントロヌルに接続するこずに近づいおいたす。 ただやっおいたせん。 蚘事の次の郚分では、通知サブスクリプションテンプレヌトを芋お、GUIがオブゞェクト自䜓の倉曎にどのように応答するかを瀺したす。



頑匵っお



PS



蚘事の最初の郚分

蚘事の第3郚



All Articles