VCL中网格控件原理分析

丶児児児児

丶児児児児

2016-02-19 12:51

岁数大了,QQ也不闪了,微信也不响了,电话也不来了,但是图老师依旧坚持为大家推荐最精彩的内容,下面为大家精心准备的VCL中网格控件原理分析,希望大家看完后能赶快学习起来。
VCL中网格控件原理分析lxpbuaa(桂枝香在故国晚秋)2004-9-15 去年还在成都的时候,因为同事工作需要,我研究了一下TDBGrid,最后有点收获,在TDBGrid中加入了固定列及相关一些(如固定列可得到焦点、可拖放、数据可修改等)功能。前几天,有人在我的Blog(http://blog.csdn.net/lxpbuaa)上开骂:“TMD,我还准备来看点技术文章,Delphi区大版主的Blog上除了几篇破译文就这些烂东西”(因为言词不雅,删除了。大意如此)。想想也是,所以今天抽空整理了一下原来的东西,发篇小文,对VCL中网格控件的实现原理作个简单介绍(但不会涉及给TDBGrid添加固定列等具体内容,那是三言两语说不清楚的事^@^)。欢迎指正、补充。 网格(Grid)控件,可直观描述二维信息。因此它具有横向和纵向二轴,就是一个二维表格。一、类继承结构图                                TCustomGrid                               /                    TCustomDrawGrid                 TCustomDBGridTDrawGrid                             TDBGrid TStringGrid1、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;
展开更多 50%)
分享

猜你喜欢

VCL中网格控件原理分析

编程语言 网络编程
VCL中网格控件原理分析

在BCB中使用VCL控件数组2

编程语言 网络编程
在BCB中使用VCL控件数组2

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

在BCB中使用VCL控件数组1

编程语言 网络编程
在BCB中使用VCL控件数组1

怎样修改WPS文字中网格线大小

wps教程 wps office教程
怎样修改WPS文字中网格线大小

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

VC优秀的网格控件CGridCtrl

编程语言 网络编程
VC优秀的网格控件CGridCtrl

教你修改WPS文字中网格线的大小

电脑入门
教你修改WPS文字中网格线的大小

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

XML 名字空间

XML 名字空间

注册文件类型,设置文件图标

注册文件类型,设置文件图标
下拉加载更多内容 ↓