:Coriolis Group 下面这本书并不专注于组件写作,但里面提到许多组件撰写者不可不知的信息: 『Secrets of Delphi 2』 作者:Ray Lischner 出版:Waite Group 另外一本组件撰写的好书,它有许多在『Developing Delphi Components』里找不到的信息: 『Programming Delphi Custom Components』 作者:Fred Bulback 出版:M&T Books
6.2 有哪些Web站台可以取得撰写组件的信息? 全世界最大的 Delphi Web 站台『Delphi SuperPage』 (译注:亚洲地区使用者可以就近到位于日本的 Mirror Site ) 我在下面这些站台中找到许多组件的原始程序代码: Temple of Delphi Delphi Free Stuff (译注:『Your Delphi Programming Resource』整理组件也十分用心! ) (译注:台湾地区目前维持最好的 Delphi 站台是『32 Bit Delphi 深度历险』及其 Mirror Site) 你也可以使用一些搜寻引擎来寻找有关 Delphi 的站台: Yahoo Alta Vista (译注:Excite 搜寻引擎也别错过啰!) -------------------------------------------------------------------------------- 第七部分 组件的储存与加载
7.1 如何将包含其它对象的对象一起存入 DFM 檔? 我试过许多方法,包括改写组件的 DefineProperties及 WriteComponents方法,但都还是失败了。所以我只能说要解决这个问题的话只有使用 Delphi 原本的方法一途。 将包含其它对象的对象一起储存起来的步骤大致如下: 确定你要储存的所有对象都是从 TComponent 类别衍生下来的。 将所有需要储存的变量宣告在 published 区段。 在组件的 Register 程序中呼叫 RegisterComponents程序来注册所有你要储存起来的类别。 每个拥有子控件的类别必须改写 GetChildren 方法以储存每个子控件。(在 Delphi 1.0 中你必须改写 WriteComponents 方法并且为每个子控件呼叫 WriteComponent方法)。 将对象加载的方法用了点小技巧。你必须改写组件的 GetChildOwner 及 GetChildParent 方法,否则 Delphi会将所有对象的拥有者都设定为组件所在的表格。(在Delphi 1.0 中你必须改写ReadState方法)。
7.2 如何得知组件是否正从资料流中读出? 当组件正从资料流中读出时,它的 ComponentState 属性会包含csLoading 旗帜。 constructor TMyClass.Create(AOwner: TComponent); begin if csLoading in AOwner.ComponentState then begin ... end else begin ... end; end;
7.3 如何确定组件的属性是否被正确地储存? 有许多很简单的方法可以验证属性是否被正确地储存在档案里: 在整合环境中用鼠标右键点选表格然后选择『View as Text』。然而万一 DFM 檔 有任何错误的话,你将什么也看不到。 开个 DOS 窗口,利用 Delphi 所附的『Convert』程序将 DFM 文件转成文字格式。 Stefan Hoffmeister 指出复制或剪下组件后,到任何一个文书编辑器(如记事本)中贴上,你就可以看到此组件的文字表示。你甚至可以编辑这些文字表示后再将它贴回 Delphi整合环境的表格上。 -------------------------------------------------------------------------------- 第八部分 Delphi 的工具
8.1 有没有Delphi版本的 YACC 及 LEX? 有。Albert Graef 这位仁兄写了 Turbo Pascal 版本的 YACC 及 LEX,也可以让 Delphi 使用。 你可以在 ftp://ftp.simtel.net/pub/simtelnet/msdos/turbopas 下取得 tply30a1.zip 及 tply30a2.zip 这两个档案,其中还包含这两个工具的原始程序哦!
8.2 如何秀出 JPEG 格式图形文件? Jacques Nomssi Nzali 将 Independent JPEG Group 所发展的免费 JPEG 链接库改写成 Pascal 版本。你可以从下取得: PASJPG10.ZIP Independent JPEG Group 的免费 JPEG 函式库 rev 6a 之 Pascal 版本 (1.0 版)。 -------------------------------------------------------------------------------- 第九部分 基本程序设计技巧
9.1 如何建立不定数目的对象数组? 最简单的方法是使用 TList 类别。我发现从 TList 衍生一个新类别很有用处。接下来的程序代码示范如何为一个特定型态撰写一个特别的 TList 类别,并且加进基本的错误检查。 TListOfMyObject = class (TList) private function GetItems(Index: Ordinal): TMyObject; public property Items[Index: Ordinal]: TMyObject read GetItems; procedure Add(AObject: TMyObject); end; function TListOfMyObject.GetItems (Index: Ordinal): TMyObject; begin if Index >= Count then raise Exception.CreateFmt(''''Index(%d) outside range 1..%d'''', [Index, Count-1]); Result := inherited Items[Index]; end; procedure TListOfMyObject.Add (AObject: TmyObject); begin inherited Add(AObject); end;
9.2 Delphi 2.0的 WinCrt单元到哪去了? Delphi 2.0并没有 WinCrt单元。 先别伤心,这是因为我们可以用其它方法来取代它。在 Project|Options 的 Linker 页次中将『Generate console application』选项打开,你就可以像以前使用 WinCrt 单元一样地写程序了!
9.3 自制组件时该从哪个类别继承? VCL 中有一些『自订』类别,而且有许多控件是直接由这些『自订』类别继承下来的。例如 TMemo 直接继承自 TCustomMemo类别。这些自订类别写好了所有该控件所拥有的功能,只是没有将属性公开出来而己。大部分情形下,你应该从那些自订类别继承而不是控件类别。 如果你要从头撰写自己的组件,那么从 TCustomControl 类别继承是个不错的主意。撰写出来的组件会具有 Window Handle 且可以接受输入焦点。 另外根据你的需要也可以从这些类别继承: TGraphicControl:视觉组件,但是没有window handle,也不能接受输入焦点。 TComponent:不可视组件,你没办法在执行时期看到它。 TWinControl:将已存在的窗口组件包装起来,如Windows标准控件或VBX组件。 -------------------------------------------------------------------------------
第十部分 进阶程序设计技巧
10.1 Delphi 有与 C++ 一样的 I/O Stream 类别吗? 答案可以说有也可以说没有。Delphi允许你建立自己的『文字文件驱动程序』,它可以让你使用Delphi 标准的 I/O 函式库来处理非标准的 I/O,如处理 UNIX 格式的文字文件或处理 Socket 所取得的资料。虽然没有像 C++ 的 I/O Stream 类别那么强大但应该也足够一般用途使用了。 建立『文字文件驱动程序』的方法在『Object Pascal Language Guide』中有明述。此 外你也可以参考 VCL 的 Printer 单元。 Delphi有 TStream 类别,不过是设计用来将对象写入资料流的,不像 C++ 的 I/O Stream 类别那么具有弹性。
10.2 如何取得列举型态变量的文字表示? 使用 TypInfo单元中的 GetEnumName 函式: type TMyType = (Value1, Value2); var TypeValue: TMyType; begin Writeln (GetEnumName(TypeInfo(TMyType), Ord(TypeValue)); end; TypInfo单元中还有许多与型别信息有关的函式。 『Secrets of Delphi 2.0』这本书有许多关于TypInfo单元的信息,值得参考。 -------------------------------------------------------------------------------- 第十一部分 组件虚拟方法
11.1 如何得知组件的window handle是何时建立的? 控件的 window handle 是在 CreateWnd 方法中建立的。如果你想要在建立 window handle 后接着做某些动作那么你应该改写 CreateWnd 方法: procedure TMyClass.CreateWnd; begin // 现在还没取得 window handle inherited CreateWnd; // 呼叫 inherited 以取得 window handle // 在这里撰写你想要执行的动作 end;
11.2 如何得知是否表格上所有组件都已加载完成? Loaded 方法是在加载完成后接着被呼叫的。 procedure TMyClass.Loaded; begin inherited Loaded; // 将ComponentState中的 csLoading 状态清除 // 在这里撰写你想要执行的动作 end;
11.3 在哪里绘制组件最适合? 你应该拦截 WM_PAINT 窗口讯息然后利用 Canvas 来绘制组件。然而 VCL 己经帮你拦 截好了,你只须改写组件的 Paint 方法即可。 procedure TMyClass.Paint; begin // 如果你的组件是己存在的组件继承下来的,那么必须在这里呼叫 inherited Paint inherited Paint ; // 在这里撰写你想要执行的动作 end;
11.4 如何改变组件的窗口式样? CreateParams方法用来设定组件的窗口式样及其它必须传递至 CreateWindowEx API 的 参数。要改变组件的窗口式样,例如增加或拿掉组件的垂直滚动条只要改写 CreateParams 方法:
procedure TMyControl.CreateParams(var Params: TCreateParams); begin inherited CreateParams(Params); if IWantAScrollBar then Params.Style := Params.Style or WS_VSCROLL else Params.Style := Params.Style and not WS_VSCROLL; end;
--------------------------------------------------------------------------------
第十二部分 Windows API 12.1 组件卷动时闪动的很厉害,如何克服这种情况? 要卷动组件本身最简单的方法就是改变它的坐标然后重画组件,但是这方法会导致组件闪动的很厉害。 比较好的方法是呼叫 ScrollWindow 或 ScrollWindowEx Windows API。 闪动的另一个原因可能来自于 WM_PAINT 及 WM_ERASEBKGND。你可以试着拦截 WM_ERASEBKGND 及 WM_PAINT 讯息然后自己处理绘图动作,包括绘制背景的动作,或许可以改善闪动的情况。
12.2 如何重新激活Windows? 使用 ExitWindowsEx Windows API。
12.3 如何快速大量地更改组件资料? 在进行大量资料更改前后,利用 WM_SETREDRAW 讯息来控制你的组件暂时不要重画,这不但可以使资料设定速度增快也防止组件闪烁的情况。 --------------------------------------------------------------------------------
第十三部分 控件边框 13.1 为什么我的组件的 Ctl3D 属性设为 True 之后,它依然没有 3D 的边框呢? 如果 ControlStyle 属性内没有包含 csFramed 旗帜那么 Ctl3D 属性就会没有作用。在组件 的建构函式内加上: ControlStyle := ControlStyle + [csFramed];
13.2 如何实作 BorderStyle 属性? 在控件设定有没有边框之后要重新建立 window handle: FBorderStyle: TBorderStyle; procedure SetBorderStyle(Style: TBorderStyle); property BorderStyle: TBorderStyle read FBorderStyle write SetBorderStyle; procedure CreateParams(var Params: TCreateParams); override; procedure TMyControl.CreateParams(var Params: TCreateParams); begin inherited CreateParams(Params); if FBorderStyle = bsSingle then Params.Style := Params.Style or WS_BORDER else Params.Style := Params.Style and not WS_BORDER; end;
procedure TMyControl.SetBorderStyle(Style: TBorderStyle); begin if Style <> FBorderStyle then begin FBorderStyle := Style; // 重新建立window handle RecreateWnd; end; end; -------------------------------------------------------------------------------- 第十四部分 控件式样
14.1 当组件重绘时如何防止闪动的情况? 如果组件的 ComponentStyle 属性没有包含 csOpaque 旗帜的话,呼叫 Invalidate方法时 会导致组件的背景先被擦掉再重绘。如果你在 Paint 方法中绘制背景,那你应该在组件的建构函式中加上: ComponentStyle := ComponentStyle + [csOpaque]; Max Nilson的回答: 引起闪动另一个原因可能是 WM_ERASEBKGND 讯息的处理。当 VCL 控件收到一个 WM_ERASEBKGND 讯息时,它会将组件的背景擦掉然后设定成预设的颜色。如果你的组件衍生自 TWinControl,而且组件的颜色与背景颜色不同(例如图形),每次重画以前都会将组件先清成背景颜色再重绘,这就是造成闪动的原因了! 解决的方法不难,你必须告诉 Windows 你要自行解决『所有的』绘图动作。不过有一个前提是,你一定要确定你的 Paint 方法将整个组件都画过,如果你漏了什么地方忘了画,那个部分的资料会由随机数组成,你能想见这情况吗?使用这个方法可以加速你的组件绘制动作(稍微快一点点),因为少了一个填满背景颜色的动作。
type TMyComponent = class (TWinControl) ... protected procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND; ... end;
procedure TBMyComponent.WMEraseBkgnd(var Message: TWMEraseBkgnd); begin // 不要重绘背景,这会造成组件闪动 Message.Result := 0 end;
-------------------------------------------------------------------------------- 第十五部分 窗口讯息
15.1 为什么我的组件得不到方向键的讯息? 你必须拦截 WM_GETDLGCODE 才能处理方向键的讯息,在 WM_GETDLGCODE 的讯息处理 者中传回 DLGC_WANTARROWS。如果你不这样做,那方向键的功用就只能用来移动窗口焦点而己。 Max Nilson 的回答: 想要你的组件能够处理方向键,你必须要拦截 CM_WANTSPECIALKEY 组件讯息。 CM_WANTSPECIALKEY 组件讯息提供你比拦截 WM_GETDLGCODE 窗口讯息更容易且灵活的判断方法来决定是否需要某些特殊键的讯息。当控件收到任何一个特殊键时就会送出CM_WANTSPECIALKEY 组件讯息给控件。 特殊键包括:VK_TAB、VK_LEFT、VK_RIGHT、VK_UP、VK_DOWN、VK_RETURN、VK_EXECUTE 、VK_ESCAPE 及 VK_CANCEL。如果讯息传回值是非零值,这个键就会被送至 KeyPress 方法以供处理,否则这个键的讯息会被送至组件的父控件,以预设方式来处理。 一个简单的范例:
type TMyComponent = class (TWinControl) ... protected procedure CMWantSpecialKey(var Message: TCMWantSpecialKey); message CM_WANTSPECIALKEY; ... end;
procedure TMyComponent.CMWantSpecialKey(var Message: TCMWantSpecialKey); begin inherited; // 我们只想处理向左方向键,其它的特殊键都给 Windows 处理 if Message.CharCode = VK_LEFT then Message.Result := 1; end; CM_WANTSPECIALKEY 组件讯息比 WM_GETDLGCODE 讯息更具有弹性的地方在这儿。我们甚至可以根据是按下的是哪个特殊键才决定是否处理这个键。例如,你的控件有三张影像,你可以让使用者利用左右方向键来回检视它们,如果翻到最后一张影像再按向右键时,焦点就让它离开组件,剩下的全部都让 Delphi 来处理。
15.2 有没有与 Visual Basic『DoEvents』同样功能的函式? 有。Application.ProcessMessages方法。
上一页 [1] [2] |