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

  • 相关阅读:
    集成学习值Adaboost算法原理和代码小结(转载)
    集成学习原理小结(转载)
    2019阿里校招测评题,光明小学完全图最短路径问题(python实现)
    第八节、图片分割之GrabCut算法、分水岭算法
    Scala2.11.8 spark2.3.1 mongodb connector 2.3.0
    spark 实现动态日期读取
    Idea 201601注册码
    linux下的crontab服务
    spark MySQL jar 包
    scala 日期格式转换
  • 原文地址:https://www.cnblogs.com/hnxxcxg/p/2940855.html
Copyright © 2020-2023  润新知