如何为listbox/treeview的每一个选择项(节点)增加一个datastring的属性?

时间:2022-02-05 15:08:05
listbox,连treeview等控件的每一个选择项(节点)都有data指针,可以指向选择项对应的属性结构。
但是,这样,需要人为预先为所有的选择项分配空间存储选择项对应的属性,才能使用,最后还要释放这个空间,
如果选择项的数量是运行时增加或减少的,每次都得分配、使用、释放,麻烦而且容易疏漏。

我是很希望当时borland能把这个指针类型的data改为string类型就好了,
这样,只要把选择项对应的属性编码为string就行了,省事多了——可以是xml,也可以是ini(name=value;...)
或者为选择项(节点)多增加一个datastring的属性。

现在,只能退而求其次:如何为listbox/treeview的每一个选择项(节点)增加一个datastring的属性?
如果能不需要修改vcl的源代码,直接通过一个外部的pas文件实现,就最好了;
如果必需修改vcl的源代码,也最好是修改得最少最简洁的。

17 个解决方案

#1


有必要改源码吗

TreeView1.Items.AddObject(nil,'abc',PChar('aaaaa'));

ShowMessage(PChar(TreeView1.Selected.Data));

#2


function AddChildObject(Node: TTreeNode; const S: string; Ptr: Pointer): TTreeNode;

C++ syntax:

TTreeNode* __fastcall AddChildObject(TTreeNode* Node, const AnsiString S, void * Ptr);

Description

The node is added as the last child of the node specified by the Node parameter. The S parameter specifies the Text property of the new node. The Ptr parameter specifies the Data property value of the new node. AddChildObject returns the node that has been added.

Note: The memory referenced by Ptr is not freed when the tree nodes object is freed.

不过试验了一下,好像没有内存泄漏。。。。。。。。

#3


加个常量做 Ptr 当然不会有内存泄漏了
当然,用string变量做 Ptr 也不会有内存泄漏,string生存期结束就自然被释放了,只不过再在别的地方引用PChar(Data)的时候容易蹦出来了错儿之类的

#4


自己定义一个Class,在每个节点被删除的时候,自动释放Data指针指向的Class,很方便的,我很久以前就这样处理了,没有问题!

#5


引用 2 楼 sz_haitao 的回复:
function AddChildObject(Node: TTreeNode; const S: string; Ptr: Pointer): TTreeNode; 

C++ syntax: 

TTreeNode* __fastcall AddChildObject(TTreeNode* Node, const AnsiString S, void * Ptr); 

Description 

The node is added as the last child of the node specified by the Node parameter. The S parameter specifies the Text property of the new node. The Ptr parameter specifies the Data property…

up

#6


汗.VOID * DATA....这样不好.非要用STRING类型.晕.VOID也能直接用与AnsiString类的值的保存的.


你把
AnsiString pp;
DATA=(Void *) pp;保存.

用(AnsiString *) node->data;取值就行了.

最后关闭时,删除.

#7


引用 3 楼 Seamour 的回复:
加个常量做 Ptr 当然不会有内存泄漏了 
当然,用string变量做 Ptr 也不会有内存泄漏,string生存期结束就自然被释放了,只不过再在别的地方引用PChar(Data)的时候容易蹦出来了错儿之类的 


我试验的是 一个动态生成的string的表达式,没有问题,————[1]
如果把这个表达式先赋给一个局部string变量s,再把s赋给data或addChildObject(p,t,s),退出s所在的函数后,这个t对应的data是不是还有效?————[2]

如果[2]有问题,addChildObject(p,t,''+s)是不是反而就没有问题了?

delphi的string真的是很方便,就怕很多时候delphi的应用做服务容易出问题,是不是也就是string没处理好?

#8


引用 4 楼 Agilehawk 的回复:
自己定义一个Class,在每个节点被删除的时候,自动释放Data指针指向的Class,很方便的,我很久以前就这样处理了,没有问题!

这个class是Ttreenode的派生还是Ttreeview的派生,如果是前者,使得Ttreeview使用你的class而不是原来的Ttreenode?
如果一定要人为删除,是可以在Ttreeview的OnDelete事件里释放,但是感觉还是不太好——相比string的不用释放


引用 6 楼 nbzip 的回复:
汗.VOID * DATA....这样不好.非要用STRING类型.晕.VOID也能直接用与AnsiString类的值的保存的. 


