Delphi ListView快速操作通用实现
作者:成晓旭
众所周知,Delphi ListView类直接进行Add、Update、Delete操作的速度是比较慢的,尤其是当数据量较大时,比如数据量达到5000、10000、50000时,速度真是可以说是“慢得惊人”。其实快速操作的方法非常简单,就当大家都知道了。在本人的工作中,很多项目都用到ListView,并且对速度的响应要求比较高,于是发生了快速操作ListView的代码散布于多个项目大量模块中的问题,并且,当界面层数据显示要求发生改变时,自然发生“重复性代码的通用问题”。考虑到对以前版本的兼容性问题,也一直没有引用第三方的成熟组件。鉴于:“程序中的重复代码最易引发问题,必须消除”的实践经验,自己设计了一个比较通用的解决此类问题的类结构。当然,远不是什么“通用框架”了(眼下市面上很多人喜欢把自己做的无论什么东西通称为框架)。在采用此结构的项目中,很容易实现MVC模式,达到业务逻辑与界面显示分离的低级的、基础的要求。
(因为,目前CSDN的软件上传功能不可用,我只有将部分代码片断放在文档中,有需要完整源码者,请留言)
类层次结构:
ListView基础操作封装在LVControler包中,核心的类是TCXXLVControler:
(说明:LVControler类是被封装通用类结构内,外部用户是不需要了解和访问的,所以不作介绍。)
传统的ListView操作基类是TLVCommonClass,如果想用传统的方法增、删、改ListView中的数据,可以直接从此类继承。
源码如下:
具体的一个从此类继承下来的用于Socket界面显示的TLVSocket的类Overvivw如下:
源码如下:
快速的ListView操作基类是TLVQuickClass,如果想用快速方法增、删、改ListView中的数据,可以直接从此类继承。
主要方法:(可以看到,里面没有真正的Public方法,子类也仅需实现两个Protected的virtual方法)
源码如下:
此类中,要求每个具体应用的子类必须实现的方法仅有两个:CheckFound():根据具体应用检测的数据是否已经存在;ProcOnDataDetail():客户端ListView的OnData()事件的数据处理回调方法。下面是几个具体实现的子类的OverView:
具体的一个从此类继承下来的用于Socket界面显示的TLVQuickSocket的类Overvivw如下:
可以看出:子类实现了两个抽象的虚方法,其它的方法,都是根据业务需要,类用户自行增加的。
源码如下:
//
// 产品名称: 成晓旭的个人软件Delphi源码库
// 产品版本: CXXSoft delphi code source lib 1.0
// 模块名称: Delphi之ListView显示控制类---应用层:Soft socket类定义单元
// 模块描述:
// 单元文件: unLVSoftSocket.pas-->unLVQuickSocket.pas
// 开发作者: 成晓旭
// 备注: 任何人使用此文件时,请保留此段自述文件,谢谢!
// 开发时间: 2005-09-26
//
// 修改历史: 2006-06-16
// 修改描述: 增加通过TList来高速增加、更新、删除数据
// 先用吧,以后再优化和完善
// 修改历史: 2006-07-10
// 修改描述: 成功地将ListView的OnData事件的List对象移入此类中
// 修改历史: 2006-07-11
// 修改描述: 重大重构:将此类分成两个类:TLVSoftSocket-->TLVSoftSocket和TLVQuickSocket
// 以遵循SRP原则
// ------------------------------------------------------------------------------
unit unLVQuickSocket;
interface
uses
ComCtrls,Classes,SysUtils,Windows,
unLVQuickClass,unLVDefine;
type
TLVQuickSocket = class (TLVQuickClass)
private
protected
class function CheckFound( const pData:Pointer; const aKey:variant): boolean ;override;
class procedure ProcOnDataDetail( const pData:Pointer;var Item: TListItem);override;
public
constructor Create();
destructor Destroy();override;
procedure InitListView(var lvTemp:TListView);
// 快速方法
// 暂时这样增加,以后在重构到基类中
procedure AddToLVSocketQuick( const aSocketStatus:TSocketStatusInfo;var lvTemp:TListView);
procedure UpdateLVSocketQuick( const aUniqueID:integer; const aSocketStatus:TSocketStatusInfo;var lvTemp:TListView);
procedure DeleteLVSocketQuick( const aUniqueID:integer; const aSocketStatus:TSocketStatusInfo;var lvTemp:TListView);
procedure DeleteAllLVSocket(var lvTemp:TListView);
procedure OnDataEvent(Item: TListItem);
function GetDataCount():integer;
end;
implementation
... { TLVQuickSocket }
procedure TLVQuickSocket.AddToLVSocketQuick(
const aSocketStatus: TSocketStatusInfo;var lvTemp:TListView);
begin
AddItemDataToListView(@aSocketStatus,sizeof(aSocketStatus),lvTemp);
end;
class function TLVQuickSocket.CheckFound( const pData: Pointer;
const aKey: variant): boolean ;
var
p: ^ TSocketStatusInfo;
begin
p : = pData;
Result : = (p.UniqueID = aKey);
end;
constructor TLVQuickSocket.Create;
begin
inherited Create();
end;
procedure TLVQuickSocket.DeleteAllLVSocket(var lvTemp: TListView);
begin
DeleteAllListView(lvTemp);
end;
procedure TLVQuickSocket.DeleteLVSocketQuick( const aUniqueID:integer;
const aSocketStatus: TSocketStatusInfo;var lvTemp:TListView);
begin
DeleteItemDataToListView(aUniqueID,@aSocketStatus,sizeof(aSocketStatus),lvTemp);
end;
destructor TLVQuickSocket.Destroy;
begin
inherited Destroy;
end;
function TLVQuickSocket.GetDataCount(): integer;
begin
Result : = Self.GetLVListCount();
end;
procedure TLVQuickSocket.InitListView(var lvTemp: TListView);
begin
InitListViewColumns(SocketStrBuffer,SocketWidthBuffer,lvTemp);
end;
procedure TLVQuickSocket.OnDataEvent(Item: TListItem);
begin
Self.OnDataToListView(Item);
end;
class procedure TLVQuickSocket.ProcOnDataDetail( const pData: Pointer;var Item: TListItem);
var
pSocket: ^ TSocketStatusInfo;
begin
// 这两个有什么不同?
// CopyMemory(pSocket,pData,sizeof(TSocketStatusInfo));
pSocket : = pData;
Item.Caption : = IntToStr(pSocket.GroupID);
Item.SubItems.Add(IntToStr(pSocket.UniqueID));
Item.SubItems.Add(pSocket.IPAddress);
Item.SubItems.Add(pSocket.SubItemName);
Item.SubItems.Add(pSocket.LoginTime);
Item.SubItems.Add(pSocket.SendNumber);
Item.SubItems.Add(pSocket.RecNumber);
Item.SubItems.Add(pSocket.Remark);
end;
procedure TLVQuickSocket.UpdateLVSocketQuick( const aUniqueID:integer;
const aSocketStatus: TSocketStatusInfo;var lvTemp:TListView);
begin
UpdateItemDataToListView(aUniqueID,@aSocketStatus,sizeof(aSocketStatus),lvTemp);
end;
end.
小结:
应用此类结构实现ListView快速数据操作的优势:
1、 可以快速实现MVC模式,达到界面显示与业务逻辑的分离。在Controllor类中,实例化数据显示子类,调用相应方法即可实现显示数据的增、删、改。
2、 与原始的快速方法相比,封装了内存数据List,大大简化了List对象的操作(尤其是当有很多相同或者类似数据要求在不同Form的ListView中显示时),并减少了List的创建、数据操作、释放等操作时发生错误的可能性。
3、 简化了多个相同、类似数据显示的控制代码,针对每个份要显示的数据及ListView,只需要实例化一个显示子类,避免了直接应用原始的快速方法时,控制代码分散在每一个具体Form类中的问题。
4、 对显示数据的业务信息份进行了集中,当要求显示的信息发生变化时,只需在数据显示子类这一个类中更改即可。
此通用类结构仍有些不足之处,欢迎有兴趣的朋友继续完善:
1、每个应用层类的外部调用方式非常类似(请参考开发的示例源码),表明,有些通用的方法没有进行更好的抽象。
2、快速访问基类对其子类的行为抽象不充分,导致子类的应用层调用代码非常类似。当初这样设计的目的是想保持类结构有充分的可扩展性。其实完全可以将基类进行改良:将抽象的虚方法更换成接口,这样,基类实现的更好的封装,并且更好地满足了“向稳定的方向依赖”和“针对接口编程”的设计原则。这样,应用层还是要实例化一个自己业务需要的类来实现此接口。但Delphi的接口用起来不像是真正的接口,通常要从TInterfacedObject等类继承,大大限制了类结构层次的演化。(因为在不支持多继承的语言环境中,如果还想增加更高层次的抽象就不那么容易了)。
3、当前的版本还没有提供针对某列进行数据排序的功能。
//
// 产品名称: 成晓旭的个人软件Delphi源码库
// 产品版本: CXXSoft delphi code source lib 1.0
// 模块名称: Delphi之ListView显示控制类---外部应用层通用类定义单元
// 模块描述: ListView快速操作方法类
// 单元文件: unLVCommonClass.pas--->unLVQuickClass.pas
// 开发作者: 成晓旭
// 备注: 任何人使用此文件时,请保留此段自述文件,谢谢!
// 开发时间: 2005-09-26
// 修改历史: 2006-05-31
// 修改描述: 解决GetLVItemOrderByValue()的参数不灵活的问题
// 并解决Method 'Create' hides virtual method of base type 'TComponent'的问题
// 修改历史: 2006-06-16
// 修改描述: 增加通过TList来高速增加、更新、删除数据
// 先用吧,以后再优化和完善
// 修改历史: 2006-07-10
// 修改描述: 成功地将ListView的OnData事件的List对象移入此类中,并将其上移到其类
// 修改历史: 2006-07-11
// 修改描述: 重大重构:将此类分成两个类:TLVCommonClass-->TLVCommonClass和TLVQuickClass,
// 以遵循SRP原则
// ------------------------------------------------------------------------------
unit unLVQuickClass;
interface
uses
SysUtils,Classes,ComCtrls,Windows,Variants,
unLVControler;
const
SpecialItemOrder = - 1 ;
CSImg_Normal = 0 ;
CSImg_Running = 1 ;
GetColumnOrder = 0 ;
type
TLVQuickClass = class (TInterfacedObject)
private
cxxLVC:TCXXLVControler;
lvDataList: TList;
// 为ListView的Item的Data属性查询关键数据在TList中的索引号
class function GetListDataIndexByKey( const listData:TList; const aKey: variant):integer;
// 快速
procedure ClearLVDataList();
protected
// 注意:约定最后一项一定是ImageIndex
// tmpList:TStringList;
function InitListViewColumns( const displayName: array of string;
const displayWidth: array of integer;
var lvTemp:TListView): boolean ;
// 子类必须实现
class function CheckFound( const pData:Pointer; const aKey:variant): boolean ;virtual; abstract ;
class procedure ProcOnDataDetail( const pData:Pointer;var Item: TListItem);virtual; abstract ;
// 快速方法
// [注意:新方法中lvList不要求传递参数,保留以兼容旧客户端版本]
// 将pData数据加入List中,实现向ListView增加数据行
function AddItemDataToListView( const pData:Pointer; const dataNumber:integer;
var lvTemp:TListView): boolean ;
// ; var lvList: TList=nil):boolean;
// 以aKey为关键值查找,并用pData数据更新List中满足条件的内存块,实现向ListView更新数据行
function UpdateItemDataToListView( const aKey:variant; const pData:Pointer; const dataNumber:integer;
var lvTemp:TListView): boolean ;
// ; var lvList: TList=nil):boolean;
// 以aKey为关键值查找,并用pData数据删除List中满足条件的内存块,实现向ListView删除数据行
function DeleteItemDataToListView( const aKey:variant; const pData:Pointer; const dataNumber:integer;
var lvTemp:TListView): boolean ;
// ; var lvList: TList=nil):boolean;
// 删除List中所有内存区,实现将ListView清空所有数据行
procedure DeleteAllListView(var lvTemp:TListView);
// ListVisw的OnData事件处理
procedure OnDataToListView(var Item: TListItem);
// ListVisw的OnData事件的List Count
function GetLVListCount():integer;
public
constructor Create();overload;
destructor Destroy();override;
end;
implementation
... { TLVQuickClass }
function TLVQuickClass.AddItemDataToListView( const pData: Pointer;
const dataNumber: integer; var lvTemp: TListView): boolean ;
// ; var lvList: TList):boolean;
var
pTemp:Pointer;
begin
// 注意:要影响性能
// lvTemp.Items.BeginUpdate();
Result : = false ;
if NOT (Assigned(pData) and Assigned(lvTemp) and Assigned(lvDataList)) then Exit;
// new(pTemp);
GetMem(pTemp,dataNumber);
lvTemp.OwnerData : = true ;
CopyMemory(pTemp,pData,dataNumber);
lvDataList.Add(pTemp);
Result : = true ;
// 注意:要影响性能
// lvTemp.Items.Count := lvList.Count;
// lvTemp.Items.EndUpdate();
end;
procedure TLVQuickClass.ClearLVDataList();
var
Loop:integer;
pTemp:Pointer;
begin
if NOT (Assigned(lvDataList) and (lvDataList.Count > 0 )) then Exit;
for Loop : = 0 to lvDataList.Count - 1 do
begin
pTemp : = (lvDataList.Items[Loop]);
if Assigned(pTemp) then
FreeMem(pTemp);
end;
end;
constructor TLVQuickClass.Create;
begin
inherited Create();
cxxLVC : = TCXXLVControler.Create();
// tmpList := TStringList.Create();
lvDataList : = TList.Create();
end;
procedure TLVQuickClass.DeleteAllListView(var lvTemp: TListView);
begin
if NOT (Assigned(lvTemp) and Assigned(lvDataList) and (lvDataList.Count > 0 )) then Exit;
lvTemp.OwnerData : = true ;
lvTemp.Items.BeginUpdate();
while (lvDataList.Count > 0 ) do
begin
Dispose(lvDataList[ 0 ]);
lvDataList.Delete( 0 );
end;
lvTemp.Items.Count : = lvDataList.Count;
lvTemp.Items.EndUpdate();
end;
function TLVQuickClass.DeleteItemDataToListView( const aKey: variant;
const pData: Pointer; const dataNumber: integer; var lvTemp: TListView): boolean ;
// var lvList: TList): boolean;
var
colIndex:integer;
begin
// 注意:要影响性能
// lvTemp.Items.BeginUpdate();
Result : = false ;
if NOT (Assigned(pData) and Assigned(lvTemp) and Assigned(lvDataList) and (lvDataList.Count > 0 )) then Exit;
colIndex : = GetListDataIndexByKey(lvDataList,aKey);
if (colIndex <> SpecialItemOrder) and (colIndex >= 0 ) and (colIndex <= lvDataList.Count - 1 ) then
begin
Dispose(lvDataList[colIndex]);
lvDataList.Delete(colIndex);
Result : = true ;
end;
// 注意:要影响性能
// lvTemp.Items.Count := lvList.Count;
// lvTemp.Items.EndUpdate();
end;
destructor TLVQuickClass.Destroy;
begin
ClearLVDataList();
if Assigned(lvDataList) then
FreeAndNil(lvDataList);
if Assigned(cxxLVC) then
FreeAndNil(cxxLVC);
// if Assigned(tmpList) then
// FreeAndNil(tmpList);
inherited Destroy;
end;
class function TLVQuickClass.GetListDataIndexByKey( const listData: TList;
const aKey: variant): integer;
var
Loop:integer;
begin
Result : = SpecialItemOrder;
if NOT Assigned(listData) then Exit;
if listData.Count <= 0 then Exit;
for Loop : = 0 to listData.Count - 1 do
begin
if CheckFound(listData[Loop],aKey) then
begin
Result : = Loop;
break ;
end;
end;
end;
function TLVQuickClass.GetLVListCount(): integer;
begin
Result : = 0 ;
if Assigned(lvDataList) then
Result : = lvDataList.Count;
end;
function TLVQuickClass.InitListViewColumns( const displayName: array of string;
const displayWidth: array of integer; var lvTemp: TListView): boolean ;
begin
Result : = false ;
if Assigned(cxxLVC) then
begin
cxxLVC.InitLVColumns(displayName,displayWidth,lvTemp);
end;
end;
procedure TLVQuickClass.OnDataToListView(var Item: TListItem);
var
pTemp:Pointer;
begin
// 以后要移到类中,做成回调
if NOT Assigned(lvDataList) then Exit;
if lvDataList.Count <= 0 then Exit;
if Item.Index > lvDataList.Count then Exit;
pTemp : = lvDataList[Item.Index];
ProcOnDataDetail(pTemp,Item);
Item.ImageIndex : = 1 ;
Item.Data : = pTemp;
end;
function TLVQuickClass.UpdateItemDataToListView( const aKey: variant;
const pData: Pointer; const dataNumber: integer; var lvTemp: TListView): boolean ;
// var lvList: TList):boolean;
var
colIndex:integer;
begin
// 注意:要影响性能
// lvTemp.Items.BeginUpdate();
Result : = false ;
if NOT (Assigned(pData) and Assigned(lvTemp) and Assigned(lvDataList) and (lvDataList.Count > 0 )) then Exit;
colIndex : = GetListDataIndexByKey(lvDataList,aKey);
if (colIndex <> SpecialItemOrder) and (colIndex >= 0 ) and (colIndex <= lvDataList.Count - 1 ) then
begin
CopyMemory(lvDataList[colIndex],pData,dataNumber);
Result : = true ;
end;
// 注意:要影响性能
// lvTemp.Items.Count := lvList.Count;
// lvTemp.Items.EndUpdate();
end;
end.
//
// 产品名称: 成晓旭的个人软件Delphi源码库
// 产品版本: CXXSoft delphi code source lib 1.0
// 模块名称: Delphi之ListView显示控制类---应用层:Soft socket类定义单元
// 模块描述:
// 单元文件: unLVSoftSocket.pas-->unLVSocket.pas
// 开发作者: 成晓旭
// 备注: 任何人使用此文件时,请保留此段自述文件,谢谢!
// 开发时间: 2005-09-26
//
// 修改历史: 2006-06-16
// 修改描述: 增加通过TList来高速增加、更新、删除数据
// 先用吧,以后再优化和完善
// 修改历史: 2006-07-10
// 修改描述: 成功地将ListView的OnData事件的List对象移入此类中
// 修改历史: 2006-07-11
// 修改描述: 重大重构:将此类分成两个类:TLVSoftSocket-->TLVSocket和TLVQuickClass
// 以遵循SRP原则
// ------------------------------------------------------------------------------
unit unLVSocket;
interface
uses
ComCtrls,Classes,SysUtils,Windows,
unLVCommonClass,unLVDefine;
type
TLVSocket = class (TLVCommonClass)
private
// 注意:此方法与顺序严重藕合
function SaveSerialStatusToStringList( const aSocketStatus:TSocketStatusInfo): boolean ;
protected
class function CheckFound( const pData:Pointer; const aKey:variant): boolean ;override;
class procedure ProcOnDataDetail( const pData:Pointer;var Item: TListItem);override;
public
constructor Create();
destructor Destroy();override;
procedure InitListView(var lvTemp:TListView);
// 传统方法
procedure AddToLVSocket( const aSocketStatus:TSocketStatusInfo;var lvTemp:TListView);
procedure UpdateLVSocket( const aSocketStatus:TSocketStatusInfo;var lvTemp:TListView);
procedure DeleteLVSocket( const aSocketStatus:TSocketStatusInfo;var lvTemp:TListView);
end;
implementation
... { TLVSocket }
procedure TLVSocket.AddToLVSocket(
const aSocketStatus: TSocketStatusInfo; var lvTemp: TListView);
begin
if SaveSerialStatusToStringList(aSocketStatus) then
begin
AddDataToListView(tmpList,lvTemp);
end;
end;
class function TLVSocket.CheckFound( const pData: Pointer;
const aKey: variant): boolean ;
var
p: ^ TSocketStatusInfo;
begin
p : = pData;
Result : = (p.UniqueID = aKey);
end;
constructor TLVSocket.Create;
begin
inherited Create();
end;
procedure TLVSocket.DeleteLVSocket(
const aSocketStatus: TSocketStatusInfo; var lvTemp: TListView);
begin
if SaveSerialStatusToStringList(aSocketStatus) then
begin
DeleteDataFromListView(tmpList,lvTemp);
end;
end;
destructor TLVSocket.Destroy;
begin
inherited Destroy;
end;
procedure TLVSocket.InitListView(var lvTemp: TListView);
begin
InitListViewColumns(SocketStrBuffer,SocketWidthBuffer,lvTemp);
end;
class procedure TLVSocket.ProcOnDataDetail( const pData: Pointer;var Item: TListItem);
var
pSocket: ^ TSocketStatusInfo;
begin
// 这两个有什么不同?
// CopyMemory(pSocket,pData,sizeof(TSocketStatusInfo));
pSocket : = pData;
Item.Caption : = IntToStr(pSocket.GroupID);
Item.SubItems.Add(IntToStr(pSocket.UniqueID));
Item.SubItems.Add(pSocket.IPAddress);
// Item.SubItems.Add(IntToStr(pSocket.CommServerPort));
Item.SubItems.Add(pSocket.SubItemName);
Item.SubItems.Add(pSocket.LoginTime);
Item.SubItems.Add(pSocket.SendNumber);
Item.SubItems.Add(pSocket.RecNumber);
Item.SubItems.Add(pSocket.Remark);
// Item.SubItems.Add(pSocket.IPAddress);
// Item.SubItems.Add(pSocket.IPAddress);
end;
function TLVSocket.SaveSerialStatusToStringList(
const aSocketStatus: TSocketStatusInfo): boolean ;
begin
Result : = false ;
if Assigned(tmpList) then
begin
tmpList.Clear();
tmpList.Add(IntToStr(aSocketStatus.GroupID));
tmpList.Add(IntToStr(aSocketStatus.UniqueID));
tmpList.Add(aSocketStatus.IPAddress);
tmpList.Add(aSocketStatus.SubItemName);
tmpList.Add(aSocketStatus.LoginTime);
tmpList.Add(aSocketStatus.SendNumber);
tmpList.Add(aSocketStatus.RecNumber);
tmpList.Add(IntToStr(CSImg_Running));
Result : = true ;
end;
end;
procedure TLVSocket.UpdateLVSocket(
const aSocketStatus: TSocketStatusInfo; var lvTemp: TListView);
begin
if SaveSerialStatusToStringList(aSocketStatus) then
begin
UpdateDataFromListView(tmpList,lvTemp);
end;
end;
end.
//
// 产品名称: 成晓旭的个人软件Delphi源码库
// 产品版本: CXXSoft delphi code source lib 1.0
// 模块名称: Delphi之ListView显示控制类---外部应用层通用类定义单元
// 模块描述: ListView传统操作方法类
// 单元文件: unLVCommonClass.pas
// 开发作者: 成晓旭
// 备注: 任何人使用此文件时,请保留此段自述文件,谢谢!
// 开发时间: 2005-09-26
// 修改历史: 2006-05-31
// 修改描述: 解决GetLVItemOrderByValue()的参数不灵活的问题
// 并解决Method 'Create' hides virtual method of base type 'TComponent'的问题
// 修改历史: 2006-06-16
// 修改描述: 增加通过TList来高速增加、更新、删除数据
// 先用吧,以后再优化和完善
// 修改历史: 2006-07-10
// 修改描述: 成功地将ListView的OnData事件的List对象移入此类中,并将其上移到其类
// 修改历史: 2006-07-11
// 修改描述: 重大重构:将此类分成两个类:TLVCommonClass-->TLVCommonClass和TLVQuickClass
// 以遵循SRP原则
// ------------------------------------------------------------------------------
unit unLVCommonClass;
interface
uses
SysUtils,Classes,ComCtrls,Windows,Variants,
unLVControler,unLVInterface;
const
SpecialItemOrder = - 1 ;
CSImg_Normal = 0 ;
CSImg_Running = 1 ;
GetColumnOrder = 0 ;
type
TLVCommonClass = class (TInterfacedObject,ILVControllor)
private
cxxLVC:TCXXLVControler;
class function StrIsNumber( const str:PChar): boolean ;
function GetLVItemOrderByValue( const lvTemp:TListView; const aKeyValue:string; const columnOrder:integer = SpecialItemOrder):integer;
procedure CommonListItemProcess( const dataList: TStringList;var liTemp:TListItem; const isUpdate: boolean );
protected
// 注意:约定最后一项一定是ImageIndex
tmpList:TStringList;
function InitListViewColumns( const displayName: array of string;
const displayWidth: array of integer;
var lvTemp:TListView): boolean ;
function AddDataToListView( const dataList:TStringList;var lvTemp:TListView): boolean ;
function UpdateDataFromListView( const dataList:TStringList;var lvTemp:TListView): boolean ;
// 注意:删除的键值默认以SubItem[0]为标准
function DeleteDataFromListView( const dataList:TStringList;var lvTemp:TListView): boolean ;
// 子类必须实现
class function CheckFound( const pData:Pointer; const aKey:variant): boolean ;virtual; abstract ;
class procedure ProcOnDataDetail( const pData:Pointer;var Item: TListItem);virtual; abstract ;
public
constructor Create();overload;
destructor Destroy();override;
end;
implementation
... { TLVCommonClass }
function TLVCommonClass.AddDataToListView( const dataList: TStringList;
var lvTemp: TListView): boolean ;
var
liTemp:TListItem;
begin
Result : = false ;
if dataList.Count > 0 then
begin
lvTemp.Items.BeginUpdate();
liTemp : = lvTemp.Items.Add();
CommonListItemProcess(dataList,liTemp, false );
// liTemp.Caption := dataList.Strings[dataIndex];
// Inc(dataIndex);
// while (dataIndex < dataList.Count) do
// begin
// liTemp.SubItems.Add(dataList.Strings[dataIndex]);
// Inc(dataIndex);
// end;
lvTemp.Items.EndUpdate();
Result : = true ;
end;
end;
procedure TLVCommonClass.CommonListItemProcess( const dataList: TStringList;
var liTemp: TListItem; const isUpdate: boolean );
var
dataIndex:integer;
begin
dataIndex : = 0 ;
liTemp.Caption : = dataList.Strings[dataIndex];
Inc(dataIndex);
while (dataIndex < dataList.Count) do
begin
if (dataIndex = dataList.Count - 1 ) and StrIsNumber(PChar(dataList.Strings[dataIndex])) then
begin
liTemp.ImageIndex : = StrToInt(dataList.Strings[dataIndex]);
end
else
begin
if isUpdate then
liTemp.SubItems[dataIndex - 1 ] : = dataList.Strings[dataIndex]
else
liTemp.SubItems.Add(dataList.Strings[dataIndex]);
end;
Inc(dataIndex);
end;
end;
constructor TLVCommonClass.Create;
begin
inherited Create();
cxxLVC : = TCXXLVControler.Create();
tmpList : = TStringList.Create();
end;
function TLVCommonClass.DeleteDataFromListView(
const dataList: TStringList; var lvTemp: TListView): boolean ;
var
colIndex:integer;
// liTemp:TListItem;
begin
Result : = false ;
if dataList.Count > 0 then
begin
colIndex : = GetLVItemOrderByValue(lvTemp,dataList.Strings[GetColumnOrder + 1 ],GetColumnOrder);
if (colIndex <> SpecialItemOrder) and Assigned(cxxLVC)then
begin
cxxLVC.DeleteItemByIndex(lvTemp,colIndex);
Result : = true ;
end;
end;
end;
destructor TLVCommonClass.Destroy;
begin
if Assigned(cxxLVC) then
FreeAndNil(cxxLVC);
if Assigned(tmpList) then
FreeAndNil(tmpList);
inherited Destroy;
end;
function TLVCommonClass.GetLVItemOrderByValue( const lvTemp: TListView;
const aKeyValue: string; const columnOrder: integer): integer;
var
Loop:integer;
isOK: boolean ;
begin
Result : = SpecialItemOrder;
isOK : = false ;
for Loop : = 0 to lvTemp.Items.Count - 1 do
begin
if columnOrder <> SpecialItemOrder then
begin
if (columnOrder >= 0 ) and (columnOrder < lvTemp.Columns.Count) then
isOK : = (lvTemp.Items[Loop].SubItems[columnOrder] = aKeyValue);
end
else
isOK : = (lvTemp.Items[Loop].Caption = aKeyValue);
if isOK then
begin
Result : = Loop;
break ;
end;
end;
end;
function TLVCommonClass.InitListViewColumns( const displayName: array of string;
const displayWidth: array of integer; var lvTemp: TListView): boolean ;
begin
Result : = false ;
if Assigned(cxxLVC) then
begin
cxxLVC.InitLVColumns(displayName,displayWidth,lvTemp);
end;
end;
class function TLVCommonClass.StrIsNumber( const str: PChar): boolean ;
var
p:Char;
i:integer;
function CheckHex(p:Char): boolean ;
var
k:Byte;
begin
k : = Ord(P);
Result : = ((k >= 48 ) and (k <= 57 )) or ((k >= 65 ) and (k <= 70 )) or ((k >= 97 ) and (k <= 102 ));
end;
begin
try
Result : = false ;
for i : = 0 to Length(str) - 1 do
begin
p : = str[i];
Result : = CheckHex(p);
if NOT Result then
break ;
end;
except
Result : = false ;
end;
end;
function TLVCommonClass.UpdateDataFromListView(
const dataList: TStringList; var lvTemp: TListView): boolean ;
var
colIndex:integer;
liTemp:TListItem;
begin
Result : = false ;
if dataList.Count > 0 then
begin
colIndex : = GetLVItemOrderByValue(lvTemp,dataList.Strings[GetColumnOrder + 1 ],GetColumnOrder);
if (colIndex <> SpecialItemOrder) then
begin
lvTemp.Items.BeginUpdate();
liTemp : = lvTemp.Items[colIndex];
CommonListItemProcess(dataList,liTemp, true );
// liTemp.Caption := dataList.Strings[dataIndex];
// Inc(dataIndex);
// while (dataIndex < dataList.Count) do
// begin
// liTemp.SubItems[dataIndex-1] := dataList.Strings[dataIndex];
// Inc(dataIndex);
// end;
lvTemp.Items.EndUpdate();
Result : = true ;
end;
end;
end;
end.