RSS
热门关键字:
当前位置 : 主页>编程开发>Python>列表

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

来源:我要研发网 作者: 时间:1970-01-01 点击:



  20.3.1.5 动态DFM文件应用揭秘

  1. 动态DFM文件概述

字串9

  动态DFM文件是相对于静态DFM文件而言。所谓静态 DFM文件是指在Delphi开发环境中设计窗体文件。窗体设计过程就是程序编制过程。因此,动态DFM文件就是指在程序运行过程生成或存 取DFM文件。

字串9

  动态DFM文件创建和使用分别如下两种情况: 字串4

  ● 在程序运行过程中,由Create方法动态生成窗体或部件,然后 动态生成其它部件插入其中生成DFM文件 字串8

  ● 在Delphi开发环境中,设计生成DFM文件,然后用DFM 文件存取函数,或者用Stream对象和 Filer对象方法,将DFM文件读入内存,进行处理,最后又存入磁盘中 字串3

  由Delphi窗体设计常规方法生成DFM文件在程序运行一开 始就规定了部件结构。因为在窗体设计过程中,窗体中每个部件都在程序对象声明中定义了部件变量。这种固定结构虽然能方便应用 ,但以牺牲灵活性为代价。 字串8

  在Delphi应用程序中有时需要在运行过程中创建控制,然后将该控制插入另一个部件中。例如:

字串4

  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;
字串2

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

字串7

  动态插入控制不足 之处是在插入控制前,无法直观地看到控制大小、风格、位置等,也就是动态插入控制过程是非可视化。但可以借助于静态DFM文件可 视化设计。这就是生成和使用动态DFM文件第二种方法。也就是在应用程序运行前,在Delphi开发环境中,使用可视化开发工具设计所需窗口 或部件样式,以DFM文件保存。然后在应用程序运行过程中,将DFM文件读入内存。DelphiStream对象和Filer对象在读取DFM文件时,会根 据DFM文件内容自动创建部件及其拥有所有部件。

字串1

  在使用动态DFM文件时有两点需要注意。 字串1

  ● 每一个动态插入控制或部 件必须在程序中调用RegisterClass进行注册 字串4

  ● 读入DFM文件自动创建部件后,如果调用了InsertControl方法, 则在关闭窗口时要调 用RemoveControl方法移去该控制,否则会产生异常事件

字串7


字串6

  2. 动态DFM文件应用之一:超媒体系统卡片设计

字串9

  Delphi多种类型可 视部件,如文本部件、编辑部件、图形图像部件、数据库部件、媒体媒放部件和OLE部件等,每一种部件在屏幕中占据一定区域,具有相当丰 富表现能力,可以作为卡片中一种媒体,因此可以利用这些可视部件进行超媒体系统卡片设计。 字串5

  超媒体卡片设计要求卡片中媒 体数目和媒体种类是不受限制,而且必须能够修改和存取卡片,因此,采用动态DFM文件是比较合适。而且如果利用Stream对象,将卡片存 储在数据库BLOB字段中,就为把超文本与关系数据库技术结合起来创造了契机。 字串8

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

字串2

  ⑴ 在应用程序中注册对象 字串5

  procedure TMainForm.FormCreate(Sender: TObject);
begin
RegisterClass(TLabel);
RegisterClass(TEdit);
RegisterClass(TMemo);
RegisterClass (TButton);
RegisterClass(TPanel);
RegisterClass(TPanelP);
RegisterClass(TBitBtn);

end;
字串5

  ⑵ 创建和插入对象 字串6

  procedure TMDIChild.FormClick(Sender: TObject);
var
Ctrl : TControl;
Point: TPoint;
begin
GetCursorPos(Point);
Point := BackGround.ScreenToClient(Point);
case CurToolIndex of
1 : begin
Ctrl := TLabel.Create(self);
TLabel(Ctrl).AutoSize := False;
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;

end;
2: begin
Ctrl := TEdit.Create(self);
TEdit(ctrl).AutoSize := True; 字串4
TEdit(ctrl).Top := Point.Y;
TEdit(ctrl).Left := Point.X;
TEdit(Ctrl).Height := 20;
BackGround.InsertControl(Ctrl);

end;
3:

end;
end;

字串1


字串4

  ⑵ 存取 动态DFM文件

字串5

  procedure TMainForm.FileOpen(Sender: TObject);
begin
if OpenDialog.Execute then
begin
DesignWin := TMDIChild.Create(Application);
ReadComponentResFile(OpenDialog.FileName, DesignWin);
DesignWin.Init;
FileName := OpenDialog.FileName;
DesignWin.Caption := FFileName;
end;
end;

字串7

  DesignWin是在TMainForm中定义TMDIChild类型窗体部件,是卡片设计平台;FFileName是私有变量,用来保存当前 编辑卡片文件名。DesignWinInit方法实现如下:

