- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我意识到我确实需要重写我的程序数据结构(不是现在,而是很快,因为截止日期是星期一),因为我目前正在使用 VST (VirtualStringTree) 来存储我的数据。
我想要实现的是联系人列表结构。根节点是类别,子节点是联系人。共有 2 个级别。
问题是,我需要一个联系人显示在多个类别中,但它们需要同步。特别是Checkstate。
目前,为了保持同步,我循环遍历整个树以查找与刚刚更改的节点具有相同 ID 的节点。但当节点数量巨大时,这样做会非常慢。
所以,我想:是否可以在多个类别中显示联系人对象的一个实例?
注意:老实说,我并不是 100% 熟悉这个术语 - 我所说的“实例”是指一个对象(或记录),因此我不必遍历整个树来查找具有相同 ID 的联系人对象。
这是一个例子:
如您所见,Todd Hirsch 出现在“测试类别”和“所有联系人”中。但在幕后,这些是 2 个 PVirtualNodes,因此当我更改其中一个节点的属性(如 CheckState)或节点的数据记录/类中的某些内容时,这两个节点不会同步。目前,我同步它们的唯一方法是循环遍历我的树,找到包含相同联系人的所有节点,并将更改应用于它们及其数据。
总结:我正在寻找的是一种使用一个对象/记录并将其显示在树中的多个类别中的方法 - 每当一个节点被检查时,包含相同联系人对象的每个其他节点也会被检查.
我在这里有意义吗?
最佳答案
当然可以。你需要在头脑中将节点和数据分开。 TVirtualStringTree 中的节点不需要保存数据,可以简单地用于指向可以找到数据的实例。当然,您可以将两个节点指向同一个对象实例。
假设您有一个 TPerson 列表,并且有一棵树,您想在其中显示不同节点中的每个人。然后您将用于节点的记录简单地声明为:
TNodeRecord = record
... // anything else you may need or want
DataObject: TObject;
...
end;
在初始化节点的代码中,您可以执行以下操作:
PNodeRecord.DataObject := PersonList[SomeIndex];
这就是要点。如果您想要一个通用的 NodeRecord,就像我上面展示的那样,那么您需要将其转换回正确的类,以便在各种 Get... 方法中使用它。当然,您也可以为每个树创建一个特定的记录,其中您将 DataObject 声明为在树中显示的特定类类型。唯一的缺点是您随后将树限制为显示该类对象的信息。
我应该在某个地方有一个更详细的例子。当我找到它时,我会将其添加到此答案中。
<小时/>示例
声明树要使用的记录:
RTreeData = record
CDO: TCustomDomainObject;
end;
PTreeData = ^RTreeData;
TCustomDomainObject 是我所有域信息的基类。它声明为:
TCustomDomainObject = class(TObject)
private
FList: TObjectList;
protected
function GetDisplayString: string; virtual;
function GetCount: Cardinal;
function GetCDO(aIdx: Cardinal): TCustomDomainObject;
public
constructor Create; overload;
destructor Destroy; override;
function Add(aCDO: TCustomDomainObject): TCustomDomainObject;
property DisplayString: string read GetDisplayString;
property Count: Cardinal read GetCount;
property CDO[aIdx: Cardinal]: TCustomDomainObject read GetCDO;
end;
请注意,此类被设置为能够保存其他 TCustomDomainObject 实例的列表。在显示您的树的表单上添加:
TForm1 = class(TForm)
...
private
FIsLoading: Boolean;
FCDO: TCustomDomainObject;
protected
procedure ShowColumnHeaders;
procedure ShowDomainObject(aCDO, aParent: TCustomDomainObject);
procedure ShowDomainObjects(aCDO, aParent: TCustomDomainObject);
procedure AddColumnHeaders(aColumns: TVirtualTreeColumns); virtual;
function GetColumnText(aCDO: TCustomDomainObject; aColumn: TColumnIndex;
var aCellText: string): Boolean;
protected
property CDO: TCustomDomainObject read FCDO write FCDO;
public
procedure Load(aCDO: TCustomDomainObject);
...
end;
Load 方法是一切开始的地方:
procedure TForm1.Load(aCDO: TCustomDomainObject);
begin
FIsLoading := True;
VirtualStringTree1.BeginUpdate;
try
if Assigned(CDO) then begin
VirtualStringTree1.Header.Columns.Clear;
VirtualStringTree1.Clear;
end;
CDO := aCDO;
if Assigned(CDO) then begin
ShowColumnHeaders;
ShowDomainObjects(CDO, nil);
end;
finally
VirtualStringTree1.EndUpdate;
FIsLoading := False;
end;
end;
它真正做的就是清除表单并将其设置为新的 CustomDomainObject,在大多数情况下,该新 CustomDomainObject 是包含其他 CustomDomainObject 的列表。
ShowColumnHeaders 方法设置字符串树的列标题,并根据列数调整标题选项:
procedure TForm1.ShowColumnHeaders;
begin
AddColumnHeaders(VirtualStringTree1.Header.Columns);
if VirtualStringTree1.Header.Columns.Count > 0 then begin
VirtualStringTree1.Header.Options := VirtualStringTree1.Header.Options
+ [hoVisible];
end;
end;
procedure TForm1.AddColumnHeaders(aColumns: TVirtualTreeColumns);
var
Col: TVirtualTreeColumn;
begin
Col := aColumns.Add;
Col.Text := 'Breed(Group)';
Col.Width := 200;
Col := aColumns.Add;
Col.Text := 'Average Age';
Col.Width := 100;
Col.Alignment := taRightJustify;
Col := aColumns.Add;
Col.Text := 'CDO.Count';
Col.Width := 100;
Col.Alignment := taRightJustify;
end;
AddColumnHeaders 被分离出来,以允许此表单用作在树中显示信息的其他表单的基础。
ShowDomainObjects 看起来像是将加载整个树的方法。事实并非如此。毕竟我们正在处理一棵虚拟树。所以我们需要做的就是告诉虚拟树我们有多少个节点:
procedure TForm1.ShowDomainObjects(aCDO, aParent: TCustomDomainObject);
begin
if Assigned(aCDO) then begin
VirtualStringTree1.RootNodeCount := aCDO.Count;
end else begin
VirtualStringTree1.RootNodeCount := 0;
end;
end;
我们现在已经基本设置完毕,只需要实现各种 VirtualStringTree 事件即可让一切顺利进行。要实现的第一个事件是 OnGetText 事件:
procedure TForm1.VirtualStringTree1GetText(Sender: TBaseVirtualTree; Node:
PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText:
string);
var
NodeData: ^RTreeData;
begin
NodeData := Sender.GetNodeData(Node);
if GetColumnText(NodeData.CDO, Column, {var}CellText) then
else begin
if Assigned(NodeData.CDO) then begin
case Column of
-1, 0: CellText := NodeData.CDO.DisplayString;
end;
end;
end;
end;
它从 VirtualStringTree 获取 NodeData,并使用获得的 CustomDomainObject 实例来获取其文本。它使用 GetColumnText 函数来完成此操作,并再次完成此操作,以允许使用此表单作为显示树的其他表单的基础。当您走这条路线时,您将声明此方法为虚拟方法并以任何后代形式覆盖它。在此示例中,它简单地实现为:
function TForm1.GetColumnText(aCDO: TCustomDomainObject; aColumn: TColumnIndex;
var aCellText: string): Boolean;
begin
if Assigned(aCDO) then begin
case aColumn of
-1, 0: begin
aCellText := aCDO.DisplayString;
end;
1: begin
if aCDO.InheritsFrom(TDogBreed) then begin
aCellText := IntToStr(TDogBreed(aCDO).AverageAge);
end;
end;
2: begin
aCellText := IntToStr(aCDO.Count);
end;
else
// aCellText := '';
end;
Result := True;
end else begin
Result := False;
end;
end;
现在我们已经告诉 VirtualStringTree 如何使用其节点记录中的 CustomDomainObject 实例,当然我们仍然需要将主 CDO 中的实例链接到树中的节点。这是在 OnInitNode 事件中完成的:
procedure TForm1.VirtualStringTree1InitNode(Sender: TBaseVirtualTree;
ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
var
ParentNodeData: ^RTreeData;
ParentNodeCDO: TCustomDomainObject;
NodeData: ^RTreeData;
begin
if Assigned(ParentNode) then begin
ParentNodeData := VirtualStringTree1.GetNodeData(ParentNode);
ParentNodeCDO := ParentNodeData.CDO;
end else begin
ParentNodeCDO := CDO;
end;
NodeData := VirtualStringTree1.GetNodeData(Node);
if Assigned(NodeData.CDO) then begin
// CDO was already set, for example when added through AddDomainObject.
end else begin
if Assigned(ParentNodeCDO) then begin
if ParentNodeCDO.Count > Node.Index then begin
NodeData.CDO := ParentNodeCDO.CDO[Node.Index];
if NodeData.CDO.Count > 0 then begin
InitialStates := InitialStates + [ivsHasChildren];
end;
end;
end;
end;
Sender.CheckState[Node] := csUncheckedNormal;
end;
由于我们的 CustomDomainObject 可以拥有其他 CustomDomainObject 的列表,因此当 lsit 的 Count 大于零时,我们还将节点的 InitialStates 设置为包含 HasChildren。这意味着我们还需要实现 OnInitChildren 事件,当用户单击树中的加号时调用该事件。同样,我们需要做的就是告诉树需要准备多少个节点:
procedure TForm1.VirtualStringTree1InitChildren(Sender: TBaseVirtualTree; Node:
PVirtualNode; var ChildCount: Cardinal);
var
NodeData: ^RTreeData;
begin
ChildCount := 0;
NodeData := Sender.GetNodeData(Node);
if Assigned(NodeData.CDO) then begin
ChildCount := NodeData.CDO.Count;
end;
end;
这就是大家!!!
正如我展示的一个带有简单列表的示例,您仍然需要弄清楚需要将哪些数据实例链接到哪些节点,但是您现在应该对需要的位置有一个大致的了解为此:在 OnInitNode 事件中将节点记录的 CDO 成员设置为指向您选择的 CDO 实例。
关于delphi - 是否可以在 VirtualStringTree 中多次显示一个对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5602090/
我有一个 if 语句,如下所示 if (not(fullpath.lower().endswith(".pdf")) or not (fullpath.lower().endswith(tup
然而,在 PHP 中,可以: only appears if $foo is true. only appears if $foo is false. 在 Javascript 中,能否在一个脚
XML有很多好处。它既是机器可读的,也是人类可读的,它具有标准化的格式,并且用途广泛。 它也有一些缺点。它是冗长的,不是传输大量数据的非常有效的方法。 XML最有用的方面之一是模式语言。使用模式,您可
由于长期使用 SQL2000,我并没有真正深入了解公用表表达式。 我给出的答案here (#4025380)和 here (#4018793)违背了潮流,因为他们没有使用 CTE。 我很欣赏它们对于递
我有一个应用程序: void deleteObj(id){ MyObj obj = getObjById(id); if (obj == null) { throw n
我的代码如下。可能我以类似的方式多次使用它,即简单地说,我正在以这种方式管理 session 和事务: List users= null; try{ sess
在开发J2EE Web应用程序时,我通常会按以下方式组织我的包结构 com.jameselsey.. 控制器-控制器/操作转到此处 服务-事务服务类,由控制器调用 域-应用程序使用的我的域类/对象 D
这更多是出于好奇而不是任何重要问题,但我只是想知道 memmove 中的以下片段文档: Copying takes place as if an intermediate buffer were us
路径压缩涉及将根指定为路径上每个节点的新父节点——这可能会降低根的等级,并可能降低路径上所有节点的等级。有办法解决这个问题吗?有必要处理这个吗?或者,也许可以将等级视为树高的上限而不是确切的高度? 谢
我有两个类,A 和 B。A 是 B 的父类,我有一个函数接收指向 A 类型类的指针,检查它是否也是 B 类型,如果是将调用另一个函数,该函数接受一个指向类型 B 的类的指针。当函数调用另一个函数时,我
有没有办法让 valgrind 使用多个处理器? 我正在使用 valgrind 的 callgrind 进行一些瓶颈分析,并注意到我的应用程序中的资源使用行为与在 valgrind/callgrind
假设我们要使用 ReaderT [(a,b)]超过 Maybe monad,然后我们想在列表中进行查找。 现在,一个简单且不常见的方法是: 第一种可能性 find a = ReaderT (looku
我的代码似乎有问题。我需要说的是: if ( $('html').attr('lang').val() == 'fr-FR' ) { // do this } else { // do
根据this文章(2018 年 4 月)AKS 在可用性集中运行时能够跨故障域智能放置 Pod,但尚不考虑更新域。很快就会使用更新域将 Pod 放入 AKS 中吗? 最佳答案 当您设置集群时,它已经自
course | section | type comart2 : bsit201 : lec comart2 :
我正在开发自己的 SDK,而这又依赖于某些第 3 方 SDK。例如 - OkHttp。 我应该将 OkHttp 添加到我的 build.gradle 中,还是让我的 SDK 用户包含它?在这种情况下,
随着 Rust 越来越充实,我对它的兴趣开始激起。我喜欢它支持代数数据类型,尤其是那些匹配的事实,但是对其他功能习语有什么想法吗? 例如标准库中是否有标准过滤器/映射/归约函数的集合,更重要的是,您能
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎与 help center 中定义的范围内的编程无关。 . 关闭 9 年前。 Improve
我一直在研究 PHP 中的对象。我见过的所有示例甚至在它们自己的对象上都使用了对象构造函数。 PHP 会强制您这样做吗?如果是,为什么? 例如: firstname = $firstname;
...比关联数组? 关联数组会占用更多内存吗? $arr = array(1, 1, 1); $arr[10] = 1; $arr[] = 1; // <- index is 11; does the
我是一名优秀的程序员,十分优秀!