注册自定义表单,以便我可以从多个项目继承它,而无需将表单复制到Object Repository文件夹

时间:2010-09-17 06:49:45

标签: delphi forms repository delphi-2010

我有一个我需要在多个项目中继承的自定义框架。这个框架包含一些代码和一些组件,它位于磁盘上的某个位置,位于它自己的项目目录中。我不想 COPY 它到Object Repository文件夹,这对我来说似乎不对:我最终有两个表单副本,一个在我的Mercurial支持的存储库中,一个在Delphi的Object Repository中。绝对不是个好主意。

我想要的是将我的框架放在一个包中并让包执行使IDE知道框架所需的所有内容,并允许IDE创建给定框架的新兄弟,而不是实际将框架添加到每个框架单个项目。

到目前为止我做了什么,我遇到的问题,我尝试过的解决方案:

  1. 我将框架添加到包中,使用RegisterClassRegisterNoIcon. 注册我的框架问题:当我进入其他项目并尝试打开派生框架时编辑它说它找不到我的原始框架。
  2. 要修复问题“1”,我想我必须将我的框架注册为自定义模块。所以我打电话给RegisterCustomModule(TMyFrameName, TCustomModule)问题:在“其他”项目中,我打开派生框架,IDE不会在原始框架上创建组件,并且IDE抱怨其中一个“继承”组件丢失。
  3. 要修复“2”,我想我会通过拨打InitInheritedComponent(Self, TFrame)来帮助IDE。这有助于,当我尝试在“其他”项目中打开框架时,所有内容都重新创建,我能够按照我的预期看到框架。 问题:当我保存框架时,它会忘记所有有关继承组件的内容,将每个组件视为添加到此特定框架的新组件。如果我查看保存的DFM,一切都以“对象”开头,没有任何东西以“继承”开头,就像我期望的那样。
  4. 不幸的是我遇到问题“3”。我尝试深入研究Classes.pas,ToolsAPI,DesignIntf​​和DesignEditors,但没有找到任何有用的东西。显然,我希望在DFM中看到的“继承”属性是由TWriter在流式传输TComponent之前分配“TWriter.Ancestor”属性时生成的,但是我无法设置它,IDE需要设置它起来。我不能说服IDE为我做这件事。

    以下是代码的累积相关部分:

    TTestFrame = class(TFrame)
    public
      constructor Create(Owner:TComponent);override;
    end;
    
    constructor TTestFrame.Create(Owner: TComponent);
    begin
      inherited;
      if csDesignInstance in ComponentState then InitInheritedComponent(Self, TFrame);
    end;
    
    procedure Register;
    begin
      RegisterClass(TTestFrame);
      RegisterNoIcon([TTestFrame]);
      RegisterCustomModule(TTestFrame, TCustomModule);
    end;
    

    任何想法,除了“放弃并将你的东西放入对象存储库”? 谢谢!


    修改

    为什么我需要这样做以及为什么依赖于实际路径名称的解决方案被写入我的项目文件中的原因不起作用:我想支持分支:当一个分支时,合理地期望同一个项目的多个版本是“活着“在同一台机器上的不同目录中。推论,我不能同时在同一个地方有同一个项目的多个版本。

    为了确保这项工作,我决定让我的项目不依赖于生活的地方,为了实施这一点,我的团队Clone(Mercurial术语)或Check Out(SVN术语)的每个人都在不同的目录中。我系统上的硬编码路径在我的同事系统中不会很好:如果我们中的任何一个人犯了将任何路径硬编码到应用程序中的错误,那么它不久就会对我们中的一个进行制动,所以错误得到修复。

    这当然是我们需要继承的某些库(因此它们不在我们项目的目录中)的一部分的表单和框架的问题!为了在处理这些文件时获得IDE支持,我们需要暂时将它们添加到项目中,我们不必忘记在完成后删除它们。如果我们忘记并推送/检查更改,这些更改将为我们的同事制造构建(因为他们在不同位置检查了库)。

    为了解决这个问题,我尝试将这些框架和表单添加到设计时包中(使用完整路径将包加载到IDE中,但路径不是项目文件的一部分,所以没关系)。不幸的是,这失败了,我发布了这个问题。

3 个答案:

答案 0 :(得分:3)

这个问题有几个方面:

  • 在设计时使用包中的框架将它们包含在表单中。
  • 在项目中创建一个继承自包中框架的新框架。
  • 启用不同的项目分支以使用不同版本的软件包,从而使用框架,而无需在dpr或dproj中使用硬编码路径。

