Programming is fun. Sometimes projects and apps are more serious, sometimes less. On my recent Delphi 10.1 Update 2 presentation two times I have been asked about data-aware versions of new VCL Win10 calendar controls. Here is the code.
unit uDBCalendarView;
interface
uses
System.Classes, Vcl.WinXCalendars, Data.DB, VCL.DBCtrls;
type
TDBCalendarView = class(TCalendarView)
private
FDataLink: TFieldDataLink;
procedure DataChange(Sender: TObject);
procedure SetDataField(const Value: string);
procedure SetDataSource(const Value: TDataSource);
function GetDataField: string;
function GetDataSource: TDataSource;
function GetField: TField;
function GetFieldDate: TDate;
protected
procedure Loaded; overrideprocedure Notification(AComponent: TComponent;
Operation: TOperation); overridepublic
constructor Create(AOwner: TComponent);
destructor Destroy; overrideproperty Field: TField read GetField;
published
property DataField: string read GetDataField write SetDataField;
property DataSource: TDataSource read GetDataSource write SetDataSource;
endimplementation
uses
System.SysUtils;
{ TDBCalendarView }
constructor TDBCalendarView.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Enabled := False; // read-only
FDataLink := TFieldDataLink.Create;
FDataLink.Control := Self;
FDataLink.OnDataChange := DataChange;
enddestructor TDBCalendarView.Destroy;
begin
FDataLink.Free;
inheritedendprocedure TDBCalendarView.Loaded;
begin
inherited Loaded;
if (csDesigning in ComponentState) then DataChange(Self);
endprocedure TDBCalendarView.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation = opRemove) and (FDataLink <> nil) and
(AComponent = DataSource) then DataSource := nilendprocedure TDBCalendarView.DataChange(Sender: TObject);
begin
self.Date := GetFieldDate;
endfunction TDBCalendarView.GetField: TField;
begin
Result := FDataLink.Field;
endfunction TDBCalendarView.GetDataField: stringbegin
Result := FDataLink.FieldName;
endprocedure TDBCalendarView.SetDataField(const Value: stringbegin
FDataLink.FieldName := Value;
endfunction TDBCalendarView.GetDataSource: TDataSource;
begin
Result := FDataLink.DataSource;
endprocedure TDBCalendarView.SetDataSource(const Value: TDataSource);
begin
if not (FDataLink.DataSourceFixed and (csLoading in ComponentState)) then
FDataLink.DataSource := Value;
if Value <> nil then Value.FreeNotification(Self);
endfunction TDBCalendarView.GetFieldDate: TDate;
begin
if FDataLink.Field <> nil then
begin
if (FDataLink.Field.DataType in [ftDate, ftDateTime, ftTimeStamp, ftTime]) then
Result := FDataLink.Field.AsDateTime
else
Result := Now;
end
else
if csDesigning in ComponentState then Result := Now;
endend.
One of the best things about Delphi is that it comes with the source code. After a lot of googling for examples of custom database aware Delphi components, the actual source code of "TDBText" component in "VCL.DBCtrls" unit was the best resource.
The most important part of a custom data-aware field component is private "TFieldDataLink". It does all the job, but as component implementer you need to expose its "DataSource" and "FieldName" properties. In the constructor there is also "OnDataChanged" event connected to "DataChanged" procedure. In this way the calendar control knows when it needs to refresh itself. This is centralized in the "GetFieldDate" method that reads the date from the internal "FDataLink.Field".
Not a rocket science:-)
The source code of custom Win10 VCL data-aware components can be downloaded from this link.
The test application of data-aware "TCalendarView" and "TCalendarPicker" looks like this and is using RAD Studio InterBase "EMPLOYEE" database.