delphi高手请进!高分奉送!!!!(100分)

  • 主题发起人 主题发起人 jzrenxiaoyan
  • 开始时间 开始时间
J

jzrenxiaoyan

Unregistered / Unconfirmed
GUEST, unregistred user!
delphi下:
.exe文件中联接了unit1.dfm文件中的窗口属性信息
.exe运行时,自动根据窗口属性信息,创建相应的窗口和控件
能不能在Mainform.create()里编程序,读另外一个unit2.dfm(和unit1.dfm里的控件种类,数量一致,仅仅是某些属性值不一样)。
即达到:每次.exe运行时根据不同的unit2.dfm,来创建不同风格的窗体!
先奉送100分!
回答的精彩,另送100!
 
看看RTL相关的文章,找找RTL相关的组件,我曾经用过一个组件就是做这个事的,但是事隔好多年了,忘记名了,你自己找找吧,
 
打错了,RTTI[:D]
 
to kkyy:
高手,帮我找找吧,急用!
定重谢!
 
上面的问题有点难,再问一个简单一点的:
“如何得到DELPHI自带的控件图标?”
比如button,label,edit等控件的图标 我应该去那找呢~~
请注意 我说的是所有控件的图标 ~
看过一篇文章说:
//用delphi自带的imageedit,在菜单tool下
//用imageedit打开bpl目录下的*.dcr就可以得到任意控件的图标
我打开了,但是bpl目录下没有*.drc文件
什么原因啊?
我安装的delphi7不全?
请高手指点!
 
呵呵,PAS和DFM文件是在程序编译时候才用的,你都编译完生成EXE文件了,EXE文件怎么还能反回去再按照另一个DFM来编译生成另一个窗体呀.

不过你非这样的话,可以动态生成窗体里的东西,来达到你的目的.

各位高手,说的不对,请指教.
 
我原来写了一个就像delphi一样的工具,必须利用rtti
还有几个函数WriteComponentResFile等,具体看看delphi的代码TypInfo单元
 
在窗体创建以后,动态的修改窗体里控件的属性。应该可以达到效果。例如动态的设置空间的高度、left top等等
 
不知道楼主说的何意?
如果两个窗体中控件种类,数量一致,想根据不同状态,显示不同窗体的话,那么这里完全可以只用一个窗体,动态创建,并修改控件的相关属性就可以了。
如:
form1 := Tform1.create(self);
if state = 1 then
form1.Edit1.Caption := 'A'
else
form1.Edit1.Caption := 'B'
end;

可能是我理解错误,
不过楼主可以说请楚点,大家才好帮你。
 
大家不太理解我的问题,那我再说一下:
我想做一个控件,在程序开发的时候加入我的控件,并且完成某些初始化。之后程序运行的时候不调用默认的资源(unit1.dfm)
通过我的控件装载相应的资源(unit2.dfm),出现的是另外一个界面form2(和unit1.dfm对应的form1里的控件种类、数量一致,某些属性变化,如button的位置改变)等。

能不能通过Hook Api截获装载资源的函数进行替换???
hook api我不懂,不知这个可行否?
 
你自已写一个解析DFM的东西,不就行了吗
 
在delphi帮助文档里面有下面的函数,可以使用
function ComponentToString(Component: TComponent): string;
var
BinStream:TMemoryStream;
StrStream: TStringStream;
s: string;
begin
BinStream := TMemoryStream.Create;
try
StrStream := TStringStream.Create(s);
try
BinStream.WriteComponent(Component);
BinStream.Seek(0, soFromBeginning);
ObjectBinaryToText(BinStream, StrStream);
StrStream.Seek(0, soFromBeginning);
Result:= StrStream.DataString;
finally
StrStream.Free;

end;
finally
BinStream.Free
end;
end;

function StringToComponent(Value: string): TComponent;
var
StrStream:TStringStream;
BinStream: TMemoryStream;
begin
StrStream := TStringStream.Create(Value);
try
BinStream := TMemoryStream.Create;
try
ObjectTextToBinary(StrStream, BinStream);
BinStream.Seek(0, soFromBeginning);
Result := BinStream.ReadComponent(nil);

finally
BinStream.Free;
end;
finally
StrStream.Free;
end;
end;
 
to 超级牛X:
这个我已经用过了,我问的不是这个问题
 
