VCL中网格控件原理分析

2016-02-19 12:51 0 1 收藏

岁数大了,QQ也不闪了,微信也不响了,电话也不来了,但是图老师依旧坚持为大家推荐最精彩的内容,下面为大家精心准备的VCL中网格控件原理分析,希望大家看完后能赶快学习起来。

【 tulaoshi.com - 编程语言 】

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;

来源:https://www.tulaoshi.com/n/20160219/1602174.html

延伸阅读
一个优秀的网格控件CGridCtrl 作者:戴绍忠   网格控件的用途非常广泛,在我的一个项目中需要实现类似EXCEL的界面,为此我采用了一个优秀的CGridCtrl控件,其原作者为 Chris Maunder (原作者所写的MFC Grid control的最新版本可以到http://www.codetools.com/miscctrl/gridctrl.asp查阅)为了方便地实现单元格...
勾选“视图”下面的“网格线”,文档中会显示网格线,但这个线是虚的,就是打印不出来的哪种,如果有这个打印需求,有两种方法可以实现,一是设置稿纸、二是插入背景。稿纸有行数限制,如果不能满足可以选择第二种,向文档中插入有网格线的背景,打印时勾选“打印背景”,那么网格线就会被打印出来。 简述 打...
以下不完整例子为生成一个TMemo的派生类及动态地创建该VCL控件。 Class TMemoEx : public TMemo { . . } extern TMemoEx memoex; Class TForm1 : Class TForm { public: TMemoEx *MemoEx; . . } void _fastcall TForm1::FormShow(TObject *Sender) ...
标签: 电脑入门
Excel2007中网格线的基本用法介绍 Excel2007中的网格线在编辑、打印操作中都会用到。下面讲解Excel2007软件中网格线的一些基本用法。 1、隐藏/显示网格线 通过视图选项卡菜单中的网格线复选框开关,你可以设置网格线在编辑表时候的显示与否, 2、打印输出网格线设置 Excel中默认的网格线在打印时候是不会输出的,除非你进行了单元格的边框...
网格控制项是VB For Windows提供的一个强有力的自定义控制项,它以网格的形式提供给用户,使用户可以快速直观地显示或编辑数据库、图片库、数组等大型数据集合。 网格中行和列的每一个交点称为单元格,单元格中可以放入文字或图片,用户可以对其中的内容进行读写操作。网格控制项的属性Col和Row指定了当前单元格在网格中的位置,这是对网格...

经验教程

644

收藏

50
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部