delphi组件开发

⑴ Delphi怎样处理HELP请求
Delphi基于关键词查询HELP请求。就是说,当用户在窗体设计窗口的已选部件上按F1键时,Delpdi将部件的名字转换成一个关键词,然后调用Windows Help引擎查找那个关键词的帮助主题。关键词是Windows Help系统的标准部分。实际上 ,WinHelp使用Help中的关键词产生Search对话框中的列表。因为用于上下文敏感搜索中的关键词不是实际供用户读的,所以要输入关键词的替代词。
例如,一个查找名为TSomething的部件的详细信息的用户可能打开WinHelp的Search对话框并输入TSomething。但不会使用用于窗体设计窗口的上下文查找的替代形式class-TSomething。因此,这个特殊的关键词Class-TSomething对用户是不可见的,以免弄乱了搜索列表。
  ⑵ 将Help插入Delphi
Delphi提供了创建和插入Windows Help文件的工具,包括Windows Help编译器HC.EXE。为自定义部件建立Help文件的机制与建立任何Help文件没什么不同,但需要遵循一些约定以与库中其它Help兼容。
  保持兼容性的方法如下:
  ● 建立Help文件
● 增加特殊的注脚
● 建立关键词文件
● 插入Help索引
 
当你为自定义部件建立完Help,有下列几个文件:
● 编译过的Help(.HLP)文件
● Help关键词(.KWF)文件
● 一个或多个Help源文件(.RTF)
● Help工程文件(.HLJ)
 
编译过的Help文件和关键词文件应当与库单元在同一目录。
  ① 建立Help文件
你可以使用任何的工具创建Windows Help文件。Delphi的多文件搜索引擎,可以包含任何数目的Help文件的要素。在编译的Help文件之外,你应当拥有RTF源文件,这样才能生成关键词文件。
  为使自定义部件的Help同库中其它部件一起工作,要遵循下列约定:
  ● 每个部件有占一页的帮助
部件帮助页应当给出部件目的的简单描述,然后列出最终用户可用的属性、事件和方法的描述。应用开发者通过在窗体上选择部件并按F1访问这一页。
  部件帮助页应当有一个用于关键词搜索的“K”脚注,脚注中包含部件名。例如,TMemo的关键词脚注读作"TMemo Component"
● 部件增加和修改的每一个属性,事件和方法应当有一页帮助
  属性、事件或方法的帮助页应当指出该项用于哪个部件,显示声明语法和描述它的使用方法。
  属性、事件或方法的帮助页应当有一个用于关键词搜索的“K”脚注,该脚注中包含该项的名字和种类。例如,属性Top的关键词脚注为“Top property”。
  Help文件的每一页也需要用于多文件索引搜索的特殊脚注。
  ② 增加特殊脚注
Delphi需要特殊的搜索关键词以区别用于部件的帮助页和其它项目。你应当为每一项提供标准的关键词搜索项。但你也需要用于Delphi的特殊脚注。
  要为来自Object Inspector窗口或代码编辑器F1的搜索增加关键词,就得为Help文件帮助页增加"B"脚注。
  “B”脚注与用于标准WinHelp关键词搜索的“K”脚注很相象,但它们只用于Delphi搜索引擎。下表列出怎样为每种部件帮助页建立“B”脚注:
 
表19.7 部件帮助页搜索注脚
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
帮助页类型 "B"脚注内容 示 例
   ──────────────────────────────────
主部件页 'class_'+部件类型名 class_TMemd
一般属性或事件页 'prop_'+属性名 prop_WordWrap
'event_'+事件名 event_OnChange
部件特有的属性 'prop_'+部件类型名 prop_TMemoWordWrap
或事件页 +属性名
'event_'+部件类型名 event_TMemoOnChange
+事件名
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 
  区别一般帮助页和部件特有的帮助页是很重要的。一般帮助页应用于所有部件上的特定属性和事件。例如Left属性是所有部件中的标识。因此,它用字符串Prop-Left进行搜索。而Borde-style依赖于所属的部件,因此,BorderStyle属性拥有自己的帮助页。例如,TEdit有BorderStyle属性的帮助页,搜索字符串为Prop_TEditBorderStyle。
  ③ 建立关键词文件
  建立和编译了Help文件,并且增加了脚注之后,还要生成独立的关键词文件,这样Delphi才能将它们插入主题搜索的索引。
  从Help资源文件RTF创建关键词文件的方法如下:
