Recent

Author Topic: How can I register a TField descendant in the IDE?  (Read 2528 times)

DavidAM

  • Newbie
  • Posts: 6
How can I register a TField descendant in the IDE?
« on: July 21, 2025, 03:27:44 am »
I have written a TField descendant class (actually a TDateTimeField descendant). I created a Design-time package for it, and installed it, recompiling the IDE. It is the only thing in the package - 1 unit file, 1 class definition.

I added the package to my project and tried to use this new class. I have a TSqlQuery component on a form with the Dataset properly defined. (The form is working 100% before adding this class). I expected to be able to click a TDateTimeField in the Object Inspector, right-click and select "Change Class", then be able to change the field's class to my new TDateTimeField (descendant) class. The new class does not appear as a choice.

I added the new field's unit to the uses clause of the form, I changed the class references in the type definition, then opened the form's .lfm file and changed the references there. Then I rebuilt the application (Clean up and Build). The new class works 100% from code. However, I cannot use "Change Class" in the Object Inspector to switch a field to my new class. I can edit the one property I added to the class in the Object Inspector.

Is there some way to register the TField descendant so it is available to the Object Inspector's "Change Class" menu choice?

This is the entire unit file.
Code: Pascal  [Select][+][-]
  1. unit utmadcustomfields;
  2.  
  3. { This file defines a custom TDateTimeField to override the standard one. The
  4.   (initial) purpose is to select a DateTime from the Database - which is in UTC Time -
  5.   and use it as a Local Time value. I decided to try this approach
  6.   for the experience of overriding a registered class.
  7.  
  8.   I tried reintroducing GetData() - all GetAsXXXXX methods ultimately call it -
  9.   but the Ancestor class calls still go to the Acnestor's implementation. So,
  10.   I am overriding the GetAsXXXXX methods that TDateTime overrides:
  11.  
  12.   GetAsDateTime
  13.   GetAsFloat - calls GetAsDateTime - no overide necessary
  14.   GetAsString - calls GetText - copied implementation from fields.inc (with changes)
  15.   GetAsVariant - returns a TDateTime or Null
  16.  
  17.   SetAsDateTime
  18.   SetAsFloat - calls SetAsDateTime - no overide necessary
  19.   SetAsString - calls StrToDateTime -
  20.   SetVarValue - calls SetAsDateTime - no overide necessary
  21. }
  22.  
  23. {$mode objfpc}{$H+}
  24.  
  25. interface
  26.  
  27. uses
  28.   Classes, SysUtils, DB, DateUtils;
  29.  
  30. type
  31.  
  32.   { TMadUtcDateTimeField }
  33.  
  34.   { This class behaves just like TDateTimeField if ShowAsLocalTime is False
  35.     (default). When ShowAsLocalTime is True, the stored data is treated as
  36.     a UTC time and is converted To/From LocalTime. }
  37.   TMadUtcDateTimeField = class(TDateTimeField)
  38.     protected
  39.       FShowAsLocalTime: Boolean;
  40.  
  41.       function GetAsDateTime(): TDateTime; override;
  42.       procedure GetText(var AText: string; ADisplayText: Boolean); override;
  43.       function GetAsVariant(): variant; override;
  44.  
  45.       procedure SetAsDateTime(aValue: TDateTime); override;
  46.       procedure SetAsString(const AValue: string); override;
  47.  
  48.     published
  49.       { If True, the source data is considered to be a UTC datetime value.
  50.         GetAsXXXXXXX will convert it and return a Local time.
  51.         SetAsXXXXXXX will convert the provided value and store as a UTC time. }
  52.       property ShowAsLocalTime: Boolean read FShowAsLocalTime write FShowAsLocalTime;
  53.   end;
  54.  
  55. procedure Register;
  56.  
  57. implementation
  58.  
  59. procedure Register;
  60. begin
  61.   RegisterClass(TMadUtcDateTimeField);
  62. end;
  63.  
  64. { TMadUtcDateTimeField }
  65.  
  66. { Return the current value as a DateTime value. If SourceIsUTC is True, the value
  67.   is considered to be a UTC value and is (converted to and) returned as a Local value }
  68. function TMadUtcDateTimeField.GetAsDateTime(): TDateTime;
  69. begin
  70.   Result := inherited GetAsDateTime();
  71.  
  72.   if (FShowAsLocalTime) then
  73.     Result := UniversalTimeToLocal(Result);
  74. end;
  75.  
  76. { Copied from fields.inc - with changes}
  77. procedure TMadUtcDateTimeField.GetText(var AText: string; ADisplayText: Boolean);
  78. var
  79.   R : TDateTime;
  80.   F : String;
  81. begin
  82.   If Not GetData(@R,False) then
  83.     AText:=''
  84.   else
  85.     begin
  86.     // MAD - convert to Local
  87.     if (FShowAsLocalTime) then R := UniversalTimeToLocal(R);
  88.  
  89.     If (ADisplayText) and (Length(Self.DisplayFormat)<>0) then
  90.       F:=Self.DisplayFormat // MAD
  91.     else
  92.       Case DataType of
  93.        ftTime : F:=DefaultFormatSettings.LongTimeFormat;  // MAD
  94.        ftDate : F:=DefaultFormatSettings.ShortDateFormat; // MAD
  95.       else
  96.        F:='c'
  97.       end;
  98.     AText:=FormatDateTime(F,R);
  99.     end;
  100. end;
  101.  
  102. { inherited returns a TDateTime or Null. Convert if it is not Null }
  103. function TMadUtcDateTimeField.GetAsVariant(): variant;
  104. begin
  105.   Result := inherited GetAsVariant();
  106.  
  107.   if ((FShowAsLocalTime) and (Result <> Null)) then
  108.     Result := UniversalTimeToLocal(Result);
  109. end;
  110.  
  111. { Set the stored value as a DateTime value. If SourceIsUTC is True, the provided
  112.   value is considered to be a Local Time and is converted to UTC. }
  113. procedure TMadUtcDateTimeField.SetAsDateTime(aValue: TDateTime);
  114. begin
  115.   if (FShowAsLocalTime) then
  116.     aValue := LocalTimeToUniversal(aValue);
  117.  
  118.   inherited SetAsDateTime(aValue);
  119. end;
  120.  
  121. { inherited sets the value, then we convert the Value if FShowAsLocalTime is True }
  122. procedure TMadUtcDateTimeField.SetAsString(const AValue: string);
  123. begin
  124.   inherited SetAsString(AValue);
  125.  
  126.   if (AValue <> '') then
  127.     if (FShowAsLocalTime) then
  128.       Self.Value := LocalTimeToUniversal(Self.Value);
  129. end;
  130.  
  131. end.
  132.  


Environment:
Operating System: Win64
Processor Architecture: x86_64
Lazarus Version: 3.2.0.0
WidgetSet: win32
Compiler Version: 3.2.2

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12592
  • FPC developer.
Re: How can I register a TField descendant in the IDE?
« Reply #1 on: July 21, 2025, 10:41:26 am »
I'm not really a designtime expert, but afaik the .pas file of the package should pass the register procedure together with the package name using registerpackage ?

DavidAM

  • Newbie
  • Posts: 6
Re: How can I register a TField descendant in the IDE?
« Reply #2 on: July 29, 2025, 01:35:04 am »
Yeah, I checked on that. The RegisterPackage call and the RegisterUnit call are in the Package Source file that is automatically generated by Lazarus. It looks like that is all correct. The file I pasted in my post is a separate unit file in the package and is (I believe) properly referenced in the RegisterUnit call.

 

TinyPortal © 2005-2018