Leaderboard
Popular Content
Showing content with the highest reputation on 04/03/23 in all areas
-
TDate convertion for default (0) value to empty string (livebinding)
JohnF replied to JohnF's topic in RTL and Delphi Object Pascal
That was very helpful, better than the awesome documentation floating about; Things I learnt. The CustomFormat property on a TPrototypeBindSource field is not used when binding to a StringGrid, If you are binding to bothat a Grid and control, set the CustomFormat on the grid as well. I wrote a couple of procedure to populate the BindSource fields and the Grid columns. A bit of inspiration/plagiarism for the binding method 🙂 I hope someone may find it useful. Cheers ps: the const I used for replacing the Date with a "blank" currently say '<Blank>' just for proof of concept/testing purposes. Please change it/anything else as required. And If I have any brain farts in the code. Please let me know 🙂 unit my.System.BindUtil; interface uses System.Classes , System.SysUtils , System.Bindings.Methods , System.Bindings.EvalProtocol , System.Bindings.Consts , Data.Bind.Components , Data.Bind.Grid , Data.Bind.ObjectScope ; { https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Creating_Custom_LiveBindings_Methods } const _sID_BindMethod_BlankDateToString = 'BlankDateToString'; _sBlankDate = '<BLANK>'; _BlankInputDate = 0; _FormatBlankDateToString = _sID_BindMethod_BlankDateToString +'(%s)'; Type TBindingUtil = Class /// Class Procs for setting CustomFormats for TDate properties /// /// useful for the TProtoTypeBindSource and the TAdapterBindSource /// /// Use this to Map a Class to a Collection of FieldDef. It populates the /// BindSource fields and sets the CustomFormat for and TDate Fields /// /// TBindingUtil.MapFieldDefs(fListBindSource.FieldDefs, fImportClass); /// /// /// Once the Gridlink is established if you dont want to hand populate it /// and want to see all the fields, then call this. It populates the grid /// columns and sets the CustomFormat for and TDate Fields /// /// TBindingUtil.CreateGridColumns(FGridLink, 150); /// class Procedure MapFieldDefs (FieldDefs: TGeneratorFieldDefs; AClass:TClass); class Procedure CreateGridColumns (Link : TLinkGridToDataSource; DefWidth: Integer = 0); End; TBindMethods = Class class function MethodBlankDateToString: IInvokable; end; implementation uses REST.Response.Adapter , System.TypInfo , System.Rtti , Data.DB , Data.Bind.DBScope ; { TBindingUtil } class procedure TBindingUtil.CreateGridColumns(Link: TLinkGridToDataSource; DefWidth: Integer); var DS: TBaseLinkingBindSource; GenFieldDefs: TGeneratorFieldDefs; FieldDefs: TFieldDefs; P: TPrototypeBindSource; GFld: TGeneratorFieldDef; DBFldDef: Data.DB.TFieldDef; Clm: TLinkGridToDataSourceColumn; I: Integer; begin DS := Link.DataSource; FieldDefs := Nil; GenFieldDefs := Nil; If DS Is TPrototypeBindSource Then Begin GenFieldDefs := TPrototypeBindSource(DS).FieldDefs; End; /// /// If DS Is TAdapterBindSource Then begin if Assigned(TAdapterBindSource(DS).Adapter) then begin if TAdapterBindSource(DS).Adapter is TDataGeneratorAdapter then GenFieldDefs := TDataGeneratorAdapter(TAdapterBindSource(DS).Adapter).FieldDefs; end; end; If DS Is TBindSourceDB Then begin if Assigned(TBindSourceDB(DS).DataSet) then FieldDefs := TBindSourceDB(DS).DataSet.FieldDefs; end; /// Make sure we have a valid collection before we go forward /// if GenFieldDefs <> Nil then Begin Link.Columns.Clear; For GFld In GenFieldDefs Do Begin Clm := Link.Columns.Add; Clm.Header := GFld.Name; Clm.MemberName := GFld.Name; If DefWidth <> 0 Then Clm.Width := DefWidth Else Clm.Width := Link.DefaultColumnWidth; If (GFld.FieldType = TGeneratorFieldType.ftDate) Or ((GFld.FieldType = TGeneratorFieldType.ftDateTime)) Then Clm.CustomFormat := _FormatBlankDateToString End; End else if FieldDefs <> Nil then Begin Link.Columns.Clear; /// For DFld In FieldDefs Do /// /// Didnt work, for some reason FieldDef -> namedItem -> CollectionItem /// /// Cannot be seen as a CollectionItem ????? /// for I := 0 to FieldDefs.Count-1 do Begin DBFldDef := FieldDefs[I]; Clm := Link.Columns.Add; Clm.Header := DBFldDef.Name; Clm.MemberName := DBFldDef.Name; If DefWidth <> 0 Then Clm.Width := DefWidth Else Clm.Width := Link.DefaultColumnWidth; If (DBFldDef.DataType = TFieldType.ftDate) Or ((DBFldDef.DataType = TFieldType.ftDateTime)) Then Clm.CustomFormat := _FormatBlankDateToString End; End; End; class procedure TBindingUtil.MapFieldDefs(FieldDefs: TGeneratorFieldDefs; AClass: TClass); var Ctx: TRttiContext; Typ: TRttiType; Prps: TArray<TRttiProperty>; I: Integer; Prp: TRttiProperty; PrpTypInfo: PTypeInfo; F:TGeneratorFieldDef; X: Integer; begin if FieldDefs.Count = 0 then begin Ctx := TRttiContext.Create; Typ := Ctx.GetType(AClass); Prps := Typ.GetProperties; /// loop through the properties to add the fieldds to the /// fielddef collection. /// For I := 0 To Length(Prps) - 1 Do Begin Prp := Prps[I]; PrpTypInfo := Prp.PropertyType.Handle; /// if the Field does not exist, Add it and then fill in the name and /// read only property /// /// Dont use FieldDefs.Find(Prp.Name) as it throws a exception if it doesnt /// find anything, also it uses IndexOf anyway /// X := FieldDefs.IndexOf(Prp.Name); if X<0 then F := Nil else F := FieldDefs[I]; if not Assigned(F) then begin F := FieldDefs.AddFieldDef; F.Name := Prp.Name;; F.ReadOnly := not Prp.IsWritable; end; /// make sure the FieldType is set, if its a Date then set the custom format /// Case PrpTypInfo.Kind Of tkEnumeration, tkInteger, tkInt64: F.FieldType := TGeneratorFieldType.ftInteger; tkFloat: Begin F.FieldType := TGeneratorFieldType.ftSingle; If PrpTypInfo = System.TypeInfo(TDate) Then Begin F.FieldType := TGeneratorFieldType.ftDate; F.CustomFormat := _FormatBlankDateToString; End; If PrpTypInfo = System.TypeInfo(TTime) Then Begin F.FieldType := TGeneratorFieldType.ftTime; F.CustomFormat := _FormatBlankDateToString; End; If PrpTypInfo = System.TypeInfo(TDateTime) Then Begin F.FieldType := TGeneratorFieldType.ftDateTime; F.CustomFormat := _FormatBlankDateToString; End; End; tkVariant, tkChar, tkWChar, tkString, tkLString, tkWString, tkUString: F.FieldType := TGeneratorFieldType.ftString; End; End; End; End; { TBindMethods } class function TBindMethods.MethodBlankDateToString: IInvokable; begin Result := MakeInvokable( function(Args: TArray<IValue>): IValue var InputValue: IValue; OutputString: String; begin // Ensure only one argument is received. if Length(Args) <> 1 then raise EEvaluatorError.Create(sFormatArgError); // Ensure that the received argument is a Float and of Type Date/DateTime. InputValue := Args[0]; If InputValue.GetType.Kind = tkFloat Then Begin If (InputValue.GetType = System.TypeInfo(TDate)) Or (InputValue.GetType = System.TypeInfo(TDateTime)) Then Begin /// if the value is 0 then its blank and assign the output to be the blank string /// from the consts /// If InputValue.GetValue.AsExtended = 0 Then OutputString := _sBlankDate Else OutputString := DateToStr(InputValue.GetValue.AsExtended) End Else OutputString := InputValue.GetValue.ToString; End Else OutputString := InputValue.GetValue.ToString; // Return the output string. Result := TValueWrapper.Create(OutputString); End); end; initialization if not TBindingMethodsFactory.HasMethod(_sID_BindMethod_BlankDateToString) then TBindingMethodsFactory.RegisterMethod( { AMethodDescription : TMethodDescription } TMethodDescription.Create( { } TBindMethods.MethodBlankDateToString, { AInvokable : IInvokable } _sID_BindMethod_BlankDateToString, { AID : string } _sID_BindMethod_BlankDateToString, { Name : string } 'my.System.BindUtil', { AUnitName : string } true, { ADefaultEnabled : boolean } sFormatDesc, { ADescription : string } nil { AFrameWorkClass : TPersistentClass } ) { } ); finalization TBindingMethodsFactory.UnRegisterMethod(_sID_BindMethod_BlankDateToString); end. -
The position in Frederick, Maryland is about 20 miles from my house. For the last 4 years this company has been advertising remote Delphi Developer positions working for the Legal & General America Headquarters located at 3275 Bennett Creek Ave in Frederick, MD 21704, United States. Many days I would receive 6 calls a day from different headhunters looking to fill this position. Frequently the connection is very bad (voip) and the indian accent is very difficult to understand. I have interviewed with one of the vendors trying to fill the position, Diverse Lynx. Eventually, I cut off all communications with this company because they appeared to be human traffickers and completely untrustworthy. I am a grey beard as was mentioned in one of the earlier posts. I could do the job with very little effort. Something is very fishy about this position in Frederick, MD. They are looking to fill the position for $50/hour. Dick Maley
-
TDate convertion for default (0) value to empty string (livebinding)
programmerdelphi2k replied to JohnF's topic in RTL and Delphi Object Pascal
LiveBinding allow that you create yourself "Methods / Conversors" to use in your projects: Create a new package (DPK) to create your procedure, for example: unit uMyMethodInvokable; interface // more info: RAD Studio HELP SYSTEM: Creating Custom LiveBindings Methods implementation uses System.SysUtils, System.Bindings.Methods, System.Bindings.EvalProtocol, System.Bindings.Consts, System.TypInfo, // System.Rtti, System.MaskUtils, System.DateUtils; // function named: MakeMethod"XXXXXXXXXXX" convention LiveBinding!!! function MyMakeMethodDateAsStringEmpty: IInvokable; begin // return a "anonimous-function "Invokable" type... result := MakeInvokable( { } function(Args: TArray<IValue>): IValue var InputValue: IValue; // receive the value in "%s" passed InputString, OutputString: string; begin // ensure only one argument is received... not more or less!!! => "%s" ---> not "%s, %s" etc... if (Length(Args) <> 1) then raise EEvaluatorError.Create(sFormatArgError); // // ensure that the received argument InputValue := Args[0]; // // if not(InputValue.GetType.Kind in [tkString, tkWideString, tkWChar, tkWString]) then // raise EEvaluatorError.Create('Error 2: ' + sFormatExpectedStr); // // my output will be a "string" with Date text!!! if InputValue.GetType.Kind = tkFloat then begin OutputString := InputValue.GetValue.ToString; // if (OutputString = '30/12/1899') then OutputString := ''; end else OutputString := '01/01/1981'; // // return the output as "IValue", expected by LiveBinding calls!!! result := TValueWrapper.Create(OutputString); end); end; initialization TBindingMethodsFactory.RegisterMethod( { AMethodDescription : TMethodDescription } TMethodDescription.Create( { } MyMakeMethodDateAsStringEmpty, { AInvokable : IInvokable } 'MyDateAsStringEmptyCustom', { AID : string } 'MyDateAsStringEmptyCustom', { Name : string } 'uMyMethodInvokable', { AUnitName : string } true, { ADefaultEnabled : boolean } sFormatDesc, { ADescription : string } nil { AFrameWorkClass : TPersistentClass } ) { } ); finalization TBindingMethodsFactory.UnRegisterMethod('MyDateAsStringEmptyCustom' { AID : string } ); end. Now, build and install the new package ... "MyDateAsStringEmptyCustom" methods is avaliable for use in any project using LiveBindings, now! now, just use it in your LiveBinding field, like this: Vcl.StdCtrls {} , uMyMethodInvokable {my custom method} ; type TForm1 = class(TForm) PrototypeBindSource1: TPrototypeBindSource; BtnEditRecord: TButton; BindingsList1: TBindingsList; StringGridPrototypeBindSource1: TStringGrid; LinkGridToDataSourcePrototypeBindSource1: TLinkGridToDataSource; NavigatorPrototypeBindSource1: TBindNavigator; Label1: TLabel; procedure BtnEditRecordClick(Sender: TObject); procedure FormShow(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.BtnEditRecordClick(Sender: TObject); var LDate: TArray<TDate>; i : integer; begin LDate := [ StrToDate('30/12/1899'), StrToDate('30/12/1900') ]; // i := random(2); Label1.Caption := 'Current value: ' + DateToStr(LDate[i]); // PrototypeBindSource1.DataGenerator.Edit; PrototypeBindSource1.DataGenerator.Fields.Items[0].SetTValue(LDate[i]); PrototypeBindSource1.DataGenerator.Fields.Items[1].SetTValue(LDate[i]); end; procedure TForm1.FormShow(Sender: TObject); begin StringGridPrototypeBindSource1.DefaultColWidth := 200; end;