unit1.pas里的语句 $R *.dfm, 编译的时候会自动把unit1.dfm里信息连接到.exe中
这是delphi规定的
我的意思是能不能把这个默认的unit2.dfm改成我想要的.dfm?
 
我实现过Form从数据库加载的功能
方法就是我说的那两个函数
严格的说,这样并非真正的运行期加载,而是要先将做好的Form registercomponent一下
看是据说ifps3__1_33这样的控件可以实现运行期加载功能,但是我只是看了Demo,没有真正研究过这个控件
 
to 超级牛x
我也尝试过你的想法
即:
1。先将做好的前台Form1 registercomponent
2。在form1的oncreate事件里编写代码:
procedure TForm1.FormCreate(Sender: TObject);
var
filestream:Tfilestream;
InStream:TMemoryStream;
i:integer;
begin
for i:=0 to form1.ComponentCount-1 do
form1.Components.Free;

filestream:=Tfilestream.Create(extractfilepath(paramstr(0))+'unit2.dfm',fmopenread); //unit2.dfm是修改后的form1的窗体属性信息
InStream:=TMemoryStream.Create;
try
instream.LoadFromStream(filestream);
instream.ReadComponentRes(form1);
finally
filestream.Free;
InStream.Free;
end;
end;

编译通过了,但是运行的时候出现异常:list index out of bounds
请问:这是什么原因?是不是不能编写在oncreate事件中?

你当时是怎么实现这一点的???
只要能实现换皮肤就可以
 
to 超级牛x:
最近很忙吗??有时间的话能不能告诉我:你实现过的Form从数据库加载的时候,是怎么实现运行期加载界面的功能的???
很急!
盼指点!
 
jzrenxiaoyan 你好
看了你的代码,觉得你编程功底功底还不错,但是怎么却忘记了对于控件的释放、数据集的逐个删除、数组元素的逐个删除、TStringList的逐个删除等地方要用
for i:=form1.ComponentCount-1 downto 0 do
呢?
 
to jzrenxiaoyan
1。先将做好的前台Form1 registercomponent
怎么做的,能不能教教我啊!
我也在学这个,不懂赐教!
 
Delphi的“动态窗体”技术实际应用

刘爱军 2003-4-24



关键字:Delphi,DFM,窗体



为了我可以少敲点字,我们先来看一些资料,也许很多朋友看到过:

==begin================================

DFM文件与标准文本文件转换

整理编辑:China ASP

  在Delphi可视化设计环境中,允许程序员在代码编辑器中以文本的方式浏览和修改DFM文件内容。当用File/Open命令直接打开DFM文件或者选择窗体设计窗口的弹出式菜单上的View as Text命令时,就会在编辑器中出现文本形式的信息。在一些资料中将这种文本形式称之为窗体设计脚本。Delphi提供的这种脚本编辑功能是对Delphi可视化设计的一大补充。当然这个脚本编辑能力是有限制的,比方说不能在脚本任意地添加和删除部件,因为代码和DFM脚本是紧密相连的,任意添加和修改会导致不一致性。但在动态生成的DFM文件中,就不存在这一限制。

  实际上,DFM文件内容是二进制数据,它的脚本是经过Delphi开发环境自动转化的,而且Delphi VCL中的Classes库单元提供了在二进制流中的文件DFM和它的脚本之相互转化的过程。它们是ObjectBinaryToText和ObjectTextToBinary、ObjectResourceToText和ObjectTextToResource。

  ObjectBinaryToText过程将二进制流中存储的部件转化为基于文本的表现形式,这样就可以用文本处理函数进行处理,还可以用文本编辑器进行查找和替代操作,最后可以将文本再转化成二进制流中的部件。

  ObjectTextToBinary过程执行的功能与ObjectBinaryToText相反,将TXT文件转换为二进制流中的部件,而且只要TXT文件内容的书写符合DFM脚本语法,ObjectTextToBinary可将任何程序生成的TXT文件转换为部件,这一功能也为DFM文件的动态生成和编辑奠定了基础。



如何在运行过程中将本窗体保存成一个文本格式的.dfm文件?

zswang(伴水) (2001-11-21 9:52:59) 得0分

function ComponentToString(Component: TComponent): string;

var

BinStream: TMemoryStream;

