VCL中網格控制項原理分析

來源:互聯網
上載者:User
VCL中網格控制項原理分析
lxpbuaa(桂枝香在故國晚秋)
2004-9-15
 

 
網格(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 API的InvalidateRect標記[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; 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.