【问题标题】:Delphi component with a variable amount of TPictures具有可变数量的 TPictures 的 Delphi 组件
【发布时间】:2016-07-21 06:12:13
【问题描述】:

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

【问题讨论】:

  • 你需要维护一个图片集合。希望能在构造函数中创建图片实例是错误的。
  • @David Heffernan:当然可以。傻我。但除此之外,我在这里尝试的方法可行吗?我应该在属性中设置 TPicture 数量,还是适当的 PropertyEditor 正确显示 TPictures 的集合?
  • 我想你想使用一个包含你的图片对象的TCollection。然后它应该很容易连接到属性编辑器。
  • 以防万一,你知道TImageList,对吗?它可以容纳动态数量的 等尺寸 TImage 支持并具有设计时编辑器的任何类型的图像。限制是图像需要相同大小,默认为 16 x 16,但在将任何图像添加到列表之前,您可以根据需要更改高度和宽度。
  • 但是考虑使用 TImageList,如果你有相同大小的图片,例如表示控件的不同状态。 TImageList 甚至可以屏蔽图片,因此如果您不希望它们显示为矩形,它们甚至不必显示为矩形。

标签: delphi vcl


【解决方案1】:

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

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

试试...

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

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

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

终于...

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

结束

【讨论】:

    【解决方案2】:

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

    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;
    

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

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

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

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

    【讨论】:

    • FWIW,循环无法编译,因为 TPicture 不是局部变量(实际上是类型)。那么真正的代码是什么?
    • 即使循环变量是 TPicture 类型的变量,例如var N: TPicture; begin for N in FPictures do N := TPicture.Create; 你得到一个 Let side cannot be assigned to 错误,因为你不能分配给循环变量。您的整个帖子似乎都在剖析该循环,但显然您没有想到它无法编译。 TPicture 没有声明,因此不是局部变量,而是类型本身,你不能分配给循环控制变量。
    猜你喜欢
    • 2016-11-25
    • 2013-08-26
    • 1970-01-01
    • 1970-01-01
    • 2013-03-09
    • 1970-01-01
    • 2018-07-20
    • 2014-06-02
    • 1970-01-01
    相关资源
    最近更新 更多