你把 
AnsiString pp; 
DATA=(Void *) pp;保存. 

用(AnsiString *) node->data;取值就行了. 

最后关闭时,删除.

主要是 需要代码去删除,麻烦,容易疏漏。。。。。。。。


#9


引用 7 楼 sz_haitao 的回复:
我试验的是 一个动态生成的string的表达式,没有问题,————[1] 
如果把这个表达式先赋给一个局部string变量s,再把s赋给data或addChildObject(p,t,s),退出s所在的函数后,这个t对应的data是不是还有效?————[2] 

如果[2]有问题,addChildObject(p,t,''+s)是不是反而就没有问题了?

怎么个动态生成法?'abc'+'bcd'么?这还是一个常量字符串。
还有是怎么试的,短字符串又没几行代码一般不会有啥问题;如果你给局部变量设个超过1M的长度,生存期结束之后的效果应该是立竿见影的
''+S 由一个编译器生成的临时局部变量来接收,还是局部变量的生存期

string是很好用,了解它怎么运作之后也不会出问题。像这种地方应该用StrNew/StrDispose或者NewStr/DisposeStr之类管理,想偷懒的话还是换.net或者java吧

#10


引用 9 楼 Seamour 的回复:
引用 7 楼 sz_haitao 的回复:
我试验的是 一个动态生成的string的表达式,没有问题,————[1] 
如果把这个表达式先赋给一个局部string变量s,再把s赋给data或addChildObject(p,t,s),退出s所在的函数后,这个t对应的data是不是还有效?————[2] 

如果[2]有问题,addChildObject(p,t,''+s)是不是反而就没有问题了? 
… 
 
怎么个动态生成法?'abc'+'bcd'么?这还是一个常量字符串。 
还有是怎么试的,短字符串又没…

