开发Delphi对象式数据管理功能(十)(转)

发Delphi对象式数据管理功能(十):

20.3.1.5 动态DFM文件应用揭秘

 

  1. 动态DFM文件概述

动态DFM文件是相对于静态DFM文件而言。所谓静态DFM文件是指在Delphi开发环境中设计的窗体文件。窗体的设计过程就是程序的编制过程。因此,动态DFM文件就是指在程序运行过程生成或存取的DFM文件。DFM文件的创建和使用分别如下两种情况: 在程序运行过程中,由Create方法动态生成窗体或部件,然后动态生成其它部件插入其中生成DFM文件 Delphi开发环境中,设计生成DFM文件,然后用DFM 文件存取函数,或者用Stream对象和Filer对象的方法,将DFM文件读入内存,进行处理,最后又存入磁盘中Delphi的窗体设计的常规方法生成的DFM文件在程序运行一开始就规定了部件的结构。因为在窗体设计过程中,窗体中的每个部件都在程序的对象声明中定义了部件变量。这种固定的结构虽然能方便应用,但以牺牲灵活性为代价。Delphi应用程序中有时需要在运行过程中创建控制,然后将该控制插入另一个部件中。例如:

  动态

  ●

  ●

 

  由

  在

 

procedure TForm1.Button1Click(Sender: Tobject);

var

Ctrl: TControl

begin

Ctrl := TEdit.Create(Self);

Ctrl.Top := 100;

Ctrl.Left := 100;

Ctrl.Width := 150;

Ctrl.Height := 20;

InsertControl(Ctrl);

end;

 DFM文件是最佳选择。DFM文件的可视化设计。这就是生成和使用动态DFM文件的第二种方法。也就是在应用程序运行前,在Delphi开发环境中,使用可视化开发工具设计所需窗口或部件的样式,以DFM文件保存。然后在应用程序运行过程中,将DFM文件读入内存。DelphiStream对象和Filer对象在读取DFM文件时,会根据DFM文件的内容自动创建部件及其拥有的所有部件。

  动态插入控制的优点是可以在任何时刻、任意位置插入任意数量的任何类型的控制。因为应用程序需求在很多情况下是在程序运行中才知道的,所以动态插入控制就显得很重要。而且在很多情况下,需要保存这些界面元素,留待程序再次调用。例如应用程序界面的定制、系统状态的保存、对话框的保存等。这时生成动态

  动态插入控制的不足之处是在插入控制前,无法直观地看到控制的大小、风格、位置等,也就是动态插入控制的过程是非可视化的。但可以借助于静态

  在使用动态DFM文件时有两点需要注意。 ● 每一个动态插入的控制或部件必须在程序中调用RegisterClass进行注册 读入DFM文件自动创建部件后,如果调用了InsertControl方法, 则在关闭窗口时要调用RemoveControl方法移去该控制,否则会产生异常事件2. 动态DFM文件应用之一:超媒体系统的卡片设计Delphi多种类型的可视部件,如文本部件、编辑部件、图形图像部件、数据库部件、媒体媒放部件和OLE部件等,每一种部件在屏幕中占据一定的区域,具有相当丰富的表现能力,可以作为卡片中的一种媒体,因此可以利用这些可视部件进行超媒体系统的卡片设计。DFM文件是比较合适的。而且如果利用Stream对象,将卡片存储在数据库BLOB字段中,就为把超文本与关系数据库技术结合起来创造了契机。DFM文件。 在应用程序中注册对象procedure TMainForm.FormCreate(Sender: TObject);end; 创建和插入对象

  ●

 

  

  

  超媒体卡片设计要求卡片中的媒体数目和媒体种类是不受限制的,而且必须能够修改和存取卡片,因此,采用动态

  下面是超媒体卡片设计子系统中的部分源程序,它演示了如何创建对象、插入对象和存取动态

  ⑴

 

begin

RegisterClass(TLabel);

RegisterClass(TEdit);

RegisterClass(TMemo);

RegisterClass(TButton);

RegisterClass(TPanel);

RegisterClass(TPanelP);

RegisterClass(TBitBtn);

 

 

procedure TMDIChild.FormClick(Sender: TObject);Ctrl := TLabel.Create(self);TLabel(Ctrl).AutoSize := False;end;end; 存取动态DFM文件procedure TMainForm.FileOpen(Sender: TObject);DesignWin.Caption := FFileName;end;DesignWin是在TMainForm中定义的TMDIChild类型的窗体部件,是卡片设计平台;FFileName是私有变量,用来保存当前编辑的卡片文件名。DesignWinInit方法实现如下:

var

Ctrl : TControl;

Point: TPoint;

begin

GetCursorPos(Point);

Point := BackGround.ScreenToClient(Point);

case CurToolIndex of

1 : begin

TLabel(ctrl).Caption := 'Label'+S;

TLabel(ctrl).Name := 'Label 1';

TLabel(ctrl).Top := Point.Y;

TLabel(ctrl).Left := Point.X;

TLabel(Ctrl).Height := Round(100*Res/1000/Ratio);

TLabel(Ctrl).Width := Round(600*Res/1000/Ratio);

TLabel(Ctrl).Color := clWhite;

TLabel(Ctrl).Font.Color := clBlack;

TLabel(Ctrl).Font.Name := 'Roman';

TLabel(Ctrl).Font.Height := -TLabel(Ctrl).Height;

TLabel(Ctrl).Font.Pitch := fpFixed;

TLabel(Ctrl).Enabled := False;

TLabel(Ctrl).OnClick := LabelClick;

TLabel(Ctrl).OnMouseMove := ReportPos;

BackGround.InsertControl(Ctrl);

CurTool.Down := False;

CurTool := nil;

2: begin

Ctrl := TEdit.Create(self);

TEdit(ctrl).AutoSize := True;

TEdit(ctrl).Top := Point.Y;

TEdit(ctrl).Left := Point.X;

TEdit(Ctrl).Height := 20;

BackGround.InsertControl(Ctrl);

end;

3:

end;

  

  ⑵

 

begin

if OpenDialog.Execute then

begin

DesignWin := TMDIChild.Create(Application);

ReadComponentResFile(OpenDialog.FileName, DesignWin);

DesignWin.Init;

FileName := OpenDialog.FileName;

end;

 

  

 

procedure TMDIChild.Init; BackGroundTPanel类型的部件,所有的动态创建对象都插入到BackGround中,所以,后面调用BackGround.InsertControl(Ctrl)ObjectIns是个仿Delphi 的媒体属性编辑器。DFM文件的存储过程是这样的:procedure TMainForm.FileSave(Sender: TObject);DesignWinInit方法中调用了InsertControl方法,所以在关闭DesignWin窗口时要相应地调用RemoveControl,否则在关闭DesignWin窗口时会产生内存错误。procedure TMDIChild.FormCloseQuery(Sender: TObject; varCanClose: Boolean);removed := False;I := 0;3. 动态DFM文件应用之二:超媒体系统脚本语言设计DFM文件可以看作是超媒体系统的卡片,DFM脚本能够表达DFM文件中的多种控制,也就是说能够表达卡片中的多种媒体对象,再加上DFM脚本的对象式表达,可编辑性,可转换为DFM文件,因此用作超媒体系统脚本语言较好的形式。ObjectBinaryToTextObjectTextToBinary过程提供了在部件和DFM脚本之间相互转化的功能,ObjectResourceToTextObjectTextToResoure过程提供了DFM文件和DFM脚本之间相互转化的功能。这样就可以在应用程序中自如实现超媒体卡片和超媒体脚本语言相互转化。

var

I: Integer;

Ctrl: TControl;

begin

BackGround.BringToFront;

with BackGround do

for I:= 0 to ControlCount - 1 do

if Controls[I].Name <> ''then

ObjectIns.ObjectList.Items.AddObject(Controls[I].Name, Controls[I]);

end;

  

  动态

 

begin

if DesignWin.CurControl <> nil then

DesignWin.CurControl.Enabled := True;

WriteComponentResFile(FFilename, DesignWin);

DesignWin.Caption := FileName;

end;

end;

 

  因为在

 

var

I: Integer;

Ctrl: TControl;

Removed: Boolean;

begin

if Modified = True then

if MessageDlg('Close the form?', mtConfirmation,

[mbOk, mbCancel], 0) = mrCancel then

CanClose := False;

if CanClose = True then

begin

repeat

repeat

if BackGround.Controls[I].Name <> '' then

begin

BackGround.RemoveControl(BackGround.Controls[I]);

Removed := True;

end;

I := I + 1

until (I >= BackGround.ControlCount) or (Removed = True);

until (Removed = False);

SendMessage(ObjectIns.Handle, WM_MDICHILDCLOSED, 0, 0);

end;

end;

 

  

  超媒体脚本语言设计是超媒体系统设计的重要内容。脚本语言必须能够表达卡片中的多种媒体对象,必须是可编程,可理解的,必须是可执行的,应该可以由脚本语言生成超媒体系统中的卡片和链。

  

  

 

  下面是卡片和脚本语言相互转化的程序:

 

procedure TMDIChild.CardToScript;ScriptEdit是个文本编辑器,它的Lines属性是TStrings类型的对象。procedure TScriptForm.ScriptToCard;ObjectBinaryToTextObjectTextToBinary过程可以细化到部件级的转换。因此超媒体脚本语言的编辑可以细化到媒体对象级。4. 超媒体编辑和表现系统与动态DFM文件的扩展DFM文件处理就显得力不从心了。解决这个矛盾有两套方案:

var

In, Out: TStream;

begin

In := TMemoryStream.Create;

Out := TMemoryStream.Create;

try

In.WriteComponentRes(Self.ClassName, Self);

ObjectResourceToText(In, out);

ScriptForm.ScriptEdit.Lines.LoadFromStream(Out);

finally

In.Free;

Out.Free;

end;

end;

 

  

 

var

In, Out: TStream;

begin

In := TMemoryStream.Create;

Out := TMemoryStream.Create;

try

ScriptForm.ScriptEdit.Lines.SaveToFromStream(In);

ObjectTextToResource(In, out);

In.ReadComponentRes(DesignWin);

finally

In.Free;

Out.Free;

end;

end;

 

  这两段程序是对整个卡片,即窗体级,进行转换的。

  

  超媒体系统的媒体编辑与卡片管理有其特殊的需求,比如链接需求。这时采用已有的窗体部件和媒体部件并按常规的

  利用Delphi部件开发技术,继承和开发新的部件增加新的超媒体特有的属性和处理方法 扩展DFM文件结构,使之能按自己的需要任意地存取和转换部件和DFM文件Delphi的面向对象部件开发技术,在存取和转换等处理上仍旧与常规DFM文件相同。而后者需要DFM的存取和转换上作比较大的改动。下文介绍扩展DFM文件的思路。DFM文件的总体思路是降低处理操作的数据的颗粒度,即从原先窗体级降低到部件级。var写相应媒体扩展信息 }

  ●

 

  前者是充分利用

  扩展动态

  下面是存取操作的扩展示范:

 

  

FileStream: TStream;

I: Integer;

begin

FileStream := TFileStream.Create('OverView.Crd', fmOpenWrite);

With TWriter.Create(FileStream, 4096) do

try

for I := 0 to DesignWin.ControlCount - 1 do

begin

WriteInteger(MMID[i]);

WriteRootComponent(DesignWin.Controls[i]);