StrStream: TStringStream;

s: string;

begin

BinStream := TMemoryStream.Create;

try

StrStream := TStringStream.Create(s);

try

BinStream.WriteComponent(Component);

BinStream.Seek(0, soFromBeginning);

ObjectBinaryToText(BinStream, StrStream);

StrStream.Seek(0, soFromBeginning);

Result := StrStream.DataString;

finally

StrStream.Free;

end;

finally

BinStream.Free

end;

end; { ComponentToString }

function StringToComponent(Value: string; Instance: TComponent): TComponent;

var

StrStream: TStringStream;

BinStream: TMemoryStream;

begin

StrStream := TStringStream.Create(Value);

try

BinStream := TMemoryStream.Create;

try

ObjectTextToBinary(StrStream, BinStream);

BinStream.Seek(0, soFromBeginning);

Result := BinStream.ReadComponent(Instance);

finally

BinStream.Free;

end;

finally

StrStream.Free;

end;

end; { StringToComponent }

 

回复人: zswang(伴水) (2001-11-21 9:54:28) 得0分

procedure TForm1.Button1Click(Sender: TObject);

begin

Memo1.Text := ComponentToString(Self);

end;

 

回复人: zswang(伴水) (2001-11-21 9:58:13) 得0分

procedure TForm1.Button2Click(Sender: TObject);

begin

