在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
Delphi ListView快速操作通用实现
作者:成晓旭
众所周知,Delphi ListView类直接进行Add、Update、Delete操作的速度是比较慢的,尤其是当数据量较大时,比如数据量达到5000、10000、50000时,速度真是可以说是“慢得惊人”。其实快速操作的方法非常简单,就当大家都知道了。在本人的工作中,很多项目都用到ListView,并且对速度的响应要求比较高,于是发生了快速操作ListView的代码散布于多个项目大量模块中的问题,并且,当界面层数据显示要求发生改变时,自然发生“重复性代码的通用问题”。考虑到对以前版本的兼容性问题,也一直没有引用第三方的成熟组件。鉴于:“程序中的重复代码最易引发问题,必须消除”的实践经验,自己设计了一个比较通用的解决此类问题的类结构。当然,远不是什么“通用框架”了(眼下市面上很多人喜欢把自己做的无论什么东西通称为框架)。在采用此结构的项目中,很容易实现MVC模式,达到业务逻辑与界面显示分离的低级的、基础的要求。 (因为,目前CSDN的软件上传功能不可用,我只有将部分代码片断放在文档中,有需要完整源码者,请留言) 类层次结构:
ListView基础操作封装在LVControler包中,核心的类是TCXXLVControler: (说明:LVControler类是被封装通用类结构内,外部用户是不需要了解和访问的,所以不作介绍。)
<shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="[email protected]@[email protected]@[email protected]@[email protected]@5xe" filled="f" stroked="f"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"></path><lock v:ext="edit" aspectratio="t"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 414.75pt; HEIGHT: 281.25pt" type="#_x0000_t75"><imagedata src="file:///C:/DOCUME~1/CHENGX~1/LOCALS~1/Temp/msohtml1/01/clip_image001.png" o:title="LVControler"></imagedata></shape> 传统的ListView操作基类是TLVCommonClass,如果想用传统的方法增、删、改ListView中的数据,可以直接从此类继承。
<shape id="_x0000_i1026" style="WIDTH: 414.75pt; HEIGHT: 372pt" type="#_x0000_t75"><imagedata src="file:///C:/DOCUME~1/CHENGX~1/LOCALS~1/Temp/msohtml1/01/clip_image003.png" o:title="CommonClass"></imagedata></shape> 源码如下:
具体的一个从此类继承下来的用于Socket界面显示的TLVSocket的类Overvivw如下:
<shape id="_x0000_i1027" style="WIDTH: 367.5pt; HEIGHT: 315pt" type="#_x0000_t75"><imagedata src="file:///C:/DOCUME~1/CHENGX~1/LOCALS~1/Temp/msohtml1/01/clip_image005.png" o:title="commonSocket"></imagedata></shape> 源码如下:
快速的ListView操作基类是TLVQuickClass,如果想用快速方法增、删、改ListView中的数据,可以直接从此类继承。
<shape id="_x0000_i1028" style="WIDTH: 414.75pt; HEIGHT: 397.5pt" type="#_x0000_t75"><imagedata src="file:///C:/DOCUME~1/CHENGX~1/LOCALS~1/Temp/msohtml1/01/clip_image007.png" o:title="quickClass"></imagedata></shape> 主要方法:(可以看到,里面没有真正的Public方法,子类也仅需实现两个Protected的virtual方法)
<shape id="_x0000_i1057" style="WIDTH: 414.75pt; HEIGHT: 370.5pt" type="#_x0000_t75"><imagedata src="file:///C:/DOCUME~1/CHENGX~1/LOCALS~1/Temp/msohtml1/01/clip_image012.png" o:title="method"></imagedata></shape> 源码如下:
此类中,要求每个具体应用的子类必须实现的方法仅有两个:CheckFound():根据具体应用检测的数据是否已经存在;ProcOnDataDetail():客户端ListView的OnData()事件的数据处理回调方法。下面是几个具体实现的子类的OverView:
<shape id="_x0000_i1055" style="WIDTH: 414.75pt; HEIGHT: 303.75pt" type="#_x0000_t75"><imagedata src="file:///C:/DOCUME~1/CHENGX~1/LOCALS~1/Temp/msohtml1/01/clip_image014.png" o:title="Quick Client"></imagedata></shape> 具体的一个从此类继承下来的用于Socket界面显示的TLVQuickSocket的类Overvivw如下:
<shape id="_x0000_i1056" style="WIDTH: 415.5pt; HEIGHT: 291pt" type="#_x0000_t75"><imagedata src="file:///C:/DOCUME~1/CHENGX~1/LOCALS~1/Temp/msohtml1/01/clip_image016.png" o:title="quick socket"></imagedata></shape> 可以看出:子类实现了两个抽象的虚方法,其它的方法,都是根据业务需要,类用户自行增加的。 源码如下:
//------------------------------------------------------------------------------
// //产品名称:成晓旭的个人软件Delphi源码库 //产品版本:CXXSoftdelphicodesourcelib1.0 //模块名称:Delphi之ListView显示控制类---应用层:Softsocket类定义单元 //模块描述: //单元文件:unLVSoftSocket.pas-->unLVQuickSocket.pas //开发作者:成晓旭 //备注:任何人使用此文件时,请保留此段自述文件,谢谢! //开发时间:2005-09-26 // //修改历史:2006-06-16 //修改描述:增加通过TList来高速增加、更新、删除数据 //先用吧,以后再优化和完善 //修改历史:2006-07-10 //修改描述:成功地将ListView的OnData事件的List对象移入此类中 //修改历史:2006-07-11 //修改描述:重大重构:将此类分成两个类:TLVSoftSocket-->TLVSoftSocket和TLVQuickSocket //以遵循SRP原则 //------------------------------------------------------------------------------ unitunLVQuickSocket; interface uses ComCtrls,Classes,SysUtils,Windows, unLVQuickClass,unLVDefine; type TLVQuickSocket=class(TLVQuickClass) private protected classfunctionCheckFound(constpData:Pointer;constaKey:variant):boolean;override; classprocedureProcOnDataDetail(constpData:Pointer;varItem:TListItem);override; public constructorCreate(); destructorDestroy();override; procedureInitListView(varlvTemp:TListView); //快速方法 //暂时这样增加,以后在重构到基类中 procedureAddToLVSocketQuick(constaSocketStatus:TSocketStatusInfo;varlvTemp:TListView); procedureUpdateLVSocketQuick(constaUniqueID:integer;constaSocketStatus:TSocketStatusInfo;varlvTemp:TListView); procedureDeleteLVSocketQuick(constaUniqueID:integer;constaSocketStatus:TSocketStatusInfo;varlvTemp:TListView); procedureDeleteAllLVSocket(varlvTemp:TListView); procedureOnDataEvent(Item:TListItem); functionGetDataCount():integer; end; implementation ...{TLVQuickSocket} procedureTLVQuickSocket.AddToLVSocketQuick( constaSocketStatus:TSocketStatusInfo;varlvTemp:TListView); begin AddItemDataToListView(@aSocketStatus,sizeof(aSocketStatus),lvTemp); end; classfunctionTLVQuickSocket.CheckFound(constpData:Pointer; constaKey:variant):boolean; var p:^TSocketStatusInfo; begin p:=pData; Result:=(p.UniqueID=aKey); end; constructorTLVQuickSocket.Create; begin inheritedCreate(); end; procedureTLVQuickSocket.DeleteAllLVSocket(varlvTemp:TListView); begin DeleteAllListView(lvTemp); end; procedureTLVQuickSocket.DeleteLVSocketQuick(constaUniqueID:integer; constaSocketStatus:TSocketStatusInfo;varlvTemp:TListView); begin DeleteItemDataToListView(aUniqueID,@aSocketStatus,sizeof(aSocketStatus),lvTemp); end; destructorTLVQuickSocket.Destroy; begin inheritedDestroy; end; functionTLVQuickSocket.GetDataCount():integer; begin Result:=Self.GetLVListCount(); end; procedureTLVQuickSocket.InitListView(varlvTemp:TListView); begin InitListViewColumns(SocketStrBuffer,SocketWidthBuffer,lvTemp); end; procedureTLVQuickSocket.OnDataEvent(Item:TListItem); begin Self.OnDataToListView(Item); end; classprocedureTLVQuickSocket.ProcOnDataDetail(constpData:Pointer;varItem: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; procedureTLVQuickSocket.UpdateLVSocketQuick(constaUniqueID:integer; constaSocketStatus:TSocketStatusInfo;varlvTemp:TListView); begin UpdateItemDataToListView(aUniqueID,@aSocketStatus,sizeof(aSocketStatus),lvTemp); end; end. 小结:
应用此类结构实现ListView快速数据操作的优势:
<!--[if !supportLists]-->1、 <!--[endif]-->可以快速实现MVC模式,达到界面显示与业务逻辑的分离。在Controllor类中,实例化数据显示子类,调用相应方法即可实现显示数据的增、删、改。 <!--[if !supportLists]-->2、 <!--[endif]-->与原始的快速方法相比,封装了内存数据List,大大简化了List对象的操作(尤其是当有很多相同或者类似数据要求在不同Form的ListView中显示时),并减少了List的创建、数据操作、释放等操作时发生错误的可能性。 <!--[if !supportLists]-->3、 <!--[endif]-->简化了多个相同、类似数据显示的控制代码,针对每个份要显示的数据及ListView,只需要实例化一个显示子类,避免了直接应用原始的快速方法时,控制代码分散在每一个具体Form类中的问题。 <!--[if !supportLists]-->4、 <!--[endif]-->对显示数据的业务信息份进行了集中,当要求显示的信息发生变化时,只需在数据显示子类这一个类中更改即可。 此通用类结构仍有些不足之处,欢迎有兴趣的朋友继续完善:
<!--[if !supportLists]-->1、<!--[endif]-->每个应用层类的外部调用方式非常类似(请参考开发的示例源码),表明,有些通用的方法没有进行更好的抽象。 <!--[if !supportLists]-->2、<!--[endif]-->快速访问基类对其子类的行为抽象不充分,导致子类的应用层调用代码非常类似。当初这样设计的目的是想保持类结构有充分的可扩展性。其实完全可以将基类进行改良:将抽象的虚方法更换成接口,这样,基类实现的更好的封装,并且更好地满足了“向稳定的方向依赖”和“针对接口编程”的设计原则。这样,应用层还是要实例化一个自己业务需要的类来实现此接口。但Delphi的接口用起来不像是真正的接口,通常要从TInterfacedObject等类继承,大大限制了类结构层次的演化。(因为在不支持多继承的语言环境中,如果还想增加更高层次的抽象就不那么容易了)。 <!--[if !supportLists]-->3、<!--[endif]-->当前的版本还没有提供针对某列进行数据排序的功能。
//------------------------------------------------------------------------------
// //产品名称:成晓旭的个人软件Delphi源码库 //产品版本:CXXSoftdelphicodesourcelib1.0 //模块名称:Delphi之ListView显示控制类---外部应用层通用类定义单元 //模块描述:ListView快速操作方法类 //单元文件:unLVCommonClass.pas--->unLVQuickClass.pas //开发作者:成晓旭 //备注:任何人使用此文件时,请保留此段自述文件,谢谢! //开发时间:2005-09-26 //修改历史:2006-05-31 //修改描述:解决GetLVItemOrderByValue()的参数不灵活的问题 //并解决Method'Create'hidesvirtualmethodofbasetype'TComponent'的问题 //修改历史:2006-06-16 //修改描述:增加通过TList来高速增加、更新、删除数据 //先用吧,以后再优化和完善 //修改历史:2006-07-10 //修改描述:成功地将ListView的OnData事件的List对象移入此类中,并将其上移到其类 //修改历史:2006-07-11 //修改描述:重大重构:将此类分成两个类:TLVCommonClass-->TLVCommonClass和TLVQuickClass, //以遵循SRP原则 //------------------------------------------------------------------------------ unitunLVQuickClass; 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中的索引号 classfunctionGetListDataIndexByKey(constlistData:TList;constaKey:variant):integer; //快速 procedureClearLVDataList(); protected //注意:约定最后一项一定是ImageIndex //tmpList:TStringList; functionInitListViewColumns(constdisplayName:arrayofstring; constdisplayWidth:arrayofinteger; varlvTemp:TListView):boolean; //子类必须实现 classfunctionCheckFound(constpData:Pointer;constaKey:variant):boolean;virtual;abstract; classprocedureProcOnDataDetail(constpData:Pointer;varItem:TListItem);virtual;abstract; //快速方法 //[注意:新方法中lvList不要求传递参数,保留以兼容旧客户端版本] //将pData数据加入List中,实现向ListView增加数据行 functionAddItemDataToListView(constpData:Pointer;constdataNumber:integer; varlvTemp:TListView):boolean; //;varlvList:TList=nil):boolean; //以aKey为关键值查找,并用pData数据更新List中满足条件的内存块,实现向ListView更新数据行 functionUpdateItemDataToListView(constaKey:variant;constpData:Pointer;constdataNumber:integer; varlvTemp:TListView):boolean; //;varlvList:TList=nil):boolean; //以aKey为关键值查找,并用pData数据删除List中满足条件的内存块,实现向ListView删除数据行 functionDeleteItemDataToListView(constaKey:variant;constpData:Pointer;constdataNumber:integer; varlvTemp:TListView):boolean; //;varlvList:TList=nil):boolean; //删除List中所有内存区,实现将ListView清空所有数据行 procedureDeleteAllListView(varlvTemp:TListView); //ListVisw的OnData事件处理 procedureOnDataToListView(varItem:TListItem); //ListVisw的OnData事件的ListCount functionGetLVListCount():integer; public constructorCreate();overload; destructorDestroy();override; end; implementation ...{TLVQuickClass} functionTLVQuickClass.AddItemDataToListView(constpData:Pointer; constdataNumber:integer;varlvTemp:TListView):boolean; //;varlvList:TList):boolean; var pTemp:Pointer; begin //注意:要影响性能 //lvTemp.Items.BeginUpdate(); Result:=false; ifNOT(Assigned(pData)andAssigned(lvTemp)andAssigned(lvDataList))thenExit; //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; procedureTLVQuickClass.ClearLVDataList(); var Loop:integer; pTemp:Pointer; begin ifNOT(Assigned(lvDataList)and(lvDataList.Count>0))thenExit; forLoop:=0tolvDataList.Count-1do begin pTemp:=(lvDataList.Items[Loop]); ifAssigned(pTemp)then FreeMem(pTemp); end; end; constructorTLVQuickClass.Create; begin inheritedCreate(); cxxLVC:=TCXXLVControler.Create(); //tmpList:=TStringList.Create(); lvDataList:=TList.Create(); end; procedureTLVQuickClass.DeleteAllListView(varlvTemp:TListView); begin ifNOT(Assigned(lvTemp)andAssigned(lvDataList)and(lvDataList.Count>0))thenExit; 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; functionTLVQuickClass.DeleteItemDataToListView(constaKey:variant; constpData:Pointer;constdataNumber:integer;varlvTemp:TListView):boolean; //varlvList:TList):boolean; var colIndex:integer; begin //注意:要影响性能 //lvTemp.Items.BeginUpdate(); Result:=false; ifNOT(Assigned(pData)andAssigned(lvTemp)andAssigned(lvDataList)and(lvDataList.Count>0))thenExit; 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; destructorTLVQuickClass.Destroy; begin ClearLVDataList(); ifAssigned(lvDataList)then FreeAndNil(lvDataList); ifAssigned(cxxLVC)then FreeAndNil(cxxLVC); //ifAssigned(tmpList)then //FreeAndNil(tmpList); inheritedDestroy; end; classfunctionTLVQuickClass.GetListDataIndexByKey(constlistData:TList; constaKey:variant):integer; var Loop:integer; begin Result:=SpecialItemOrder; ifNOTAssigned(listData)thenExit; iflistData.Count<=0thenExit; forLoop:=0tolistData.Count-1do begin ifCheckFound(listData[Loop],aKey)then begin Result:=Loop; break; end; end; end; functionTLVQuickClass.GetLVListCount():integer; begin Result:=0; ifAssigned(lvDataList)then Result:=lvDataList.Count; end; functionTLVQuickClass.InitListViewColumns(constdisplayName:arrayofstring; constdisplayWidth:arrayofinteger;varlvTemp:TListView):boolean; begin Result:=false; ifAssigned(cxxLVC)then begin cxxLVC.InitLVColumns(displayName,displayWidth,lvTemp); end; end; procedureTLVQuickClass.OnDataToListView(varItem:TListItem); var pTemp:Pointer; begin //以后要移到类中,做成回调 ifNOTAssigned(lvDataList)thenExit; iflvDataList.Count<=0thenExit; ifItem.Index>lvDataList.CountthenExit; pTemp:=lvDataList[Item.Index]; ProcOnDataDetail(pTemp,Item); Item.ImageIndex:=1; Item.Data:=pTemp; end; functionTLVQuickClass.UpdateItemDataToListView(constaKey:variant; constpData:Pointer;constdataNumber:integer;varlvTemp:TListView):boolean; //varlvList:TList):boolean; var colIndex:integer; begin //注意:要影响性能 //lvTemp.Items.BeginUpdate(); Result:=false; ifNOT(Assigned(pData)andAssigned(lvTemp)andAssigned(lvDataList)and(lvDataList.Count>0))thenExit; 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源码库 //产品版本:CXXSoftdelphicodesourcelib1.0 //模块名称:Delphi之ListView显示控制类---应用层:Softsocket类定义单元 //模块描述: //单元文件:unLVSoftSocket.pas-->unLVSocket.pas //开发作者:成晓旭 //备注:任何人使用此文件时,请保留此段自述文件,谢谢! //开发时间:2005-09-26 // //修改历史:2006-06-16 //修改描述:增加通过TList来高速增加、更新、删除数据 //先用吧,以后再优化和完善 //修改历史:2006-07-10 //修改描述:成功地将ListView的OnData事件的List对象移入此类中 //修改历史:2006-07-11 //修改描述:重大重构:将此类分成两个类:TLVSoftSocket-->TLVSocket和TLVQuickClass //以遵循SRP原则 //------------------------------------------------------------------------------ unitunLVSocket; interface uses ComCtrls,Classes,SysUtils,Windows, unLVCommonClass,unLVDefine; type TLVSocket=class(TLVCommonClass) private //注意:此方法与顺序严重藕合 functionSaveSerialStatusToStringList(constaSocketStatus:TSocketStatusInfo):boolean; protected classfunctionCheckFound(constpData:Pointer;constaKey:variant):boolean;override; classprocedureProcOnDataDetail(constpData:Pointer;varItem:TListItem);override; public constructorCreate(); destructorDestroy();override; procedureInitListView(varlvTemp:TListView); //传统方法 procedureAddToLVSocket(constaSocketStatus:TSocketStatusInfo;varlvTemp:TListView); procedureUpdateLVSocket(constaSocketStatus:TSocketStatusInfo;varlvTemp:TListView); procedureDeleteLVSocket(constaSocketStatus:TSocketStatusInfo;varlvTemp:TListView); end; implementation ...{TLVSocket} procedureTLVSocket.AddToLVSocket( constaSocketStatus:TSocketStatusInfo;varlvTemp:TListView); begin ifSaveSerialStatusToStringList(aSocketStatus)then begin AddDataToListView(tmpList,lvTemp); end; end; classfunctionTLVSocket.CheckFound(constpData:Pointer; constaKey:variant):boolean; var p:^TSocketStatusInfo; begin p:=pData; Result:=(p.UniqueID=aKey); end; constructorTLVSocket.Create; begin inheritedCreate(); end; procedureTLVSocket.DeleteLVSocket( constaSocketStatus:TSocketStatusInfo;varlvTemp:TListView); begin ifSaveSerialStatusToStringList(aSocketStatus)then begin DeleteDat |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论