{

  …… end; 语句是写入媒体标识。DFM的程序:

  

WriteListEnd;

finally.

Free;

end;

FileStream.Free;

end;

 

WriteInteger(MMID[i])

  下面是相应的读扩展

 

  var  ……  end;    ……

PropInfo: PPropInfo;

Method : TMethod;

FileStream: TStream;

I: Integer;

begin

FileStream := TFileStream.Create('OverView.Crd', fmOpenRead);

With TReader.Create(FileStream, 4096) do

try

while not EndOfList do

begin

case ReadInteger of

IDText: begin

Ctrl := TControl(ReadRootComponent(nil));

PropInfo := GetPropInfo(Ctrl.ClassInfo, 'OnClick');

Method.Code:= Self.MethodAddress(MethodName);

Method.Data := Self;

if Method.Code <> nil then

SetMethodProp(Ctrl, PropInfo, Method);

DesignWin.InsertControl(Ctrl);

end;

IDImage:

WriteListEnd; SetMethodProp过程是用于重新联接控制和它的事件处理过程。类似的功能还可以用TReader对象的OnFindMethod事件的处理过程来实现。数据库BLOB字段应用

end;

finally.

Free;

end;

FileStream.Free;

end;

 

  

  实现脚本语言扩展的基本方法与存取扩展类似,但它还要加扩展媒体信息转换为文本,并插入到部件的脚本描述中。

 

20.3.2

 

  Delphi VCL提供了TBlobStream对象支持对数据库BLOB字段的存取。Delphi TBlobStream对象的作用在于一方面可以使Delphi应用程序充分利用多媒体数据库的数据管理能力。另一方面又能利用Delphi Object Pascal的程序设计能力给关系型多媒体数据库提供底层控制能力和全方位的功能扩展余地。的使用TBlobStream对象用一个TBlobField类型的对象作为参数来创建与BLOB字段相联的BLOB流,接着就可用流的存取方法在BLOB字段中存取数据。var写相应媒体扩展信息 }

 

20.3.2.1 TBlobStream

 

  

 

  

BlobStream: TBlobStream;

I: Integer;

begin