● 在DOS提示行下,进入包含RTF文件的目录
● 运行关键词文件产生程序——KWGEN.EXE,后跟Help工程文件,如KWGEN SPECIAL.HPJ。当KWGEN运行完毕后,就有了与Help工程文件相同的关键词文件,但以.KWF为扩展名
● 将关键词文件放在编译完的库单元和Help文件相同的目录
当你在Component Palette上安装部件时,希望关键词插入Delphi Help系统的搜索索引。
 
④ 插入Help索引
以自定义部件建立关键词文件后,要将关键词插入Delphi的Help索引。
  将关键词文件插入Detphi Help索引的方法如下:
● 将关键词文件放在与编译完的库单元和Heph文件相同的目录中
● 运行HELPINST程序
 
HELPINST运行完后,Delphi的Help索引文件(.HDX)包含自定义部件帮助页的关键词。
⑶ 存储和装入属性
Delphi将窗体及其拥有的部件存储在窗体文件(.DFM)中,DFM文件用二进制表示窗体的属性和它的部件。当Delphi用户将自定义部件加入窗体中时,自定义部件应当具有存储它们的属性的能力。同样,当被调入Delphi或应用程序时,部件必须能从DFM文件中恢复它们。
  在大多数时候,不需要做任何使部件读写DFM文件的事。存储和装入都是继承的祖先部件的行为的一部分。然而在某些情况下,你可能想改变部件存储和装入时初始化的方法。因此,应当理解下述的机制:
● 存储和装入机制
● 描述缺省值
● 决定存储什么
● 装入后的初始化
 
① 存储和装入机制
当应用开发者设计窗体时,Delphi将窗体的描述存储在DFM文件中。当用户运行程序时,它读取这些描述。
  窗体的描述包含了一系列的窗体属性和窗体中部件的相似描述。每一个部件,包括窗体本身,负责存储和装入自身的描述。
  在缺省情况下,当存储时,部件将所有public和published属性的不同于缺省值的值以声明的顺序写入。当装入时,部件首先构造自己,并将所有属性设为缺省值;然后,读存储的、非缺省的属性值。
  这种缺省机制,满足了大多数部件的需要,而又不需部件编写者的任何工作。然而自己定义存储和装入过程以适合自定义部件需要的方法也有几种。
  ② 描述缺省值。
  Delphi部件只存储那些属性值不同于缺省值的属性。如果你不描述,Delphi假设属性没有缺省值,这意味着部件总是存储属性。
  一个属性的值没被构造函数设置,则被假设为零值。为了描述一个缺省值,在属性声明后面加default指令和新的缺省值。
  你也能在重声明属性时描述缺省值。实际上,重声明属性的一个原因是指定不同的缺省值。只描述缺省值,那么在对象创建时并不会自动地给属性赋值,还需要在部件的Create方法中赋所需的值。
  下面的代码用Align属性演示了描述缺省值的过程.
 
type
TStatusBar=class(TPanel)
public
constructor Create(Aowner: TComponent); override; { 覆盖以设置新值 }
published
property Align default alBottom; { 重新声明缺省值 }
end;
 
constructor TStatusBar.Create(Aowner: TComponent);
begin
inherited Create(Aowner); { 执行继承的初始化过程 }
Align := alBottom; { 为Align赋新的缺省值 }
end;
 
③ 决定存储什么
用户也可以控制Delphi是否存储部件的每一个属性。缺省情况下,在对象的published部分声明的所有属性都被存储。然而,可以选择不存储所给的属性,或者设计一个函数在运行时决定是否存储属性。
  控制Delphi是否存储属性的方法是在属性声明后面加stored指令,后跟True或False,或者是布尔方法名。你可以给任何属性的声明或重声明加stored表达式。下面的代码显示了部件声明三种新属性。一个属性是总是要存储,一个是不存,第三个则决定于布尔方法的值:
 
type
TSampleCompiment = class(TComponent)
protected
function storeIt: Boolean;
public { 正常情况下在不存 }
property Important: Integer stored True; { 总是存储 }
published { 正常情况下保存 }
property UnImportant: Integer stored False; { 不存 }
property Sometimes: Integer stored StoreIt; { 存储依赖于函数值 }
end;
 
④ 载入后的初始化
在部件从存储的描述中读取所有的属性后,它调用名为Loaded的虚方法,这提供了按需要执行任何初始化的机会。调用Loaded是在窗体和它的控制显示之前,因此,不需要担心初始化会带来屏幕闪烁。
  在部件载入属性时初始化它,要覆盖Loaded方法。
  在Loaded方法中,要做的第一件事是调用继承的Loaded方法。这使得在你的部件执行初始化之前,任何继承的属性都已初始化。
  下面的代码来自于TDatabase部件。在装入后,TDatabase试图重建在它存储时已打开的连接,并描述在连接发生异常时如何处理。
 
  procedure TDatabase.Loaded
begin
inherited Loaded; { 总是先调用继承的方法 }
Modified; { 设置内部标志 }
try
if FStreamedConnected then Open; { 重建联接 }
except
if csDesigning in ComponentState then { 在设计时 }
Application.HandleException(self) { 让Delphi处理异常 }
else raise; { 否 则 }
end;
end;
 
 
19.3 Delphi部件编程实例
 
19.3.1 创建数据库相关的日历控制-TDBCalendar
 
  当处理数据库联接时,将控制和数据直接相联是很重要的。就是说,应用程序可以建立控制与数据库之间的链。Delphi包括了数据相关的标签、编辑框、列表框和栅格。用户可以使自己的控制与数据相关。
  数据相关有若干等级。最简单的是只读数据相关或数据浏览,以及反映数据库当前状态的能力。比较复杂的是数据相关的编辑,也即用户可以在控制上操作数据库中的数据。
  在本部分中将示例最简单的情况,即创建联接数据库的单个字段的只读控制。本例中将使用Component Palette的Samples页中的TCalendar部件。
创建数据相关的日历控制包括下列几步:
● 创建和注册部件
● 使控制只读
● 增加数据联接(Data Link)
● 响应数据改变
 
19.3.1. 1创建和注册部件
 
每个部件的创建都从相同的方式开始,在本例中将遵循下列过程:
● 将部件库单元命名为DBCal
● 从TCalendar继承一个新部件,名为TDBCalendar
● 在Component Palette的Samples页中注册TDBCalendar
 
下面就是创建的代码:
 
unit DBCal;
 
interface
 
uses SysUtils, WinTypes, WinProc, Messages, Classes, Graphics, Controls,
Forms, Grids, Calendar;
type
TDBCalendar=class(TCalendar)
end;
 
procedure Register;
 
implementation
 
procedure Register;
begin
RegisterComponents(Samples,[TDBabendar]);
end;
 
end.
 
19.3.1.2 使控制只读
 
因为这个数据日历以只读方式响应数据,所以用户不能在控制中改变数据并指望它们反映到数据库中。
使日历只读包含下列两步:
● 增加只读属性
● 允许所需的更新
 
1. 增加只读属性
给日历控制增加只读选项是直接过程。通过增加属性,可以提供在设计时使控制只读的方法,当属性值被设为True,将使控制中所有元素不可被选。
⑴ 增加属性声明和保存值的private域:
 
type
TDBCalendar=class(TClendar)
private
FReadOnly: Boolean;
public
constructor Create (Aowner: TComponent); override;
published
property ReadOnly: Boolean read FReadOnly write FReadOnly default True;
end;
 
constructor TDBCalendar.Create(Aowner: TComponent);
begin
inherited Create(AOwner);
FReadOnly := True;
end;
 
⑵ 覆盖SelectCell方法,使得当控制是只读时,不允许选择:
 
function TDBCalendar.SelectCell(ACol, Arow: Longint): Boolean;
begin
if FReadOnly then
Result := False
else
Result := inherited SelectCell(Acol,ARow);
end;
 
还要在TDBcalendar的声明中声明SelectCell。
如果现在将Calendar加入窗体,会发现部件完全忽略鼠标和击键事件,而且当改变日期时,也不能改变选择的位置。下面将使控制响应更新。
2. 允许所需的更新
只读日历使用SelectCell方法实现各种改变,包括设置Row和Col的值。当日期改变时,UpdateCalendar方法设置Row和Col的值,但因为SelectCell不允许你改变,即使日期改变了,选择仍留在原处。
可以给日历增加一个Boolean标志,当标志为True时允许改变:
 
type
TDBCalendar=class(TCalendar)
private
Fupdating: Boolean;
protected
function SelectCell(Acol, Arow: Longint); Boolean; override;
public
procedure UpdateCalendar; override;
end;
 
function TDBCalendar.SelectCell(ACol, ARow: Longint): Boolean;
begin
if (not FUpdating) and FReadOnly then
Result := False { 如果更新则允许选择 }
else
Result := inherited SelectCell(ACol, ARow); { 否则调用继承的方法 }
end;
 
