DELPHI基础教程
第十九章 Delphi自定义部件开发(四)
19.3.2 创建图形部件 图形控制是一类简单的部件。因为纯图形部件从不需要得到键盘焦点,所以它没有也不要窗口句柄。包含图形控制的应用程序用户仍然可以用鼠标操作控制,但没有键盘界面。 在本例中提供的图形部件是 TShape 。 Shape 部件位于 Component Palette 的 Additional 页。本例中的 Shape 部件有所不同,因此称其为 TSampleShape 。 创建图形部件需要下列三个步骤: ● 创建和注册部件 ● 公布 (publishing) 继承的属性 ● 增加图形功能
19.3.2.1 创建和注册部件
每个部件的创建都从相同的方式开始,在本例中如下: ● 建立名为 Shapes 的部件单元 ● 从 TGraphicControl 继承,将新部件称为 TSampleShape ● 在 Component Palette 的 Samples 页上注册 TSampleShape
unit Shapes
intertace
use SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics , Controls , Forms;
type TSampleShape=class(TGraphicControl) end;
implementation
procedure Register; begin RegisterComponents('Samples' , [TSampleShape]); end;
end.
19.3.2.2 公布继承的属性
一旦决定了部件类型,就能决定在父类的 protected 部分声明哪些属性和事件能为用户可见。 TGraphicControl 已经公布了所有作为图形控制的属性,因此,只需公布响应鼠标和拖放事件的属性。
type TSampleShape=class(TGraphicControl) published property DragCursor; property DragMode; property OnDragDrop; property OnDragOver; property ONEndDrag; property OnMouseDown; property OnMouseMove; property OnMouseup; end;
这样,该 Shape 控制具有通过鼠标和拖放与用户交互的能力。
19.3.2.3 . 增加图形能力
一旦你声明了图形部件并公布了继承的属性,就可以给部件增加图形功能。这时需要知道两点: ● 决定画什么 ● 怎样画部件图形
lang="ZH-CN" 在 Shape 控制的例子中,需要增加一些能使用户在设计时改变形状的属性。
1. 决定画什么 图形部件通常都具有改变外观的能力,图形控制的外观取决于其某些属性的结合,例如 Gauge 控制具有决定其形状、方向和是否图形化地显示其过程的能力。同样, Shape 控制也应有决定显示各种形状的能力 . 给予 Shape 控制这种能力,增加名为 Shape 的属性。这需要下列三步: ● 声明属性类型 ● 声明属性 ● 编写实现方法
⑴ 声明属性类型 当声明一个用户自定义类型的属性时,必须首先声明属性类型。最普通地用于属性的自定义类型是枚举类型。 lang="ZH-CN" 对 Shape 控制来说,需要声明一个该控制能画形状的枚举,下面是枚举类型的声明:
type TSampleShapeType=(sstRectangle, sstSquare, sstRoundRect, sstRoundSquare, sstEllipse, sstCircle); TSampleShape = class(TGraphicControl) end;
这样,就可以用该类型来声明属性。 ⑵ 声明属性 当声明一个属性时,通常需要声明私有域来保存属性值,然后描述读写属性值的方法。 对于 Shape 控制,将声明一个域保存当前形状,然后声明一个属性通过方法调用来读写域值。
type TSampleShape=class(TGrahpicControl) private FShape: TSampleShapeType; procedure SetShape(value: TSampleShapeType); published property Shape: TSampleShapeType read FShape write SetShape; end;
现在,只剩下 SetShape 的实现部分了。 ⑶ 编写实现方法 下面是 SetShape 的实现:
procedure TSampleShape.SetShape(value: TSampleShapeType); begin if FShape<>value then begin FShape := value; Invalidate(True); { 强制新形状的重画 } end; end;
2. 覆盖 constructor 和 destructor 为了改变缺省属性值和初始化部件拥有的对象,需要覆盖继承的 constructor 和 destructor 方法。 图形控制的缺省大小是相同的,因此需要改变 Width 和 Height 属性。 本例中 Shape 控制的大小的初始设置为边长 65 个象素点。 ⑴ 在部件声明中增加覆盖 constructor
type TSampleShape=class(TGraphicControl) public constructor Create(Aowner: TComponent); override; end;
⑵ 用新的缺省值重新声明属性 Height 和 width
type TSampleShape=class(TGrahicControl) published property Height default 65; property Width default 65; end;
⑶ 在库单元的实现部分编写新的 constructor
constructor TSampleShape.Create(Aowner: TComponent); begin inherited Create(AOwner); width := 65; Height := 65; end;
3. 公布 Pen 和 Brush 在缺省情况下,一个 Canvas 具有一个细的、黑笔和实心的白刷,为了使用户在使用 Shape 控制时能改变 Canvas 的这些性质,必须能在设计时提供这些对象;然后在画时使用这些对象,这样附属的 Pen 或 Brush 被称为 Owned 对象。 管理 Owned 对象需要下列三步: ● 声明对象域 ● 声明访问属性 ● 初始化 Owned 对象
⑴ 声明 Owned 对象域 拥有的每一个对象必须有对象域的声明,该域在部件存在时总指向 Owned 对象。通常,部件在 constructor 中创建它,在 destructor 中撤消它。 Owned 对象的域总是定义为私有的,如果要使用户或其它部件访问该域,通常要提供访问属性。 下面的代码声明了 Pen 和 Brush 的对象域:
type TSampleShape=class(TGraphicControl) private FPen: TPen; FBrush: TBrush; end;
⑵ 声明访问属性 可以通过声明与 Owned 对象相同类型的属性来提供对 Owned 对象的访问能力。这给使用部件的开发者提供在设计时或运行时访问对象的途径。 下面给 Shape 控制提供了访问 Pen 和 Brush 的方法
type TSampleShape=class(TGraphicControl) private procedure SetBrush(Value: TBrush); procedure SetPen(Value: TPen); published property Brush: TBrush read FBrush write SetBrush; property Pen: TPen read FPen write SetPen; end;
然后在库单元的 implementation 部分写 SetBrush 和 SetPen 方法:
procedure TSampleShape.SetBrush(Value: TBrush); begin FBrush.Assign(Value); end;
procedure TSampleShape.SetPen(Value: TPen); begin FPen.Assign(Value); end;
⑶ 初始化 Owned 对象 部件中增加了的新对象,必须在部件 constructor 中建立,这样用户才能在运行时与对象交互。相应地,部件的 destructor 必须在撤消自身之前撤消 Owned 对象。 因为 Shape 控制中加入了 Pen 和 Brush 对象,因此,要在 constructor 中初始化它们,在 destructor 中撤消它们。 ① 在 Shape 控制的 constructor 中创建 Pen 和 Brush
constructor TSampleShape.Create(Aowner: TComponent); begin inherited Create(AOwner); Width := 65; Height := 65; FPen := TPen.Create; FBrush := TBrush.Create; end;
② 在部件对象的声明中覆盖 destructor
type TSampleShape=class(TGraphicControl) public construstor.Create(Aowner: TComponent); override; destructor.destroy; override; end;
③ 在库单元中的实现部分编写新的 destructor
destructor TSampleShape.destroy; begin FPen.Free; FBrush.Free; inherited destroy; end;
④ 设置 Owned 对象的属性 处理 Pen 和 Brush 对象的最后一步是处理 Pen 和 Brush 发生改变时对 Shape 控制的重画问题。 Pen 和 Brush 对象都有 OnChange 事件,因此能够在 Shape 控制中声明 OnChange 事件指向的事件处理过程。 下面给 Shape 控制增加了该方法并更新了部件的 constructor 以使 Pen 和 Brush 事件指向新方法:
type TSampleShape = class(TGraphicControl) published procdeure StyleChanged(Sender: TObject); end;
implemintation
constructor TSampleShape.Create(AOwner:TComponent); begin inherited Create(AOwner); Width := 65; Height := 65; Fpen := TPen.Create; FPen.OnChange := StyleChanged; Fbrush := TBrush.Create; FBrush.OnChange := StyleChanged; end;
procedure TSampleShape.StyleChanged(Sender: TObject); begin Invalidate(true); end;
当变化发生时,部件重画以响应 Pen 或 Brush 的改变。
4. 怎样画部件图形 图形控制基本要素是在屏幕上画图形的方法。抽象类 TGraphicControl 定义了名为 Paint 的虚方法,可以覆盖该方法来画所要的图形。 Shape 控制的 paint 方法需要做: ● 使用用户选择的 Pen 和 Brush ● 使用所选的形状 ● 调整座标。这样,方形和圆可以使用相同的 Width 和 Height
覆盖 paint 方法需要两步: ● 在部件声明中增加 Paint 方法的声明 ● 在 implementation 部分写 Paint 方法的实现
下面是 Paint 方法的声明:
type TSampleShape = class(TGraphicControl) protected procedure Paint; override; end;
然后,编写 Paint 的实现:
procedure TSampleShape.Paint; begin with Canvas do begin Pen := FPen; Brush := FBrush; case FShape of sstRectangle, sstSquare : Rectangle(0, 0, Width, Height); sstRoundRect, sstRoundSquare: RoundRect(0, 0, Width, Height, Width div 4, Height div 4); sstCircle, sstEllipse : Ellipse(0, 0, Width, Height); end; end; end; 无论任何控制需要更新图形时, Paint 就被调用。当控制第一次出现,或者当控制前面的窗口消失时, Windows 会通知控制画自己。也可以通过调用 Invalidate 方法强制重画,就象 StyleChanged 方法所做的那样。 |