BlobStream := TBlobStream.Create(TBlobField(CardTable.Fields[10], bmWrite);

With TWriter.Create(BlobStream, 4096) do

try

for I := 0 to DesignWin.ControlCount - 1 do

begin

WriteInteger(MMID[i]);

WriteRootComponent(DesignWin.Controls[i]);

{

  …… end; Fields变量是表示数据库记录的字段数组,Fields[10]正是数据库的BLOB 字段。CardTablePost方法将数据库的修改反馈到数据库的物理存储上。

  

WriteListEnd;

finally.

Free;

end;

BlobStream.Free;

CardTable.Post;

end;

 

  

  上面这段程序是超媒体卡片存储的部分源程序,我们就是将卡片保存在数据库BLOB字段中,实现将超文本和关系数据库两种数据管理方式结合起来。读卡片的程序如下:var  ……  end;    ……

 

  

PropInfo: PPropInfo;

Method: TMethod;

Blobtream: TStream;

I: Integer;

begin

BlobStream := TBlobStream.Create(TBlobField(CardTable.Fields[10]), bmRead);

With TReader.Create(BlobStream, 4096) do

try

while not EndOfList do

begin

case ReadInteger of

IDText: begin

Ctrl := TControl(ReadRootComponent(nil));

PropInfo := GetPropInfo(Ctrl.ClassInfo, 'OnClick');

Method.Code:= Self.MethodAddress(MethodName);

Method.Data := Self;

if Method.Code <> nil then

SetMethodProp(Ctrl, PropInfo, Method);

DesignWin.InsertControl(Ctrl);

end;

IDImage:

WriteListEnd; 字段与图形图像BLOB字段是以二进制数据存储方式,因此它完全可以表达图形图像数据。TBlobField对象中提供了LoadFromBitMapSaveToBitMap方法存取位图数据。它们在实现上都是使用BlobStream对象。procedure TBlobField.LoadFromBitmap(Bitmap: TBitmap);Header: TGraphicHeader;begin程序中按两种方式存取数据,对于位图数据,数据的起点是流的Potition0处,对于图形或其它类型的Blob数据,则以流的PositionSizeOf(Header) + 1处开始, 即多了个头信息。

end;

finally.

Free;

end;

FileStream.Free;

end;

 

20.3.2.2 BLOB

 

  在多媒体数据库中处理得比较多的是图形图像,因此早期的多媒体数据库在扩展关系数据库时往往是增加一个图像字段。

  在

 

var

BlobStream: TBlobStream;

Header: TGraphicHeader;

begin

BlobStream := TBlobStream.Create(Self, bmWrite);

try

if (DataType = ftGraphic) or (DataType = ftTypedBinary) then

begin

Header.Count := 1;

Header.HType := $0100;

Header.Size := 0;

BlobStream.Write(Header, SizeOf(Header));

Bitmap.SaveToStream(BlobStream);

Header.Size := BlobStream.Position - SizeOf(Header);

BlobStream.Position := 0;

BlobStream.Write(Header, SizeOf(Header));

end else

Bitmap.SaveToStream(BlobStream);

finally

BlobStream.Free;

end;

end;

 

procedure TBlobField.SaveToBitmap(Bitmap: TBitmap);

var

BlobStream: TBlobStream;

Size: Longint;

BlobStream := TBlobStream.Create(Self, bmRead);

try

Size := BlobStream.Size;

if Size >= SizeOf(TGraphicHeader) then

begin

BlobStream.Read(Header, SizeOf(Header));

if (Header.Count <> 1) or (Header.HType <> $0100) or

(Header.Size <> Size - SizeOf(Header)) then

BlobStream.Position := 0;

end;

Bitmap.LoadFromStream(BlobStream);

finally

BlobStream.Free;

end;

end;

 

 字段与文本Delphi BLOB字段中增加了大型文本的处理能力。可以在TBlobFieldStrings中自由地交换数据。procedure TBlobField.LoadFromStrings(Strings: TStrings);end;end;字段与Stream对象

20.3.2.3 BLOB

 

  

 

var

BlobStream: TBlobStream;

begin

BlobStream := TBlobStream.Create(Self, bmWrite);

try

Strings.SaveToStream(BlobStream);

finally

BlobStream.Free;

end;

end;

 

procedure TBlobField.SaveToStrings(Strings: TStrings);

var

BlobStream: TBlobStream;

begin

BlobStream := TBlobStream.Create(Self, bmRead);

try

Strings.LoadFromStream(BlobStream);

finally

BlobStream.Free;

 

20.3.2.4 BLOB

 

  因为Delphi中,BLOB字段是通过BLOB流来访问的,所以可以很容易地在BLOB字段和Stream对象之间传递数据。为此,TBlobField对象提供了LoadFromStreamSaveToStream方法。procedure TBlobField.LoadFromStream(Stream: TStream);存取嵌入在OleContainer对象中的OLE服务器的数据

 

var

BlobStream: TBlobStream;

begin

BlobStream := TBlobStream.Create(Self, bmWrite);

try

BlobStream.CopyFrom(Stream, 0);

finally

BlobStream.Free;

end;

end;

 

procedure TBlobField.SaveToStream(Stream: TStream);

var

BlobStream: TBlobStream;

begin

BlobStream := TBlobStream.Create(Self, bmRead);

try

Stream.CopyFrom(BlobStream, 0);

finally

BlobStream.Free;

end;

end;

 

20.3.3

 

  对象链接和嵌入(Object Linking and Embedding,简称OLE),是一组服务功能,它提供了一种用来源于不同应用程序的信息创建复合文档的强有力方法。

  通过把图像、图形、表格、声音、注解、文件和其它表示手段描述成对象,用它能在不同软件厂家提供的应用程序中更为容易地交换合成和处理数据它是应用程序的集成更为容易。OLE2.0支持直观编辑。用户不需切换到不同窗口就能在文档中直接对对象进行操作,改进了操作环境。用户不用再关注应用程序和操作环境,只需关注于使用对象技术的数据和文件,便能完成全部工作。OLE已成为操作系统功能上的一大标准,各大软商纷纷在开发工具中支持OLE 2.0规范。Delphi 2.0提供了OleContainer对象支持OLE窗户应用程序的开发。OLE可以用来源于不同应用程序的信息创建复合文档,充分体现以任务、以文档为中心的思想,但是很难分解来自其它应用程序中的嵌入数据,以进行特殊的处理。Word 或写字板之类的强大的编辑排版功能,就可以省却重新开发一个文档编辑的费用,使用具有直观编辑的OLE复合文档嵌入WordDOC数据或RTF数据当然是最佳的选择。 但问题在于全文检索系统要求能直接在文档中搜索关键字,因此要求将文档数据从OLE嵌入数据或文档中的本地数据中分离出来。Delphi 2.0OleContainer部件支持存储OLE对象数据。OLE对象数据包括两部分:OLE类描述信息和OLE服务器嵌入数据。一般说来,OLE服务器嵌入数据是以服务器支持的数据格式存储的; 比方说,中文Word 6.0的嵌入数据的格式就是Word 6.0文档的格式。因此,要将文档数据从OLE 嵌入式文档中分离出来就是要访问第二部分数据。

  

  尽管通过

  例如,一套多媒体电子文档管理系统,系统需要数据库管理功能文档编辑功能,全文检索功能等。在文档编辑功能的实现上,如果能利用中文

  

我们分析了Delphi2.0OleContainer对象存取复合文档的程序,得到分离数据的方法。

  让我们来看一段OleContainer对象存储数据的程序:

 

procedure TOleContainer.SaveToStream(Stream: TStream);Buffer: Pointer;Header: TStreamHeader; ……begin   ……

var

DataHandle: HGlobal;

R: TRect;

try   ……

if FOldStreamFormat then程序中,OleContainer对象执行了两次往流中写数据的操作。

begin

R := BoundsRect;

Header.PartRect.Left := R.Left;

Header.PartRect.Top := R.Top;

Header.PartRect.Right := R.Right;

Header.PartRect.Bottom := R.Bottom;

end else

begin

Header.Signature := StreamSignature;

Header.DrawAspect := FDrawAspect;

end;

Header.DataSize := GlobalSize(DataHandle);

Stream.WriteBuffer(Header, SizeOf(Header));

Buffer := GlobalLock(DataHandle);

try

Stream.WriteBuffer(Buffer^, Header.DataSize);

finally

GlobalUnlock(DataHandle);

end;

finally

ReleaseObject(TempStorage);

ReleaseObject(TempLockBytes);

end;

end;

 

   Stream.WriteBuffer(Header,Size(Header)); 前一语句是写入OLE类描述信息,后一句语句是写入OLE服务器的嵌入数据。HeaderTStreamHeader记录类型的变量。TStreamHeader记录的定义如下:

Stream.WriteBuffer(Buffer^, Header.DataSize);

 

 新版OLE对象 }旧版OLE对象 }OLE服务器嵌入数据时,要跳过文件头的TStreamHeader记录。下面就是如何分离OLE服务器嵌入数据的程序:var包含的服务器对象是中文Word 6.0,程序中将分离出的数据存储在磁盘文件“TEST.DOC”上。如果希望存储在不同的媒介上,可以使用相应的Stream对象,分离的方法类似。但是,这种方法并非对所有的OLE服务器数据都适用,如Windows 95 附件中的写字板(WordPad)就不行。

TStreamHeader = record

case Integer of

0: ( {

Signature: Integer;

DrawAspect: Integer;

DataSize: Integer);

1: ( {

PartRect: TSmallRect);

end;

 

  因此读

 

Stream : TMemoryStream;

FileStream : TFileStream;

begin

Stream := TMemoryStream.Create;

FileStream := TFileStream.Create('TEST.DOC', fmCreate) ;

with OleContainer1 do

if (State <> osEmpty) then

SaveToStream(Stream);

Stream.Seek(Sizeof(TStreamHeader), 0);

FileStream.CopyFrom(Stream, Stream.Size - SizeOf(TStreamHeader));

Stream.Free;

FileStream.Free;

end;

 

OleContainer1


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10294527/viewspace-123284/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/10294527/viewspace-123284/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值