启用不同的项目分支以使用您自己的软件包的不同版本

  • 使用Tools |在Delphi IDE中创建一个Environment变量选项|环境变量并将其指向当前分支的框架所在的文件夹。在本次讨论中,我们将此环境变量称为“MyLib”,并将其指向“D:\ Whatever \ Version1”文件夹。
  • 进入注册表编辑器并将此条目导出到文件“MyLibEnvironmentVariable.reg”。将此文件置于源代码管理下并对其进行编辑,以删除同一注册表项下也存在的任何其他环境变量。
  • 导出Delphi安装的Known Packages项的内容。
  • 编辑导出的文件 - 删除不是您自己的包的所有值。 - 将您自己的软件包的值从例如D:\\Whatever\\Version1\xxx.bpl更改为$(MyLib)\\xxx.bpl
  • 从“已知包”键中删除指向您自己的包的所有键,然后导入刚刚编辑过的文件。这有效地改变了您的包注册以使用MyLib var,并确保IDE现在也将使用MyLib var来查找您的包。

在设计时使用包中的帧

  • 创建一个包“LibraryPackage”,将其保存在D:\ Whatever \ Version1文件夹中。
  • 添加一个“LibraryFrame”框架,将其保存在D:\ Whatever \ Version1文件夹中。
  • 在框架上放置一些控件或给它一个难看的颜色,以便您可以在视觉上识别它。
  • Register;程序添加到LibraryFrame单元,并将RegisterComponents('MyFrames', [TLibraryFrame]);放入其实施中。
  • 构建程序包并将其安装在IDE中。
  • 从Environment Option的Library路径中删除此包的编码路径和/或将其更改为使用$(MyLib)环境变量。
  • 更改此程序包的注册表项以使用MyLib环境变量。
  • 在“工具调色板”下,框架仍然不可用框架,但它可以从自己的MyFrames Tool Palette页面获得,您可以将它包含在您想要的任何形式中。 IDE将把框架的单元名称添加到forms uses子句中。它不应该向dpr添加任何内容,但它仍然可以为dproj添加完全硬编码的路径。为了避免这种情况,您必须导出/更改bpl的路径,以便为此包使用$(MyLib)/双击技巧。

继承包中的框架

既然已经设置了上述所有内容,那么包中的Frame仍然无法继承自“创建”它的库之外的任何其他项目。但是,现在使其可用于继承非常简单。只需将LibraryFrame_fr in 'LibraryFrame_fr.pas' {LibraryFrame},添加到项目的dpr中,当您使用“Add New | Other”向项目中添加内容时,框架现在将显示在“可继承项目”列表中。

不幸的是,当你选择它时,IDE会抱怨: Cannot open file "D:\Whatever\SecondFormReusingFrame\LibraryFrame_fr.pas". The system cannot find the file specified.

显然现在需要项目文件夹中的LibraryFrame,而不是试图在MyLib文件夹中找到它。 IDE还会在表单中重新标注TLibraryFrame类型,尽管按住它可以显示正确的文件......

LibraryFrame_fr in 'LibraryFrame_fr.pas' {LibraryFrame},更改为 $(MyLib)\LibraryFrame_fr in 'LibraryFrame_fr.pas' {LibraryFrame}, 尽管IDE现在不再抱怨没有找到LibraryFrame_fr.pas。但它确实会产生这样的效果:当您保存所有内容时,IDE“友好地”将其更改为相对路径。在我的情况下,..\FrameToBeReusedSecond\LibraryFrame_fr in 'LibraryFrame_fr.pas' {LibraryFrame},击败了整个练习对象,因为它重新引入了对路径名的依赖,尽管是部分。

然而,将LibraryFrame_fr in 'LibraryFrame_fr.pas' {LibraryFrame},留在dpr中也不是一个选择。重新加载项目时,即使可以在搜索路径中找到该框架,IDE也会抱怨找不到该框架。

从这个框架派生出来的时候,这样做并忽略IDE投诉,实际上并不是一个选择。 IDE没有添加相应的dfm ...虽然您可以编辑dpr中从MyOwnFrame_fr in 'MyOwnFrame_fr.pas'MyOwnFrame_fr in 'MyOwnFrame_fr.pas' {LibraryFrame1}的用法并手动添加dfm存根,但IDE仍然会因为无法进行操作而感到困惑找到LibraryFrame_fr.pas ...

总而言之,Cosmin似乎IDE只是设置了它想要和期望的东西。我认为您想要的是可以实现的,但前提是您愿意并且能够让您的图书馆文件夹始终与您的项目位于相同的位置。

答案 1 :(得分:1)

