I have followed a few tutorials on creating a custom property editor dialog, but there are so many things involved that I could not get it to work right. What I am trying to
I did not want to ask this question here and expect anyone to answer it for me, so I did the research myself to solve my issues and I would like to share the unique experience involved in this mini project, as I'm sure others are frustrated with the same thing.
There are many different possibilities with custom property editors, dialogs, and component editors. This in particular would call for a TDateTimeProperty descendant. This would allow you to be able to edit the value of the property directly in the Object Inspector as plain text (String) while keeping the DateTime formatting.
I am assuming that you already have a general knowledge of creating custom components and a package in which you can publish this property editor from, because that's a lesson in its own which I will not cover. This calls for just one line of code to be placed inside the Register procedure, but we'll get to that later.
First, you need to create a new form in your Design-Time package, where your components are registered. Name the unit DateTimeProperty.pas, and name the form DateTimeDialog (thus making the form's class TDateTimeDialog). Place whatever controls you need, in this case a TMonthCalendar, TDateTimePicker (with Kind set to dtkTime), and 2 TBitBtn controls, one labeled OK with ModalResult of mrOK and the other labeled Cancel with ModalResult of mrCancel.
Your unit should look something like this:
unit DateTimeProperty;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.ComCtrls, Vcl.StdCtrls, Vcl.Buttons;
type
TDateTimeDialog = class(TForm)
dtDate: TMonthCalendar;
dtTime: TDateTimePicker;
BitBtn1: TBitBtn;
BitBtn2: TBitBtn;
private
public
end;
var
DateTimeDialog: TDateTimeDialog;
implementation
{$R *.dfm}
end.
And here is the DFM code behind this form:
object DateTimeDialog: TDateTimeDialog
Left = 591
Top = 158
BorderIcons = [biSystemMenu]
BorderStyle = bsToolWindow
Caption = 'Pick Date/Time'
ClientHeight = 231
ClientWidth = 241
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
Position = poScreenCenter
DesignSize = (
241
231)
PixelsPerInch = 96
TextHeight = 13
object dtDate: TMonthCalendar
Left = 8
Top = 31
Width = 225
Height = 166
Anchors = [akLeft, akRight, akBottom]
Date = 41261.901190613430000000
TabOrder = 1
end
object dtTime: TDateTimePicker
Left = 8
Top = 8
Width = 113
Height = 21
Date = 41261.000000000000000000
Time = 41261.000000000000000000
Kind = dtkTime
TabOrder = 2
end
object BitBtn1: TBitBtn
Left = 158
Top = 200
Width = 75
Height = 25
Caption = 'OK'
Default = True
ModalResult = 1
TabOrder = 0
end
object BitBtn2: TBitBtn
Left = 77
Top = 200
Width = 75
Height = 25
Caption = 'Cancel'
ModalResult = 2
TabOrder = 3
end
end
Now, add DesignEditors and DesignIntf to your uses clause. Make sure you have DesignIDE declared in the Requires of this Design-Time package. This is required for publishing any property editors.
In the form, create a new public property called DateTime of type TDateTime with a property getter and setter. This property will allow you to easily read/write the full TDateTime value the selection actually represents. So you should have this in your form:
private
function GetDateTime: TDateTime;
procedure SetDateTime(const Value: TDateTime);
public
property DateTime: TDateTime read GetDateTime write SetDateTime;
....
function TDateTimeDialog.GetDateTime: TDateTime;
begin
Result:= Int(dtDate.Date) + Frac(dtTime.Time);
end;
procedure TDateTimeDialog.SetDateTime(const Value: TDateTime);
begin
dtDate.Date:= Value;
dtTime.DateTime:= Value;
end;
Next we need to add the actual property editor class. Create this class just beneath the {$R *.dfm} which is just under implementation:
type
TDateTimeEditor = class(TDateTimeProperty)
public
procedure Edit; override;
function GetAttributes: TPropertyAttributes; override;
function GetValue: String; override;
procedure SetValue(const Value: String); override;
end;
procedure TDateTimeEditor.Edit;
var
F: TDateTimeDialog;
begin
//Initialize the property editor window
F:= TDateTimeDialog.Create(Application);
try
F.DateTime:= GetFloatValue;
if F.ShowModal = mrOK then begin
SetFloatValue(F.DateTime);
end;
finally
F.Free;
end;
end;
function TDateTimeEditor.GetAttributes: TPropertyAttributes;
begin
//Makes the small button show to the right of the property
Result := inherited GetAttributes + [paDialog];
end;
function TDateTimeEditor.GetValue: String;
begin
//Returns the string which should show in Object Inspector
Result:= FormatDateTime('m/d/yy h:nn:ss ampm', GetFloatValue);
end;
procedure TDateTimeEditor.SetValue(const Value: String);
begin
//Assigns the string typed in Object Inspector to the property
inherited;
end;
Finally, we need to add a Register procedure to perform the actual registration of this new property editor:
procedure Register;
begin
RegisterPropertyEditor(TypeInfo(TDateTime), nil, '', TDateTimeEditor);
end;
Now there's an important piece to understand in this call to RegisterPropertyEditor. Since the 2nd and 3rd parameters are nil and an empty string, this means the editor will apply to all instances of TDateTime. Look into this procedure for more information about making it specific to certain components and property instances.
And here's the final result after installing...

Some good resources for custom property editors which contributed are as follows: