DELPHI基础教程:文件管理(二)[3]

DELPHI基础教程:文件管理(二)[3],第1张

函数NotInList用于判断待添加的字符串是否已存在于一个TStrings对象中 函数返回一个布尔型变量

NotInList的具体实现如下

Function TFileCtrForm NotInList(FileName: String;Items: TStrings) Boolean;

var

i: Integer;

begin

for I := to Items Count do

if Items[i] = FileName then

begin

NotInList := False;

Exit;

end;

NotInList := True;

end;

按指定匹配字符串显示当前目录中的文件

当在FileEdit中输入一个匹配字符串 并回车 文件列表框将显示匹配结果 这一功能在FileEdit的OnKeyPress事件中实现

procedure TFileCtrForm FileEditKeyPress(Sender: TObject; var Key: Char)

begin

if Key = # then

begin

FileListBox ApplyFilePath(FileEdit Text)

Key := # ;

end;

end;

文件列表框提供的ApplyFilePath方法是解决这一问题的关键所在

按指定匹配字符串查找当前目录中的文件

为了进行比较 我们用另一种方法来实现文件的查找功能 即利用标准过程FindFirst FindNext FileList 与ListBox 中的内容完全一致

当用户单击 查找 按钮时 与FileEdit 中字符串相匹配的文件将显示在ListBox 中 下面是实现代码

procedure TFileCtrForm Button Click(Sender: TObject)

var

i: Integer;

SearchRec: TSearchRec;

begin

Searched := True;

Label Caption := Search Result ;

ListBox Items Clear;

FindFirst(FileEdit text faAnyFile SearchRec)

ListBox Items Add(SearchRec Name)

Repeat

i := FindNext(SearchRec)

If i = then

ListBox Items Add(SearchRec Name)

until i <> ;

end;

SearchRec是一个TSearchRec类型的记录 TSearchRec的定义如下

TSearchRec = record

Fill: array[ ] of Byte;

Attr: Byte;

Time: Longint;

Size: Longint;

Name: string[ ];

end;

在这一结构中提供了很多信息 灵活应用将给编程带来很大方便 下面我们举几个例子

检测给定文件的大小

function GetFileSize(const FileName: String) LongInt;

var

SearchRec: TSearchRec;

begin

if FindFirst(ExpandFileName(FileName) faAnyFile SearchRec) = then

Result := SearchRec Size

else

Result := ;

end;

这一程序将在下一节中应用

获取给定文件的时间戳 事实上等价于FileAge函数

function GetFileTime(const FileName: String) Longint;

var

SearchRec: TSearchRec;

begin

if FindFirst(ExpandFileName(FileName) faAnyFile SearchRec) = then

Result := SearchRec Time

else

Result := ;

end;

检测文件的属性 如果文件具有某种属性 则

SearchRec Attr And GivenAttr >

属性常量对应的值与意义如下表

表 属性常量对应的值与意义

━━━━━━━━━━━━━━━━━━━━

常量 值 描述

─────────────────────

faReadOnly $ 只读文件

faHidden $ 隐藏文件

faSysFile $ 系统文件

faVolumeID $ 卷标文件

faDirectory $ 目录文件

faArchive $ 档案文件

faAnyFile $ F 任何文件

━━━━━━━━━━━━━━━━━━━━

文件管理综合举例 文件管理器的实现

在本章的最后 我们利用Delphi提供的文件控件和文件管理函数开发一个简单的文件管理器 虽然这一文件管理器还无法和Windows提供的文件管理器相比拟 但它也为一般的文件 *** 作提供了足够多的功能 而且如果读者感兴趣 还可以对它做进一步的扩充 在后边的拖放 *** 作一章中 我们就为它提供了拖放支持 使它看起来更象一个 文件管理器

设计基本思路

窗口设计

文件管理器的主窗口是一个多文档界面(MDI) 有关文件 目录的显示和文件管理功能的实现都放在子窗口中 在程序执行过程中将根据需要d出一些完成不同 *** 作的对话框 这些对话框都是在需要时动态生成的 表 给出了本程序所设计窗体的清单

表 FileManger窗体清单

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

窗体类 功能 用于创建该类窗体的菜单项

──────────────────────────────────────

TFileManager 主窗口

TFMForm 子窗口 Windows|New Window

TFileAttrForm 显示文件属性 File|Properties;Function|Search

TChangeForm 文件移动 拷贝 改名 改变 File|Move Cope Rename 当前目录等 *** 作的输入对话框 Directory|change Directory

TSearchForm 输入待查找文件的名称和路径 Function|Search

TDiskViewForm 显示磁盘信息 Function|Disk View

TViewDir 输入待创建的子目录 Directory|CreateDirectory

TAboutBox 显示版权信息 Help|About

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

界面设计

主窗口界面主要是主菜单和用于表示当前目录 当前文件的状态条

表 主窗口界面设计

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

部件 属性 功能

─────────────────────────────

FileManager Style=fsMDI 主窗口

WindowMenu=Windows

Position=poDefault

MainMenu 主菜单

FilePanel Align=alBottom 显示当前选中文件

BevelInner=bvLowered

BevelWidth=

DirectoryPanel Align=alBottom 显示当前选中目录

Alignment=taLeftJustify

BevelInner=bvLowered

BevelWidth=

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

主窗口主菜单包括File WIndows Help三项 File菜单项在子窗口生成时被子窗口同名菜单项所取代 设置Windows Help的GroupIndex = 可以使子窗口生成时这两个菜单项仍存在

子窗口界面包括主菜单 目录树(DirectoryOutline) 文件列表框 用于显示驱动器的标签集(TabSet)以及三个用于显示驱动器类型的TImage部件

表 子窗口界面设计

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

部件 属性 功能

───────────────────────────────────────

FMForm ActiveControl=DirectoryOutline 子窗口

Position=poDefault

Style=fsMDIChild

MainMenu 主菜单

DriveTabSet Align=alTop 显示驱动器

style=tsOwnerDraw

DirectoryOutline Align=alLeft 显示当前驱动器的目录树

options=[ooDrawTreeRoot

ooDrawFocusRect ooStretchBitmaps]

FileList Align=alClient 显示当前目录中的文件

FileType=[ftReadOnly

ftHidden ftSystem ftArchive ftNormal]

ShowGlyphs=True

Neork(Image) Picture(Neork bmp) 标志网络驱动器

Vsible=False

Floppy(Image) Picture(Floppy bmp) 标志软驱

Visible=False

Fixed(Image) Picture(Fixed bmp) 标志硬驱

Visible=False

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

lishixinzhi/Article/program/Delphi/201311/25238

edit2Text := datetostr(datetimepicker1Date) + ' ' + edit1Text;

datetimepicker控件的内容输入tdatetime或者tdate类型,可以使用类型转换

如果要精确到秒可以使用datetimetostr函数进行类型转换

给你个最简单的方法

放一个 timer控件 一个edit edit默认内容0 然后双击timer控件 timer1interval=1000; 1000毫秒执行一次 =1秒执行一次

procedure TForm1Timer1Timer(Sender: TObject);

begin

edit1Text:=inttostr(strtoint(edit1Text)+1);

end;

默认timer1enabled:=fales;

需要记时的时候timer1enabled:=true;

需要停止的时候 timer1enabled:=fales;

没有编译器 应该没问题

edti1里的内容就是运行时间啦 很简单的一个原理 有不会+QQ448912 希望给分

利用Delphi建立精确计数器

在Windows中的很多场合下编程(例如工业控制、游戏)中需要比较精确的记时器,本文讨论的是在Delphi下实现记时器的若干方法以及它们的精度控制问题。

在Delphi中最常用的是Timer控件,它的设置和使用都非常方便,理论上它的记时精度可以达到1ms(毫秒)。但是众所周知的,实际上Timer在记时间隔小于50ms之下是精度是十分差的。它只适用于对于精度要求不太高的场合。

这里作者要介绍的是两种利用Windows API函数实现精确记时的方法。第一中方法是利用高性能频率记数(作者本人的称呼)法。利用这种方法要使用两个API函数QueryPerformanceFrequency和QueryPerformanceCounter。QueryPerformanceFrequency函数获得高性能频率记数器的震荡频率。

调用该函数后,函数会将系统频率记数器的震荡频率(每毫秒)保存到一个LargeInteger中。不过利用该函数在几台机器上做过试验,结果都是1193180。读者朋友可以在自己的机器上试一下

QueryPerformanceCounter函数获得系统频率记数器的震荡次数,结果也保存到一个Largenteger中。

很显然,如果在计时中首先使用QueryPerformanceFrequency获得高性能频率记数器每毫秒的震荡次数,然后在计时开始时使用QueryPerformanceCounter函数获得当前系统频率记数器的震荡次数。在计时结束时再调用QueryPerformanceCounter函数获得系统频率记数器的震荡次数。将两者相减,再将结果除以频率记数器每毫秒的震荡次数,就可以获得某一事件经过的准确时间。(次数除以频率等于时间)

另外的一种精确记时器的功能是利用多媒体记时器函数(这也是作者的定义,因为这个系列的函数是在Winmmdll中定义并且是为媒体播放服务的)。

实现多媒体记时器首先要使用timeSetEvent函数建立计时事件。该函数在Delphi中的mmsystempas中有定义,定义如下:

function timeSetEvent(uDelay, uResolution: UINT;

lpFunction: TFNTimeCallBack; dwUser: DWORD; uFlags: UINT): MMRESULT; stdcall

函数定义中参数uDelay定义延迟时间,以毫秒为单位,该参数相当于Timer控件的Interval属性。参数uResolution定义记时精度,如果要求尽可能高的精度,要将该参数设置为0;参数lpFunction定义了timeSetEvent函数的回调函数。该函数相当于一个定时中断处理函数,每当经过一个uDelay长度的时间间隔,该函数就会被调用,编程者可以在该函数中加入相应的处理语句。参数dwUser定义用户自定义的回调值,该值将传递给回调函数。参数uFlags定义定时类型,如果要不间断的记时,该值应设置为1。

如果函数调用成功,在系统中建立了一个多媒体记时器对象,每当经过一个uDelay时间后lpFunction指定的函数都会被调用。同时函数返回一个对象标识,如果不再需要记时器则必须要使用timeKillEvent函数删除记时器对象。

由于Windows是一个多任务的 *** 作系统,因此基于API调用的记时器的精度都会受到其它很多因素的干扰。到底这两中记时器的精度如何,我们来使用以下的程序进行验证:

设置三种记时器(Timer控件、高性能频率记数、多媒体记时器)。将它们的定时间隔设置为10毫秒,让它们不停工作直到达到一个比较长的时间(比如60秒),这样记时器的误差会被累计下来,然后同实际经过的时间相比较,就可以得到它们的精度。

下面是具体的检测程序。

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

StdCtrls, ExtCtrls,mmSystem;

type

TForm1 = class(TForm)

Edit1: TEdit;

Edit2: TEdit;

Edit3: TEdit;

Button1: TButton;

Button2: TButton;

Timer1: TTimer;

procedure FormCreate(Sender: TObject);

procedure Button1Click(Sender: TObject);

procedure Timer1Timer(Sender: TObject);

procedure Button2Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

actTime1,actTime2:Cardinal;

smmCount,sTimerCount,sPCount:Single;

hTimeID:Integer;

iTen:Integer;

proTimeCallBack:TFNTimeCallBack;

procedure TimeProc(uTimerID, uMessage: UINT;

dwUser, dw1, dw2: DWORD) stdcall;

procedure proEndCount;

implementation

{$R DFM}

//timeSetEvent的回调函数

procedure proEndCount;

begin

actTime2:=GetTickCount-actTime1;

Form1Button2Enabled :=False;

Form1Button1Enabled :=TRue;

Form1Timer1Enabled :=False;

smmCount:=60;

sTimerCount:=60;

spCount:=-1;

timeKillEvent(hTimeID);

end;

procedure TimeProc(uTimerID, uMessage: UINT;

dwUser, dw1, dw2: DWORD) stdcall;

begin

Form1Edit2Text:=FloatToStr(smmCount);

smmCount:=smmCount-001;

end;

procedure TForm1FormCreate(Sender: TObject);

begin

Button1Caption :='开始倒计时';

Button2Caption :='结束倒计时';

Button2Enabled :=False;

Button1Enabled :=True;

Timer1Enabled :=False;

smmCount:=60;

sTimerCount:=60;

sPCount:=60;

end;

procedure TForm1Button1Click(Sender: TObject);

var

lgTick1,lgTick2,lgPer:TLargeInteger;

fTemp:Single;

begin

Button2Enabled :=True;

Button1Enabled :=False;

Timer1Enabled :=True;

Timer1Interval :=10;

proTimeCallback:=TimeProc;

hTimeID:=timeSetEvent(10,0,proTimeCallback,1,1);

actTime1:=GetTickCount;

//获得系统的高性能频率计数器在一毫秒内的震动次数

QueryPerformanceFrequency(lgPer);

fTemp:=lgPer/1000;

iTen:=Trunc(fTemp10);

QueryPerformanceCounter(lgTick1);

lgTick2:=lgTick1;

sPCount:=60;

while sPCount>0 do begin

QueryPerformanceCounter(lgTick2);

//如果时钟震动次数超过10毫秒的次数则刷新Edit3的显示

If lgTick2 - lgTick1 > iTen Then begin

lgTick1 := lgTick2;

sPCount := sPCount - 001;

Edit3Text := FloatToStr(sPCount);

Delphi虽然为我们提供极其强大的调试功能 查找Bug仍然是一项艰巨的工作 通常我们写代码和调试代码的所消耗的时间是大致相同的 甚至有可能更多 为了减少无谓的时间和精力的浪费 有时我们还是需要专业调试工具的帮助来提高锁定Bug的效率 本文中我们将介绍著名的调试工具CodeSite Pro (它获得了 年度Delphi Informant读者选择的最佳调试工具奖的第二名) 它的官方网址是

CodeSite的主要功能是可以让开发者使用代码来发送运行时的详细信息到特殊的接收器 以便于进一步分析 更精确的说通过CodeSite实现的TCodeSite类 我们可以打包并发送运行时的信息给CodeSite Dispatcher(CodeSite的消息分发器) 它可以路由这些消息到一个或多个接收器来察看 缺省的信息接收器是CodeSite Viewer(消息察看器)

CodeSite的效率体现在它不同于简单的显示消息的对话框或设定断点来检查变量 它更类似于Delphi自带的事件日志功能(Event Log) 当然毫无疑问它要比Event Log强大的多 它的消息是可持续的 也就是可以保存的 便于回溯分析

在介绍CodeSite的具体使用前 我们先来看一下它的三个组成部分

CodeSite对象

正如前面提到的 从运行的应用程序中向外发送CodeSite消息是通过使用TCodeSite类(定义在CSIntf单元中)的一个实例来完成的 我们只要简单的调用TCodeSite类的方法就可以 把消息发送给CodeSite Dispatcher 比如 可以使用对象的SendMsg方法来发送一个简单的字符串消息 TCodeSite 对象实现了大量的方法来支持各种类型的信息发送而无须任何数据转换 比如对象的SendObject方法有两个参数 一个是消息字符串 一个是对对象实例的引用 这个方法会获取对象所有published的属性 然后把这些属性的信息打包进CodeSite的消息中

CodeSite Dispatcher

大多数情况下 CodeSite Dispatcher会安静的运行在系统的托盘区 它的唯一功能是路由从各个TCodeSite对象发来的CodeSite的消息到它们的目的地 缺省时 CodeSite消息都会发给CodeSite Viewer 我们甚至不需要启动CodeSite Dispatcher 因为它会被TCodeSite等对象自动启动

TCodeSite 类定义了一个DestinationDetails属性 它允许开发者设定发送的CodeSite消息是如何被CodeSite Dispatcher路由到不同目的地 比如日志文件 但通常没有必要修改这个属性

CodeSite Viewer

虽然CodeSite 支持发送消息到不同的目标 但决大多数情况下CodeSite Viewer是主要的发送目标 即使是发送到其他目标 比如日志文件或另外一台机器 CodeSite Viewer仍然是察看分析消息的主要工具

CodeSite Viewer由下面四个面板构成 消息列表 消息察看器 调用堆栈和Scratch面板 CodeSite Viewer的主要工作区是Message列表 它用来显示发送给Viewer的全部消息或是从日志文件中加载的消息

消息察看器用来察看同消息关联的额外信息 比如如果当前的消息是由SendObject方法发送的话 消息察看器就会显示对象全部的publised属性当前值

调用堆栈面板会根据c EnterMethod消息显示一个堆栈视图

Scratch面板则是用来显示非可持续的信息的 当我们想跟踪某些信息 但又不想在消息日志中记录它们的时候 比如当我们想察看象鼠标当前位置这类大量的并重复的消息时 Scratch面板是非常有用的 这时我们可以可以使用TCodeSite对象的WritePoint方法 并指定Line ID参数以便指定用来容纳鼠标信息的scratch面板行数

下面就让我们用一个简单的例子来演示一下如何从程序中发送消息给CodeSite Viewer

( )创建一个新的项目 然后切换组件面板到CodeSite页面(CodeSite安装后会在系统中安装两个组件TCSGlobalObject和TCSObject) 选择TCSGlobalObject组件然后放到窗体上 TCSGlobalObject组件提供了设计时对全局TCodeSite对象的交互(全局TCodeSite是在CSInft单元中被初始化的)

( )添加一个按钮 然后在它的OnClick事件中写下如下代码 //CodeSite就是全局的TCodeSite对象CodeSite SendMsg( CodeSite的第一条消息 );

( )编译并运行这个简单的程序 运行后点击按钮 CodeSite Dispatcher和CodeSite Viewer将会运行 同时在CodeSite Viewer的消息列表中将会看到程序发出的消息(注意 我们没有必要在程序运行前启动CodeSite Dispatcher和CodeSite Viewer 因为TCodeSite 对象在需要发送消息的时候会自动启动它们的) 运行结果如下图 所示

图     ( )接下来 停止程序 在OnClick事件处理过程中添加下面代码

CodeSite SendObject( Form Form )

