Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ssl/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 如何在TCustomListBox控件中用我自己发布的基于对象列表的类型替换TListbox项属性?_Delphi - Fatal编程技术网

Delphi 如何在TCustomListBox控件中用我自己发布的基于对象列表的类型替换TListbox项属性?

Delphi 如何在TCustomListBox控件中用我自己发布的基于对象列表的类型替换TListbox项属性?,delphi,Delphi,概述 这个问题是我最近提出的第二个问题: 虽然我接受了这个问题的答案,而且它奏效了,但我很快意识到,t收集并不是我想要的解决方案或要求 要求 为了使我的要求尽可能简单明了,我正试图: 基于TCustomListBox 将Items属性替换为我自己的Items类型,例如TList TList(Items属性)将保存对象,每个对象都包含标题和图像索引属性等 Owner绘制我的列表框并绘制其图标和文本等 创建属性编辑器,以便在设计时编辑项 记住这一点,我知道如何创建自定义控件,我知道如何使用TLi

概述

这个问题是我最近提出的第二个问题:

虽然我接受了这个问题的答案,而且它奏效了,但我很快意识到,
t收集
并不是我想要的解决方案或要求

要求

为了使我的要求尽可能简单明了,我正试图:

  • 基于
    TCustomListBox
  • Items
    属性替换为我自己的
    Items
    类型,例如
    TList
  • TList
    (Items属性)将保存对象,每个对象都包含标题和图像索引属性等
  • Owner绘制我的列表框并绘制其图标和文本等
  • 创建属性编辑器,以便在设计时编辑
记住这一点,我知道如何创建自定义控件,我知道如何使用
TList
甚至
TObjectList
例如,我知道如何拥有该控件,我还知道如何创建属性编辑器

问题

我不知道的是如何用我自己的替换标准的listbox
Items
type?嗯,我是这样做的(发布我自己共享同一名称的属性),只是我需要确保它完全可以通过dfm进行流化

我在这个主题上进行了广泛的搜索,并尝试研究了
TListView
TTreeView
etc发布其
项目
类型的代码,但我发现自己比以往任何时候都更加困惑

事实上,我在另一个网站上遇到了别人问的一个非常老的问题,这个问题问了我很多想做的事情:。如果链接丢失,我在下面引用了它:

我最近编写了一个发布TList属性的组件。然后,我为TList创建了一个属性编辑器,以启用设计时编辑。问题是TList不会流式传输到dfm文件,因此当项目关闭时,所有更改都会丢失。我认为这是因为TList继承自TObject,而不是TPersistant。我希望有一个简单的方法来解决这个问题(或者我一开始就误解了这个问题)。现在我能想到的就是切换到TCollection或重写DefineProperties方法。是否有其他方法可以从dfm获取TList中的信息

我在搜索诸如
DefineProperties()
之类的关键词时发现,鉴于这是Remy Lebeau在顶部链接的前一个问题中简要提到的另一个选项,它似乎也是该问题的答案

问题

我需要知道如何用我自己的
Items(TList)
Items(TObjectList)
etc类型替换
TCustomListBox
派生控件的
Items(TStrings)
属性,但要使用dfm使其完全可流化。从前面的评论中我知道
TList
是不可流化的,但是我不能像标准
TListBox
控件那样使用
TStrings
,我需要使用我自己的可流化的基于对象的列表

我不想使用
TCollection
DefineProperties
听起来很有希望,但我不知道该如何具体实现这一点

我将非常感谢您的帮助


谢谢。

TCustomListBox
中重写
DefineProperties
过程(在这里命名为
TMyListBox
)。在那里可以“注册”任意多的字段,它们将以与其他字段相同的方式存储在dfm中,但在对象检查器中看不到它们。老实说,我从未遇到过这样定义多个属性,称为“数据”或“字符串”

您可以定义“普通”属性或二进制属性“Normal”属性对于字符串、整数、枚举等非常方便。下面是如何实现带有
标题
图像索引
的项目:

TMyListBox = class(TCustomListBox)
private
  //other stuff
  procedure ReadData(reader: TReader);
  procedure WriteData(writer: TWriter);
protected
  procedure DefineProperties(filer: TFiler); override;
  //other stuff
public
  //other stuff
  property Items: TList read fItems; //not used for streaming, not shown in object inspector. Strictly for use in code itself. We can make it read-only to avoid memory leak. 
published
  //some properties
end;
这就是
DefineProperties
实现:

procedure TMyListBox.DefineProperties(filer: TFiler);
begin
  filer.DefineProperty('data', ReadData, WriteData, items.Count>0);
