我的主页: http://www.tommstudio.com/ 属性编辑器对于大多数Delphi程序员来说无疑是很熟悉的,在对象编辑器的内核中有着大量的属性编辑器,每个对象编辑器中的属性都对应一个属性编辑器类的实例。
Delphi5中提供了一些新的高级特性,使我们能够定义新的属性编辑器,为以有的属性提供新的功能,或者设定和显示新的控件的新的属性的显示方法。在Delphi5以前,对象编辑器只能够以文本的形式显示属性值。在Delphi 5中给属性编辑器提供了新的特性,使我们能够以任何形式显示属性的名称和值,如下图所示如果属性有一个下拉列表,我们就可以为每一个列表项添加一个图标。下面我们就来研究一下如何实现属性编辑器的自绘画的功能。
属性编辑刷新器
所有的属性编辑器都是从TpropertyEditor继承下来的。我们可以为特定的属性类型、属性名或控件注册一个属性编辑器。对象编辑器检查每一个要显示的属性的名称和类型,选择合适的属性编辑器类。然后它会创建这个类的一个实例(每个属性对应一个实例)。当我们选择了另一个控件,对象编辑器会释放全部的属性编辑器对象,然后为新的控件创建新的对象。
属性编辑器可以决定如何显示属性的值以及用户如何设定一个新的属性值。比如,TintegerProperty调用IntToStr函数以字符串的形式显示整数值并用StrToInt函数来转换用户输入的新值。 当用户输入了一个新的属性值时,TcolorProperty同样使用一个整型值来表示,但把整数解释为颜色,并尽可能地映射颜色值为一个名称(如clBlack或clBtnFace) 。
一个属性编辑器实现上述功能是通过重载TpropertyEditor的一个或多个方法来实现的。绝大多数的属性编辑器需要重载GetValue方法,GetValue方法获得属性值的字符串形式。以及SetValue方法,SetValue方法把一个字符串转化为属性值。要想了解关于编写属性编辑器的进一步信息,需要仔细研究DsgnIntf.pas文件(在Delphi5\Source\Toolsapi目录下)以及Delphi 5 在线帮助(在"property editors, creating"部分里)。
基础步骤
要实现一个最基本的自绘画属性编辑器,我们只需要重载TpropertyEdiotr的PropDrawValue 方法。比如如前面图中所见到的,TcolorProperty属性重载了PropDrawValue方法在颜色名前显示一个对应于相应颜色的彩色小方块。为了理解如何使用PropDrawValue方法,我们为Tfont对象写一个新的属性编辑器,新的编辑器将会用当前字体名对应的字体来显示Tfont对应的属性。
Delphi本身已经提供了一个属性编辑器TfontProperty,它在对象编辑器中添加了一个省略按钮,用户可以点击按钮调出标准的Windows字体选择对话框来设定字体的属性。我们可以直接从TfontProperty继承新的编辑器,类的声明如下:
type TVisualFontProperty = class(TFontProperty) public procedure PropDrawValue(Canvas: TCanvas; const Rect: TRect; Selected: Boolean); override; end;
当对象编辑器需要显示属性值的时候,IDE会调用PropDrawValue方法来画属性值。Delphi传递一个画布对象(Canvas)及绘画区域来供程序画属性值。Selected参数现在还没用,我们可以忽略它。
注意:Delphi并不会为给定的绘画区域设定剪裁区域,也就是说我们必须严格按照给定的区域绘画,如果超出界限,会把别的属性值给覆盖掉。
TvisualFontProperty对象的唯一任务就是选择相应于Font名字的字体来画属性值。它设定字体的名称,样式以及颜色(当颜色不同于背景色的时候),字体的大小显示保留不动,以免使用非常大或非常小的字体大小画值的时候会出现的问题。下面就是PropDrawValue的实现部分:
// 替换乏味的Tfont属性值的显示方式,用选定的字体样式 //和字体来画相应的属性值,用户可能会选择比较大的字体 //尺寸,所以这里保留字体大小不动,只有当字体颜色不同 //于背景色的时候,才用相应的颜色画,否则前景背景一样 //的话就无法看到字体的属性值了
procedure TVisualFontProperty.PropDrawValue( Canvas: TCanvas; const Rect: TRect; Selected: Boolean); var Font: TFont; begin Font := TFont(GetOrdValue); if Font <> nil then begin if ColorToRGB(Font.Color) <> ColorToRGB(clBtnFace) then Canvas.Font.Color := Font.Color; Canvas.Font.Name := Font.Name; Canvas.Font.Style := Font.Style; end; inherited; end;
另外我们重载GetValue方法来提供更多的信息,比如字体名和大小。
function TVisualFontProperty.GetValue: string; var Font: TFont; begin Font := TFont(GetOrdValue); if Font = nil then Result := inherited GetValue else Result := Format(''''%s, %d'''', [Font.Name, Font.Size]); end;
我们可以画任何东西到画布上,比如图标和位图的属性编辑器是TgraphicProperty。它显示把图标属性显示为一个乏味的字符串”TIcon”。 我们可以把图标属性显示为对应的图标,这样的界面更加友好。这里我们继承一个TvisualGraphicProperty对象重载PropDrawValue来实现这一功能。
Tpicture属性的情况也是类似的,所以我们用一个公用的过程DrawGraphic来实现,DraGraphic缩放图形对象使之符合对象编辑器可用空间的大小,同时它维持原来的宽高比,缩放图像为最小的可能的尺寸。对于图标来说,由于Windows不能缩放图标,所以DrawGraphic调用StretchIcon过程把图标画到位图上,然后缩放位图。下面是过程代码:
// Windows不能缩放图标,所以如果图标大小不匹配的话, //把它画到一个临时的位图上,然后缩放位图。 procedure StretchIcon(Canvas: TCanvas; const Rect: TRect; Icon: TIcon); var Bitmap: TBitmap; begin Bitmap := TBitmap.Create; try Bitmap.Height := Icon.Height; Bitmap.Width := Icon.Width; Bitmap.Canvas.Brush.Color := clBtnFace; Bitmap.Canvas.FillRect(Rect); Bitmap.Canvas.Draw(0, 0, Icon); Canvas.StretchDraw(Rect, Bitmap); finally Bitmap.Free; end; end;
procedure DrawGraphic(Canvas: TCanvas; const Rect: TRect; Graphic: TGraphic; const Value: string); var R: TRect; HeightRatio, WidthRatio: Single; begin Canvas.FillRect(Rect); //缩放图像使其符合给定空间大小, //同时保持图像宽高比不变 HeightRatio := (Rect.Bottom - Rect.Top) / Graphic.Height; WidthRatio := (Rect.Right - Rect.Left) / Graphic.Width; R := Rect; if HeightRatio < WidthRatio then R.Right := R.Left + Trunc(Graphic.Width * HeightRatio) else R.Bottom := R.Top + Trunc(Graphic.Height * WidthRatio); if (Graphic is TIcon) and ((HeightRatio > 1) or (WidthRatio > 1)) then StretchIcon(Canvas, R, TIcon(Graphic)) else Canvas.StretchDraw(R, Graphic); // 在图像的右边,让继承的编辑器画缺省的文本,比如“Ticon“ R.Left := R.Right; R.Right := Rect.Right; R.Top := Rect.Top; R.Bottom := Rect.Bottom; Canvas.TextRect(R, R.Left+1, R.Top+1, Value); end;
我们在DrawGraphic过程中写了主要的代码,所以PropDrawValue就显得简单多了,主要的作用是确保属性有一个有效的图形对象,如果没有就调用继承的方法来处理。代码如下:
procedure TVisualGraphicProperty.PropDrawValue( Canvas: TCanvas; const Rect: TRect; Selected: Boolean); var Graphic: TGraphic; begin Graphic := TGraphic(GetOrdValue); if (Graphic = nil) or Graphic.Empty or (Graphic.Height = 0) or (Graphic.Width = 0) then inherited else DrawGraphic(Canvas, Rect, Graphic, GetVisualValue); end;
自绘画的名字
我们可以重载PropDrawName方法,它同PropDrawValue方法工作方式类似,只不过一个是画值,一个是画名称。大多数属性的名字不需要任何特殊的处理,对于BoldFace属性的名字来说,把名字加粗可以便于用户了解BoldFace属性的情况。下面的代码显示了TboldComponentNameProperty类的如何实现PropDrawName方法的。 [1] [2] [3] 下一页 |