( )重新编译运行程序 再点按钮一次 这回你会在CodeSite Viewer中看到两条消息 其中Form 对应的消息包括Form 的对象信息

( )为了看到Form 的相关联的对象信息 选择CodeSite Viewer的菜单命令View|Inspector会在消息列表右侧显示一个新的面板 Form 的published属性都被显示在其中 如下图 所示

( )再次停止程序 然后修改OnClick过程中代码如下 CodeSite EnterMethod( Button Click );CodeSite SendMsg( CodeSite的第一条消息 );CodeSite SendObject( Form Form );CodeSite ExitMethod( Button Click );    ( )这次我们再运行程序点击按钮后 就会看到 CodeSite的第一条消息 和 Form 的消息被缩进在 Button Click 消息之间 如下图 所示

    通过添加EnterMethod和ExitMethod方法的调用 我们可以生成一个日志来记录方法何时被调用

看过例子之后 我们就会发现CodeSite的功能是非常强大的 我们只要简单的在程序中添加几条语句就可以生成非常详细的信息 并通过CodeSite Viewer以生动的图表表现出来 接下来 我们再来谈谈CodeSite的高级应用技术

发送消息到日志文件

每个程序或多或少都会有Bug 不在这时发生 也会在那时发生 短时间内不发生 很长时间就可能发作 有时反复出现 有时非常偶然的才能被发现 如果一个人告诉你他写的程序在任何时候都没有任何问题 他一定是在撒谎 正是由于Bug的偶然性和隐蔽性 就使得我们往往很难重复用户提交的Bug 这就给我们调试程序并找到问题的原因产生了极大的障碍 而CodeSite能够发送消息到日志文件的特性就使得用户报告Bug变得更容易 他们只要把运行时生成的信息文件提交就可以了 相应的我们调试程序的工作也会变得更轻松 我们可以使用CodeSite Viewer来直观的分析错误发生的原因和位置

要想改变消息发送的目标 我们可以通过设定TCodeSite 对象的DestinationDetails属性来实现 这项功能要求客户的机器上必须安装了CodeSite Dispatcher 它属于CodeSite中可自由分发的部分 下面的要讲具体过程仍然是基于前面讲过的例子

( )在窗体的OnCreate事件中添加下面代码

CodeSite DestinationDetails = File[Path=C FirstLog csl]

( )编译并余兴程序 这回我们在点击按钮后 消息就不再被发送给CodeSite Viewer而是发送到C盘的FirstLog csl文件中

( )使用CodeSite Viewer加载FirstLog csl文件 这回我们就象先前一样察看被保存的CodeSite消息了

( )如果我们想把消息同时发送到CodeSite Viewer和日志文件的话 只修改前面的代码为

CodeSite DestinationDetails = Viewer File[Path=C FirstLog csl]

发送用户定制的数据

虽然TCodeSite 类提供了大量的处理不同数据类型的方法 但有时我们可能会需要发送某种自定义格式的数据信息 为此 TCodeSite 类定义了SendCustomData 方法 它支持发送任意的数据类型 并会根据一个自定义的格式器来格式化数据以便CodeSite Viewer可以正确的显示数据

首先我们需要创建一个TCSFormatter 对象的子类 然后重载对象的FormatData InspectorType和TypeName方法 然后调用CodeSite对象管理器对象CSObjectManager的来注册新的TCSFormatter子类 此外 我们还需要调用RegisterCustomFormat方法来注册一个新的消息类型

下面是一个实际应用的例子 单元CSEmployee pas中实现了一个TCSEmployeeRecord记录类型的定制格式器 unit CSEmployee;interfaceusesWindows Graphics CSIntf;constc EmployeeSummary = c User + ;c EmployeeDetails = c User + ;

首先在Uses部分添加对CSIntf 单元的引用 第二步是为每一个格式器定义新的CodeSite消息类型常数 上面我们定义了两个常数 注意常数应该大于c User 但不能大过

typeTCSEmployee = recordLastName: string;FirstName: string;Address: string;City: string;State: string;ZipCode: string;PhoneNumber: string;HireDate: TDateTime;Salary: Currency;VacationDays: Integer;SickDays: Integer;Manager: Boolean;end;    上面的记录就是我们要发送的自定义的数据类型 TCSEmployeeSummaryFormatter = class( TCSFormatter )publicfunction InspectorType: TCSInspectorType; override;procedure FormatData( var Data ); override;function TypeName: string; override;end;TCSEmployeeDetailsFormatter = class( TCSFormatter )publicfunction InspectorType: TCSInspectorType; override;procedure FormatData( var Data ); override;function TypeName: string; override;end;    上面是两个定制的格式器类的定义 第一个格式器将把TCSEmployee 记录格式化为一个文本格式 第二个格式化器将把TCSEmployee 记录格式化为网格样式

