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 .增加图形能力
一旦你声明了图形部件并公布了继承的属性,就可以给部件增加图形功能。这时需要知道两点:
● 决定画什么
● 怎样画部件图形
在Shape控制的例子中,需要增加一些能使用户在设计时改变形状的属性。
1. 决定画什么
图形部件通常都具有改变外观的能力,图形控制的外观取决于其某些属性的结合,例如Gauge控制具有决定其形状、方向和是否图形化地显示其过程的能力。同样,Shape控制也应有决定显示各种形状的能力.
给予Shape控制这种能力,增加名为Shape的属性。这需要下列三步:
● 声明属性类型
● 声明属性
● 编写实现方法
⑴ 声明属性类型
当声明一个用户自定义类型的属性时,必须首先声明属性类型。最普通地用于属性的自定义类型是枚举类型。
对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方法所做的那样。