程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> VCL中網格控件原理分析

VCL中網格控件原理分析

編輯:Delphi
去年還在成都的時候,因為同事工作需要,我研究了一下TDBGrid,最後有點收獲,在TDBGrid中加入了固定列及相關一些(如固定列可得到焦點、可拖放、數據可修改等)功能。 前幾天,有人在我的Blog(http://blog.csdn.Net/lxpbuaa)上開罵:“TMD,我還准備來看點技術文章,Delphi區大版主的Blog上除了幾篇破譯文就這些爛東西”(因為言詞不雅,刪除了。大意如此)。想想也是,所以今天抽空整理了一下原來的東西,發篇小文,對VCL中網格控件的實現原理作個簡單介紹(但不會涉及給TDBGrid添加固定列等具體內容,那是三言兩語說不清楚的事^@^)。歡迎指正、補充。   網格(Grid)控件,可直觀描述二維信息。因此它具有橫向和縱向二軸,就是一個二維表格。 一、類繼承結構圖                                 TCustomGrid                                /                     TCustomDrawGrid                 TCustomDBGrid TDrawGrid                             TDBGrid TStringGrid 1、TCustomGrid為所有網格控件的父類,定義了網格控件的主要特征和網格控件的主要功能。在這裡,我們著重要了解的是它的兩個保護級(protected)方法: (1)procedure Paint; 所有TWinControl的子類都可通過Paint來繪制自身外形。在TCustomGrid.Paint中,主要實現兩個功能:繪制網格線和填充網格數據。其中,網格數據的填充具體實現由下述的DrawCell完成。在後面的內容,我會結合源代碼詳細解釋Paint。    (2)procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState:  TGridDrawState); virtual; abstract; 這是一個純虛方法,被Paint調用,用以實現網格數據的填充。因此,所有TCustomGrid的子類都可以覆蓋(override)這個方法,根據實際需要實現填充方式。 2、TCustomDrawGrid並沒有實際用處。它主要完成兩件事情: (1)覆蓋TCustomGrid的抽象方法加以實現。TCustomDrawGrid不再是一個抽象類。 (2)添加了一些事件。 比如它覆蓋了TCustomGrid.DrawCell,並在其中觸發了OnDrawCell事件。因此,我們在OnDrawCell中添加代碼,就可以改變特定行列網格中的數據及其填充方式。但要注意的是TCustomDrawGrid覆蓋DrawCell後,並沒有真正實現數據填充(因為它還不知道數據是什麼)。簡化後的DrawCell源代碼如下:     procedure TCustomDrawGrid.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);     begin       if Assigned(FOnDrawCell) then         FOnDrawCell(Self, ACol, ARow, ARect, AState);     end; 3、TDrawGrid、TStringGrid都是用戶可以在設計時使用的類,或者簡單的說都是控件。但TDrawGrid是TCustomDrawGrid的一個簡單包裝,因此DrawCell仍然只簡單地觸發事件OnDrawCell,而沒有真正實現數據填充。也正因為如此,TDrawGrid的使用就相當靈活,我們可以利用它繪制文本、圖形圖像等多種信息。 TStringGrid派生於TDrawGrid,專門用於描述文本信息。從以下源代碼可以看到,它真正實現了數據填充:     procedure TStringGrid.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);     begin       if DefaultDrawing then         Canvas.TextRect(ARect, ARect.Left+2, ARect.Top+2, Cells[ACol, ARow]);{即這句}       inherited DrawCell(ACol, ARow, ARect, AState);     end; 4、TDBGrid是數據敏感類的網格控件。它是對TCustomDBGrid的簡單包裝,而TCustomDBGrid的實現原理和普通網格控件是類似的,主要的區別在於數據源不同。比如TStringGrid的數據來自於TStringGrid.Cells,而TCustomDBGrid的數據來自於TCustomDBGrid.DataSource.DataSet。   二、TCustomGrid的主要功能 前面已經說了,TCustomGrid定義了網格控件的主要功能,具有網格控件的主要特征,因此要理解網格控件的基本原理,重點在於TCustomGrid的兩個方法:Paint和DrawCell。 DrawCell是一個純虛方法,在Paint中被調用(具體過程參見下文),因此理解的重點是在兩個地方: (1)Paint有什麼用,Paint是如何運作的。 (2)Paint中做了什麼工作。 1、Paint的運作機制。 前面說過了,Paint用來繪制控件自身外形。Paint內部定義了具體的繪制方法,因此,只要在適當的時間和地點調用Paint,就可以改變控件外觀。 在VCL中,可將Paint方法簡單理解為TControl對Windows標准消息WM_PAINT的反應。調用Win32 API中的UpdateWindow、RedrawWindow和InvalidateRect以及VCL中TControl的Repaint、Refresh和Update方法等都會直接或者間接引發相應的WM_PAINT消息。 因此,網格控件的基本運作原理就是:數據或者數據源本身發生變化後,通過適當方式調用Paint方法,從而更新數據填充。拿TStringGrid為例,其Cells的數據改變後:     procedure TStringGrid.SetCells(ACol, ARow: Integer; const Value: string);     begin       TStringGridStrings(EnsureDataRow(ARow))[ACol] := Value;       EnsureColRow(ACol, True);       EnsureColRow(ARow, False);       Update(ACol, ARow); {這句內部調用Win32 APIInvalidateRect標記[ACol, ARow]所指區域需要重畫系統接著就會發送一個WM_PAINT消息。最終引起Paint的執行。} end; 2、Paint所做工作。先看看我簡化後的源代碼,更容易說清楚。以“★”為各功能部分劃分標記:     procedure TCustomGrid.Paint;         procedure DrawLines(DoHorz, DoVert: Boolean; Col, Row: Longint;         const CellBounds: array of Integer; OnColor, OffColor: TColor);       begin         {……}       end;         procedure DrawCells(ACol, ARow: Longint; StartX, StartY, StopX, StopY: Integer; Color: TColor; IncludeDrawState: TGridDrawState);       begin         {……}         {其中調用了TCustomGrid的純虛方法DrawCell        因此TCustomGrid的子類可以覆蓋這個方法自定義數據的填充方式}         DrawCell(CurCol, CurRow, Where, DrawState);         {……}       end;       begin       {0:計算網絡繪制參數}       CalcDrawInfo(DrawInfo);         with DrawInfo do       begin         {1:繪制網格線(如果線寬>0}         if (Horz.EffectiveLineWidth > 0) or (Vert.EffectiveLineWidth > 0) then         begin           {左上角固定列}           DrawLines(goFixedHorzLine in Options, goFixedVertLine in Options, 0, 0, [0, 0, Horz.FixedBoundary, Vert.FixedBoundary], clBlack, FixedColor);           {橫向固定列}           DrawLines(goFixedHorzLine in Options, goFixedVertLine in Options, LeftCol, 0, [Horz.FixedBoundary, 0, Horz.GridBoundary, Vert.FixedBoundary], clBlack, FixedColor);           {縱向固定列}           DrawLines(goFixedHorzLine in Options, goFixedVertLine in Options, 0, TopRow, [0, Vert.FixedBoundary, Horz.FixedBoundary, Vert.GridBoundary], clBlack, FixedColor);           {非固定列}           DrawLines(goHorzLine in Options, goVertLine in Options, LeftCol, TopRow, [Horz.FixedBoundary, Vert.FixedBoundary, ??? Horz.GridBoundary, Vert.GridBoundary], LineColor, Color);         end;           {2填充數據}         {左上角固定列}         DrawCells(0, 0, 0, 0, Horz.FixedBoundary, Vert.FixedBoundary, ??? FixedColor, [gdFixed]);         {橫向固定列}         DrawCells(LeftCol, 0, Horz.FixedBoundary - FColOffset, 0, ??? Horz.GridBoundary, Vert.FixedBoundary, FixedColor, [gdFixed]);         {縱向固定列}         DrawCells(0, TopRow, 0, Vert.FixedBoundary, Horz.FixedBoundary, ?Vert.GridBoundary, FixedColor, [gdFixed]);         {非固定列}         DrawCells(LeftCol, TopRow, Horz.FixedBoundary - FColOffset, Vert.FixedBoundary, Horz.GridBoundary, Vert.GridBoundary, Color, []);           {3:給被選中網格繪制外框}         Canvas.DrawFocusRect(FocRect);           {4:填充客戶區中未被網格占用的區域}         {橫向部分}         if Horz.GridBoundary < Horz.GridExtent then         begin           Canvas.Brush.Color := Color;           Canvas.FillRect(Rect(Horz.GridBoundary, 0, Horz.GridExtent, ??? Vert.GridBoundary));         end;         {縱向部分}         if Vert.GridBoundary < Vert.GridExtent then         begin           Canvas.Brush.Color := Color;           Canvas.FillRect(Rect(0, Vert.GridBoundary, Horz.GridExtent, Vert.GridExtent));         end;       end;     end;   從以上代碼可見,TCustomGrid.Paint主要可以分為五個部分。其中★0用於計算當前繪制參數,結果用於後面4個部分。接下來4個部分中,★1和★2是主體。因此我們關注的重點是★0、★1和★2。★1和★2已有詳細注解,所以不逐行解釋了,有興趣但看不懂的可慢慢琢磨。最後對★0的DrawInfo作個解釋。 DrawInfo為TGridDrawInfo類型,定義如下:     TGridDrawInfo = record {網絡繪制參數}       Horz, Vert: TGridAxisDrawInfo; {分為橫向和縱向兩個部分}     end;   下面以橫向為例,解釋TGridAxisDrawInfo的含義:     TGridAxisDrawInfo = record       EffectiveLineWidth: Integer;    {網格線寬}       FixedBoundary: Integer;        {網格固定列總寬度(含網格線)}       GridBoundary: Integer;        {網格各列總寬度(含網格線、固定列)}       GridExtent: Integer;        {網格客戶區總寬度}       LastFullVisibleCell: Longint;    {當前最後一個未超出客戶區(即能全部看見)的列}       FullVisBoundary: Integer;    {當前可全部看見列的總寬度(含網格線)}       FixedCellCount: Integer;    {固定列個數}       FirstGridCell: Integer;    {第一個非固定列,即LeftCol橫向)或者TopRow(縱向)}       GridCellCount: Integer;    {ColCount,總列數}       GetExtent: TGetExtentsFunc;    {一個函數,用於取得列寬,相當於ColWidths[Index]} end;
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved