Delphi ListView快速操作通用设计与实现

Delphi ListView快速操作通用实现

作者:成晓旭

众所周知,Delphi ListView类直接进行AddUpdateDelete操作的速度是比较慢的,尤其是当数据量较大时,比如数据量达到50001000050000时,速度真是可以说是“慢得惊人”。其实快速操作的方法非常简单,就当大家都知道了。在本人的工作中,很多项目都用到ListView,并且对速度的响应要求比较高,于是发生了快速操作ListView的代码散布于多个项目大量模块中的问题,并且,当界面层数据显示要求发生改变时,自然发生“重复性代码的通用问题”。考虑到对以前版本的兼容性问题,也一直没有引用第三方的成熟组件。鉴于:“程序中的重复代码最易引发问题,必须消除”的实践经验,自己设计了一个比较通用的解决此类问题的类结构。当然,远不是什么“通用框架”了(眼下市面上很多人喜欢把自己做的无论什么东西通称为框架)。在采用此结构的项目中,很容易实现MVC模式,达到业务逻辑与界面显示分离的低级的、基础的要求。

(因为,目前CSDN的软件上传功能不可用,我只有将部分代码片断放在文档中,有需要完整源码者,请留言)

类层次结构:

ListView基础操作封装在LVControler包中,核心的类是TCXXLVControler

 (说明:LVControler类是被封装通用类结构内,外部用户是不需要了解和访问的,所以不作介绍。)

传统的ListView操作基类是TLVCommonClass,如果想用传统的方法增、删、改ListView中的数据,可以直接从此类继承。

 源码如下:

 

具体的一个从此类继承下来的用于Socket界面显示的TLVSocket的类Overvivw如下:

源码如下:

 

快速的ListView操作基类是TLVQuickClass,如果想用快速方法增、删、改ListView中的数据,可以直接从此类继承。

主要方法:(可以看到,里面没有真正的Public方法,子类也仅需实现两个Protectedvirtual方法)

源码如下:

 

此类中,要求每个具体应用的子类必须实现的方法仅有两个:CheckFound():根据具体应用检测的数据是否已经存在;ProcOnDataDetail():客户端ListViewOnData()事件的数据处理回调方法。下面是几个具体实现的子类的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对象的操作(尤其是当有很多相同或者类似数据要求在不同FormListView中显示时),并减少了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.

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 30
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值