Delphi组件具有可变数量的TPictures

时间:2016-07-21 06:12:14

标签: delphi vcl

我正在尝试创建一个从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 ...

2 个答案:

答案 0 :(得分:1)

我会回答这个问题:你有什么期望这样做:

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

首先,正如所写的那样,这根本就不能编译。未声明 TPicture 循环变量(并且与类型名称相同会​​导致后续编译错误,即使它已被声明)。

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

问题不会在那里结束。

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

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

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

或许最简单的解释方法是"展开循环" (展开循环会为每次迭代显式重写代码,就好像根本没有循环一样)。

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

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

请记住,这样做编译,当我们展开它将创建的循环时,这有助于防止我们犯错误的原因变得明显,如果可能:

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

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

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

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

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

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

答案 1 :(得分:1)

你告诉我们你的方法“没有用”(我假设你的意思是“没有编译”):这是因为一些语法错误,而且你没有使用正确的工具来完成工作。 / p>

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

尝试...

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

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

如果它不支持,请包含单位 Contnrs 并使用TObjectList。从该列表中读取时,您必须使用as进行投射,即as TPicture,否则您可以执行类似操作。

最后...

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

结束