procedure UpdateCalendar;
begin
FUpdating := True; { 将标志设为允许更新 }
try
inherited UpdateCalendar; { 象通常一样更新 }
finally
FUpdating := False; { 总是清除标志 }
end;
end;
 
  现在日历仍旧不允许用户修改,但当改变日期属性时能正确反映改变;目前已有了一个真正只读控制,下一步是增加数据浏览能力。
 
  3. 增加数据联接
  控制和数据库的联接是由一个名为DataLink的对象处理。Delphi提供了几种类型的Datalink。将控制与数据库单个域相联的DataLink对象是TFieldDatalink。Delphi也提供了与整个表相联的DataLink。
  一个数据相关控制拥有DataLink对象,就是说,控制负责创建和析构DataLink。
  要建立作为拥有对象的Datalink,要执行下列三步:
  ● 声明对象域
  ● 声明访问属性
  ● 初始化DataLink
 
  ⑴ 声明对象域
  每个部件要为其拥有对象声明一个对象域。因此,日历对象DataLink 声明TFieldDataLink类型的域。
  日历部件中DataLink的声明如下:
 
type
TDBCalendar = class(TSampleCalendar)
private
FDataLink: TFieldDataLink;
end;
 
  ⑵ 声明访问属性
  每一个数据相关控制有一个DataSource属性,该属性描述应用程序给控制提供数据的数据源。而且,访问单个域的数据库还需要一个DataField 属性描述数据源中的域。
  下面是DataSource和DataField的声明和它们的实现方法:
 
type
TDBCalendar = class(TSampleCalendar)
private { 属性的实现方法是 }
function GetDataField: string; { 返回数据库字段的名字 }
function GetDataSource: TDataSource; { 返回数据源(Data source)的引用 }
procedure SetDataField(const Value: string); { 给数据库字段名赋值 }
procedure SetDataSource(Value: TDataSource); { 给数据源赋值 }
published { 使属性在设计时可用 }
property DataField: string read GetDataField write SetDataField;
property DataSource: TDataSource read GetDataSource write SetDataSource;
end;
 
……
 
function TDBCalendar.GetDataField: string;
begin
Result := FDataLink.FieldName;
end;
 
function TDBCalendar.GetDataSource: TDataSource;
begin
Result := FDataLink.DataSource;
end;
 
procedure TDBCalendar.SetDataField(const Value: string);
begin
FDataLink.FieldName := Value;
end;
 
procedure TDBCalendar.SetDataSource(Value: TDataSource);
begin
FDataLink.DataSource := Value;
end;
 
  现在,就建立了日历和DataLink的链,此外还有一个更重要的步骤。你必须在日历构建时创建DataLink对象,在日历析构时,撤消DataLink对象。
  ⑶ 初始化DataLink
  在数据相关控制在其存在的期间要不停地访问DataLink对象,因此,必须在其构建函数中创建DataLink创建并且在析构时,撤消DataLink对象,因此要覆盖日历的Create和Destroy方法。
 
type
TDBCalendar=class(TCalendar)
public
constructor Create(Aowna: TComponent); override;
destructor Destroy; override;
end;
 
constructor TDBCalendar Create (Aowner: TComponent);
begin
inherited Create(AOwner);
FReadOnly := True;
FDataLink := TFieldDataLink.Create;
end;
 
destructor TDBCalendar Destroy;
begin
FDataLink.Free;
inherited Destroy;
end;
 
现在,部件已拥有完整的DataLink,但部件还不知从相联的域中读取什么数据。
 
19.3.1.4 响应数据变化
 
  一旦控制拥有了数据联接(DataLink)和描述数据源和数据域的属性。就需在数据记录改变时响应域中数据的变化。
  DataLink对象都有个名为OnDataChange的事件。当数据源指示数据发生变化时,DataLink对象调用任何OnDataChange所联接的事件处理过程。
  要在数据改变时更新数据,就需要给DataLink对象的OnDataChange事件增加事件处理过程。
  下面声明了DataChange方法,并将其赋给DataLink对象的OnDataChange事件:
 
type
TDBCalendar=class(TCalendar)
private
procedure Datachange(Sender: TObject);
end;
 
constructor TDBCalendar Create(AOwner:TComponent);
begin
inherited Create(AOwner);
FReadOnly := True;
FDataLink := TFieldDataLink.Create;
FDataLink.OnDataChange := DataChange;
end;
 
destructor TDBcalendar.Destroy;
begin
FDataLink.OnDataChange := nil;
FDataLink.Free;
inherited Destroy
end;
 
procedure TDBCalendar.DataChange(Sender: TObject);
begin
if FDataLink.Filed=nil then
CalendarDate := 0;
else
CalendarDate := FDataLink.Field.AsDate;
end;
;
(四)
创建图形部件 
图形控制是一类简单的部件。因为纯图形部件从不需要得到键盘焦点,所以它没有也不要窗口句柄。包含图形控制的应用程序用户仍然可以用鼠标操作控制,但没有键盘界面。
  在本例中提供的图形部件是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方法所做的那样。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值