delphi6中自定义属性编辑器所遇到的问题...(50分)

  • 主题发起人 主题发起人 野芒
  • 开始时间 开始时间

野芒

Unregistered / Unconfirmed
GUEST, unregistred user!
我在定义自己的属性编辑器时,继承自tstringproperty,并加入单元dsgnintf,
编译后被告知找不到dsgnintf.dcu文件,后来到网上找了一下资料,知道它在delphi6/source/toolsapi目录下(该目录下全部为.pas文件),于是将其编译后
放入delphi6/lib目录中,此时提示tstringproperty是未定义的感到很奇怪,又
到网上翻了一下,

摘自如下;工程中对于dsgnintf的引用,需要更新到一个新的名字:designintf。(指delphi6) 可能还得加上designeditors,vcleditors 和rtlconsts 到你的引用列表中。并且你还得将designide加入到你的package的requires的列表中。

好,又清楚了,原来还要再引用其它单元,于是又加了几个但是遇到麻烦了,
在编译designeditors.pas时找不到proxies单元,试着将该单元移去,结果提示该单元内的
一个IsProxyClass()找不到了,现在就是没办法了,找不到该单元的任何资料,
只好等着大家给解决解决了~~~~~~~~~~~~~~:(

 
参看下面范例:
interface
implementation
uses Classes, TypInfo,
{$IFDEF EH_LIB_6} DesignIntf, DesignEditors, VCLEditors, Variants,
{$ELSE} DsgnIntf, {$ENDIF}
包中Requiers还要加上 dclstd.dcp文件
 
抱歉, 你有没有看清楚我的问题所在,现在我把designeditors单元的有关定义给出,
interface
uses
Types, SysUtils, Classes, TypInfo, Variants, DesignIntf, DesignMenus;

{这是该单元里头与我的问题有关的一个类定义,它的一个对象方法里用到了isproxyclass()函数,}
TCustomModule = class(TBaseCustomModule, ICustomModule)
private
FRoot: TComponent;
FDesigner: IDesigner;
FFinder: TClassFinder;
public
constructor Create(ARoot: TComponent; const ADesigner: IDesigner); override;
destructor Destroy; override;
procedure ExecuteVerb(Index: Integer); virtual;
function GetAttributes: TCustomModuleAttributes; virtual;
function GetVerb(Index: Integer): string; virtual;
function GetVerbCount: Integer; virtual;
procedure Saving; virtual;
procedure PrepareItem(Index: Integer; const AItem: IMenuItem); virtual;
procedure ValidateComponent(Component: TComponent); virtual; // 一个是它,
function ValidateComponentClass(ComponentClass: TComponentClass): Boolean; virtual; //这是另一个;
function Nestable: Boolean; virtual;
property Root: TComponent read FRoot;
property Designer: IDesigner read FDesigner;
end;

implementation
uses DesignConst, Consts, RTLConsts, Contnrs,proxies; // 引用了proxies单元;

// 下面也只给出与问题有关的该类的定义的两个对象方法,

procedure TCustomModule.ValidateComponent(Component: TComponent);
begin
if not ValidateComponentClass(TComponentClass(Component.ClassType)) then
raise Exception.CreateResFmt(@sClassNotApplicable, [Component.ClassName]);
end;

// 下面的这个方法里头就用到该函数;
function TCustomModule.ValidateComponentClass(ComponentClass: TComponentClass): Boolean;
var
Base: TComponent;
begin
if FFinder = nil then
begin
Base := Root;
if Base.Owner <> nil then
// If the root has an owner then we want to find classes in the owners
// class group not just the root's. This represents what will be active
// when the class is loaded at runtime.
Base := Base.Owner;
FFinder := TClassFinder.Create(TPersistentClass(Base.ClassType));
end;
while IsProxyClass(ComponentClass) do //就在这里了。。。
ComponentClass := TComponentClass(ComponentClass.ClassParent);
// We should only accept classes that are the same as the streaming system
// will see.
Result := FFinder.GetClass(ComponentClass.ClassName) = ComponentClass;
end;

现在的问题是,该单元的implemetation部分引用了proxies单元,但无法找到它,
所以试着将其去除,又提示isproxyclass()是未定义的, 本人水平实在有限没法弄懂
这个函数到底是什么意思,不知大家是否有所了解,希望能给予解答。
 
Is the "proxies.pas" in the Lib path or Browse path?
 
我就是找不到proxies 啊, 你知道它是什么吗?
 
在Delphi5光盘的Runimage/lib目录里可以找到!
 
http://www.delphibbs.com/delphibbs/dispq.asp?lid=588983
http://www.delphibbs.com/delphibbs/dispq.asp?lid=678622
 
DPK里的Requires部份加上DesignIde的引用就行了。
其他不用动。
 
同意xianjun
 
就按照xianjun所说的做吧,我试过了,可以的,100%成功.
 
自从Delphi6出来以后,一个经常被提到的问题是Proxies.pas文件从源文件中消失了。
  
  这个改变是大趋势的一个部分。Borland在Delphi 5中没有装载DsgnIntf.dcu,这显然是要强迫迎合Delphi和C++Builder的许可协议。运行时代码在很多控件中常被不经意地用到。在某些方面Borland鼓励:如果你运用新的控件向导,你将发现这个向导只创建了一个单元,它把控件运行时的框架代码和注册函数放在同一个单元中。

  在Delphi6中Borland更进一步,不仅用DesignIntf替换了DsgnIntf,而且属性编辑器也被放进了DesignEditors,DesignMenus,DesignWindows和其它的一些设计文件里。特别是DesignEditors使用了其它的一个名叫Proxies的IDE文件。(Proxies代码放在DesignIDE.bpl文件中。)不用说这些改变将会产生编译时的错误。

  如果你的运行时代码已经与设计时代码分开了,那么就很容易修改。打开设计时包,然后选择一个目录,点击Add按钮。填写designide.dcp和点击确定。重新编译你的包,这时错误已经没有了。

  如果你的设计时代码和运行时代码已经混合在一起了,那应该怎么解决呢?在Delphi里DesignIDE.bpl不是一个可以再分发的包。因此,即使是只是设计时包使用了组件的运行时代码或是只是控件dcu用了都将产生问题。

  99.99%的情况事实上很容易解决。你的运行时代码事实上没有使用设计时代码;问题是没有合适的分开。

  设计时包应该包括:

  1、所有的注册声明。
  2、所有的属性编辑器。
  3、所有的组件编辑器。
  4、将需要DesignIDE和每一个保存组件自己的运行时包。

  运行时包应该包括:

  1、组件自己。
  2、任何编辑器也许会用到的组件可能自己在运行时调用的窗体。

  维一有点混惑的地方是:属或组件是否使用了一个窗体。假如这个窗体在运行时对于组件是可用的,那么它应该包含在运行时包里。如果它只是在设计时可使用,那它就应该包含在设计时包里。一个常见的错误是误认这个窗体本身是一个编辑器,但事实上它不是。而是组件编辑器调用了这个窗体,它是设计时编辑器。

  你应该养成一个把组件分开成两个包的习惯,即使是你只在程序中静态地进行链接,因为混合运行时和设计时代码将使你的代码膨胀。你的设计时代码在运行时不会被执行,但是链接器不会知道,所以把它也一起链接进去了。(这就是为什么DsgnIntf要设法链接进去的原因。)

  让我们看一个简单的例子,了解如何把设计时代码从运行时代码中分离出去:

{ TMixedComponent }
TMixedComponent = class(TComponent)
private
FFileName: String;
published
property FileName : String read FFileName write FFileName;
{ Published declarations }
end;
{ TMixedFileNameProperty }
TMixedFileNameProperty = class(TPropertyEditor)
function AllEqual: boolean; override;
procedure Edit; override;
function GetAttributes: TPropertyAttributes; override;
function GetValue: string; override;
procedure SetValue (const Value: string); override;
end;
procedure Register;

implementation

procedure Register;
begin
RegisterPropertyEditor(TypeInfo(string), TMixedComponent, ’FileName’,

TMixedFileNameProperty);
RegisterComponents(’Samples’, [TMixedComponent]);
end;

function TMixedFileNameProperty.AllEqual: boolean;
var
FirstVal: string;
i: Integer;
begin
FirstVal := GetStrValue;
Result := True;
i := 1;
while Result and (i < PropCount) do
begin
Result := Result and (GetStrValueAt(i) = FirstVal);
Inc(i);
end;
end;

procedure TMixedFileNameProperty.Edit;
var
Dlg: TOpenDialog;
begin
Dlg := TOpenDialog.Create(Application);
try
with Dlg do
begin
Title := ’File for ’ + TComponent(GetComponent(0)).Name;
FileName:= Value;
if Execute then Value := FileName;
end;
finally
FreeAndNil(Dlg);
end
end;

function TMixedFileNameProperty.GetAttributes: TPropertyAttributes;
begin
Result := [paDialog]
end;

function TMixedFileNameProperty.GetValue: string;
begin
Result := GetStrValue;
end;

procedure TMixedFileNameProperty.SetValue(const Value: string);
begin
SetStrValue(Value);
end;

end.

  把设计时代码从运行时代码中分离出去的最简单方法是,把所有需要DesignIntf 和DesignEditors 代码放入它们自己的单元中去,那个单元将要添加使用组件单元的声明。组件自己不需要知道那个自己运作的IDE编辑器。首先,把DesignIntf 和 DesignEditors 单元从组件单元的Uses部分删除掉,然后让编译/链接器告诉你哪些类需要移到它们自己的单元中去:

[Error] MixedComponent.pas(23): Undeclared identifier: ’TPropertyEditor’
[Error] MixedComponent.pas(23): Class type required
[Error] MixedComponent.pas(24): Method ’AllEqual’ not found in base class
[Error] MixedComponent.pas(25): Method ’Edit’ not found in base class
[Error] MixedComponent.pas(26): Undeclared identifier: ’TPropertyAttributes’
[Error] MixedComponent.pas(27): Method ’GetValue’ not found in base class
[Error] MixedComponent.pas(28): Method ’SetValue’ not found in base class
[Error] MixedComponent.pas(35): Undeclared identifier: ’RegisterPropertyEditor’
[Error] MixedComponent.pas(35): Undeclared identifier: ’TMixedFile’
[Error] MixedComponent.pas(46): Undeclared identifier: ’GetStrValue’
[Error] MixedComponent.pas(49): Undeclared identifier: ’PropCount’
[Error] MixedComponent.pas(51): Undeclared identifier: ’GetStrValueAt’
[Error] MixedComponent.pas(51): Operator not applicable to this operand type
[Error] MixedComponent.pas(64): Undeclared identifier: ’GetComponent’
[Error] MixedComponent.pas(65): Undeclared identifier: ’Value’
[Error] MixedComponent.pas(75): Undeclared identifier: ’paDialog’
[Error] MixedComponent.pas(80): Undeclared identifier: ’GetStrValue’
[Error] MixedComponent.pas(85): Undeclared identifier: ’SetStrValue’
[Fatal Error] JOComponents.dpk(33): Could not compile used unit

’MixedComponent.pas’

  下一步是创建一个新的单元存放这些代码。可以命名为类似MixedComponentReg的名子。把Register函数也移到那个单元中去。下面我们可以从错误信息中得知哪些需要移走。第一个错误信息是[Error] MixedComponent.pas(23): Undeclared identifier: ’TPropertyEditor’,这个信息指出了一个继承自那个设计时单元的类。这是个很清楚的指示,它指明了它是设计时代码和这个类要被移到一个新创建的单元。

  到此,运行时包将会被成功编译(如果还不行,继续把设计时代码从单元中移去,直到没有错误产生)。现在组件在你的应用程序运行时已不再需要Proxies.pas和其它设计时单元了。这个运行时组件非常简单,如下:

unit MixedComponent;

interface

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

type
{ TMixedComponent }
TMixedComponent = class(TComponent)
private
FFileName: String;
published
property FileName : String read FFileName write FFileName;
{ Published declarations }
end;

implementation

end.

  这最后一步就是把你的组件和它的属性编辑器编译到一个设计时包中,然后安装到IDE中去。

  通过File | New | Other 选择 package创建一个新的包。调出包选项,选择design-time only,给Description 项填一个描述文字。选择Requires folder,点击Add按钮。在Requires Package 编辑对话框填写Designide.dcp,点击OK。同样,为你的组件运行时包添加dcp。在这种情况下,它放在了JOComponents.dpk,因此JOComponents.dcp被添加到requires项。在requires里有:JOComponents, designide 和 rtl。最后,选择包含目录,添加MixedComponentReg.pas 到包里。

  到现在我们已经基本完成了!打开MixedComponentReg.pas 添加一对单元到uses部分,这要看你的组件或属性编辑器是否要在声明中使用这个组件(一些复杂的编辑器有时需要知道这个组件的存在)。如果是这样,把它加到Interface的uses部分。否则,加到implementation的uses部分。DesignIntf和DesignEditors就放到Interface的uses里。SysUtils, Forms, Dialogs, 和 Classes 在内部的属性编辑器的不同地方使用,因此就放到implementation部分。最后的MixedComponentReg应该象下面这样:

unit MixedComponentReg;

interface

uses DesignIntf, DesignEditors;

type
{ TMixedFileNameProperty }
TMixedFileNameProperty = class(TPropertyEditor)
function AllEqual: boolean; override;
procedure Edit; override;
function GetAttributes: TPropertyAttributes; override;
function GetValue: string; override;
procedure SetValue (const Value: string); override;
end;

procedure Register;

implementation

uses MixedComponent, SysUtils, Forms, Dialogs, Classes;

procedure Register;
begin
RegisterPropertyEditor(TypeInfo(string), TMixedComponent, ’FileName’,

TMixedFileNameProperty);
RegisterComponents(’Samples’, [TMixedComponent]);
end;

function TMixedFileNameProperty.AllEqual: boolean;
var
FirstVal: string;
i: Integer;
begin
FirstVal := GetStrValue;
Result := True;
i := 1;
while Result and (i < PropCount) do
begin
Result := Result and (GetStrValueAt(i) = FirstVal);
Inc(i);
end;
end;

procedure TMixedFileNameProperty.Edit;
var
Dlg: TOpenDialog;
begin
Dlg := TOpenDialog.Create(Application);
try
with Dlg do
begin
Title := ’File for ’ + TComponent(GetComponent(0)).Name;
FileName:= Value;
if Execute then Value := FileName;
end;
finally
FreeAndNil(Dlg);
end
end;

function TMixedFileNameProperty.GetAttributes: TPropertyAttributes;
begin
Result := [paDialog]
end;

function TMixedFileNameProperty.GetValue: string;
begin
Result := GetStrValue;
end;

procedure TMixedFileNameProperty.SetValue(const Value: string);
begin
SetStrValue(Value);
end;

end.

  剩下的就是编译和安装设计时包了。现在运行时代码已经完全从设计时代码中分了出来。这是一个简单的例子,它唯一有点复杂的是属性编辑器使用一个窗体获得数据,而且那个窗体在运行时也可以利用。在这种情况下,窗体被保留在运行时包里,设计时属性编辑器会从运行时包里调用这个窗体。
 
好文章,收藏
 
后退
顶部