end;
第四个参数,
hasData
是布尔值。当您的组件保存到dfm时,将调用
DefineProperties
,此时可以确定是否有值得保存的数据。如果不是,则省略“数据”属性。在本例中,如果不存在任何项,则不具有此属性

如果我们希望使用此控件的可视继承(例如,使用此列表框创建一个具有预定义值的框架,然后在形成时最终对其进行更改),则可以检查此属性的值是否与我们的祖先的值有任何不同。祖先属性用于它。您可以在
t字符串中查看它是如何完成的:

procedure TStrings.DefineProperties(Filer: TFiler);

  function DoWrite: Boolean;
  begin
    if Filer.Ancestor <> nil then
    begin
      Result := True;
      if Filer.Ancestor is TStrings then
        Result := not Equals(TStrings(Filer.Ancestor))
    end
    else Result := Count > 0;
  end;

begin
  Filer.DefineProperty('Strings', ReadData, WriteData, DoWrite);
end;
在dfm中,它将如下所示:

object MyListBox1: TMyListBox
  data = (
    'item1'
    -1
    'item2'
    -1
    'item3'
    0
    'item4'
    1)
end
object MyListBox1: TMyListBox1
  data = {
    789C636260606005E24C86128654865C064386FF40802C62C40002009C5607CA}
end
对我来说,TCollection的输出似乎更优雅(三角括号,然后是项目,一个接一个),但我们这里的就足够了

现在读它:

procedure TMyListBox.ReadData(reader: TReader);
var item: TListBoxItem;
begin
  reader.ReadListBegin;
  while not reader.EndOfList do begin
    item:=TListBoxItem.Create;
    item.Caption:=reader.ReadString;
    item.ImageIndex:=reader.ReadInteger;    
    items.Add(item); //maybe some other registering needed
  end;
  reader.ReadListEnd;
end;
就这样。通过这种方式,相当复杂的结构可以很容易地流式处理,例如,二维数组,我们在写新行时开始写列表,然后在写新元素时开始写列表

当心
WriteStr
/
ReadStr
-这些是为了向后兼容而存在的一些古老过程,请始终使用
WriteString
/
ReadString

另一种方法是定义二进制属性。这主要用于将图像保存到dfm中。比如说,listBox有数百个条目,我们希望压缩其中的数据以减小可执行文件的大小。然后:

TMyListBox = class(TCustomListBox)
private
  //other stuff
  procedure LoadFromStream(stream: TStream);
  procedure SaveToStream(stream: TStream);
protected
  procedure DefineProperties(filer: TFiler); override;
//etc
end;

procedure TMyListBox.DefineProperties(filer: TFiler);
  filer.DefineBinaryProperty('data',LoadFromStream,SaveToStream,items.Count>0);
end;

procedure TMyListBox.SaveToStream(stream: TStream);
var gz: TCompressionStream;
    i: Integer;
    value: Integer;
    item: TListBoxItem;
begin
  gz:=TCompressionStream.Create(stream);
  try
    value:=items.Count;
    //write number of items at first
    gz.Write(value, SizeOf(value)); 
    //properties can't be passed here, only variables
    for i:=0 to items.Count-1 do begin
      item:=TListBoxItem(items[I]);
      value:=Length(item.Caption);
      //almost as in good ol' Pascal: length of string and then string itself
      gz.Write(value,SizeOf(value));
      gz.Write(item.Caption[1], SizeOf(Char)*value); //will work in old Delphi and new (Unicode) ones
      value:=item.ImageIndex;
      gz.Write(value,SizeOf(value));
    end;
  finally
    gz.free;
  end;
end;

procedure TMyListBox.LoadFromStream(stream: TStream);
var gz: TDecompressionStream;
    i: Integer;
    count: Integer;
    value: Integer;
    item: TListBoxItem;
begin
  gz:=TDecompressionStream.Create(stream);
  try
    gz.Read(count,SizeOf(count)); //number of items
    for i:=0 to count-1 do begin
      item:=TListBoxItem.Create;
      gz.Read(value, SizeOf(value)); //length of string
      SetLength(item.caption,value);
      gz.Read(item.caption[1],SizeOf(char)*value); //we got our string
      gz.Read(value, SizeOf(value));  //imageIndex
      item.ImageIndex:=value;
      items.Add(item); //some other initialization may be needed
    end;
 finally
    gz.free;
 end;
end;
在dfm中,它将如下所示:

object MyListBox1: TMyListBox
  data = (
    'item1'
    -1
    'item2'
    -1
    'item3'
    0
    'item4'
    1)
end
object MyListBox1: TMyListBox1
  data = {
    789C636260606005E24C86128654865C064386FF40802C62C40002009C5607CA}
end
78
是ZLib的一种签名,
9C
表示默认压缩,因此它可以工作(