呃,简单地将要重用的框架添加到要重复使用的项目中会出现什么问题?

e.g。

  • Project1.dpr使用Form1和Frame1,您想重用Frame1。
  • 在另一个文件夹中启动新的VCL表单项目。
  • 当您点击Tool Palette的“Standard”页面上的Frames时,表示没有。
  • 通过项目管理器将Frame1单元添加到此项目中(以便将其添加到dpr中的使用列表中)。
  • 现在点击工具调色板“标准”页面上的框架,可以选择框架。
  • 右键单击项目管理器中的项目,然后选择add new |其他并转到“可继承的项目”,也将Frame1显示为可继承的项目。

修改

如果您不想在dpr中包含帧的硬编码路径,则总是存在使用IDE环境变量的策略。

  • 转到工具|选项|环境变量。
  • 添加一个var MYLIB并将其指向适用于您当前分支的文件夹。
  • 将包含文件夹路径的文件添加到源中,并将其添加到源代码管理中。这可能是现在包含MYLIB值的注册表键的导出。
  • 将$(MYLIB)添加到项目的库路径中。
  • 将框架添加到项目中。它们现在应该包含在没有路径的dpr中(因为它们可以在库路径中找到)。
  • 集成分支时:确保具有MYLIB值的源文件已正确更改。
  • 切换分支时:激活MYLIB的正确值。如果您将.reg文件添加到源代码管理:只需双击它以更改MYLIB注册表的键值。

答案 2 :(得分:0)

当我们为我工作的最后一家公司创建构建过程时,我们想做同样的事情。我们使用subversion并为我们的共享组件创建了一个项目,其中包含一个(Finalbuilder)项目来构建我们所有的共享包。

然后我将使用与Marjan(MyLib)变量类似的技术。并从具有最新组件的批​​处理文件中启动Delphi。

最后我们发现没有必要。我们主要框架的已发布属性变化很少,我们只使用了一套已安装的软件包,如

所示

c:\ BDS \ Components \ D12 \ Bpl(这可能与开发人员不同)

但是坐在

中的代码

C:\ BDS \ MyProject的\共享 在编译时始终链接,因为这是在构建项目时的相对搜索路径

C:\ BDS \ MyProjectExperimental 在构建时也会使用正确分支的代码。

我们实际上通过使用CodeSmith(代码生成器)生成框架来解决存储库问题,并增加了奖励,它们将被放入正确的命名约定(以及任何特定于分支的更改)的正确文件夹中。我们这样做是为了节省时间,但我们(偶然)一起避免了这个问题。

CodeSmith模板花了一点时间来设置第一帧,但是我们很容易调整它来创建其他子类而没有太多的脑循环(对于诸如dataModules之类的东西)。

cfEditFrame.dfm.cst

  
<%@ CodeTemplate Language="C#" TargetLanguage="Delphi" Src="" Inherits="" Debug="False" Description="cfEditFrames.dfm Template" ResponseEncoding="ASCII" %>  
<%@ Property Name="TypeName" Type="System.String" Default="TypeName" Optional="False" Category="Strings" Description="Name of Type. eg Account; AgeMonths" %>  
inherited cf<%=TypeName%>EditFrame: Tcf<%=TypeName%>EditFrame  
  Width = 425  
  Height = 63  
end  

cfEditFrame.cst - 我们必须调用以生成新的子类框架的一个脚本

  
<%@ CodeTemplate Language="C#" TargetLanguage="Delphi" Src="" Inherits="" Debug="False" Description="cfSDMEditComp Template." ResponseEncoding="ASCII" %>  
<%@ Property Name="OutputFolder" Type="System.String" Default="..\\SharedNonInstalled" Optional="False" Category="Strings" Description="" %>  
<%@ Property Name="TypeName" Type="System.String" Default="TypeName" Optional="False" Category="Strings" Description="Name of Type. eg Account; AgeMonths." %>    
<%@ Register Name="EditFramesPasTemplate" Template="cfEditFrames.pas.cst" %>  
<%@ Register Name="EditFramesDfmTemplate" Template="cfEditFrames.dfm.cst" %>  
<%@ Import NameSpace="System.IO" %>  
<script runat="template">  

