Delphi 如何为特定类型的所有实例实现自己的自定义属性编辑器?

Delphi 如何为特定类型的所有实例实现自己的自定义属性编辑器?,delphi,ide,delphi-xe2,custom-component,propertyeditor,Delphi,Ide,Delphi Xe2,Custom Component,Propertyeditor,我已经学习了一些关于创建自定义属性编辑器对话框的教程,但其中涉及的内容太多,我无法让它正常工作。我试图完成的是一个带有日期选择器(日历)、时间选择器以及OK和Cancel按钮的自定义表单。表单一点问题都没有,但是我如何实现它,以便使用启动属性编辑器的按钮在特定类型的任何组件中发布属性 我想完全覆盖TDateTime类型,并将自定义编辑器放在适当的位置,这样无论何时发布TDateTime并在对象检查器中可见,我都可以使用此编辑器在同一窗口中同时修改日期和时间 问题是,关于创建自定义属性编辑器的文档

我已经学习了一些关于创建自定义属性编辑器对话框的教程,但其中涉及的内容太多,我无法让它正常工作。我试图完成的是一个带有日期选择器(日历)、时间选择器以及OK和Cancel按钮的自定义表单。表单一点问题都没有,但是我如何实现它,以便使用启动属性编辑器的按钮在特定类型的任何组件中发布属性

我想完全覆盖
TDateTime
类型,并将自定义编辑器放在适当的位置,这样无论何时发布
TDateTime
并在对象检查器中可见,我都可以使用此编辑器在同一窗口中同时修改日期和时间


问题是,关于创建自定义属性编辑器的文档很差,尽管有些资源非常详尽,但它们对功能的详细说明太多,并且没有触及最常见场景的要点。

我不想在这里问这个问题,希望有人能帮我回答这个问题,所以我自己做了这项研究来解决我的问题,我想分享这个小型项目中的独特经验,因为我相信其他人也会对同样的事情感到沮丧

自定义特性编辑器、对话框和组件编辑器有许多不同的可能性。这尤其需要
TDateTimeProperty
子体。这将允许您在对象检查器中直接以纯文本(字符串)形式编辑属性值,同时保留日期时间格式

我假设您已经掌握了创建自定义组件的一般知识,以及可以从中发布此属性编辑器的包,因为这是我将不介绍的课程。这只需要将一行代码放在
寄存器
过程中,但我们稍后将讨论这一点

首先,您需要在
设计时
包中创建一个新表单,在其中注册组件。将单元命名为
DateTimeProperty.pas
,并将表单命名为
DateTimeDialog
(从而使表单的类
TDateTimeDialog
)。放置您需要的任何控件,在本例中为
TMonthCalendar
TDateTimePicker
(将
Kind
设置为
dtkTime
),以及2个
TBitBtn
控件,一个标记为
OK
,带有
mrOK的
ModalResult
,另一个标记为
Cancel
,带有
mrCancel的
ModalResult

你的单位应该是这样的:

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.
这是此表单后面的
DFM
代码:

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
现在,将
DesignEditors
DesignIntf
添加到
uses
子句中。确保在该
设计时间
包的
要求
中声明了
设计方
。这是发布任何属性编辑器所必需的

在表单中,使用属性getter和setter创建一个名为
DateTime
的类型为
TDateTime
的新公共属性。此属性允许您轻松读取/写入所选内容实际代表的完整
TDateTime
值。所以你应该在你的表格里写上:

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;
接下来,我们需要添加实际的属性编辑器类。在
实现
下的
{$R*.dfm}
下面创建这个类:

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;
最后,我们需要添加一个
Register
过程来执行此新属性编辑器的实际注册:

procedure Register;
begin
  RegisterPropertyEditor(TypeInfo(TDateTime), nil, '', TDateTimeEditor);
end;
现在,在调用
RegisterPropertyEditor
中有一个重要的部分需要理解。由于第二个和第三个参数是
nil
和一个空字符串,这意味着编辑器将应用于
TDateTime
的所有实例。有关使其特定于某些组件和属性实例的详细信息,请查看此过程

这是安装后的最终结果

自定义属性编辑器的一些有用资源如下:


  • 更好的资源可以是FLOSS库,如CnWizards或JediVCL,它们实现并注册一些全局属性编辑器,只需阅读和学习,不要忘记Ray Konopka的D3,不再印刷,但仍然适用。Ray在他的网站上有一个pdf版本出售:+1做得很好。将更改时间和日期控件的顺序…;-)+1我不明白为什么要覆盖
    GetValue
    SetValue
    。尤其是
    GetValue
    强制您在世界上使用首选的日期格式。但是
    SetValue
    没有。我只想删除这两种方法。@DavidHeffernan是的,我同意,我想指出它可以被覆盖以进行验证,尽管我没有解释这一部分。