zoukankan      html  css  js  c++  java
  • vcl grid原理

    网格(Grid)控件,可直观描述二维信息。因此它具有横向和纵向二轴,就是一个二维表格。

    1、TCustomGrid为所有网格控件的父类,定义了网格控件的主要特征和网格控件的主要功能。在这里,我们着重要了解的是它的两个保护级(p

    rotected)方法:
    (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后,并没有真正实现数据填充(因为它还不知道数据是什么)。简化后的Draw

    Cell源代码如下:
        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方法等都会直接或者间接引发相应的W

    M_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;

  • 相关阅读:
    PyQt作品 – PingTester – 多点Ping测试工具
    关于和技术人员交流的一二三
    Pyjamas Python Javascript Compiler, Desktop Widget Set and RIA Web Framework
    Hybrid Qt applications with PySide and Django
    pyjamas build AJAX apps in Python (like Google did for Java)
    PyQt 维基百科,自由的百科全书
    InfoQ:请问为什么仍要选择Java来处理后端的工作?
    Eric+PyQt打造完美的Python集成开发环境
    python select module select method introduce
    GUI Programming with Python: QT Edition
  • 原文地址:https://www.cnblogs.com/hnxxcxg/p/2940855.html
Copyright © 2011-2022 走看看