レコードデータをJSON形式で読み書きできたら
あとはリスト化して管理することになります。
リスト化するという事で通常はTListを使うところですが、
今回はTDictionaryを使ってみようかと思います。
ジェネリックス関数を利用することになるので Uses節に
System.Generics.Defaults,System.Generics.Collections
が必要になります。後々のために Rtti も追加しておくと幸せになれます。
さて、
基本的な考え方として、それぞれのリスト項目にIDを振り、
項目にアクセスするためには、そのIDを通じて行う。
とします。そのリスト格納部分にTDictionaryを使用します。
すべてのレコード形式に対応するようにジェネリックス関数を利用するわけですが、
その準備段階としてレコード形式に関係しないベースとなるクラスを作成します。
とりあえずソースを。これは今使っている簡易的な物です。
コピペなのでこのままでは動かないかもしれませんが、
雰囲気を感じ取ってください。
ゆくゆくは整備していきたいと思いますが、今はとりあえずこれで。
(宣言部) TNYBaseList=class private FModified:boolean; FVST:TVirtualStringTree; //--------------------------- // Property Access Method //--------------------------- function FGetCount:integer; virtual; abstract; protected //------------------------------------------ // 表示用リスト //------------------------------------------ FDispList:TList; //表示されているリストのIDを列挙 public constructor Create; destructor Destroy;override; //File IO function SaveRecordToJSON:string; virtual; abstract; procedure LoadRecordFromJSON(val:string); virtual; abstract; //Filter procedure Filter; virtual; abstract; procedure FilterEx(idx:integer;val:TValue); virtual; abstract; //Sort //procedure Sort; property DispIDs:TList read FDispList; //Modified property Modified:boolean read FModified; //Count property Count:integer read FGetCount; //フィルタリング用のVst property VST:TVirtualStringTree read FVST write FVST; end; (実装部) constructor TNYBaseList.Create; begin FDispList:=TList .Create; FModified:=False; end destructor TNYBaseList.Destroy; begin FreeAndNil(FDispList); inherited; end;
で、実際の格納クラスはこちら。
(宣言部) const _SetNewRecordID =-1; _MaintainRecordID=-2; //--------------------------------------------------------------------- // 具体的なレコードを格納するクラス //--------------------------------------------------------------------- // ※イベントはレコードサイドで実装する // このクラスではイベントとして接続する //--------------------------------------------------------------------- // ■ イベント設定例 // with StyleContainer do begin // StyleList:=Lists[AddList]; // with StyleList do begin // OnGetRecordID :=TNYFormStyleRec.GetID; // OnSetRecordID :=TNYFormStyleRec.SetID; // OnSaveToJson :=TNYFormStyleRec.SaveToJson; // OnLoadFromJson:=TNYFormStyleRec.LoadFromJson; // end; // end; // // あるいは レコード側で // class procedure TNY_XXXXRec.SetEvent(List:TNYRecList); // begin // with List do begin // OnGetRecordID :=GetID; // OnSetRecordID :=SetID; // OnSaveToJson :=SaveToJson; // OnLoadFromJson:=LoadFromJson; // end; // end; // みたいな感じでもいいかも // //--------------------------------------------------------------------- TNYRecList =Class(TNYBaseList) type TNYGetRecID= reference to function(Val:T):integer; TNYSetRecID= reference to function(Val:T;ID:integer):T; TNYToJSon = reference to function(Serializer:TJsonSerializer;Val:T):string; TNYFromJSon= reference to function(Val:string;Serializer:TJsonSerializer):T; TNYRecFilterEV=reference to function (Sender:TObject;RecData:T;Mode:integer;Val:TValue):boolean; private //------------------------- // データ格納領域 //------------------------- FDic:TDictionary ; // ID=Val FFilterVal:TValue; FFilterIdx:integer; //------------------------- // DataStoker Pointer //------------------------- FCategoryTree:TNY_ExCategoryVST; //------------------------- // EventHandler Pointer //------------------------- FOnGetRecordID :TNYGetRecID; FOnSetRecordID :TNYSetRecID; FOnSaveToJson :TNYToJSon; FOnLoadFromJson:TNYFromJSon; FOnFilter :TNYRecFilterEV; function FGetCount:integer; override; function FGetRecord(ID:integer):T; function FGetRecFromIndex(index:integer):T; procedure FSetIndexList; //フィルタ用 あとでPublicに public constructor Create; destructor Destroy;override; function GetNewID:integer; //RecordAccess procedure Clear; function SetRecord(Id:integer;Val:T;EV:TNYSetRecID=nil;Ev2:TNYGetRecID=nil):integer; procedure DeleteRecord(ID:integer); //File IO function SaveRecordToJSON:string; override; procedure LoadRecordFromJSON(Val:string); override; //Filter procedure Filter; override; procedure FilterEx(idx:integer;val:TValue); override; //----------------- // 基本プロパティ //----------------- // 【ID指定】によるレコード取得 property Records[ID:integer]:T read FGetRecord; default; // 【インデックス指定】によるレコード取得 property RecFromIndex[index:integer]:T read FGetRecFromIndex; // データ本体へのアクセス property Dictionary:TDictionary read FDic; //------------------------- // DataStoker Property //------------------------- property CategoryTree:TNY_ExCategoryVST read FCategoryTree write FCategoryTree; property FilterVal:TValue read FFilterVal write FFilterVal; property FilterIdx:integer read FFilterIdx write FFilterIdx; //----------------- // EventProperty //----------------- property OnGetRecordID :TNYGetRecID read FOnGetRecordID write FOnGetRecordID; property OnSetRecordID :TNYSetRecID read FOnSetRecordID write FOnSetRecordID; property OnSaveToJson :TNYToJSon read FOnSaveToJson write FOnSaveToJson; property OnLoadFromJson:TNYFromJSon read FOnLoadFromJson write FOnLoadFromJson; property OnFilter :TNYRecFilterEV read FOnFilter write FOnFilter; end; (実装部) constructor TNYRecList .Create; begin inherited; FDic:=TDictionary .Create; end; destructor TNYRecList .Destroy; begin //-------------------------------------- // Tはレコードなので開放の必要がない //-------------------------------------- FreeAndNil(FDic); inherited; end; //------------------------------------------------------------------------ procedure TNYRecList .Clear; begin FDic.Clear; FModified:=False; end; function TNYRecList .GetNewID:integer; var id:integer; begin Result:=1; for id in FDic.Keys do begin if id>=Result then Result:=id+1; end; end; function TNYRecList .FGetRecord(ID:integer):T; var BL:boolean; begin //要修正:ID不存在の際の戻り値が不定のため //OK//BL:=(ID<0)or(ID>FList.Count-1); if not NP_DispErrMes('ID Not Exists.',FDic.ContainsKey(ID)) then exit; Result:=FDic[ID]; end; function TNYRecList .FGetRecFromIndex(index:integer):T; begin Result:=FDic[FDispList[Index]]; end; function TNYRecList .FGetCount:integer; begin Result:=FDic.Count; end; procedure TNYRecList .FSetIndexList; var id:integer; begin FDispList.Clear; for id in FDic.Keys do FDispList.Add(id); FDispList.Sort; end; procedure TNYRecList .DeleteRecord(ID:integer); var idx:integer; begin if not FDic.ContainsKey(ID) then exit; FDic.Remove(ID); //filter FSetIndexList; //// 要修正 //ModifiedSet FModified:=True; end; function TNYRecList .SetRecord(Id:integer;Val:T;EV:TNYSetRecID=nil;Ev2:TNYGetRecID=nil):integer; var idx,aID:integer; begin (*----------------------------------------------------------------- //EVの具体的な内容 : コピーでOK class function GetID(Val:TNYFormStyleRec):integer; static; class function SetID(Val:TNYFormStyleRec;ID:integer):TNYFormStyleRec; static; class function TNYFormStyleRec.GetID(Val:TNYFormStyleRec):integer; begin Result:=Val.ID; end; class function TNYFormStyleRec.SetID(Val:TNYFormStyleRec;ID:integer):TNYFormStyleRec; begin Result:=Val; Result.ID:=ID; end; -----------------------------------------------------------------*) if not Assigned(EV) then EV:=FOnSetRecordID; if not NP_DispErrMes('イベントが設定されていません(OnSetRecordID)',Assigned(EV)) then exit; if not Assigned(EV2) then EV2:=FOnGetRecordID; if not NP_DispErrMes('イベントが設定されていません(OnGetRecordID)',Assigned(EV2)) then exit; //------------- // 戻り値はID //------------- case id of _MaintainRecordID :Result:=EV2(Val); //ID変更せず(TのIDを取得) _SetNewRecordID..0:Result:=GetNewID; //新規ID設定 (基礎クラスのIDリスト追加) else begin //------------------------------------- // IDを指定する場合 // ⇒ 同一レコードの書き換えが前提 // ⇒ IDの変更は想定外 //------------------------------------- //指定IDを戻り値に設定 Result:=ID; end; end; //-------------------------------------------------------- // レコードのフィールド「ID」を設定するイベント // Result ← IDが入ってきている //-------------------------------------------------------- if id<>_MaintainRecordID then Val:=EV(Val,Result); //-------------------------------------------------------------------- // 追加 OR 上書き //-------------------------------------------------------------------- FDic.AddOrSetValue(Result,Val); //filter FSetIndexList; //// 要修正 //Modified設定 FModified:=True; end; function TNYRecList .SaveRecordToJSON:string; var i:integer; serializer: TJsonSerializer; begin (*----------------------------------------------------------------- //EVの具体的な内容 : コピーでOK class function SaveToJson(serializer:TJsonSerializer;Rt:TNYFormStyleRec):string; static; class function TNYFormStyleRec.SaveToJson(serializer:TJsonSerializer;Rt:TNYFormStyleRec):string; begin Result:=serializer.Serialize(Rt); end; -----------------------------------------------------------------*) Result:=''; //if not Assigned(EV) then EV:=FOnSaveToJson; if not NP_DispErrMes('イベントが設定されていません(OnSaveToJson)',Assigned(FOnSaveToJson)) then exit; serializer := TJsonSerializer.Create; try for i in FDic.Keys do Result:=Result+FOnSaveToJson(serializer,FDic[i])+_CRLF; FModified:=False; finally FreeAndNil(serializer); end; end; procedure TNYRecList .LoadRecordFromJSON(Val:string); var i:integer; s:string; serializer: TJsonSerializer; begin (*----------------------------------------------------------------- //EVの具体的な内容 : コピーでOK class function LoadFromJson(Val:string;serializer:TJsonSerializer):TNYFormStyleRec; static; class function TNYFormStyleRec.LoadFromJson(Val:string;serializer:TJsonSerializer):TNYFormStyleRec; begin serializer.Populate(Val,Result); end; -----------------------------------------------------------------*) //if not Assigned(EV) then EV:=FOnLoadFromJson; if not NP_DispErrMes('イベントが設定されていません(OnLoadFromJson)',Assigned(FOnLoadFromJson)) then exit; FDic.Clear; serializer := TJsonSerializer.Create; try try while Val<>'' do begin s:=NP_CutFirst(Val,_CRLF); SetRecord(_MaintainRecordID,FOnLoadFromJson(s,serializer)); end; FSetIndexList; //// FModified:=False; except NP_DispErrMes('データの読込時にエラーが発生しました'); end; finally FreeAndNil(serializer); end; end; procedure TNYRecList .FilterEx(idx:integer;val:TValue); begin FFilterIdx:=idx; FFilterVal:=Val; Filter; end; procedure TNYRecList .Filter; var id:integer; Dt:T; begin //------------------------------------------------------------------------------ // フィルタの使用条件 // あらかじめパラメータを設定しておく // フィルタ項目:フィールド文字列でフィルタするか、カテゴリでフィルタするか // フィルタ対象:文字列フィールド、VST // フィルタ内容:フィルタ文字列、カテゴリID //------------------------------------------------------------------------------ //FDispListクリア FDispList.Clear; //フィルタイベントがない場合は全表示 if not Assigned(FOnFilter) then begin FSetIndexList; exit; end; //フィルタリング for id in FDic.Keys do begin if FOnFilter(Self,FDic[id],FFilterIdx,FFilterVal) then FDispList.add(id); end; //VSTカウント設定 FVST.SetRowCountFocus(FDispList.Count); end;
TNYBaseListについて
基本クラスです。内容は以下。
・変更状態の保持
・表示用IDのリストの保持
・抽象メソッドの定義
・SaveRecordToJSON
・LoadRecordFromJSON
・Filter
・プロパティ
・DispIDs 表示用IDリスト
・Modified 変更状態
・Count 登録されている項目数
・VST 表示用コンポーネント(現時点では TVirtualStringTree を想定)
表示用IDリストというのはリスト内の項目のうち、
リスト表示用コンポーネント上に表示する項目のIDを列挙したものです。
フィルタリングをした後の項目というとわかるでしょうか。
表示用にIDリストを用意しておきます。
これは項目の表示にはTVirtualStringTreeコンポーネントの利用を想定しているので
この様な形になっています。
レコードリストの本体はTNYRecList<T>てレコード型 T を指定して生成します。
TNYRecList<T>について
(以下準備中)