动态生成法==红色部分:
        c:=TVer.Items.AddChildObject(p,Qer.FieldByName('fname').AsString,
          pchar( makeTaskInfo(Qer.FieldByName('fid').AsString
            ,Qer.FieldByName('forder').AsString
            ,Qer.FieldByName('fdeleted').AsString
            ,Qer.FieldByName('fdept').AsString)
          )


哦,不行!内存泄漏是没有,但是pchar(node.data)已经不是原来的字符串了,好像是后来处理的节点的。。。。。。。

看来还是需要预先分配一个全局的字符串??

#11


内存泄漏检测是明确的:
---------------------------
xxxx.exe: Memory Leak Detected
---------------------------
This application has leaked memory. The small block leaks are (excluding expected leaks registered by pointer):

21 - 28 bytes: TCriticalSection x 1
61 - 68 bytes: TlogThread x 1
(这2项是正常的)

Note: To obtain a log file containing detail on memory leaks, enable the "FullDebugMode" and "LogMemoryLeakDetailToFile" conditional defines. To disable this memory leak check, undefine "EnableMemoryLeakReporting".

---------------------------
确定   
---------------------------

#12


看来是白高兴一场

开始没有泄漏,以为delphi对于string空间是以 被引用次数(降到0) 为自动释放的条件的
看来还是没这么智能

#13


引用 12 楼 sz_haitao 的回复:
看来是白高兴一场 

开始没有泄漏,以为delphi对于string空间是以 被引用次数(降到0) 为自动释放的条件的 
看来还是没这么智能

没错啊,delphi是这么做的,只不过cast成指针类型不增加引用计数。而且这个也没法增加,因为转指针的时候判断增加好办,判断该怎么减少就难了

另外,如果重用率挺高的,你又不是特在乎那点儿内存的话,用TStringList维护也许操作起来比StrNew/StrDispose简单,虽然后者也不一定多多少代码

#14


我现在是使用 动态数组,每个元素是一个结构,node.data指向元素的下标(因为node是只增不减,要么是全部清除再重新加载)

这样看来,由回到最初的希望了:Ttreenode增加一个string类型的属性
这样是不用 分配、释放 的了

#15


为Ttreenode增加一个string类型的属性DataString
如果是新写一个Ttreenode类,如何让Ttreeview也能转用新类而不是原来的Ttreenode?
还需要重新写一个Ttreeview?
实在不想使用第三方的控件,哪怕是自己写的。。。。。。

如果有类似面向方面的“切入”就好了,随时为已经存在的类增加一个属性、方法、方法的前置处理、后续处理。。。。。。。
这个估计是得语言支持才行了

#16


直接在comctrl.pas的Ttreenode的类定义里增加一个public的DataString:String;使用多天,好像没发现什么问题:
可以直接使用treenode.datastring,最后也没有内存泄漏。。。。。。。

看来只能这样了:简单,但是需要修改标志库的代码

#17


treeview 是treenode 吧,要想有string 修改Ttreenode,

在treeview里应该有个object数组吧,相应的修改下

#1


有必要改源码吗

TreeView1.Items.AddObject(nil,'abc',PChar('aaaaa'));

ShowMessage(PChar(TreeView1.Selected.Data));

#2


function AddChildObject(Node: TTreeNode; const S: string; Ptr: Pointer): TTreeNode;

C++ syntax:

TTreeNode* __fastcall AddChildObject(TTreeNode* Node, const AnsiString S, void * Ptr);

Description

The node is added as the last child of the node specified by the Node parameter. The S parameter specifies the Text property of the new node. The Ptr parameter specifies the Data property value of the new node. AddChildObject returns the node that has been added.

Note: The memory referenced by Ptr is not freed when the tree nodes object is freed.

不过试验了一下,好像没有内存泄漏。。。。。。。。

#3


加个常量做 Ptr 当然不会有内存泄漏了
当然,用string变量做 Ptr 也不会有内存泄漏,string生存期结束就自然被释放了,只不过再在别的地方引用PChar(Data)的时候容易蹦出来了错儿之类的

#4


自己定义一个Class,在每个节点被删除的时候,自动释放Data指针指向的Class,很方便的,我很久以前就这样处理了,没有问题!

#5


引用 2 楼 sz_haitao 的回复:
function AddChildObject(Node: TTreeNode; const S: string; Ptr: Pointer): TTreeNode; 

C++ syntax: 

TTreeNode* __fastcall AddChildObject(TTreeNode* Node, const AnsiString S, void * Ptr); 

Description 

The node is added as the last child of the node specified by the Node parameter. The S parameter specifies the Text property of the new node. The Ptr parameter specifies the Data property…

up

#6


汗.VOID * DATA....这样不好.非要用STRING类型.晕.VOID也能直接用与AnsiString类的值的保存的.


你把
AnsiString pp;
DATA=(Void *) pp;保存.

用(AnsiString *) node->data;取值就行了.

最后关闭时,删除.

#7


引用 3 楼 Seamour 的回复:
加个常量做 Ptr 当然不会有内存泄漏了 
当然,用string变量做 Ptr 也不会有内存泄漏,string生存期结束就自然被释放了,只不过再在别的地方引用PChar(Data)的时候容易蹦出来了错儿之类的 


我试验的是 一个动态生成的string的表达式,没有问题,————[1]
如果把这个表达式先赋给一个局部string变量s,再把s赋给data或addChildObject(p,t,s),退出s所在的函数后,这个t对应的data是不是还有效?————[2]

如果[2]有问题,addChildObject(p,t,''+s)是不是反而就没有问题了?

delphi的string真的是很方便,就怕很多时候delphi的应用做服务容易出问题,是不是也就是string没处理好?

#8


引用 4 楼 Agilehawk 的回复:
自己定义一个Class,在每个节点被删除的时候,自动释放Data指针指向的Class,很方便的,我很久以前就这样处理了,没有问题!

这个class是Ttreenode的派生还是Ttreeview的派生,如果是前者,使得Ttreeview使用你的class而不是原来的Ttreenode?
如果一定要人为删除,是可以在Ttreeview的OnDelete事件里释放,但是感觉还是不太好——相比string的不用释放


引用 6 楼 nbzip 的回复:
汗.VOID * DATA....这样不好.非要用STRING类型.晕.VOID也能直接用与AnsiString类的值的保存的. 


你把 
AnsiString pp; 
DATA=(Void *) pp;保存. 

用(AnsiString *) node->data;取值就行了. 

最后关闭时,删除.

主要是 需要代码去删除,麻烦,容易疏漏。。。。。。。。


#9


引用 7 楼 sz_haitao 的回复:
我试验的是 一个动态生成的string的表达式,没有问题,————[1] 
如果把这个表达式先赋给一个局部string变量s,再把s赋给data或addChildObject(p,t,s),退出s所在的函数后,这个t对应的data是不是还有效?————[2] 

如果[2]有问题,addChildObject(p,t,''+s)是不是反而就没有问题了?

怎么个动态生成法?'abc'+'bcd'么?这还是一个常量字符串。
还有是怎么试的,短字符串又没几行代码一般不会有啥问题;如果你给局部变量设个超过1M的长度,生存期结束之后的效果应该是立竿见影的
''+S 由一个编译器生成的临时局部变量来接收,还是局部变量的生存期

string是很好用,了解它怎么运作之后也不会出问题。像这种地方应该用StrNew/StrDispose或者NewStr/DisposeStr之类管理,想偷懒的话还是换.net或者java吧

#10


引用 9 楼 Seamour 的回复:
引用 7 楼 sz_haitao 的回复:
我试验的是 一个动态生成的string的表达式,没有问题,————[1] 
如果把这个表达式先赋给一个局部string变量s,再把s赋给data或addChildObject(p,t,s),退出s所在的函数后,这个t对应的data是不是还有效?————[2] 

如果[2]有问题,addChildObject(p,t,''+s)是不是反而就没有问题了? 
… 
 
怎么个动态生成法?'abc'+'bcd'么?这还是一个常量字符串。 
还有是怎么试的,短字符串又没…

动态生成法==红色部分:
        c:=TVer.Items.AddChildObject(p,Qer.FieldByName('fname').AsString,
          pchar( makeTaskInfo(Qer.FieldByName('fid').AsString
            ,Qer.FieldByName('forder').AsString
            ,Qer.FieldByName('fdeleted').AsString
            ,Qer.FieldByName('fdept').AsString)
          )


哦,不行!内存泄漏是没有,但是pchar(node.data)已经不是原来的字符串了,好像是后来处理的节点的。。。。。。。

看来还是需要预先分配一个全局的字符串??

#11


内存泄漏检测是明确的:
---------------------------
xxxx.exe: Memory Leak Detected
---------------------------
This application has leaked memory. The small block leaks are (excluding expected leaks registered by pointer):

21 - 28 bytes: TCriticalSection x 1
61 - 68 bytes: TlogThread x 1
(这2项是正常的)

Note: To obtain a log file containing detail on memory leaks, enable the "FullDebugMode" and "LogMemoryLeakDetailToFile" conditional defines. To disable this memory leak check, undefine "EnableMemoryLeakReporting".

---------------------------
确定   
---------------------------

#12


看来是白高兴一场

开始没有泄漏,以为delphi对于string空间是以 被引用次数(降到0) 为自动释放的条件的
看来还是没这么智能

#13


引用 12 楼 sz_haitao 的回复:
看来是白高兴一场 

开始没有泄漏,以为delphi对于string空间是以 被引用次数(降到0) 为自动释放的条件的 
看来还是没这么智能

没错啊,delphi是这么做的,只不过cast成指针类型不增加引用计数。而且这个也没法增加,因为转指针的时候判断增加好办,判断该怎么减少就难了

另外,如果重用率挺高的,你又不是特在乎那点儿内存的话,用TStringList维护也许操作起来比StrNew/StrDispose简单,虽然后者也不一定多多少代码

#14


我现在是使用 动态数组,每个元素是一个结构,node.data指向元素的下标(因为node是只增不减,要么是全部清除再重新加载)

这样看来,由回到最初的希望了:Ttreenode增加一个string类型的属性
这样是不用 分配、释放 的了

#15


为Ttreenode增加一个string类型的属性DataString
如果是新写一个Ttreenode类,如何让Ttreeview也能转用新类而不是原来的Ttreenode?
还需要重新写一个Ttreeview?
实在不想使用第三方的控件,哪怕是自己写的。。。。。。

如果有类似面向方面的“切入”就好了,随时为已经存在的类增加一个属性、方法、方法的前置处理、后续处理。。。。。。。
这个估计是得语言支持才行了

#16


直接在comctrl.pas的Ttreenode的类定义里增加一个public的DataString:String;使用多天,好像没发现什么问题:
可以直接使用treenode.datastring,最后也没有内存泄漏。。。。。。。

看来只能这样了:简单,但是需要修改标志库的代码

#17


treeview 是treenode 吧,要想有string 修改Ttreenode,

在treeview里应该有个object数组吧,相应的修改下