【问题标题】:delphi component property: TObjectList<TPicture>delphi 组件属性:TObjectList<TPicture>
【发布时间】:2016-07-21 12:31:29
【问题描述】:

我正在尝试创建一个 VCL 组件,它允许您插入多个不同大小的 TImage 作为属性。 有人告诉我最好使用 TObjectList (Delphi component with a variable amount of TPictures),但现在我正在努力使单个 TPictures 可在属性编辑器中分配

我现在拥有的:(它编译)

unit ImageMultiStates;

interface

uses
  Vcl.Graphics, Vcl.StdCtrls, System.SysUtils, System.Classes, Vcl.Controls, Vcl.ExtCtrls, Forms, Generics.Collections;

type

  TImageMultiStates = class(TImage)
  private
    FPictures: TObjectList<TPicture>;
    procedure SetPicture(Which: Integer; APicture: TPicture);
    function GetPicture(Which: Integer): TPicture;
  public
    Count: integer;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Activate(Which: Integer);
  published
    // property Pictures: TObjectList<TPicture> read GetPicture write SetPicture;
    // property Pictures[Index: Integer]: TObjectList<TPicture> read GetPicture write SetPicture;
    property Pictures: TObjectList<TPicture> read FPictures write FPictures;
  end;

procedure Register;

implementation

constructor TImageMultiStates.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FPictures := TObjectList<TPicture>.Create;
end;

destructor TImageMultiStates.Destroy;
begin
  FPictures.Free;
  inherited Destroy;
end;

procedure TImageMultiStates.SetPicture(Which: Integer; APicture: TPicture);
begin
  FPictures[Which] := APicture;
  if Which=0 then
    Picture.Assign(APicture);
end;

function TImageMultiStates.GetPicture(Which: Integer): TPicture;
begin
  Result := FPictures[Which];
end;

procedure TImageMultiStates.Activate(Which: Integer);
begin
  Picture.Assign(FPictures[Which]);
end;

procedure Register;
begin
  RegisterComponents('Standard', [TImageMultiStates]);
end;

end.

不工作的是 PropertyEditor 中的最终结果。它显示一个名为“Pictures”的项目,其值为“(TObjectList)”。单击它不会做任何事情,我没有得到合适的编辑器。有问题的行的其他想法已被注释掉,它们带来了其他错误: 第一个抛出编译器错误“E2008 Incompatible Types”,第二个抛出“Published property 'Pictures' can not be of type ARRAY”。

【问题讨论】:

  • 我不确定您是否可以将泛型用作已发布的属性(我不确定您也不能,顺便说一句)。您是否有理由不只是使用 TOwnedCollection,流系统知道如何处理?并且发布的属性不能是数组类型;你可以有一个带有 getter/setter 的 public 数组属性,但不能 published
  • 您应该使用我在 cmets 中对您上一个问题所说的集合
  • 我喜欢 TObjectList 的建议,以防止复杂性。好的,下一个方法从集合开始。干杯!
  • @David 是对的。他不是指像 Generics.Collections 这样的集合,他指的是 VCL TCollection。此类旨在用于多个项目(例如状态栏中的面板或列表视图标题中的列 - 这些都作为 TCollection 的后代实现)..
  • 因为 IDE 特别允许在设计时编辑基于 TCollection 的属性,并且 DFM 流系统也支持 TCollection。 Generic 集合无法获得同样的支持。

标签: delphi vcl


【解决方案1】:

IDE 不知道如何在设计时编辑 TObjectList,而 DFM 流系统不知道如何流式传输 TObjectList。您必须实现自定义属性编辑器和自定义流逻辑。这当然是可能的,但工作量很大。

改用System.Classes.TCollection 可以更好地处理您尝试做的事情。 IDE 和 DFM 流式传输系统都内置支持自动为您处理 TCollection 编辑和流式传输。

试试这样的:

unit ImageMultiStates;

interface

uses
  System.Classes, Vcl.Controls, Vcl.ExtCtrls, Vcl.Graphics;

