视图用来表现数据,也是用户接口。当数据更新时,视图应该随之改变,在交互过程中,视图需要响应用户,这些情况下都会引起视图的绘制。 cxGrid可以用多种视图表现关系型数据,最常用的是表格视图,我们以表格视图作为默认的对象,分析cxGrid用以实现视图绘制的基本结构。
表格视图按行,列布局,行优先顺序绘制,绘制的基本元素是行和单元(cell)。由于数据内容的动态性,行是动态生成的。表格视图提供了各种可定制性,包括可选的分组行风格和若干自定义风格,这些复杂特性背后有很好的设计。
视图绘制相关的几组类:
ViewInfo类族:
描述视图元素的绘制信息,视图元素要绘制成什么样子,完全由其ViewInfo确定。祖先类是TcxCustomGridCellViewInfo,数据行,数据单元,分组行等视图元素以及视图本身都有相应的派生类。
Painter类族:
按照ViewInfo,在画布上绘制视图元素。祖先类是TcxCustomGridCellPainter。 TcxCustomGridCellPainter = class
private
FCanvas: TcxCanvas;
FViewInfo: TcxCustomGridCellViewInfo;
View类族:
整个视图的描述,包括了各种设置信息。祖先是TcxCustomGridView。
View和ViewInfo的不同在ViewInfo描述绘制信息,而View描述设置信息,ViewInfo是动态构造的,当Grid创建时,如果有一个分组多条记录,那么每条记录会生成一个ViewInfo实例,如果收起分组,这些实例将被销毁,展开分组,会再次生成每条记录的ViewInfo实例,而一个表格视图始终只有一个实例。
LookAndFeelPainter类族:
用于进行有关分组行风格的绘制。祖先是TcxCustomLookAndFeelPainter。
绘制视图元素时,涉及到分组行风格(GroupRowStyle)的绘制任务,都被委托给LookAndFeelPainter类的实例,由此实现多态的风格。
Style和Styles类族:
Style类的祖先是TcxCustomStyle,Styles类的祖先是TcxCustomStyles。TcxCustomStyle类表示一种自定义风格,TcxCustomStyles类表示一组风格。TcxCustomStyles内部用一个map保存这些风格,给出键,可通过Values属性取出指定的风格。
TcxCustomStyles = class(TcxInterfacedPersistent, IcxStyleChangeListener)
public
property Values[Index: Integer]: TcxCustomStyle read GetValue write SetValue;
键的取值范围如下:
vsCustomTableFirst = vsCustomLast + 1;
vsContent = vsCustomTableFirst;
vsContentEven = vsCustomTableFirst + 1;
vsContentOdd = vsCustomTableFirst + 2;
vsFilterBox = vsCustomTableFirst + 3;
vsInactive = vsCustomTableFirst + 4;
vsIncSearch = vsCustomTableFirst + 5;
vsSelection = vsCustomTableFirst + 6;
// vsCustomTableLast = vsSelection;
vsHotTrack = vsCustomTableFirst + 7;
vsCustomTableLast = vsHotTrack;
考查以上几组类的关系:
Painter和ViewInfo:
Painter类和ViewInfo类具有对应关系,在视图元素绘制时创建出Painter类的实例,其中有多态机制的作用,下面具体了解一下这个创建过程。
在TcxCustomGridCellPainter派生的一组类中,只有它自己有构造函数:
constructor TcxCustomGridCellPainter.Create(ACanvas: TcxCanvas;
AViewInfo: TcxCustomGridCellViewInfo);
begin
inherited Create;
FCanvas := ACanvas;
FViewInfo := AViewInfo;
end;
那么当需要调用其派生类方法时,是如何构造出派生类实例的?Painter类实例都是在ViewInfo类的方法中创建出来的,代码如下:
GetPainterClass.Create()
TcxGridDataRowViewInfo = class(TcxCustomGridRowViewInfo)
protected:
function GetPainterClass: TcxCustomGridCellPainterClass; override;
function TcxGridDataRowViewInfo.GetPainterClass: TcxCustomGridCellPainterClass;
begin
Result := TcxGridDataRowPainter;
end;
这样,创建哪个Painter类的实例决定于ViewInfo类的实例,通过某个ViewInfo实例调用GetPainterClass.Create(),就返回对应Painter类的实例。
ViewInfo类进行绘制时,会调用Painter类的绘制方法;Painter类进行绘制时,也会调用ViewInfo类的绘制方法,比如:
procedure TcxCustomGridRecordsPainter.Paint;
var
I: Integer;
begin
with FViewInfo do
for I := 0 to Count - 1 do
with Items[I] do
if Calculated then Paint;
end;
跟踪绘制过程可以看到,正是在两种调用的交替中绘出了视图的每个元素,最终在显示设备上的绘制是由Painter类实现的,而ViewInfo的绘制方法会起到调度的作用,比如把数据行的绘制分解成各数据单元的绘制。
我们已经了解到,前一种调用是通过构造局部实例,那么后一种呢?
TcxCustomGridCellPainter中定义了FViewInfo私有成员,并将其定义为保护属性
TcxCustomGridCellPainter = class
private
FCanvas: TcxCanvas;
FViewInfo: TcxCustomGridCellViewInfo;
protected
property ViewInfo: TcxCustomGridCellViewInfo read FViewInfo;
其派生类继承了该属性,并将其重新定义
TcxGridDataRowPainter = class(TcxCustomGridRowPainter)
private
function GetViewInfo: TcxGridDataRowViewInfo;
protected
property ViewInfo: TcxGridDataRowViewInfo read GetViewInfo;
function TcxGridDataRowPainter.GetViewInfo: TcxGridDataRowViewInfo;
begin
Result := TcxGridDataRowViewInfo(inherited ViewInfo);
end;
View和Styles:
视图风格是视图的属性:
TcxCustomGridView = class(TcxControlChildComponent, IcxStoredObject, IcxStoredParent,
IcxGridViewLayoutEditorSupport, IcxGridViewDesignerMenu)
private
FStyles: TcxCustomGridStyles;
Protected:
property Styles: TcxCustomGridStyles read FStyles write SetStyles;
procedure TcxCustomGridView.SetStyles(Value: TcxCustomGridStyles);
begin
FStyles.Assign(Value);
end;
特定的视图使用特定的视图风格,以表视图为例,可以看到两者是如何对应的。
TcxGridTableView = class(TcxCustomGridTableView)
Published:
property Styles: TcxGridTableViewStyles read GetStyles write SetStyles;
function TcxGridTableView.GetStyles: TcxGridTableViewStyles;
begin
Result := TcxGridTableViewStyles(inherited Styles);
end;
procedure TcxGridTableView.SetStyles(Value: TcxGridTableViewStyles);
begin
inherited Styles := Value;
end;
View和LookAndFeelPainter:
ViewInfo,GridStyles和GridView类都有LookAndFeelPainter属性,因为分组行风格设置是作用于整个视图的,所以该属性真正定义是在GridView中,GridStyles和ViewInfo只是返回一个引用,提供该属性是为了方便,比如ViewInfo进行绘制时,要用到风格的设置。
function TcxCustomGridView.GetLookAndFeelPainter: TcxCustomLookAndFeelPainterClass;
begin
if Control = nil then
Result := TcxStandardLookAndFeelPainter
else
Result := TcxCustomGrid(Control).LookAndFeelPainter;
end;
function TcxCustomGridStyles.GetLookAndFeelPainter: TcxCustomLookAndFeelPainterClass;
begin
Result := GridView.LookAndFeelPainter;
end;
/* TcxGridViewHandler是视图ViewInfo的祖先类。*/
function TcxGridViewHandler.GetLookAndFeelPainter: TcxCustomLookAndFeelPainterClass;
begin
Result := FGridView.LookAndFeelPainter;
end;
/* TcxCustomGridViewCellViewInfo是视图元素ViewInfo的祖先类,FGridViewInfo是GridViewInfo属性的成员变量。*/
function TcxCustomGridViewCellViewInfo.GetLookAndFeelPainter: TcxCustomLookAndFeelPainterClass;
begin
Result := FGridViewInfo.LookAndFeelPainter;
end;