public override void Render(TextWriter writer)  
{  
  EditFramesPasTemplate cfEditFramesPasTemplate = new EditFramesPasTemplate();  
  this.CopyPropertiesTo(cfEditFramesPasTemplate);  
  cfEditFramesPasTemplate.RenderToFile(String.Format("{0}\\Edit\\cf{1}EditFrames.pas", OutputFolder, TypeName), true);  

  EditFramesDfmTemplate cfEditFramesDfmTemplate = new EditFramesDfmTemplate();  
  this.CopyPropertiesTo(cfEditFramesDfmTemplate);  
  cfEditFramesDfmTemplate.RenderToFile(String.Format("{0}\\Edit\\cf{1}EditFrames.dfm", OutputFolder, TypeName), true);  
}

</script>

可能还有其他源代码生成器也可以这样做,如果不是更好的话。我们有CodeSmith,因此使用它。希望上面的文件格式正常。我是一个新手,有希望渲染像代码的HTML是正确的。

如果要在这些框架上同时拥有属性和组件,则需要执行一项奇怪的操作。您需要分两步完成。首先,您需要在第一个类中添加框架属性,然后在其子类中添加组件。

例如,我们在cfBaseEditFrames.TcfBaseEditFrame中添加自定义属性。

然后我们在cfEditFrames.TcfEditFrame = class(TcfBaseEditFrame)中继承它。 这是我们添加组件的位置(在我们的示例中为TActionList和TImageList)

在包装中注册时,我们添加了

RegisterCustomModule(TcfBaseEditFrame,TWinControlCustomModule);

然后我们确保此包在我们的项目组中,并且打开新的子类帧没有问题。

最后一点,从内存中重要的是后代帧(TcfEditFrame)是添加组件的帧。您无法在TcfBaseEditFrame中添加组件,也无法在TcfEditFrame中添加属性。

BaseEditFrames.pas

unit BaseEditFrames;

interface

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

type
  TBaseEditFrame = class(TFrame)
  private
    { Private declarations }
    FNewFormProperty: string;
  published
    { Published declarations }
    property NewFormProperty: string read FNewFormProperty write FNewFormProperty;
  end;

implementation

{$R *.dfm}

end.

BaseEditFrames.dfm

object BaseEditFrame: TBaseEditFrame
  Left = 0
  Top = 0
  Width = 320
  Height = 240
  TabOrder = 0
end

EditFrames.pas

unit EditFrames;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ActnList, BaseEditFrames;

type
  TEditFrame = class(TBaseEditFrame)
    ActionList: TActionList;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

implementation

{$R *.dfm}

end.

EditFrames.dfm

object EditFrame: TEditFrame
  Left = 0
  Top = 0
  Width = 320
  Height = 240
  TabOrder = 0
  object ActionList: TActionList
    Left = 72
    Top = 16
  end
end

FramePackage.dpk

package FramePackage;

{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
{$DEBUGINFO ON}
{$EXTENDEDSYNTAX ON}
{$IMPORTEDDATA ON}
{$IOCHECKS ON}
{$LOCALSYMBOLS ON}
{$LONGSTRINGS ON}
{$OPENSTRINGS ON}
{$OPTIMIZATION ON}
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
{$REFERENCEINFO OFF}
{$SAFEDIVIDE OFF}
{$STACKFRAMES OFF}
{$TYPEDADDRESS OFF}
{$VARSTRINGCHECKS ON}
{$WRITEABLECONST OFF}
{$MINENUMSIZE 1}
{$IMAGEBASE $400000}
{$DESCRIPTION 'Inheritable Frames'}
{$IMPLICITBUILD ON}

requires
  rtl,
  vcl,
  designide;

contains
  RegisterFramePackage in 'RegisterFramePackage.pas',
  BaseEditFrames in 'BaseEditFrames.pas' {BaseEditFrame: TFrame},
  EditFrames in 'EditFrames.pas' {EditFrame: TFrame};

end.
unit RegisterFramePackage;

interface

procedure Register;

implementation

uses Classes, DesignIntf, WCtlForm, BaseEditFrames;

procedure Register;
begin
  RegisterCustomModule(TBaseEditFrame, TWinControlCustomModule);
end;

end.

您需要安装此设计时包。然后,您可以在另一个项目中拥有一个框架,如下所示

EditFrameDescendants.pas

unit EditFrameDescendants;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
  Dialogs, EditFrames, ActnList;

type
  TEditFrameDescendant = class(TEditFrame)
    Action1: TAction;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

implementation

{$R *.dfm}

end.

EditFrameDescendants.dfm

    
inherited EditFrameDescendant: TEditFrameDescendant
  ParentFont = False
  inherited ActionList: TActionList
    object Action1: TAction
      Caption = 'Action1'
    end
  end
end

您应该能够打开EditFrameDescendant,编辑其惊人的“NewFormProperty”并将操作添加到其操作列表中。对我有用....祝你好运