字串3

  procedure TMDIChild.Init;
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;
字串1

  BackGround是TPanel类型部件,所有动态创建对象都插入到BackGround中,所以,后面调用 BackGround.InsertControl(Ctrl);ObjectIns是个仿Delphi 媒体属性编辑器。 字串5

  动态DFM文件存储过程是这样字串2

  procedure TMainForm.FileSave(Sender: TObject);
begin
if DesignWin.CurControl <> nil then
DesignWin.CurControl.Enabled := True;
WriteComponentResFile(FFilename, DesignWin);
DesignWin.Caption := FileName;
end;
end;
字串1

  因为在DesignWinInit方法中调用了InsertControl方法,所以在关闭DesignWin窗口时要相 应地调用RemoveControl,否则在关闭DesignWin窗口时会产生内存错误。

字串4

  procedure TMDIChild.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
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
removed := False;
I := 0;
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;

字串6


字串8

  3. 动态DFM文 件应用之二:超媒体系统脚本语言设计 字串6

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

字串8

  DFM文件可以看作是超媒体系统 卡片,DFM脚本能够表达DFM文件中多种控制,也就是说能够表达卡片中多种媒体对象,再加上DFM脚本对象式表达,可编辑性,可转换 为DFM文件,因此用作超媒体系统脚本语言较形式。

字串1

  ObjectBinaryToText和ObjectTextToBinary过程提供了在部件和DFM脚本之间相 互转化功能,ObjectResourceToText和ObjectTextToResoure过程提供了DFM文件和DFM脚本之间相互转化功能。这样就可以在应用程序中自 如实现超媒体卡片和超媒体脚本语言相互转化。

字串6

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

  procedure TMDIChild.CardToScript;
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;

字串1

  ScriptEdit是个文本编辑器,它Lines属性是TStrings类型对象。 字串9

  procedure TScriptForm.ScriptToCard;
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;

字串1

  这两段程序是对整 个卡片,即窗体级,进行转换。ObjectBinaryToText和ObjectTextToBinary过程可以细化到部件级转换。因此超媒体脚本语言编辑可以 细化到媒体对象级。

字串5

  4. 超媒体编辑和表现系统与动态DFM文件扩展

字串5

  超媒体系统媒体编辑与卡片管理有其特殊需求,比如 链接需求。这时采用已有窗体部件和媒体部件并按常规DFM文件处理就显得力不从心了。解决这个矛盾有两套方案: 字串7

  ● 利用 Delphi部件开发技术,继承和开发部件增加新超媒体特有属性和处理方法

字串2


字串1

  ● 扩展DFM文件结构,使之能按自己需要任意地存 取和转换部件和DFM文件

字串5

  前者是充分利用Delphi面向对象部件开发技术,在存取和转换等处理上仍旧与常规DFM文件相同。 字串4

  而 后者需要DFM存取和转换上作比较大改动。下文介绍扩展DFM文件思路。

字串7

  扩展动态DFM文件总体思路是降低处理操作数据颗 粒度,即从原先窗体级降低到部件级。

字串4

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

字串7

  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;
WriteListEnd;
finally.
Free;
end;
FileStream.Free;
end;
字串9

  WriteInteger(MMID[i])语句是写入媒体标识 。

字串6

  下面是相应读扩展DFM程序:

字串7

  var
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:
  ……
  end;
   ……
WriteListEnd;
end;
finally.
Free;
end;
FileStream.Free;
end;

字串4

  SetMethodProp过程是用于重新联接控制和它事件处理过程。类似功能还可以用TReader对象OnFindMethod事件处理过 程来实现。 字串6

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


字串6

  20.3.2 数据库BLOB字段应用

字串6

  Delphi VCL提供了TBlobStream对象支持对数据库BLOB字段存取。Delphi TBlobStream对象 作用在于一方面可以使Delphi应用程序充分利用多媒体数据库数据管理能力。另一方面又能利用Delphi Object Pascal程序设计能力给关 系型多媒体数据库提供底层控制能力和全方位功能扩展余地。 字串7

  20.3.2.1 TBlobStream使用

字串2

  TBlobStream对象用一个 TBlobField类型对象作为参数来创建与BLOB字段相联BLOB流,接着就可用流存取方法在BLOB字段中存取数据。 字串4

  var
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;
WriteListEnd;
finally.
Free;
end;
BlobStream.Free;
CardTable.Post;
end;

字串1

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

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

   var
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:
  ……
  end;
   ……
WriteListEnd;
end;
finally.
Free;
end;
FileStream.Free;
end;

字串4


字串1

  20.3.2.2 BLOB字段与图形图像

字串3

  在多媒体数据库中处理得比较多是图形图像,因此 早期多媒体数据库在扩展关系数据库时往往是增加一个图像字段。BLOB字段是以二进制数据存储方式,因此它完全可以表达图形图像数据。

字串9

  在TBlobField对象中提供了LoadFromBitMap和SaveToBitMap方法存取位图数据。它们在实现上都是使用BlobStream对象。