实现一个自定义的格式化器的第一步是确定哪种类型的内置察看器将被用来察看格式化后的数据 这里使用的是字符串列表察看器 察看器类型将被FormatData方法所使用 procedure TCSEmployeeSummaryFormatter FormatData( var Data );varEmpRec: TCSEmployee;beginEmpRec := TCSEmployee( Data );AddLine( EmpRec FirstName + + EmpRec LastName );AddLine( EmpRec Address );AddLine( EmpRec City + + EmpRec State + + EmpRec ZipCode );AddLine( );AddLine( Phone: + EmpRec PhoneNumber );AddLine( Hire Date: + DateToStr( EmpRec HireDate ) );AddLine( Salary: + Format( %m [ EmpRec Salary ] ) );AddLine( );AddLine( Vacation Days: + IntToStr( EmpRec VacationDays ) );AddLine( Sick Days: + IntToStr( EmpRec SickDays ) );if EmpRec Manager thenAddLine( Manager: Yes )elseAddLine( Manager: No );end;    FormatData 方法是核心部分 注意传递给FormatData方法的Data参数是一个无类型的可变参数 这就意味着这个参数可以是任何数据类型的 通过格式注册过程 我们可以确保强制类型映射为自定义的数据记录 而不会发生转换错误

转换数据类型后 我们就可以对数据进行格式化了 这里使用TCSFormatter 基类的 AddLine方法在字符串间添加分割线来进行格式化 function TCSEmployeeSummaryFormatter TypeName: string;beginResult := TCSEmployee ;end;

TypeName方法的重载是可任选的 但通常我们可以用它来返回显示在消息列表中的字符串 {=========================================}{== TCSEmployeeDetailsFormatter Methods ==}{=========================================}function TCSEmployeeDetailsFormatter InspectorType: TCSInspectorType;beginResult := itStockGrid;end;    对于employeedetails格式器来说 命名网格察看器将被用来察看数据信息 procedure TCSEmployeeDetailsFormatter FormatData( var Data );varEmpRec: TCSEmployee;beginEmpRec := TCSEmployee( Data );AddNameValuePair( LastName EmpRec LastName );AddNameValuePair( FirstName EmpRec FirstName );AddNameValuePair( Address EmpRec Address );AddNameValuePair( City EmpRec City );AddNameValuePair( State EmpRec State );AddNameValuePair( ZipCode EmpRec ZipCode );AddNameValuePair( PhoneNumber EmpRec PhoneNumber );AddNameValuePair( HireDate EmpRec HireDate );AddNameValuePair( Salary Format( %m [ EmpRec Salary ] ) );AddNameValuePair( VacationDays EmpRec VacationDays );AddNameValuePair( SickDays EmpRec SickDays );AddNameValuePair( Manager EmpRec Manager );end;    这里为了在网格察看器中格式化数据 我们使用AddNameValuePair方法来实现 function TCSEmployeeDetailsFormatter TypeName: string;beginResult := TCSEmployee ;end;    下面两个过程是用来封装对SendCustomData方法的调用的 这里对全局的TCodeSite对象实例CodeSite进行了调用

{=====================}{== Support Methods ==}{=====================}procedure CSSendEmployeeSummary( const Msg: string; EmpRec: TCSEmployee );beginCodeSite SendCustomData( c EmployeeSummary Msg EmpRec );end;procedure CSSendEmployeeDetails( const Msg: string; EmpRec: TCSEmployee );beginCodeSite SendCustomData( c EmployeeDetails Msg EmpRec );end;    最后 不要忘了调用CSObjectManager RegisterCustomFormatter方法把格式器注册到CodeSite对象管理器中

lishixinzhi/Article/program/Delphi/201311/24692

介绍

下面我要介绍的Bold for Delphi就是是一套优秀的基于UML模型驱动的面向对象的数据库开发框架 包括了几十个组件组件 以及 个以上的类 可以用来轻松地实现信息模型设计及基于信息模型的的应用程序

基础概念介绍

为了使大家对Bold for Delphi整个框架的使用有一个大概的了解 下面将演示如何用UML设计一个简单的模型并用Bold来完成 并包括如何用Bold快速实现一个简单的 *** 作界面

自打我和我老婆认识以后 就染上了她的臭毛病 比较喜欢乱花钱 没有节制 结果搞的自己常常是挣的不如花的多 老要借外债 后来痛定思痛 决定要对每月收支情况做预算 严格控制费用支出 为此写了还写了好多的财务小程序 下面要讲的这个例子程序就是一个常见的家庭小账本程序 它可以用来统计家庭中的收支情况 软件的功能要求如下

