Delphi ListView快速操作通用实现

news/2024/7/18 3:36:29

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.





http://www.niftyadmin.cn/n/3649171.html

相关文章

anime.js_如何使用CSS,anime.js和segment.js与微交互构建下载按钮

anime.js引言 (Introductions) In user experience design, microinteractions are small moments of feedback that help a user navigate an interface. Often, microinteractions are made with subtle animation in website design. 在用户体验设计中&#xff0c; 微交互是…

解决amazon RDS 出现“Illegal mix of collations (latin1_swedish_ci,IMPLICIT)”的解决办法

今天遇到一个问题&#xff0c;我把数据库从EC2上安装的postgres替换为了amazon提供的RDS&#xff0c;之后在保存中文的时候出现错误&#xff1a; Illegal mix of collations (latin1_swedish_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) 出现这个问题的原因是因为RDS的mys…

Android gallery 3D效果

在看了iOS上面的CoverFlow后&#xff0c;感觉效果真的不错&#xff0c;就想在android上面实现一个&#xff0c;这个程序在网上参考了一此核心的代码&#xff0c;当然我添加了一些其他的东西&#xff0c;废话不多说&#xff0c;先看效果&#xff0c;不然就是无图无真相。 Demo下…

监听电量广播

第一步&#xff1a;自定义广播 class MyReceiver extends BroadcastReceiver {Overridepublic void onReceive(Context context, Intent intent) {int level intent.getIntExtra("level", 0);setBattery(level);} } 第一步&#xff1a;注册广播 receiver new MyRec…

如何将Docker插件用于Visual Studio代码

介绍 (Introduction) In this article we’ll use the Docker Plugin for Visual Studio Code. Docker allows us to package our applications into images and run them as containers on any platform that has Docker installed. We’ll use this plugin with two develope…

[收藏]深入浅出的《网络socket编程指南》

网络socket编程指南http://www.chinaunix.net 作者:流浪者 发表于&#xff1a;2003-04-11 17:09:07Beej网络socket编程指南 -------------------------------------------------------------------------------- 介绍   Socket 编程让你沮丧吗&#xff1f;从man pages中很难…

Python实现备份EC2的重要文件和MySQL数据库到S3

今天尝试了使用boto这个工具来用python备份文件到S3&#xff0c;废话不说&#xff0c;上代码&#xff1a; 1. 备份重要文件到S3&#xff1a; import os connected 0 def connect():access_key YOURKEYsecret_key YOURKEYfrom boto.s3.connection import S3Connectionglobal…

手势的简单使用

第一步&#xff1a;定义手势 private GestureDetector detector 第二步&#xff1a;初始化手势并初始化事件 detectornew GestureDetector(this,new GestureDetector.SimpleOnGestureListener(){Overridepublic void onLongPress(MotionEvent e) {super.onLongPress(e);Toast.…