StringToComponent(

'object Label1: TLabel'#13#10 +

' Left = 232'#13#10 +

' Top = 56'#13#10 +

' Width = 26'#13#10 +

' Height = 13'#13#10 +

' Caption = #20320#22909'#13#10 +

' Font.Charset = GB2312_CHARSET'#13#10 +

' Font.Color = clRed'#13#10 +

' Font.Height = -13'#13#10 +

' Font.Name = #23435#20307'#13#10 +

' Font.Style = []'#13#10 +

' ParentFont = False'#13#10 +

'end'#13#10, Label1);

end;

//要注册类

==end=================================

好了,理解了上面的这段文字,一些朋友就会自然想到,利用这几个函数应该可以弄出点有用的东西出来,我就弄出了一点应用,并全面应用到了项目中,现在我来给大家完整描述出来:



首先我要求我的程序有如下能力:

1. 我的程序的窗体是可以动态替换的,不用编译Exe,只要替换一个DFM窗体设计脚本就可以了(当然,你可以重新包装一下这个DFM文件,比如换成txt后缀名等)。

2. 我可以预览所有的DFM文件,让它变成实际的Form察看。

不要小看这两点,在很多情况下,这意义非常重大,举几个例子①开发阶段,可以把界面设计和程序设计完全分开,分工进行②现场维护时,有些界面的调整和功能设置不需要再找源代码到Delphi下去编译一遍了,老出差做Mis类的朋友应该能从这点体会出好处③某些功能界面的升级简单了不少,只要让用户下载一个DFM文件覆盖原来的就可以了。

好,不费话了,下面详细说明怎么达到以上两点要求。

显然我们要让一段文本变成一个Form,那么就用这个函数:

function StringToComponent(Value: string; Instance:TComponent): TComponent;

var

StrStream:TStringStream;

BinStream: TMemoryStream;

begin

StrStream := TStringStream.Create(Value);

try

BinStream := TMemoryStream.Create;

try

ObjectTextToBinary(StrStream, BinStream);

BinStream.Seek(0, soFromBeginning);

Result := BinStream.ReadComponent(Instance);

finally

BinStream.Free;

end;

finally

StrStream.Free;

end;

end;

但是,所有的Class必须是注册过的,例如,如下的Form1FRM.DFM文件

object Form1: TForm1

Left = 222

Top = 168

Width = 485

Height = 290

Caption = 'Form1'

Color = clBtnFace

Font.Charset = DEFAULT_CHARSET

Font.Color = clWindowText

Font.Height = -11

Font.Name = 'MS Sans Serif'

Font.Style = []

OldCreateOrder = False

PixelsPerInch = 96

TextHeight = 13

object Panel1: TPanel

Left = 0

Top = 0

Width = 477

Height = 33

Align = alTop

TabOrder = 0

object BitBtn1: TBitBtn

Left = 4

Top = 4

Width = 75

Height = 25

Caption = 'OK'

TabOrder = 0

end

end

object Memo1: TMemo

Left = 0

Top = 33

Width = 477

Height = 230

Align = alClient

TabOrder = 1

end

end

你应该这么使用,

var list:TstringList;form:TForm



list.Lines.LoadFromFile(‘Form1FRM.DFM’);

RegisterClass(TForm1);

RegisterClass(TPanel);

RegisterClass(TBitBtn);

RegisterClass(TMemo);

form := StringToComponent(list.Lines.Text,nil);

form.ShowModal();



这样就能显示出一个窗体了。

但是这有个问题,Delphi自带的VCL控件是固定的,用RegisterClass(…)注册一遍没有问题,可TForm1不是,如果连TForm1都要注册的话,就无法达成第2点要求。我们可以变通一下,因为所有的Form都是从Tform继承的,所以,应该都可以用注册Tform来取代,因此,有了下面这样一个函数:

function LoadTextForm(FileName:String):TForm;

var

list:TStrings;

FirstLine:String;

iPos : Integer;

Form : TForm;

begin

Result := nil;

if FileExists(FileName)=False then

Exit;

Form := TForm.Create(Application);

list := TStringList.Create;

try

list.LoadFromFile(FileName);

if list.Count=0 then

Exit;

FirstLine := list[0];

iPos := Pos(': ',FirstLine);

if iPos = 0 then //找不到': ',格式不对

Exit;

list[0]:=Copy(FirstLine,1,iPos)+' TForm';

DeleteErrorLines(list);

StringToComponent(list.Text,Form);

Result := Form;

except

Form.Free;

Result := nil;

end;

list.Free;

end;

原理就是读入DFM文件后把窗体的类别偷换成Tform。其中还有一个函数:

procedure DeleteErrorLines(list:TStrings);

var

i:Integer;

line:String;

begin

if list.Count=0 then

Exit;



i:=0;

while i<list.Count do

begin

line := Trim(list);

if Copy(line,1,2)='On' then

list.Delete(i)

else

Inc(i);

end;

end;

这个函数是把凡是含有“On”开头的行删除,应为在Delphi中,所有控件的事件都是以“On”开头,删除了这样的行,就能保证StringToComponent(list.Text,Form);不出错,用以上的两个函数就可以写一个DFM窗体察看器了,到目前为止,我还没有搜到哪个人发布了DFM窗体察看器。这样我们就完成了第2个要求。





实际应用中,一个窗体几乎肯定会有事件处理函数,所以我们要达成第1个要求。我这儿提供了两个方案,各有优缺点:

方案一:

程序员在开发时,在窗体的FormCreate(…)中,用LoadTextForm(…)生成窗体文件,然后把窗体上的控件全部移到本窗体上,最后查找窗体上的控件,动态设置事件处理函数。这个方法要求有一套好的控件命名规则,而且开发比较烦琐,享受不到Delphi的IDE所见即所得,自动生成事件关联代码的好处了。不过对Form文件的制作人员限制很小,他们可以直接用Delphi来制作窗体。

方案二:

用这个函数

procedure ReadForm(aFrom : TComponent;aFileName :string='');

var

FrmStrings : TStrings;

begin

RegisterClass(TPersistentClass(aFrom.ClassType));

FrmStrings:=TStringlist.Create ;

try

if trim(aFileName)='' then FrmStrings.LoadFromFile( gsPathInfo+'/'+aFrom.Name+'.txt')

else FrmStrings.LoadFromFile(aFileName);

while aFrom.ComponentCount>0 do aFrom.Components[0].Destroy ;

aFrom:=StringToComponent(FrmStrings.Text,aFrom)

finally

FrmStrings.Free;

end;

UnRegisterClass(TPersistentClass(aFrom.ClassType));

end;

在FormCreate中调用ReadForm(self,…)。

这个方案没有第一个方案的限制,但是要求开发人员必须先完成一个完整的Form文件交给Form文件制作人员, Form文件的制作人员不能修改控件的name,不能添加或删除控件,而且必须保留开发人员给定所有事件处理函数,不能修改函数名。不过很多问题可以写一个Form编辑器来保证不出问题。

具体代码就不写了。

我想,肯定还有跟好的方案来解决动态窗体的问题,希望大家讨论。

(以上代码使用Delphi6编写)
 
后退
顶部