Delphi 10.3.3 VCL应用中如何临时隐藏TListView的TListItem?
Delphi 10.3.3 VCL中TListItem的临时隐藏/显示实现方案
以下是几种无需删除项即可实现TListView行隐藏的实用方案,隐藏后仍可直接访问项的所有属性:
方案一:利用TListItem.Data存储隐藏标记(轻量化实现)
无需自定义控件,直接用现有属性实现:
标记隐藏状态:通过
Data属性存储布尔标记(用Integer类型转换适配Pointer)// 标记第N行隐藏 ListView1.Items[N].Data := Pointer(Integer(True)); // 恢复显示第N行 ListView1.Items[N].Data := Pointer(Integer(False));拦截绘制过程:重写
OnCustomDrawItem事件,跳过隐藏项的绘制procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView; Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean); begin // 检查是否标记为隐藏,是则不执行默认绘制 if Assigned(Item.Data) and (Integer(Item.Data) = Integer(True)) then DefaultDraw := False; end;访问隐藏项:直接通过
ListView1.Items索引访问,项并未被删除,只是不显示// 获取隐藏项的标题 ShowMessage(ListView1.Items[HiddenItemIndex].Caption);
方案二:自定义TListItem子类添加Hidden属性(语义更清晰)
如果需要更直观的属性控制,可以自定义子类:
定义子类:
type THiddenListItem = class(TListItem) private FHidden: Boolean; public property Hidden: Boolean read FHidden write FHidden; end;创建自定义项:
var NewItem: THiddenListItem; begin NewItem := THiddenListItem.Create(ListView1.Items); NewItem.Caption := '示例行'; NewItem.Hidden := False; // 默认显示 // 可继续设置SubItems等属性 end;过滤绘制:在
OnCustomDrawItem中判断Hidden属性procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView; Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean); begin if Item is THiddenListItem then DefaultDraw := not THiddenListItem(Item).Hidden; end;访问隐藏项:强转类型后直接操作属性
if ListView1.Items[Index] is THiddenListItem then ShowMessage(THiddenListItem(ListView1.Items[Index]).Caption);
方案三:虚拟列表模式(大数据量场景更高效)
如果ListView包含大量项,使用虚拟模式(OwnerData)能大幅提升性能:
开启虚拟模式:设置
ListView1.OwnerData := True;维护独立数据源:定义存储所有项数据的结构,包含隐藏状态
type TListViewItemData = record Caption: string; SubItems: array of string; Hidden: Boolean; end; var ItemDataList: TList<TListViewItemData>;绑定数据到ListView:实现
OnData事件,仅加载非隐藏项procedure TForm1.ListView1Data(Sender: TCustomListView; Item: TListItem); var VisibleIdx, RealIdx: Integer; begin VisibleIdx := Item.Index; RealIdx := -1; // 找到当前可见项对应的真实数据源索引 while VisibleIdx >= 0 do begin Inc(RealIdx); if not ItemDataList[RealIdx].Hidden then Dec(VisibleIdx); end; // 赋值标题和子项 Item.Caption := ItemDataList[RealIdx].Caption; Item.SubItems.Clear; for var SubItemStr in ItemDataList[RealIdx].SubItems do Item.SubItems.Add(SubItemStr); end;更新显示:修改隐藏状态后,刷新ListView的可见项数量并重绘
procedure TForm1.RefreshListViewDisplay; var VisibleCount: Integer; begin VisibleCount := 0; for var Data in ItemDataList do if not Data.Hidden then Inc(VisibleCount); ListView1.Items.Count := VisibleCount; ListView1.Invalidate; end;访问隐藏项:直接操作
ItemDataList即可,完全不受ListView显示状态影响
补充优化:避免选中隐藏项
方案一和二中,隐藏项仍存在于Items集合中,可能被意外选中,可通过OnSelectItem事件拦截:
procedure TForm1.ListView1SelectItem(Sender: TCustomListView; Item: TListItem; Selected: Boolean); begin if Selected then begin // 方案一的判断逻辑 if Assigned(Item.Data) and (Integer(Item.Data) = Integer(True)) then ListView1.Selected := nil; // 方案二的判断逻辑 if (Item is THiddenListItem) and THiddenListItem(Item).Hidden then ListView1.Selected := nil; end; end;
内容的提问来源于stack exchange,提问作者Xel Naga
相关产品推荐
相关产品推荐