可以定义家庭中的各个人员的信息 可以输入收支情况 并同消费的人员关联起来 给出一定时期内消费的情况统计 作为未来家庭预算的依据

建立信息模型

在产品的需求分析阶段 我们首先要建立数据库程序的信息模型 一般来说信息模型主要是指基于ER图的实体关系模型 这是因为我们使用的数据库大部分都是关系型数据库 虽然有些数据库 比如Oracle有面向对象的特性 但不是很完善 一般很少使用 而关系型数据库有一个很大的问题就是无法直观的体现面向对象的思想 关系型的ER模型能够清晰地描述业务域的静态的数据视图 但你无法从模型获取实体的 *** 作及其相互之间的交互 同时 也很难在关系型数据库中简单地实现继承 重载 多态等等面向对象的技术 因此现代数据库开发方法所提倡的面向对象的编程思想无法简单 清晰 平滑地映射为关系型数据库中的表结构

统一建模语言(UML)是一种以可视化的方式建立软件系统框架 并进行文档化的语言 UML语言是对当今软件工程领域成熟设计实践的一个总结 并且已经被实践证明是可以成功地描述大型的复杂系统的 目前国内很多的大型公司已经开始在软件开发过程中使用UML作为一种标准的信息模型设计语言了 Bold for Delphi就是基于UML的 它内置了一套自己的UML建模工具 当然我们也可以使用Rose或者ModelMaker来进行UML设计

面向对象的UML类图则可以说是对ER模型的一个扩展 它对实体之间的关系以及相互之间的作用也进行了描述 ER模型只是对要进行保存的数据进行的模型化 而类图则包括了全部的类实体的属性以及它们的 *** 作和相互作用 它可以使我们对业务域问题有一个更精确的视图 通过使用各种类图技术可以更容易地 也更快速地建立正确的软件系统

基于Bold for Delphi的数据库开发革命性的一点就是允许我们直接把基于UML的类图映射为关系型数据库的存储 而无须手工的通过代码进行转换 要注意一点的是 Bold同其它建模工具如Together ModelMaker不同 它生成框架代码时只使用了UML中的类图 而Together等可以利用UML图中的类图 协作图等其它UML元素来生成代码框架 但是Together不负责生成对象模型对应的关系数据库模型

类模型

下面的这个类图就是我们的账本程序的一个简单类图

图中显示了两个类 人员信息类 Person 以及账目信息类AcctItem 人员类和账目类之间的连线描述了两个类之间的关系 关系包括一个标题PayAssoc揭示了两者之间的关系是支付的关系 每个属性 PayPerson 和 Pay 以及关系多重度因子 和 n 表明每个人可以完成多个账目的收支 而每个账目至少要有一个关联的人员 同时类图还描述了下面一些业务规则

一个人的信息要有名称 账目信息中包含收支金额大小 以及发生日期

上面的类图如果使用关系型数据库来实现的话 需要建立主从表 并将人员和账目之间的关联约束通过应用程序代码强制一些运行逻辑来完成 这时通常要通过补充详细的文档来描述需要强制的业务逻辑 如果没有详细的设计文档 实现代码时就很容易遗漏某些重要的商业规则 同时这些文档在整个的数据库开发的生命周期里面都需要人来手工地维护 难免会出现文档和模型不匹配的错误 而且文档的工作量比较大 而程序员数量又相对不足的话 程序员会觉得既要写代码又要写文档 无形中增加了很多工作量 难免会有抵触情绪 这些都会影响工作的效率

对于这样的问题 Bold则通过精确描述信息模型 无须详细规则描述文档可以将模型自动的转变为实现代码 商业规则在整个数据库开发生命周期内由Bold的类来维护 减少了文档的工作量和出错的可能

建立示例程序

首先 我们要安装Bold for Delphi Bold的一个月评估版可以从 boldsoft 获取 同时D 的架构版内置了Bold 这里我就不详细介绍申请和安装的过程了 安装好后Bold会在IDE的组件面板中添加很多组件 接下来我们就开始建立使用Bold的Delphi程序了

在Delphi中选File|New Application创建一个新的应用程序

保存窗体文件为MainForm pas保存工程文件为CMoney dpr

添加一个数据模块 设定数据模块的名字为DmMoney

将数据模块保存为CDataModule pas

为了使用Bold来建立系统的信息模型 要进行下列 *** 作

从Bold Handles 组件页上选择BoldModal(命名为bmMoney) BoldSystemTypeInfoHandle(命名为bsthMoney)和BoldSystemHandle(命名为bshMoney)到数据模块中

设定bsthMoney的BoldModal属性为bmMoney

