Delphi 具有可变数量的 TPictures 的组件

Delphi component with a variable amount of TPictures

我正在尝试创建一个从 TImage 下降的组件,不同之处在于我可以在 属性 列表中分配可变数量的 TPictures(不是通过代码分配 TPictures)并激活其中之一通过代码显示在 TImage 中。

设置 TPictures 总数(动态数组的长度)的 属性 不是问题,如果有必要在属性中分配所有 TPictures。

unit ImageMultiStates;

interface

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

type
  TPictures = Array of TPicture;
  TImageMultiStates = class(TImage)
  private
      FPictures: TPictures;
      procedure SetPicture(Which: Integer; APicture: TPicture);
  public
      constructor Create(AOwner: TComponent); override;
      destructor Destroy; override;
      procedure Activate(Which: Integer);
  published
    property Images: TPictures read FPictures write FPictures; default;
  end;

procedure Register;

implementation

constructor TImageMultiStates.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  for TPicture in FPictures do
    TPicture := TPicture.Create;
end;

destructor TImageMultiStates.Destroy;
var
  APicture: TPicture;
begin
  for APicture in FPictures do
    APicture.Free;
  inherited Destroy;
end;

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

procedure TImageMultiStates.SetPicture(Which: Integer; APicture: TPicture);
begin // i would also like to use SetPicture instead of "write FPictures"
  FPictures[Which].Assign(APicture);
  if Which=0 then // because: First Picture will be displayed in the VCL editor
    Picture.Assign(FPictures[Which]);
end;

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

end.

我以多种方式修改了这段代码,但我就是无法真正发挥任何作用。

我的组件中确实已经有了完全相同的想法 'Image2States'。然后我需要 'Image4States' 等等,直到我决定我绝对需要它和可变数量的 TPictures...

我会这样回答:你希望它做什么:

for TPicture in FPictures do
  TPicture := TPicture.Create;

首先,如所写,这根本无法编译。 TPicture循环变量未声明(与类型名称相同,即使声明也会导致后续编译错误)。

即使假设循环是有效的可编译代码,当构造函数执行时 FPictures 是一个空数组,因此此循环将执行 0(零)次。您没有显示任何代码来告诉组件它应该支持多少张图片,因此它始终支持 0(零)。

问题不止于此。

如果您确实声明了一个适当的变量用作循环变量,循环代码仍将无法编译,因为它涉及对循环变量的赋值,这是不允许的。

即使在简单的 for 循环以及基于迭代器的循环(例如您尝试使用的循环)中也是如此。在简单循环的情况下,这是为了使编译器能够生成最佳代码。在迭代器循环的情况下,它还可以保护您避免可能容易犯的错误。

考虑到在这种情况下,在循环的每次迭代中,您将创建一个新的 TPicture 实例并将其分配给循环变量 ,但这确实 将其分配给初始化循环变量的数组中的项目

也许最简单的解释方法是 "unroll the loop"(展开循环就是为每次迭代明确地重写代码,就好像根本没有循环一样)。

所以考虑一下循环是否包含 2 个项目,让我们也更改循环变量的名称以使事情既有效又更清晰。我们将简单地使用 pic。换句话说,我们将展开此循环的 2 次迭代版本:

for pic in FPictures do  // fPictures contains 2 items
  pic := TPicture.Create;

请记住,这 不会 编译,当我们展开它将创建的循环时,这有助于防止我们犯错的原因变得显而易见,if 有可能:

// Iteration #1
pic := fPictures[0];
pic := TPicture.Create;

// Iteration #2
pic := fPictures[1];
pic := TPicture.Create;

希望您能看到问题所在。您在每次迭代时覆盖 循环变量 的值,但这不会修改数组项本身。更糟糕的是,循环的每次迭代都在泄漏 TPicture.

希望这现在可以帮助您理解为什么您的代码无法正常工作(实际上,无法正常工作)并进行必要的更正。

  1. 您需要一些机制来设置您的组件支持的图片数量

  2. 您需要正确初始化保存这些图片的数组中的项目

您告诉我们您的方法“无效”(我假设您的意思是“未编译”):那是因为一些语法错误并且因为您没有使用正确的工具来完成工作.

如果您只有 相同尺寸的图片,那么请考虑使用已经存在、经过良好测试和 IDE 支持的 TImageList 并且不要'尝试重新发明轮子。

尝试...

但是,如果您必须拥有不同尺寸图片的列表,请使用 TObjectList 而不是 array of TPicture。 TObjectLists 允许您添加、删除、查询等对象,如果您愿意,它们可以自动释放它们。

如果您的编译器支持泛型,则包含 System.Generics.Collections 并使用 TObjectList<TPicture> 来管理您的图片。这样,您就不必强制转换为 TPicture,因为泛型列表是类型安全的。

如果不支持它们,请包含单位 Contnrs 并使用 TObjectList。从该列表中读取时,您将必须使用 as 进行转换,即 as TPicture,否则您可以做类似的事情。

终于...

你的类型name让我觉得你只需要多个状态来实现某个控件。在那种情况下,我认为 TImageList 是完成这项工作的最佳工具(并且它已经适用于具有类似需求的其他控件)并且没有必要制作自己的工具。但是如果你想自己制作,不要使用动态数组,也不要产生像 for TPicture in FPictures do 这样的循环。帮自己一个忙,使用对象列表。

结束