type
  TImagePictureItem = class(TCollectionItem)
  private
    FPicture: TPicture;
    procedure PictureChanged(Sender: TObject);
    procedure SetPicture(Value: TPicture);
  public
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
  published
    property Picture: TPicture read FPicture write SetPicture;
  end;

  TImagePictureEvent = procedure(Sender: TObject; Index: Integer) of object; 

  TImagePictures = class(TOwnedCollection)
  private
    FOnPictureChange: TImagePictureEvent;
    function GetPicture(Index: Integer): TImagePictureItem;
    procedure SetPicture(Index: Integer; Value: TImagePictureItem);
  protected
    procedure Update(Item: TCollectionItem); override;
  public
    constructor Create(Owner: TComponent); reintroduce;
    property Pictures[Index: Integer]: TImagePictureItem read GetPicture write SetPicture; default;
    property OnPictureChange: TImagePictureEvent read FOnPictureChange write FOnPictureChange;
  end;

  TImageMultiStates = class(TImage)
  private
    FActivePicture: Integer;
    FPictures: TImagePictures;
    function GetPicture(Index: Integer): TPicture;
    procedure PictureChanged(Sender: TObject; Index: Integer);
    procedure SetActivePicture(Index: Integer);
    procedure SetPicture(Index: Integer; Value: TPicture);
    procedure SetPictures(Value: TImagePictures);
  protected
    procedure Loaded; override;
  public
    constructor Create(Owner: TComponent); override;
    function Count: integer;
    property Pictures[Index: Integer]: TPicture read GetPicture write SetPicture;
  published
    property ActivePicture: Integer read FActivePicture write SetActivePicture default -1;
    property Picture stored False;
    property Pictures: TImagePictures read FPictures write SetPictures;
  end;

procedure Register;

implementation

{ TImagePictureItem }

constructor TImagePictureItem.Create(Collection: TCollection);
begin
  inherited Create(Collection);
  FPicture := TPicture.Create;
  FPicture.OnChange := PictureChanged;
end;

destructor TImagePictureItem.Destroy;
begin
  FPicture.Free;
  inherited;
end;

procedure TImagePictureItem.PictureChanged(Sender: TObject);
begin
  Changed(False);
end;

procedure TImagePictureItem.SetPicture(Value: TPicture);
begin
  FPicture.Assign(Value);
end;

{ TImagePictures }

constructor TImagePictures.Create(Owner: TComponent);
begin
  inherited Create(Owner, TImagePictureItem);
end;

function TImagePictures.GetPicture(Index: Integer): TImagePictureItem;
begin
  Result := TImagePictureItem(inherited GetItem(Index));
end;

procedure TImagePictures.SetPicture(Index: Integer; Value: TImagePictureItem);
begin
  inherited SetItem(Index, Value);
end;

procedure TImagePictures.Update(Item: TCollectionItem);
begin
  if Assigned(FOnPictureChange) then
  begin
    if Item <> nil then
      FOnPictureChange(Self, Item.Index)
    else
      FOnPictureChange(Self, -1);
  end;
end;

{ TImageMultiStates }

constructor TImageMultiStates.Create(Owner: TComponent);
begin
  inherited Create(Owner);
  FPictures := TImagePictures.Create(Self);
  FPictures.OnPictureChange := PictureChanged;
  FActivePicture := -1;
end;

procedure TImageMultiStates.Loaded;
begin
  inherited;
  PictureChanged(nil, FActivePicture);
end;

function TImageMultiStates.Count: Integer;
begin
  Result := FPictures.Count;
end;

procedure TImageMultiStates.PictureChanged(Sender: TObject; Index: Integer);
begin
  if (FActivePicture <> -1) and ((Index = -1) or (Index = FActivePicture)) then
    Picture.Assign(GetPicture(FActivePicture));
end;

function TImageMultiStates.GetPicture(Index: Integer): TPicture;
begin
  Result := FPictures[Index].Picture;
end;

procedure TImageMultiStates.SetPicture(Index: Integer; Value: TPicture);
begin
  FPictures[Index].Picture.Assign(Value);
end;

procedure TImageMultiStates.SetActivatePicture(Value: Integer);
begin
  if FActivePicture <> Value then
  begin
    if ComponentState * [csLoading, csReading] = [] then
      Picture.Assign(GetPicture(Value));
    FActivePicture := Value;
  end;
end;

procedure Register;
begin
  RegisterComponents('Standard', [TImageMultiStates]);

  // the inherited TImage.Picture property is published, and you cannot
  // decrease the visibility of an existing property.  However, if you move
  // this procedure into a separate design-time package, you can then use
  // DesignIntf.UnlistPublishedProperty() to hide the inherited
  // Picture property at design-time, at least:
  //
  // UnlistPublishedProperty(TImageMultiStates, 'Picture');
  //
  // Thus, users are forced to use the TImageMultiStates.Pictures and
  // TImageMultiStates.ActivePicture at design-time.  The inherited
  // Picture property will still be accessible in code at runtime, though...
end;

end.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-30
    • 2018-12-25
    • 2016-08-06
    • 2021-09-20
    • 1970-01-01
    • 2013-02-06
    相关资源
    最近更新 更多