Как я могу реализовать собственный редактор собственных свойств для всех экземпляров определенного типа?

Я следил за несколькими учебниками по созданию диалогового окна редактора настраиваемых свойств, но есть так много всего, что я не мог заставить его работать правильно. То, что я пытаюсь выполнить, - это настраиваемая форма с выбором даты (календарь), таймером и кнопками "ОК" и "Отмена". Форма вообще не проблема, но как я могу ее реализовать, чтобы опубликовать свойство в любом компоненте определенного типа с помощью кнопки для запуска редактора свойств?

Я хотел бы полностью переопределить тип TDateTime и разместить свой собственный редактор на своем месте, поэтому везде, где TDateTime публикуется и отображается в Object Inspector, я могу использовать этот редактор для одновременного изменения даты и времени в том же окне.

Проблема заключается в том, что документация по созданию редактора настраиваемых свойств плохой, и хотя некоторые ресурсы очень тщательны, они вникают в слишком подробные сведения о возможностях и не доходят до точки наиболее распространенных сценариев.

Ответ 1

Я не хотел задавать этот вопрос здесь и ожидал, что кто-нибудь ответит на него за меня, поэтому я сам сделал исследование, чтобы решить свои проблемы, и я хотел бы поделиться уникальным опытом, связанным с этим мини-проектом, поскольку я уверенные другие разочарованы тем же самым.

Существует множество различных возможностей с редакторами настраиваемых свойств, диалоговыми окнами и редакторами компонентов. Это, в частности, потребовало бы потомка TDateTimeProperty. Это позволит вам редактировать значение свойства непосредственно в Object Inspector как обычный текст (String), сохраняя форматирование DateTime.

Я предполагаю, что у вас уже есть общие знания о создании пользовательских компонентов и пакета, в котором вы можете опубликовать этот редактор свойств, потому что это урок, который я не буду использовать. Это требует только одной строки кода, которая будет помещена внутри процедуры Register, но мы доберемся до нее.

Сначала вам нужно создать новую форму в вашем пакете Design-Time, где зарегистрированы ваши компоненты. Назовите блок DateTimeProperty.pas и назовите форму DateTimeDialog (таким образом, создав класс формы TDateTimeDialog). Поместите все необходимые вам средства, в этом случае TMonthCalendar, TDateTimePickerKind, установленным на dtkTime), и 2 TBitBtn, один помеченный OK с ModalResult of mrOK и другой - Cancel с ModalResult of mrCancel.

Ваше устройство должно выглядеть примерно так:

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. Убедитесь, что у вас есть DesignIDE, объявленный в Requires этого пакета Design-Time. Это необходимо для публикации любых редакторов свойств.

В форме создайте новое публичное свойство DateTime типа TDateTime с атрибутом getter и setter. Это свойство позволит вам легко прочитать/записать полное значение 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}, который находится под 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;

Наконец, нам нужно добавить процедуру Register для фактической регистрации этого нового редактора свойств:

procedure Register;
begin
  RegisterPropertyEditor(TypeInfo(TDateTime), nil, '', TDateTimeEditor);
end;

Теперь в этом призыве к RegisterPropertyEditor есть важная часть. Поскольку второй и третий параметры nil и пустая строка, это означает, что редактор будет применяться ко всем экземплярам TDateTime. Изучите эту процедуру для получения дополнительной информации о том, как сделать ее конкретной для определенных компонентов и экземпляров свойств.

И вот окончательный результат после установки...

Sample of final property editor

Некоторые полезные ресурсы для внесенных в редакцию настраиваемых редакторов свойств: