我正在尝试创建一个从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 ...
答案 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 :(得分: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
那样的循环。帮自己一个忙,并使用一个对象列表。