【问题标题】:Creating custom TSetProperty property editor创建自定义 TSetProperty 属性编辑器
【发布时间】:2014-05-21 06:23:09
【问题描述】:

我正在尝试为某些自定义组件创建自定义属性编辑器。自定义属性编辑器旨在编辑一些设置属性,例如

type
  TButtonOption = (boOption1, boOption2, boOption3);
  TButtonOptions = set of TButtonOption;

我的属性编辑器来自 TSetProperty 类。问题是:我的自定义属性编辑器没有注册,Delphi IDE 似乎使用自己的默认设置属性编辑器,因为属性编辑器方法内部的 ShowMessage() 调用永远不会执行!我从头开始创建了一个示例包/组件,尽可能简单,显示了这个问题。代码如下:

unit Button1;

interface

uses
  System.SysUtils, System.Classes, Vcl.Controls, Vcl.StdCtrls, DesignIntf, DesignEditors;

type
  TButtonOption = (boOption1, boOption2, boOption3);

  TButtonOptions = set of TButtonOption;

  TButtonEx = class(TButton)
  private
    FOptions: TButtonOptions;
    function GetOptions: TButtonOptions;
    procedure SetOptions(Value: TButtonOptions);
  published
    property Options: TButtonOptions read GetOptions write SetOptions default [];
  end;

  TMySetProperty = class(TSetProperty)
  public
    function GetAttributes: TPropertyAttributes; override;
    procedure GetProperties(Proc: TGetPropProc); override;
    function GetValue: string; override;
  end;

procedure Register;

implementation

uses
  Dialogs;

// TButtonEx - sample component

function TButtonEx.GetOptions: TButtonOptions;
begin
  Result := FOptions;
end;

procedure TButtonEx.SetOptions(Value: TButtonOptions);
begin
  if FOptions <> Value then
  begin
    FOptions := Value;
  end;
end;

// register stuff

procedure Register;
begin
  RegisterComponents('Samples', [TButtonEx]);
  RegisterPropertyEditor(TypeInfo(TButtonOptions), nil, '', TMySetProperty);
end;

function TMySetProperty.GetAttributes: TPropertyAttributes;
begin
  ShowMessage('GetAttributes');
  Result := inherited GetAttributes;
end;

procedure TMySetProperty.GetProperties(Proc: TGetPropProc);
begin
  ShowMessage('GetProperties');
  inherited;
end;

function TMySetProperty.GetValue: string;
begin
  ShowMessage('GetValue');
  Result := inherited GetValue;
end;

end.

请注意:

  1. 我正在为所有具有 TButtonOptions 属性的组件注册新的属性编辑器 (TMySetProperty)。我也试过只为 TButtonEx 做,但结果是一样的。
  2. 我在自定义属性编辑器的所有重写方法中添加了 ShowMessage() 调用,而这些方法永远不会被调用。
  3. 我已经调试了包并且RegisterPropertyEditor() 执行了。尽管如此,我在重写方法中的自定义代码永远不会执行。
  4. 我已经看到其他第 3 方组件使用此类属性编辑器(TSetProperty 后代)在较旧的 Delphi IDE 中运行,但我找不到任何相关的代码差异。也许 Delphi XE2+ 需要其他东西?

所以问题是: 为什么我的自定义属性编辑器无法注册/工作?

注意:这个问题至少发生在 Delphi XE2、XE3、XE4 和 XE5 中。其他 IDE 未经过测试,但可能具有相同的行为。

【问题讨论】:

  • 你不能在同一个包中实现你的组件和编辑器。组件需要位于其自己的仅运行时包中,而编辑器需要位于其自己的需要运行时包的仅设计时包中。
  • 嗨雷米。感谢您的答复。不幸的是,这不是问题所在。这里的代码被简化(为了清楚起见),所有代码都在同一个单元内。我的真实代码使用 2 个不同的包,一个用于运行时的东西,另一个用于设计时的东西。问题是一样的。无论哪种方式,自定义属性编辑器都不起作用。
  • 我可以在 XE2 中重现该问题,但我不知道为什么它不起作用。显然,设计时包是通过RegisterComponents() 工作的。所以我添加了另一个基于Integer 的属性并为其注册了一个编辑器,它运行良好。所以问题一定是IDE处理基于Set的属性的方式有问题。

标签: delphi


【解决方案1】:

我终于找到了一个解决方案……在测试了我能想象到的一切之后——没有成功——我开始在 DesignEditors.pas 和 DesignIntf​​.pas 单元中寻找“新”的东西。阅读 GetEditorClass() 函数,我发现它首先检查 PropertyMapper。可以使用 RegisterPropertyMapper() 函数注册属性映射器。使用它而不是 RegisterPropertyEditor() 可以按预期工作。这是我修改后的工作代码,还展示了一些有趣的应用程序:显示或隐藏我的基于集合的属性的一些选项,基于一些标准:

unit Button1;

interface

uses
  System.SysUtils, System.Classes, Vcl.Controls, Vcl.StdCtrls,
  DesignIntf, DesignEditors;

type
  TButtonOption = (boOptionA, boOptionB, boOptionC);
  TButtonOptions = set of TButtonOption;

type
  TButtonEx = class(TButton)
  private
    FOptions: TButtonOptions;
    function GetOptions: TButtonOptions;
    procedure SetOptions(Value: TButtonOptions);
  published
    property Options: TButtonOptions read GetOptions write SetOptions default [];
  end;

  TMySetProperty = class(TSetProperty)
  private
    FProc: TGetPropProc;
    procedure InternalGetProperty(const Prop: IProperty);
  public
    procedure GetProperties(Proc: TGetPropProc); override;
  end;

procedure Register;

implementation

uses
  TypInfo;

// TButtonEx - sample component

function TButtonEx.GetOptions: TButtonOptions;
begin
  Result := FOptions;
end;

procedure TButtonEx.SetOptions(Value: TButtonOptions);
begin
  if FOptions <> Value then
  begin
    FOptions := Value;
  end;
end;

// Returns TMySetProperty as the property editor used for Options in TButtonEx class
function MyCustomPropMapper(Obj: TPersistent; PropInfo: PPropInfo): TPropertyEditorClass;
begin
  Result := nil;
  if Assigned(Obj) and (Obj is TButtonEx) and SameText(String(PropInfo.Name), 'Options') then begin
    Result := TMySetProperty;
  end;
end;

procedure Register;
begin
  RegisterComponents('Samples', [TButtonEx]);
  // RegisterPropertyEditor does not work for set-based properties.
  // We use RegisterPropertyMapper instead
  RegisterPropertyMapper(MyCustomPropMapper);
end;

procedure TMySetProperty.GetProperties(Proc: TGetPropProc);
begin
  // Save the original method received
  FProc := Proc;
  // Call inherited, but passing our internal method as parameter
  inherited GetProperties(InternalGetProperty);
end;

procedure TMySetProperty.InternalGetProperty(const Prop: IProperty);
var
  i: Integer;
begin
  if not Assigned(FProc) then begin   // just in case
    Exit;
  end;

  // Now the interesting stuff. I just want to show boOptionA and boOptionB in Object inspector
  // So I call the original Proc in those cases only
  // boOptionC still exists, but won't be visible in object inspector
  for i := 0 to PropCount - 1 do begin
    if SameText(Prop.GetName, 'boOptionA') or SameText(Prop.GetName, 'boOptionB') then begin
      FProc(Prop);       // call original method
    end;
  end;
end;

end.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-08
    • 1970-01-01
    相关资源
    最近更新 更多