设定bshMoney的BoldSystemTypeInfoHandle 属性为bsthMoney

其中BoldModel组件将被用来保存模型 即类 类的关系 约束以及类型等 这些信息将在设计时作为字符串保存到Delphi的窗体和数据模块文件中 在运行时Bold将执行一些模型的中间转换过程 将模型转化为BoldSystemTypeInfoHandle控件所使用的格式 并选择实现可持续性的机制

在设计时储存在BoldModel组件中的信息模型可以被看做元数据 就象数据库的库表和字段结构一样的信息 而BoldSystemTypeInfoHandle组件则保存BoldSystemHandle所需要的运行时信息 这些信息是对UML模型的一种运行时的表达 这个组件是其他Bold组件的信息源 BoldSystemHandle组件则被用来表达整个系统的业务域元素 可以理解为对象空间 通过对象空间我们可以在运行时获得设计时元数据表达的对象的运行实例 目前用到的三个控件已经可以很好的应用在不需要保存数据的环境中了 但账目记录这类数据库程序必须要保存用户输入的信息 因此还需要添加支持数据可持久性的控件 这里为了快速演示的需要 我们使用XML文件作为存储介质 接下来要添加XML可持续控件到数据模块中

从Bold Persistence组件页上选择BoldPersistenceHandleFileXML控件(命名为bphxMoeny)添加到数据模块中

设定组件的BoldModel属性为bmMoney控件

设定bshMoney组件的PersistenceHandle属性为bphxMoeny组件

现在组件关系示意图如下

BoldPersistenceHandleFileXML组件将使我们的程序可以使用XML文件来保存和读取对象 这是一个使用很方便的控件 特别是在快速原型设计期间 因为在原型设计期间 模型经常会被改动 而重新生成数据库表是很费时间的 而XML文件可以使我们非常快的变更我们的模型设计 当模型基本稳定后 可以去掉这个控件 转而切换为其他使用关系型数据库进行存储的可持续性控件 这样的开发方式可以使我们不需要改动整个程序就能很容易地改变数据持续层的存储策略 也就是前面所说的 数据库平台无关设计

除了前面的一些基本的属性设置外 我们还要设定下列控件属性

组件 属性 值 说明 bsthMoney UseGeneratedCode false 是否使用bold生成类代码 这里暂时先不使用 稍后我们会进一步介绍 bphxMoeny FileName Data xml 指定保存数据的xml文件名 bshMoney AutoActivate true 告诉Bold控件在程序运行后马上打开xml文件用于数据存储

建立模型 下面的步骤是建立我们的模型 Bold for Delphi内置了一个树形的UML建模工具(应该说Bold美中不足的一点就是没有提供象Visio和Rose那样基于拖放的模型设计界面) 我们可以双击BoldModel(bmMoney)组件调出模型设计工具 bold UML模型编辑器(见下图)包含了应用程序模型信息 数据类型信息和关系数据库映射信息

模型编辑器支持下列实体类型

Model: 模型 全部业务域实体集合

Package: 包 整个模型的一个子集所包含的实体 可以将大模型分解为小模型来减少系统复杂度

Class:类 类似于Delphi的类的概念(Delphi的类可以从UML的类来生成) 但包含Object Pascal无法直接描述的类的信息和相互关系 Bold框架通过关联类和特殊的列表类封装了一些额外的功能使得我们可以很容易的处理复杂的类关系

Attribute: 属性 类似于Delphi中的property概念 然而在Bold中 这些属性可以在模型中直接保存而无需我们编写属性的Get Set方法

Operation: *** 作 等价于Delphi中的类的过程和函数

Association: 关联 代表了类之间的关系 关联可以使用类来表达 关联也可以有 *** 作和属性 在Bold中建立关联的复杂工作同样可以由框架来实现 我们无须编写代码来完成

Role: 角色 代表关联同类的连接

Data Type: 表示模型所支持的不同数据类型 它可以被扩展以支持用户自定义的数据类型

下图是不同实体类型在模型编辑器中是如何标识的

所有的实体类型都可以通过编辑器的右键菜单来创建和修改属性 同时我们选中实体节点后 实体和全局的选项会显示在右侧的编辑器中 其中重要的有

Name: 模型的名称

lishixinzhi/Article/program/Delphi/201311/24785

以上就是关于DELPHI基础教程:文件管理(二)[3]全部的内容,包括:DELPHI基础教程:文件管理(二)[3]、DELPHI高手进:datetimepicker的用法、如何在delphi7中,测试一段代码的执行时间等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/10163282.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-05
下一篇2023-05-05

发表评论

登录后才能评论

评论列表(0条)

    保存