字串1

  procedure TBlobField.LoadFromBitmap(Bitmap: TBitmap);
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;
Header: TGraphicHeader;
begin
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 字串6
(Header.Size <> Size - SizeOf(Header)) then
BlobStream.Position := 0;
end;
Bitmap.LoadFromStream(BlobStream);
finally
BlobStream.Free;
end;
end;
字串3

  程序中按两种方式存取数据,对于位图数据,数据起点是流Potition为0处,对于图形或其它类型Blob数据,则以 流Position为SizeOf(Header) 1处开始, 即多了个头信息。 字串8

  20.3.2.3 BLOB字段与文本 字串3

  Delphi BLOB字段中增加了大型文本 处理能力。可以在TBlobField和Strings中自由地交换数据。  字串6

  procedure TBlobField.LoadFromStrings(Strings: TStrings);
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;
end;
end;
字串6


字串9

  20.3.2.4 BLOB字段与Stream对象

字串4

  因为Delphi中,BLOB字段是通过BLOB流来访问,所以可以很容易 地在BLOB字段和Stream对象之间传递数据。为此,TBlobField对象提供了LoadFromStream和SaveToStream方法。

字串9

  procedure TBlobField.LoadFromStream(Stream: TStream);
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;
字串1

  20.3.3 存取嵌入在OleContainer对象中OLE服务器数据 字串6

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

  通过把图像、图形、表格、声音、注解、文件和其它表示手段描述成对象,用它能在不同软件厂家提供应用程序中更为容易地交换合 成和处理数据它是应用程序集成更为容易。OLE2.0支持直观编辑。用户不需切换到不同窗口就能在文档中直接对对象进行操作,改进了操作 环境。用户不用再关注应用程序和操作环境,只需关注于使用对象技术数据和文件,便能完成全部工作。

字串6

  OLE已成为操作系统功能上 一大标准,各大软商纷纷在开发工具中支持OLE 2.0规范。Delphi 2.0提供了OleContainer对象支持OLE窗户应用程序开发字串4

  尽管通 过OLE可以用来源于不同应用程序信息创建复合文档,充分体现以任务、以文档为中心思想,但是很难分解来自其它应用程序中嵌入数据 ,以进行特殊处理。

字串3

  例如,一套多媒体电子文档管理系统,系统需要数据库管理功能文档编辑功能,全文检索功能等。在文档编辑功 能实现上,如果能利用中文Word 或写字板之类强大编辑排版功能,就可以省却重新开发一个文档编辑费用,使用具有直观编辑OLE 复合文档嵌入WordDOC数据或RTF数据当然是最佳选择。 但问题在于全文检索系统要求能直接在文档中搜索关键字,因此要求将文档数据从 OLE嵌入数据或文档中本地数据中分离出来。

字串2

  Delphi 2.0OleContainer部件支持存储OLE对象数据。OLE对象数据包括两部分:OLE类 描述信息和OLE服务器嵌入数据。一般说来,OLE服务器嵌入数据是以服务器支持数据格式存储; 比方说,中文Word 6.0嵌入数据格式 就是Word 6.0文档格式。因此,要将文档数据从OLE 嵌入式文档中分离出来就是要访问第二部分数据。

字串5


字串9

  我们分析了Delphi 2.0 OleContainer对象存取复合文档程序,得到分离数据方法。 字串5

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

字串6

  procedure TOleContainer.SaveToStream(Stream: TStream);
var
DataHandle: HGlobal;
Buffer: Pointer;
Header: TStreamHeader;
R: TRect;
 ……
begin
   ……
try
   ……
if FOldStreamFormat then
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;

字串5

  程序中,OleContainer对象执 行了两次往流中写数据操作。 字串5

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

字串8

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

字串1

  前一语句是写入OLE类描述信息,后一句语句是写入OLE服务器嵌入数据。Header是TStreamHeader记录类型变量 。TStreamHeader记录定义如下: 字串8

  TStreamHeader = record
case Integer of
0: ( { 新版OLE对象 }
Signature: Integer;
DrawAspect: Integer;
DataSize: Integer);
1: ( { 旧版OLE对象 }
PartRect: TSmallRect);
end;
字串3

  因此读OLE服务器嵌入数据时,要跳过文件头TStreamHeader记录。下面就是如何分离OLE服务器嵌 入数据程序:

字串6

  var
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;  
字串7


字串7

  OleContainer1包含服务器对象是中文Word 6.0,程序中将分离出数据存储在磁盘文件“TEST.DOC”上。如果希望存储在不 同媒介上,可以使用相应Stream对象,分离方法类似。但是,这种方法并非对所有OLE服务器数据都适用,如Windows 95 附件中写 字板(WordPad)就不行。 字串4

字串4

最新评论共有 0 位网友发表了评论
发表评论
评论内容:不能超过250字,需审核,请自觉遵守互联网相关政策法规。
用户名: 密码:
匿名?
注册
相关文章