XE7下修改FMX.Grid.pas解决Grid列头与文本内容的样式设定,实现标题栏文字居中,内容的文本可右对齐。
在XE6上,已经有人研究过 Grid 的样式。在QQ群里,【深圳从头再来】说它的XE5,对于设置列头列内容的样式很完美,并帮我做了一个测试例子,可是发给我在XE6上编译,却没有想要的结果。尽管对于列头的样式,在点过按钮中确实生效,但是我想在Create事件中调用按钮的样式设定,却不成功,不知是什么原因。后来,我把XE6卸载,安装了XE7,运行后,与XE6上的不理想效果一致,这令我很惊讶。按道理,工具版本的升级,只可能更方便更完美,却是如此的不尽人意。网上也参考了【红鱼儿糊思乱想】的Grid试用结果,并且,后来【广州weii】也进行了试验,博文如下:http://www.cnblogs.com/weii/p/3977320.html。weii是最接近效果的,它设置标头的办法与红鱼儿博文中差不多。另外,对StringGrid进行了TextSettings设置,这个参数影响到网格内所有列的显示样式。仍然是不能独立为各个列设置对齐方式。
我们的习惯,标题的文字一定是居中的,并且有时为了突出显示,或是设为粗体,或是红色什么的。对于内容,比如显示的字符长度固定,往往居中;对于金额,右对齐。可是,目前来说这个Grid,已有的属性无法做到。而且,在设置统一右对齐时,太靠右了,列头右线和内容右线也有1个像素的偏差。如下图所展现的:

要找到解决办法,我先尝试着看一看TStringGrid类的代码,它在C:\Program Files (x86)\Embarcadero\Studio\15.0\source\fmx\FMX.Grid.pas文件中。因此,我们可以把这个源文件复制一份,放在当前测试的工程目录下。然后对代码进行更改,因为编译时,对于同名单元,优先当前目录的源文件。TStringGrid是TCustomGrid的子类,同样,TGrid也是从TCustomGrid继承而来的。TCustomGrid中各列,其基类是TColumn,所以不管是TStringColumn, TDateColumn,TCheckColumn,都有一些共同属性。通过对FMX.Grid.pas的研究修改,最终实现了想要的效果。现把要FMX.Grid.pas中增加的代码发布出来:
1. TColumn类定义进行修改,第4行代码是原来的设置值为2,注释掉,设为5,使得显示的内容离左、右边线稍远一点点。
1 TColumn = class(TStyledControl) 2 private const 3 { 边距设大一点,效果更美观些 } 4 // HorzTextMargin = 2; 5 HorzTextMargin = 5; 6 {\\\ end add 2014/9/18} 7 VertTextMargin = 1; 8 private 9 ...... 10 [Weak] FGrid: TCustomGrid; 11 {/// 为列头和内容设置样式的参数 } 12 FHeadAlign : TTextAlign; // 列头文字对齐方式 13 FHeadFontColor : TAlphaColor; // 列头文字颜色 14 FHeadFontName : TFontName; // 列头文字字体 15 FHeadFontSize : Single; // 列头文字大小 16 FHeadFontStyle : TFontStyles; // 列头文字样式 17 FTextAlign : TTextAlign; // 内容文本对应方式 18 FTextFontColor : TAlphaColor; // 内容文本颜色 19 FTextFontName : TFontName; // 内容文本字体 20 FTextFontSize : Single; // 内容文本大小 21 FTextFontStyle : TFontStyles; // 内容文本样式 22 {\\\ end add 2014/9/18} 23 procedure SetHeader(const Value: string); 24 ...... 25 published 26 {/// 放出参数可进行设置 } 27 property HeadAlign : TTextAlign read FHeadAlign write FHeadAlign; 28 property HeadFontColor : TAlphaColor read FHeadFontColor write FHeadFontColor; 29 property HeadFontName : TFontName read FHeadFontName write FHeadFontName; 30 property HeadFontSize : Single read FHeadFontSize write FHeadFontSize; 31 property HeadFontStyle : TFontStyles read FHeadFontStyle write FHeadFontStyle; 32 property TextAlign : TTextAlign read FTextAlign write FTextAlign; 33 property TextFontColor : TAlphaColor read FTextFontColor write FTextFontColor; 34 property TextFontName : TFontName read FTextFontName write FTextFontName; 35 property TextFontSize : Single read FTextFontSize write FTextFontSize; 36 property TextFontStyle : TFontStyles read FTextFontStyle write FTextFontStyle; 37 {\\\ end add 2014/9/18} 38 ...... 39 end;
2. TColumn的Create方法,设置加入属性的默认值,此处设置列头(标题栏)默认居中。
1 constructor TColumn.Create(AOwner: TComponent); 2 begin 3 inherited; 4 {/// 增加参数设置默认值 } 5 FHeadAlign := TTextAlign(0); // 列头默认居中 6 FHeadFontColor := TAlphaColors.Black; 7 FHeadFontName := 'Tahoma'; 8 FHeadFontSize := 12.0; 9 FHeadFontStyle := []; 10 FTextAlign := TTextAlign(1); 11 FTextFontColor := TAlphaColors.Black; 12 FTextFontName := 'Tahoma'; 13 FTextFontSize := 12.0; 14 FTextFontStyle := []; 15 {\\\ end add 2014/9/18} 16 FLastRow := -1; 17 FDrawLayouts := TObjectList<TTextLayout>.Create; 18 Width := 100; 19 HitTest := False; 20 CanFocus := False; 21 FEditMode := -1; 22 FApplyImmediately := True; 23 end;
3. 列内容的绘制效果定义,全在DefaultDrawCell中了。
1 procedure TColumn.DefaultDrawCell(const Canvas: TCanvas; const Bounds: TRectF; const Row: Integer; 2 const Value: TValue; const State: TGridDrawStates); 3 var 4 R: TRectF; 5 Layout: TTextLayout; 6 LocalRow: Integer; 7 begin 8 if FDrawable <> nil then 9 FDrawable.DrawCell(Canvas, Bounds, Row, Value, State) 10 else 11 begin 12 R := Bounds; 13 { 根据对齐设置,进行调整 } 14 // R.Inflate(-HorzTextMargin, -VertTextMargin); // 原来是这句,注释了,由下面部分来设定。解决右对齐时太靠边线的问题 15 if FTextAlign = TTextAlign.Leading then 16 R.Inflate(-HorzTextMargin, -VertTextMargin) 17 else if FTextAlign = TTextAlign.Trailing then 18 R.Inflate(HorzTextMargin, -VertTextMargin); 19 {\\\ end add 2014/9/18} 20 LocalRow := Row - Grid.TopRow; 21 if LocalRow >= FDrawLayouts.Count then 22 begin 23 Layout := TTextLayoutManager.DefaultTextLayout.Create(Canvas); 24 FDrawLayouts.Add(Layout); 25 end 26 else 27 Layout := FDrawLayouts[LocalRow]; 28 29 Layout.BeginUpdate; 30 try 31 Layout.TopLeft := R.TopLeft; 32 Layout.Text := ValueToString(Value); 33 Layout.MaxSize := PointF(Width, Grid.RowHeight); 34 Layout.WordWrap := False; 35 Layout.Opacity := AbsoluteOpacity; 36 Layout.HorizontalAlign := Grid.TextSettingsControl.ResultingTextSettings.HorzAlign; 37 Layout.VerticalAlign := Grid.TextSettingsControl.ResultingTextSettings.VertAlign; 38 Layout.Font.Assign(Grid.TextSettingsControl.ResultingTextSettings.Font); 39 Layout.Color := Grid.TextSettingsControl.ResultingTextSettings.FontColor; 40 Layout.Trimming := TTextTrimming.Character; 41 { 设置文本内容显示的样式 } 42 Layout.Font.Family := FTextFontName; 43 Layout.Font.Size := FTextFontSize; 44 Layout.Font.Style := FTextFontStyle; 45 Layout.Color := FTextFontColor; 46 Layout.HorizontalAlign := FTextAlign; 47 Layout.VerticalAlign := TTextAlign(0); // 如有特别要求,可注释此行 48 {\\\ end add 2014/9/18} 49 finally 50 Layout.EndUpdate; 51 end; 52 Layout.RenderLayout(Canvas); 53 end; 54 end;
从上面代码第36~39行可知,原来在Grid中设置的TextSettings值,在所有列都应用它的设定值了。因此,添加第42~47行代码替换。
4. 列头样式的设定,需要更改TCustomGrid.UpdateHeader部分:
1 procedure TCustomGrid.UpdateHeader; 2 var 3 I: Integer; 4 Item: THeaderItem; 5 C: TFmxObject; 6 LHeader: TOpenHeader; 7 begin 8 if not Assigned(FHeader) then 9 Exit; 10 LHeader := TOpenHeader(FHeader); 11 12 LHeader.Sizing := TGridOption.ColumnResize in Options; 13 LHeader.DragReorder := TGridOption.ColumnMove in Options; 14 { 加1个值,让列头与内容右线对齐 } 15 LHeader.Offset := -ViewportPosition.X + 1; 16 {\\\ end add 2014/9/18} 17 LHeader.RemoveObject(LHeader.LastItem); 18 ...... 19 for I := 0 to ColumnCount - 1 do 20 begin 21 TGridHeaderItem(LHeader.Children[I]).Text := Columns[I].Header; 22 TGridHeaderItem(LHeader.Children[I]).Width := Columns[I].Width; 23 TGridHeaderItem(LHeader.Children[I]).Visible := Columns[I].Visible; 24 TGridHeaderItem(LHeader.Children[I]).Column := Columns[I]; 25 { 设置列头的样式 } 26 with TGridHeaderItem(LHeader.Children[I]) do begin 27 StyledSettings := []; // 如果没有这句,则字体大小,颜色不会改变 28 TextAlign := Columns[I].HeadAlign; 29 Font.Family := Columns[I].HeadFontName; 30 Font.Size := Columns[I].HeadFontSize; 31 Font.Style := Columns[I].HeadFontStyle; 32 FontColor := Columns[I].HeadFontColor; 33 end; 34 {\\\ end add 2014/9/18} 35 end; 36 LHeader.Realign; 37 38 if FHeader.Visible <> (TGridOption.Header in FOptions) then 39 begin 40 FHeader.Visible := TGridOption.Header in FOptions; 41 RealignContent; 42 end; 43 end;
至此,源码部分已经更改完毕。我们测试例子,放上Button1加载数据,Button2对齐数据,添加代码如下:
1 procedure TForm1.FormCreate(Sender: TObject); 2 begin 3 Button1Click(nil); 4 end; 5 6 procedure TForm1.Button1Click(Sender: TObject); 7 var i,j:Integer; 8 begin 9 StringGrid1.RowCount := 10; 10 StringGrid1.BeginUpdate; 11 for j := 0 to 2 do 12 begin 13 StringGrid1.Columns[j].Width := 80; 14 for I := 0 to 9 do 15 StringGrid1.Cells[j, I] := 'XE7-'+inttostr(i)+inttostr(j); 16 end; 17 StringGrid1.EndUpdate; 18 end; 19 20 procedure TForm1.Button2Click(Sender: TObject); 21 var i: Integer; 22 begin 23 StringGrid1.BeginUpdate; 24 for i := 0 to StringGrid1.ColumnCount - 1 do 25 begin // 设置所有列头文字,蓝色粗体并大一些, 26 StringGrid1.Columns[i].HeadFontColor := TAlphaColors.Blue; 27 StringGrid1.Columns[i].HeadFontSize := 15; 28 StringGrid1.Columns[i].HeadFontStyle := [TFontStyle.fsBold]; 29 //StringGrid1.Columns[i].HeadAlign := TTextAlign(2); // 默认是居中了,也可以更改 30 end; 31 // 设置第二列文本,居中,绿字 32 StringColumn2.TextAlign := TTextAlign(0); 33 StringColumn2.TextFontName := '宋体'; 34 StringColumn2.TextFontColor := TAlphaColors.Green; 35 // 设置第三列文本,右对齐,红字 36 StringColumn3.TextAlign := TTextAlign(2); 37 StringColumn3.TextFontColor := TAlphaColors.Red; 38 39 StringGrid1.RealignContent; // 使内容文本样式有效 40 StringGrid1.EndUpdate; // 使列头文字样式有效 41 end;
运行效果如下:

后记:所增加的10个参数,最理想的是在可视化设计时设置。可惜我不知道FMX.Grid.pas是在系统的哪个dpk包,所以没法重编译安装一下。
并且,之所以用10个参数,而不能像TStringGrid的TextSetting参数,是因为之前我也用了两个
FHeadSettings : TTextSettings
FTextSettings : TTextSettings
来包含现在的,但是这是一个组合的属性类,在TColumn的Create时也创建FHeadSettings,FTextSettings,并且在Destory释放。
但是在测试例子赋值后,DefaultDrawCell,UpdateHeader执行时却仍得不到期望的,所以只得精简出10个有用的来设置。
使用方法:将更改过的 FMX.Grid.pas 拷到你的Project目录下,并最好加到 Project 中。至于其它版本,应该根据这个思路修改,再去测试调整。
本测试例子,感谢【[深圳]从头再来(358880222)】给我的帮助,以及网络资源一大堆我记不住名字的有名氏。
有改进的东东,比如多行标题,某标题列还能合并,加入合计栏等... 不妨email给我一份:3822640@qq.com,共同学习。
浙公网安备 33010602011771号