Delphi程序设计.3
4.5定义公共体系结构:使用对象库
Delphi使应用程序的开发变得容易,以前,要花费很大的精力用于建立应用程序的体系结构,但现在可以轻松多了。问题是,很多开发者往往急于写代码而很少考虑应用程序的结构,这使得一个项目往往以失败而告终。
4.5.1考虑应用程序的体系结构
本书不打算专门讲述体系结构或面向对象的分析和设计。不过,我们认为这是非常重要的。附录C"参考读物"列出了一些关于面向对象的主题。在开始编写代码之前最好先阅读附录C的内容。
下面列出了一些应当考虑的问题:
1. 体系结构支持代码重用吗?
2. 应用程序中的模块、对象等能够本地化吗?
3. 修改体系结构非常容易吗?
4. 用户界面和后端可以本地化吗?
5. 体系结构支持团队开发吗?或者说,团队的成员可以工作于各自的模块吗?
上面这几个问题其实只是开发过程中要考虑的一部分问题。
关于相关内容的书籍很多,我们无意与它们竞争。下面将举例说明怎样设计一个数据库应用程序的通用用户界面。
4.5.2Delphi固有的体系结构
你可能经常听到这样一句话,即作为一个Delphi开发者,没必要是一个组件编写者。尽管这句话是正确的,但下面这句话也是正确的:如果你是一个组件编写者,就一定是一个更优秀的Delphi开发者。
这是因为,组件编写者清楚地知道对象模式和Delphi应用程序的体系结构,这意味着组件编写者能够更好地发挥它们的优势。你可能已经听说过,事实上Delphi本身就是用组件编写的。Delphi本身就是一个运用体系结构的例子。
即使并不想编写一个组件,但掌握体系结构还是有好处的。应当像熟悉Win32操作系统那样熟悉VCL和Object Pascal模型。
4.5.3体系结构的例子
为了证明窗体继承以及对象库的能力,下面将定义一个通用的应用程序体系结构。重点是代码重用性、修改的灵活性、一致性和易于团队开发。
窗体继承,更准确地说是框架,它们的典型应用是在数据库应用程序中。窗体应当对数据库的操作(编辑、添加或浏览)具有感知能力。窗体还应当包含一些通用控件,例如工具栏和状态栏,以便对数据库表进行操作。这些控件随窗体状态变化。另外,这些窗体还应当提供事件,以便跟踪窗体模式的变化。
应用程序的框架应当允许团队开发,每个成员可以各自工作于应用程序的一部分,而不至于出现重复和覆盖。
框架分为3个层次,后面将详细介绍这3个层次。表4-4描述了框架中每个窗体的用途。表4-4框架中的数据库窗体
4.5.4子窗体TChildForm
TChildForm是那些能够被单独打开的模式窗口无模式窗体并能成为其他窗口的子窗口的基类。
TChildForm支持团队开发,每个成员可以工作于应用程序的一部分。同时,TChildForm也实现了漂亮的用户界面,用户可以在应用程序内打开一个窗体,作为一个单独的实体。清单4-3是TChildForm的源代码。清单4-3TChildForm的源代码
上述代码演示了下列技术:首先是重载,这是对Object Pascal语言的扩展;其次是怎样使一个窗体成为另一个窗口的子窗口。
1.提供第二个构造器
你可能注意到了,上述代码中声明了两个构造器(constructor)。第一个构造器用于创建一个普通的窗体,它需要传递一个参数。第二个构造器需要传递两个参数,它重载了第一个构造器。如果要使窗体成为子窗口,应当使用第二个构造器。其中,AParent参数用于传递父窗口。注意,这里用了reintroduce指示符,这样编译器就不会发出警告了。
第一个构造器只是简单地把FAsChild变量设为False,以保证创建的是一个普通的窗体。第二个构造器把这个变量设为True,并且把FTempParent设为AParent参数的值,这个值将在Loaded()方法中作为父窗口。
2.使一个窗体成为子窗口
要使一个窗体成为子窗口,有几件事情需要做。首先,要确保窗体的属性已经正确设置,正如在TChildForm.Loaded()中看到的那样。清单4-3的代码能保证窗体变成一个子窗口而不是一个对话框,这是通过把边框隐去来实现的。如果这个窗体只用做子窗口,可以在设计时设置这些属性。如果这个窗体有可能要用作一个普通的窗体,应在FAsChild变量设为True的情况下才设置这些属性。
还要覆盖CreateParams(),以告诉Windows把窗体作为子窗口。要实现这一点,需要把Params.Style属性设为WS_CHILD风格。
TChildForm并不只限于数据库应用程序。事实上,可以把它用在任何需要把窗体作为子窗口的场合。
4.5.5数据库基础模式窗体TDBModeForm
TDBModeForm是从TChildForm继承下来的。它能够感知数据库的状态(浏览、插入、编辑)。TDBModeForm还提供了一个事件,以跟踪数据库状态的变化。
清单4-4列出了TDBModeForm的源代码。清单4-4TDBModeForm
TDBModeForm的实现比较简单。尽管这里使用了一些目前还没有介绍的技术,但你应当能看出它的作用。首先,这里声明了一个枚举类型TFormMode,用于表示窗体的状态。其次,TDBModeForm提供了一个FormMode属性以及它的读写方法。关于属性和读写方法将在第21章"编写自定义组件"中详细介绍。
4.5.6数据库导航/状态窗体TDBNavstatForm
TDBNavstatForm具有框架的许多功能。TDBNavstatForm中包含了一些用于数据库应用程序的通用组件。特别是,它包含一个导航栏和状态栏,能够随数据库的状态而发生变化。例如,当数据库处于fsBrowse状态,导航栏上的Accept按钮和Cancel按钮就被禁止。当用户使数据库进入fsInsert状态或fsEdit状态,这两个按钮将生效。状态栏上将显示数据库的状态。
下面的清单4-5列出了TDBNavstatForm的源代码。注意,这里去掉了组件的列表。当打开这个范例项目时会看到这些列表。
这里主要处理了一些TToolButton组件的事件,用于设置窗体的当前状态。这实际上是调用覆盖SetFormMode(),再由SetFormMode()调用SetButtons()和SetStatusBar()实现的。SetButtons()能够根据窗体的状态来决定按钮的可用或不可用。清单4-5TDBNavstatForm
你可能注意到了,上面的代码中有两个过程用于修改TToolBar组件和TStatusBar组件的父窗体。当窗体作为子窗口打开时,应当把TToolBar组件和TStatusBar组件的父窗体设为主窗体。当运行随书附带光盘上\FormFramework目录中的项目时,就会知道这样做的意义。
正如前面提到的那样,TDBNavStatForm既可以是一个独立的窗体,也可以是一个子窗口。下面的代码演示了怎样把TDBNavstatForm作为一个独立的窗体调用:
下面的代码演示了怎样把TDBNavStatForm作为子窗口调用:
上面这个过程不仅把TDBNavStatForm作为pnlParent(TPanel组件)的子窗口,同时还使主窗体成为TToolBar组件和TStatusBar组件的父窗体。另外,TMainForm.mmMainMenu.Merge()这一行的作用是把TDBNavstatForm实例上的菜单合并到主窗体的菜单中。当然,当释放TDBNavStatForm的实例时,必须这样调用TMainForm.mmMainMenu.UnMerge():
看一看随书光盘中的范例。图4-l显示了TDBNavStatForm作为一个独立的窗体和作为一个子窗口的情况。实际上,这里可以用TImage组件来代替子窗口。
以后,我们将把这个框架扩展为功能齐全的数据库应用程序。图4-1TDBNavstatForm作为一个独立的窗体和作为一个子窗口
4.5.7使用框架进行应用程序结构设计
Delphi5提供框架功能,可以创建能被嵌入到其他窗体中的组件容器。这一点和我们用TChildForm进行的演示类似。然而,框架允许在设计时使用组件容器,并且可以把这些组件容器加到组件板中,以便将来重用。清单4-6演示了框架的功能。清单4-6框架演示
在清单4-6的代码中,我们显示了一个主窗体,它包含两个由独立的面板构成的长方块。右边的面板用于包容框架。我们已经定义了两个独立的框架。在private段,FFrame被定义为TFrame类型。由于两个框架都是直接来自TFrame,所以FFrame能够直接访问它们。位于主窗体上的两个按钮,各自创建一个不同的TFrame并且将其赋给FFrame。这和TChildForm的效果相同。
4.6一些项目管理的功能
下面将介绍一些项目管理的功能,这对许多使用Delphi5的开发者是有帮助的。
4.6.1在项目中添加资源
前面讲过.res文件是应用程序的资源文件,以及什么是Windows资源。要在项目中添加资源,可以创建一个单独的.res来存储要加到应用程序中的位图、图标、光标等资源。
必须使用专门的资源编辑器来创建.res文件。创建了.res后,只要在项目文件中加上下面这行语句,就能使资源链接到应用程序中:
上面这行语句可以紧接在下面这行语句的后面。下面这行语句的作用是,把一个与项目文件同名的资源文件链接到应用程序中:
如果已经这样做了,这时可以通过TBitmap.LoadFromResourceName()或TBitmap.LoadFromReourceID()来调入资源文件中的资源。清单4-7演示了怎样从资源文件中调入一个位图、图标和光标。注意,这里用到了一些WindowsAPI函数,例如LoadIcon()和LoadCursor(),可以在WindowsAPI的帮助中找到它们的说明。
注意:WindowsAPI中有一个LoadBitmap()函数,它可以调入一个位图,但它不能返回调色板,也就是说,它无法调入256色的位图。因此,建议使用TBitmap.LoadFromResourceName()或TBitmap.LoadFromResourceID()。清单4-7从资源文件中调入资源的例子
4.6.2改变屏幕光标
Cursor属性可能是TScreen最常用的属性之一,它的作用是改变应用程序的光标。例如,下面的代码把光标改为砂漏状,表示现在正在进行一个较长时间的操作。
crHourGlass是一个预定义的常量,其他预定义的常量有crBeam和crSize等。这些常量值的范围是从0到-20(crDefault到crHelp)。可以从在
补充:软件开发 , Delphi ,