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
class functionCheckFound( const pData:Pointer; const aKey:variant): boolean ;override;
class procedureProcOnDataDetail( const pData:Pointer;varItem:TListItem);override;
public
constructorCreate();
destructorDestroy();override;
procedureInitListView(varlvTemp:TListView);
// 快速方法
// 暫時這樣增加,以後在重構到基類中
procedureAddToLVSocketQuick( const aSocketStatus:TSocketStatusInfo;varlvTemp:TListView);
procedureUpdateLVSocketQuick( const aUniqueID:integer; const aSocketStatus:TSocketStatusInfo;varlvTemp:TListView);
procedureDeleteLVSocketQuick( const aUniqueID:integer; const aSocketStatus:TSocketStatusInfo;varlvTemp:TListView);
procedureDeleteAllLVSocket(varlvTemp:TListView);
procedureOnDataEvent(Item:TListItem);
functionGetDataCount():integer;
end;
implementation
... {TLVQuickSocket}
procedureTLVQuickSocket.AddToLVSocketQuick(
const aSocketStatus:TSocketStatusInfo;varlvTemp:TListView);
begin
AddItemDataToListView(@aSocketStatus,sizeof(aSocketStatus),lvTemp);
end;
class functionTLVQuickSocket.CheckFound( const pData:Pointer;
const aKey: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( const aUniqueID:integer;
const aSocketStatus: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;
class procedureTLVQuickSocket.ProcOnDataDetail( const pData: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( const aUniqueID:integer;
const aSocketStatus: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中的索引号
class functionGetListDataIndexByKey( const listData:TList; const aKey:variant):integer;
// 快速
procedureClearLVDataList();
protected
// 注意:約定最後一項一定是ImageIndex
// tmpList:TStringList;
functionInitListViewColumns( const displayName:arrayofstring;
const displayWidth:arrayofinteger;
varlvTemp:TListView): boolean ;
// 子類必須實作
class functionCheckFound( const pData:Pointer; const aKey:variant): boolean ;virtual; abstract ;
class procedureProcOnDataDetail( const pData:Pointer;varItem:TListItem);virtual; abstract ;
// 快速方法
// [注意:新方法中lvList不要求傳遞參數,保留以相容舊用戶端版本]
// 将pData資料加入List中,實作向ListView增加資料行
functionAddItemDataToListView( const pData:Pointer; const dataNumber:integer;
varlvTemp:TListView): boolean ;
// ;varlvList:TList=nil):boolean;
// 以aKey為關鍵值查找,并用pData資料更新List中滿足條件的記憶體塊,實作向ListView更新資料行
functionUpdateItemDataToListView( const aKey:variant; const pData:Pointer; const dataNumber:integer;
varlvTemp:TListView): boolean ;
// ;varlvList:TList=nil):boolean;
// 以aKey為關鍵值查找,并用pData資料删除List中滿足條件的記憶體塊,實作向ListView删除資料行
functionDeleteItemDataToListView( const aKey:variant; const pData:Pointer; const dataNumber: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( const pData:Pointer;
const dataNumber:integer;varlvTemp:TListView): boolean ;
// ;varlvList:TList):boolean;
var
pTemp:Pointer;
begin
// 注意:要影響性能
// lvTemp.Items.BeginUpdate();
Result: = false ;
if NOT(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
if NOT(Assigned(lvDataList)and(lvDataList.Count > 0 ))thenExit;
for Loop: = 0 tolvDataList.Count - 1 do
begin
pTemp: = (lvDataList.Items[Loop]);
if Assigned(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
if NOT(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( const aKey:variant;
const pData:Pointer; const dataNumber:integer;varlvTemp:TListView): boolean ;
// varlvList:TList):boolean;
var
colIndex:integer;
begin
// 注意:要影響性能
// lvTemp.Items.BeginUpdate();
Result: = false ;
if NOT(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();
if Assigned(lvDataList)then
FreeAndNil(lvDataList);
if Assigned(cxxLVC)then
FreeAndNil(cxxLVC);
// ifAssigned(tmpList)then
// FreeAndNil(tmpList);
inheritedDestroy;
end;
class functionTLVQuickClass.GetListDataIndexByKey( const listData:TList;
const aKey:variant):integer;
var
Loop:integer;
begin
Result: = SpecialItemOrder;
if NOTAssigned(listData)thenExit;
if listData.Count <= 0 thenExit;
for Loop: = 0 tolistData.Count - 1 do
begin
if CheckFound(listData[Loop],aKey)then
begin
Result: = Loop;
break ;
end;
end;
end;
functionTLVQuickClass.GetLVListCount():integer;
begin
Result: = 0 ;
if Assigned(lvDataList)then
Result: = lvDataList.Count;
end;
functionTLVQuickClass.InitListViewColumns( const displayName:arrayofstring;
const displayWidth:arrayofinteger;varlvTemp:TListView): boolean ;
begin
Result: = false ;
if Assigned(cxxLVC)then
begin
cxxLVC.InitLVColumns(displayName,displayWidth,lvTemp);
end;
end;
procedureTLVQuickClass.OnDataToListView(varItem:TListItem);
var
pTemp:Pointer;
begin
// 以後要移到類中,做成回調
if NOTAssigned(lvDataList)thenExit;
if lvDataList.Count <= 0 thenExit;
if Item.Index > lvDataList.CountthenExit;
pTemp: = lvDataList[Item.Index];
ProcOnDataDetail(pTemp,Item);
Item.ImageIndex: = 1 ;
Item.Data: = pTemp;
end;
functionTLVQuickClass.UpdateItemDataToListView( const aKey:variant;
const pData:Pointer; const dataNumber:integer;varlvTemp:TListView): boolean ;
// varlvList:TList):boolean;
var
colIndex:integer;
begin
// 注意:要影響性能
// lvTemp.Items.BeginUpdate();
Result: = false ;
if NOT(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( const aSocketStatus:TSocketStatusInfo): boolean ;
protected
class functionCheckFound( const pData:Pointer; const aKey:variant): boolean ;override;
class procedureProcOnDataDetail( const pData:Pointer;varItem:TListItem);override;
public
constructorCreate();
destructorDestroy();override;
procedureInitListView(varlvTemp:TListView);
// 傳統方法
procedureAddToLVSocket( const aSocketStatus:TSocketStatusInfo;varlvTemp:TListView);
procedureUpdateLVSocket( const aSocketStatus:TSocketStatusInfo;varlvTemp:TListView);
procedureDeleteLVSocket( const aSocketStatus:TSocketStatusInfo;varlvTemp:TListView);
end;
implementation
... {TLVSocket}
procedureTLVSocket.AddToLVSocket(
const aSocketStatus:TSocketStatusInfo;varlvTemp:TListView);
begin
if SaveSerialStatusToStringList(aSocketStatus)then
begin
AddDataToListView(tmpList,lvTemp);
end;
end;
class functionTLVSocket.CheckFound( const pData:Pointer;
const aKey:variant): boolean ;
var
p: ^ TSocketStatusInfo;
begin
p: = pData;
Result: = (p.UniqueID = aKey);
end;
constructorTLVSocket.Create;
begin
inheritedCreate();
end;
procedureTLVSocket.DeleteLVSocket(
const aSocketStatus:TSocketStatusInfo;varlvTemp:TListView);
begin
if SaveSerialStatusToStringList(aSocketStatus)then
begin
DeleteDataFromListView(tmpList,lvTemp);
end;
end;
destructorTLVSocket.Destroy;
begin
inheritedDestroy;
end;
procedureTLVSocket.InitListView(varlvTemp:TListView);
begin
InitListViewColumns(SocketStrBuffer,SocketWidthBuffer,lvTemp);
end;
class procedureTLVSocket.ProcOnDataDetail( const pData: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(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;
functionTLVSocket.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;
procedureTLVSocket.UpdateLVSocket(
const aSocketStatus:TSocketStatusInfo;varlvTemp:TListView);
begin
if SaveSerialStatusToStringList(aSocketStatus)then
begin
UpdateDataFromListView(tmpList,lvTemp);
end;
end;
end.
// ------------------------------------------------------------------------------
//
// 産品名稱:成曉旭的個人軟體Delphi源碼庫
// 産品版本:CXXSoftdelphicodesourcelib1.0
// 子產品名稱:Delphi之ListView顯示控制類---外部應用層通用類定義單元
// 子產品描述:ListView傳統操作方法類
// 單元檔案:unLVCommonClass.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原則
// ------------------------------------------------------------------------------
unitunLVCommonClass;
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 functionStrIsNumber( const str:PChar): boolean ;
functionGetLVItemOrderByValue( const lvTemp:TListView; const aKeyValue:string; const columnOrder:integer = SpecialItemOrder):integer;
procedureCommonListItemProcess( const dataList:TStringList;varliTemp:TListItem; const isUpdate: boolean );
protected
// 注意:約定最後一項一定是ImageIndex
tmpList:TStringList;
functionInitListViewColumns( const displayName:arrayofstring;
const displayWidth:arrayofinteger;
varlvTemp:TListView): boolean ;
functionAddDataToListView( const dataList:TStringList;varlvTemp:TListView): boolean ;
functionUpdateDataFromListView( const dataList:TStringList;varlvTemp:TListView): boolean ;
// 注意:删除的鍵值預設以SubItem[0]為标準
functionDeleteDataFromListView( const dataList:TStringList;varlvTemp:TListView): boolean ;
// 子類必須實作
class functionCheckFound( const pData:Pointer; const aKey:variant): boolean ;virtual; abstract ;
class procedureProcOnDataDetail( const pData:Pointer;varItem:TListItem);virtual; abstract ;
public
constructorCreate();overload;
destructorDestroy();override;
end;
implementation
... {TLVCommonClass}
functionTLVCommonClass.AddDataToListView( const dataList:TStringList;
varlvTemp: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;
procedureTLVCommonClass.CommonListItemProcess( const dataList:TStringList;
varliTemp: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 )andStrIsNumber(PChar(dataList.Strings[dataIndex]))then
begin
liTemp.ImageIndex: = StrToInt(dataList.Strings[dataIndex]);
end
else
begin
if isUpdatethen
liTemp.SubItems[dataIndex - 1 ]: = dataList.Strings[dataIndex]
else
liTemp.SubItems.Add(dataList.Strings[dataIndex]);
end;
Inc(dataIndex);
end;
end;
constructorTLVCommonClass.Create;
begin
inheritedCreate();
cxxLVC: = TCXXLVControler.Create();
tmpList: = TStringList.Create();
end;
functionTLVCommonClass.DeleteDataFromListView(
const dataList:TStringList;varlvTemp: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)andAssigned(cxxLVC)then
begin
cxxLVC.DeleteItemByIndex(lvTemp,colIndex);
Result: = true ;
end;
end;
end;
destructorTLVCommonClass.Destroy;
begin
if Assigned(cxxLVC)then
FreeAndNil(cxxLVC);
if Assigned(tmpList)then
FreeAndNil(tmpList);
inheritedDestroy;
end;
functionTLVCommonClass.GetLVItemOrderByValue( const lvTemp:TListView;
const aKeyValue:string; const columnOrder:integer):integer;
var
Loop:integer;
isOK: boolean ;
begin
Result: = SpecialItemOrder;
isOK: = false ;
for Loop: = 0 tolvTemp.Items.Count - 1 do
begin
if columnOrder <> SpecialItemOrderthen
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 isOKthen
begin
Result: = Loop;
break ;
end;
end;
end;
functionTLVCommonClass.InitListViewColumns( const displayName:arrayofstring;
const displayWidth:arrayofinteger;varlvTemp:TListView): boolean ;
begin
Result: = false ;
if Assigned(cxxLVC)then
begin
cxxLVC.InitLVColumns(displayName,displayWidth,lvTemp);
end;
end;
class functionTLVCommonClass.StrIsNumber( const str:PChar): boolean ;
var
p:Char;
i:integer;
functionCheckHex(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 toLength(str) - 1 do
begin
p: = str[i];
Result: = CheckHex(p);
if NOTResultthen
break ;
end;
except
Result: = false ;
end;
end;
functionTLVCommonClass.UpdateDataFromListView(
const dataList:TStringList;varlvTemp: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.