Listview 的自繪

開發的過程中,經常要使用到 TListview,爲了界面美觀,需要自繪 TListview,下面就 TListview 自繪的三個方面,總結一下。
  1、顯示千萬條數據記錄;   
  2、表格自繪;              
  3、表頭自繪;  
         

  1、顯示千萬條數據記錄;
 
     A:OwnerData 設爲 True, OwnerDraw 暫時設爲 False, 不進行自繪,由系統來完成繪製。
            我們的重點放在顯示千萬條記錄上面;
     
     B:數據來源,數據庫查詢:

 ADOQuery1.SQL.Clear; 
 ADOQuery1.Close;
 ADOQuery1.SQL.Text := 'select ROW_NUMBER() over(order by ID) as RowNum, * from TableName';
 ADOQuery1.Open;

        這裏:
        ID                :數據庫表中自增長字段(換成你要查詢的數據庫表中的自增長字段);
        TableName :數據庫表名;
        *                  :數據庫中所有字段名稱;
                               實際使用中,最好不要用 * ,需要顯示哪些字段就填入字段名稱 ,效率會大大提高。
         注:                        
          ID 是必須的,爲最後一列,不顯示的一列,用於記錄標識;當選中/雙擊時,根據此列值,可找到數據庫對應記錄。

          無論你的查詢語句如何寫,select ROW_NUMBER() over(order by ID) as RowNum 是必不可少的
      
      ADOQuery1 查詢結束後,添加代碼:

lvData.Items.Count := ADOQuery1.RecordCount; 

     將觸發 OnData 事件:

     C:雙擊 OnData 事件,進入代碼界面:
        假設 ADOQuery1 查詢結果中包含千萬條記錄
        顯示數據前,列名先準備好。動態創建也可以。
        注:
         列名一般使用字段名,但字段名一般爲英文。實際使用肯定是需要中文的。
         我們可以使用字段的說明屬性,來當作列名。
         建立字段時,順手給字段的列屬性中的說明屬性加個中文說明,
         程序中獲取字段對應的說明屬性來當作列名,就OK了。

      procedure TfrmListViewData.lvDataData(Sender: TObject; Item: TListItem);
      var
        I: Integer;
      begin
        ADOQuery1.RecNo := Item.Index + 1;
        Item.Caption    := ADOQuery1.Fields[1].AsString;
        for I           := 2 to ADOQuery1.Fields.Count - 1 do
        begin
          if ADOQuery1.Fields[I].DataType = ftBlob then
            Item.SubItems.Add('')
          else
            Item.SubItems.Add(ADOQuery1.Fields[I].AsString);
        end;
      end;

        OK,大功告成。上面代碼只是示例,細節根據自己需要,自己把控。
      
  2、表格自繪; 
       
這一步比較容易;
        A:設置 OwnerDraw 爲 True ,就會觸發自繪事件;
        B:雙擊 OnCustomDrawItem 事件,在事件中輸入自繪代碼。
              有畫布 Canvas,TListview(Sender).Canvas,有 Item ,想繪製成啥樣,就繪製成啥樣。
              劃過/選中/動態,等等效果都可以在這裏實現。
    
  3、表頭自繪;        
      想繪製表頭,就需要知道表頭句柄。
      想得到表頭句柄,就需要攔截 ListView 的 WMParentNotify 消息,因爲表頭的創建是由它完成的。     
      類定義:

type
        TListView = Class(ComCtrls.TListView)
        private
            FHeaderHandle: Integer;
            { 表頭的回調過程 }
            FOldHeaderProc, FHeaderInstance: Pointer;
        protected
          procedure WMParentNotify(var Message: TWMParentNotify); message WM_PARENTNOTIFY;
          procedure HeaderWndProc(var Message: TMessage);
      end;

        實現:

procedure TListView.WMParentNotify(var Message: TWMParentNotify);
begin
  if (Message.Event = WM_CREATE) then
  begin
    FHeaderHandle  := message.ChildWnd;                                       // 獲取到表頭句柄
    FOldHeaderProc := Pointer(GetWindowLong(FHeaderHandle, GWL_WNDPROC));     // 保存好表頭原有的回調過程
    SetWindowLong(FHeaderHandle, GWL_WNDPROC, Integer(FHeaderInstance));      // 指向表頭新的回調過程,用於自繪
  end;
  inherited;
end;

     注:
         這裏還有一些細節,比如:
         WINXP、WIN7、WIN10下創建表頭句柄是有點差別的;
         創建時,需要經過幾次纔會真正得到句柄;
        上面的代碼只是核心代碼,細化沒有寫出,自己動手試試,也就能理解了。

     

     事前要設置一下表頭新的回調過程: 

FHeaderInstance := MakeObjectInstance(HeaderWndProc);
 { 自繪表頭 }
 procedure TListView.HeaderWndProc(var Message: TMessage);
 begin
   with Message do
   begin
   { 自繪表頭 }
     if msg = WM_PAINT then
     begin
       { 有句柄 FHeaderHandle,就可以得到畫布 Canvas ,有畫布就可以自繪了 }
     end;
   end;
 end;

注:
  還有其它消息可以處理:
  WM_SETCURSOR  :當鼠標移動到表頭分割線時;
  WM_NCHITTEST   :調整表頭寬度;
  HDM_LAYOUT       :調整表頭高度;
  等等,繪製表頭悉數掌控。


OK,繪製完畢。

效果圖:
效果圖
 

滾動條的繪製比較好玩。以後在寫吧。不在 TListview 的自繪範圍內。
它是一個通用的繪製類,可以給任意控件來用(除了IE控件)。
滾動條自繪

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章