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

发布于 2024-09-19 18:58:43 字数 2148 浏览 3 评论 0原文

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

我想要的是将我的框架放在一个包中,并让该包执行使 IDE 知道该框架所需的所有操作,并允许 IDE 创建给定框架的新同级框架,而无需实际将该框架添加到每个项目中。

到目前为止我所做的、遇到的问题、尝试的解决方案:

  1. 我将框架添加到包中,使用 RegisterClassRegisterNoIcon 注册我的框架。 < em>问题:当我进入其他项目并尝试打开派生框架进行编辑时,它说找不到我的原始框架。
  2. 为了解决问题“1”,我想我必须将我的框架注册为自定义模块。所以我调用了RegisterCustomModule(TMyFrameName, TCustomModule)。 问题:我从“其他”项目中打开一个派生框架,IDE 没有在我的原始框架上创建组件,并且 IDE 抱怨缺少一个“继承”组件。
  3. 为了修复“2”,我想通过调用 InitInheritedComponent(Self, TFrame) 来为 IDE 提供帮助。这很有帮助,当我尝试在“其他”项目中打开框架时,所有内容都被重新创建,并且我能够按照预期看到框架。 问题:当我保存框架时,它会忘记所有继承的组件,将每个组件视为添加到该特定框架的新组件。如果我查看保存的 DFM,一切都以“对象”开头,没有任何内容以“继承”开头,正如我所期望的那样。

不幸的是我陷入了问题“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;

除了“放弃并将您的东西放入对象存储库”之外,还有什么想法吗? 谢谢!


编辑

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

为了确保这一点,我决定让我的项目不依赖于生活地点,并且为了强制执行这一点,我让我们团队中的每个人克隆(Mercurial 术语)或签出(SVN 术语)我们在不同目录中的项目。我的系统上的硬编码路径在我同事的系统上并不好:如果我们中的任何一个人犯了将任何类型的路径硬编码到应用程序中的错误,那么不久之后它就会对我们中的一个人造成阻碍,所以错误得到修复。

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

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

I've got a custom frame I need to inherit from in multiple projects. This frame includes some code and some components, and it resides somewhere on disk, in it's own project directory. I don't want to COPY it to the Object Repository folder, that doesn't seem right to me: I'd end up having two copies of the form, one in my Mercurial-backed repository, one in Delphi's Object Repository. Absolutely not a good idea.

What I want is to have my frame in a Package and have the Package do all that's required to make the frame known to the IDE and allow the IDE to create new siblings of the given frame, without actually adding the frame to every single project.

What I've done so far, problems I've encountered, solutions I tried:

  1. I added my frame to a package, registered my frame using both RegisterClass and RegisterNoIcon. Problem: When I go into some other project and try to open a derived frame for editing it says it can't find my original frame.
  2. To fix problem "1" I figured I'd have to register my frame as an Custom Module. So I called RegisterCustomModule(TMyFrameName, TCustomModule). Problem: From the 'other' project I open a derived frame, the IDE doesn't create the components on my original frame and the IDE is complaining about one of the "inherited" components missing.
  3. To fix "2" I thought I'd give the IDE a helping hand by calling InitInheritedComponent(Self, TFrame). This helped, when I tried opening the frame in the 'other' project everything got re-created and I was able to see the frame as I expected. Problem: when I save the frame it forgets all about the inherited components, treats every single component as a new component added to this particular frame. If I look into the saved DFM everything starts with "object", nothing starts with "inherited" as I'd expect.

Unfortunately I got stuck on problem "3". I tried digging into Classes.pas, ToolsAPI, DesignIntf and DesignEditors but didn't find anything helpful. Apparently the "inherited" attribute I was hoping to see in the DFM is generated by TWriter when it's "TWriter.Ancestor" property is assigned before streaming a TComponent, but there's no way for me to set it up, the IDE needs to set it up. And I can't convince the IDE to do it for me.

Here are the cumulated, relevant parts of code:

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;

Any ideas, besideds "give up and put your stuff into Object Repository"?
Thanks!


Edit

Why I need to do this and why solutions that depend on actual path names getting written into my project's files don't work: I want to support Branching: when one branches it's reasonable to expect multiple versions of the same project being "alive" in different directories on the same machine. The corollary, I can't have multiple versions of the same project in the same place at the same time.

To make sure this works I decided to make my projects not depend on where the live, and to enforce this I have everyone on our team Clone (Mercurial terminology) or Check Out (SVN terminology) our projects in different directories. A hard-coded path on my system will not be good on my colleague's system: if any of us makes the mistake of hard-coding any sort of path into the application, it will not be long before it brakes on one of us, so the error gets fixed.

This is of course a problem with forms and frames that are part of some library (so they're not in our project's directory) that we need to inherit from! In order to get IDE support when working with those files we need to temporarily add them to the project and we need not forget removing them after we're done. If we forget and push/check in changes, the changes would brake the build for our colleagues (because they have the libraries checked out at different locations).

In order to solve this I attempted adding those frames and forms to a design time package (packages are loaded using full-path into the IDE, but the path is not part of the project's files so it's OK). Unfortunately this failed, and that's wyhy I posted this question.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

街角卖回忆 2024-09-26 18:58:44

这个问题有几个方面:

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

允许不同的项目分支使用您自己的包的不同版本

  • 使用 Tools | 在 Delphi IDE 中创建环境变量选项|环境变量并将其指向当前分支的框架所在的文件夹。在本次讨论中,我们将此环境变量称为“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 中。
  • 从环境选项的库路径中删除此包的编码路径和/或将其更改为使用 $(MyLib) 环境变量。
  • 更改此包的注册表项以使用 MyLib 环境变量。
  • 该框架在工具选项板 | 下仍然不可用。框架,但可以从其自己的 MyFrames 工具选项板页面获取它,并且您可以将其包含在您想要的任何表单中。 IDE 会将框架的单元名称添加到表单的使用子句中。它不应向 dpr 添加任何内容,但仍可能向 dproj 添加完全硬编码的路径。为了避免这种情况,您必须导出/更改 bpl 的路径以对该包使用 $(MyLib)/双击技巧。

从包中的框架继承

既然以上所有内容均已设置,则包中的框架仍然无法从“创建”它的库之外的任何其他项目中继承。然而,现在使其可用于继承相当简单。只需将 LibraryFrame_fr in 'LibraryFrame_fr.pas' {LibraryFrame}, 添加到项目的 dpr 中,当您使用“添加新项 | 其他”添加内容时,框架现在将显示在“可继承项目”列表中到你的项目。

然而不幸的是,当你选择它时,IDE 会抱怨:
无法打开文件“D:\Whatever\SecondFormReusingFrame\LibraryFrame_fr.pas”。系统找不到指定的文件。

显然,现在需要在项目文件夹中找到 LibraryFrame,而不是尝试在 MyLib 文件夹中查找它。 IDE 还红线了表单中的 TLibraryFrame 类型,尽管按住 Ctrl 键单击它确实会显示正确的文件...

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

然而,将 LibraryFrame_fr 留在 'LibraryFrame_fr.pas' {LibraryFrame}, 中也不是真正的选择。当您重新加载项目时,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 的设置有点过于它想要和期望的内容。我认为你想要的是可以实现的,但前提是你愿意并且能够让你的库文件夹始终位于与你的项目相同的相对位置。

There are a couple of aspects to this problem:

  • Using frames from a package at design time to include them on forms.
  • Creating a new frame in a project that inherits from a frame in a package.
  • Enabling different project branches to use different versions of the package and thus the frames, without being faced with hardcoded paths in either the dpr or the dproj.

Enabling different project branches to use different versions of your own packages

  • Create an Environment variable in the Delphi IDE using Tools | Options | Environment variables and point it to the folder where the frames for your current branch reside. For this discussion we'll call this environment variable "MyLib" and point it to a "D:\Whatever\Version1" folder.
  • Go into the registry editor and export this entry to a file "MyLibEnvironmentVariable.reg". Put this file under source control and edit it to remove any other environment variables that were also present under the same registry key.
  • Export the contents of the Known Packages key of your Delphi installation.
  • Edit the exported file
    -- Remove all values that are not your own packages.
    -- Change the values of your own packages from for example D:\\Whatever\\Version1\xxx.bpl to $(MyLib)\\xxx.bpl.
  • Remove all keys pointing to your own packages from the Known Packages key and import the file you just edited. This effectively changes your package registrations to use the MyLib var and will ensure that the IDE will now also use the MyLib var to find your packages.

Using frames from a package at design time

  • Create a package "LibraryPackage", save it in the D:\Whatever\Version1 folder.
  • Add a frame "LibraryFrame", save it in the D:\Whatever\Version1 folder.
  • Put some controls on the frame or give it an ugly color so you can visually recognize it.
  • Add a Register; procedure to the LibraryFrame unit, and put RegisterComponents('MyFrames', [TLibraryFrame]); in its implementation.
  • Build the package and install it in the IDE.
  • Remove the harcoded path to this package from the Environment Option's Library path and/or change it to use the $(MyLib) environment variable.
  • Change the Registry entry for this package to use the MyLib environment variable.
  • The frame will still not be available under Tool Palette | Frames, but it will be available from its own MyFrames Tool Palette page and you can include it on any form you want. The IDE will add the frame's unit name to the forms uses clause. It shouldn't add anything to the dpr, but it may still add a fully hardcoded path to your dproj. To avoid that you will have to do the export / change the bpl's path to use $(MyLib) / double-click trick for this package as well.

Inheriting from a frame in a package

Now that all of the above is set up, the Frame from the package is still not available to be inherited from in any other project than the library in which it was "created". However, it is now fairly simple to make it available for inheritance. Simply add LibraryFrame_fr in 'LibraryFrame_fr.pas' {LibraryFrame}, to your project's dpr and the frame will now show up in the "inheritable items" list when you use "Add New | Other" to add something to your project.

Unfortunately however, when you select it, the IDE will complain:
Cannot open file "D:\Whatever\SecondFormReusingFrame\LibraryFrame_fr.pas". The system cannot find the file specified.

It obviously now expects the LibraryFrame in the project's folder instead of trying to find it in the MyLib folder. The IDE also redlines the TLibraryFrame type in the form although ctrl-clicking it does bring up the correct file...

Changing LibraryFrame_fr in 'LibraryFrame_fr.pas' {LibraryFrame}, to
$(MyLib)\LibraryFrame_fr in 'LibraryFrame_fr.pas' {LibraryFrame},
helps in as much as the IDE now no longer complains about not finding the LibraryFrame_fr.pas. But it does have the effect that when you save everything, the IDE "kindly" changes it to a relative path. In my case ..\FrameToBeReusedSecond\LibraryFrame_fr in 'LibraryFrame_fr.pas' {LibraryFrame}, which defeats the entire object of the exercise as it reintroduces dependency on path names albeit partly.

However, leaving LibraryFrame_fr in 'LibraryFrame_fr.pas' {LibraryFrame}, in the dpr is not really an option either. When you reload the project the IDE complains about not finding the frame even though it can be found on the search path.

Also leaving it like that and ignoring the IDE complaint when deriving from this frame, is not really an option. The IDE does not add a corresponding dfm... And while you could edit the uses in the dpr from MyOwnFrame_fr in 'MyOwnFrame_fr.pas' to MyOwnFrame_fr in 'MyOwnFrame_fr.pas' {LibraryFrame1} and add a dfm stubb manually, the IDE still gets confused with not being able to find LibraryFrame_fr.pas ...

All in all Cosmin, it seems the IDE is just a bit too set in it what it wants and expects. I think what you want is achievable but only if you are willing and able to have your library folders always in the same relative location to your projects.

酸甜透明夹心 2024-09-26 18:58:44

呃,如果只是将要重用的框架添加到要重用的项目中,有什么问题吗?

例如

  • Project1.dpr使用Form1和Frame1,您想重用Frame1。
  • 在不同的文件夹中启动新的 VCL 表单项目。
  • 当您单击工具选项板“标准”页面上的框架时,系统会提示没有框架。
  • 通过项目管理器将 Frame1 单元添加到该项目(以便将其添加到 dpr 中的使用列表中)。
  • 现在单击工具选项板“标准”页面上的框架将显示可供选择的框架 1。
  • 并右键单击项目管理器中的项目,然后选择添加新|其他并转到“可继承项目”,也将 Frame1 显示为可继承项目。

编辑

如果您不想在 dpr 中包含框架的硬编码路径,则始终可以使用 IDE 的环境变量。

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

Uh, what's wrong with simply adding the frame you want to reuse to the project in which you want to reuse it?

e.g.

  • Project1.dpr uses Form1 and Frame1, you want to reuse Frame1.
  • Start new VCL forms project in a different folder.
  • When you click on Frames on the "Standard" page of the Tool Palette is says there are none.
  • Add Frame1 unit to this project via the project manager (so that it is added to the uses list in the dpr).
  • Now clicking on Frames on the "Standard" page of the Tool Palette shows Frame1 available for selection.
  • And right clicking on the project in the project manager, and then selecting add new | other and going to "inheritable items", also shows Frame1 as an inheritable item.

Edit

If you do not want to include the hardcoded path to the frame in the dpr, there is always the ploy of using the IDE's environment variables.

  • Go to Tools | Options | Environment variables.
  • Add a var MYLIB and point it to the folder that is applicable for the branch you are currently in.
  • Add a file containing the folder path to your sources and add it to source control. This could be an export of the registry's key now containing the value of MYLIB.
  • Add $(MYLIB) to your project's library path.
  • Add the frames to your project. They should now be included in the dpr without the path (because they can be found on the library path).
  • When integrating branches: ensure that the source file with the value for MYLIB is changed appropriately.
  • When switching branches: activate the proper value for MYLIB. If you added a .reg file to source control: simply double click it to change the MYLIB registry's key value.
看海 2024-09-26 18:58:44

当我们为我工作的最后一家公司创建构建流程时,我们想做完全相同的事情。我们使用 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”并将操作添加到其操作列表中。为我工作......祝你好运

When we created the build process out the last company I worked for, we wanted to do exactly the same thing. We use subversion and had a project for our shared components which contained a (Finalbuilder) project to build all our shared packages.

I was then going to use a similar technique to Marjan's (MyLib) variable. And launch Delphi from a batch file with the latest set of components.

In the end we found it wasn't necessary. The published properties of our main frames changed so infrequently that we just used one installed set of packages say in

c:\BDS\Components\D12\Bpl (This could be different from developer to developer)

But the code that was sitting in

c:\BDS\MyProject\Shared
was always linked in when compiling because that was on the relative search path for when building the project

c:\BDS\MyProjectExperimental
would use the correctly branched code when building as well.

We actually got around the repository problem by using CodeSmith (a code generator) to generate the frames, with the added bonus that they would be put in the right folder with correct naming conventions (and any branch specific changes as well). We did this as a time saver for us but we (by chance) avoided this problem all together.

The CodeSmith templates took a little bit of time to set up for the first frame, but then we easily adapted it to create other subclasses without too many brain cycles (for things such as 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 - The one script we had to call to generate a new subclassed frame

  
<%@ 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>

There may be other source code generators out there that do this just as well, if not better. We had CodeSmith and therefore used it. Hopefully the above files are formatted OK. I'm a SO newbie and the hopefully rendering of html like code is correct.

As far as having both properties and components on these frames you want to inherit from you need to do one strange thing. You need to do it in two steps. First you need to add the frame properties in the first class and THEN add the components in a subclass of that.

For example we add the custom properties in cfBaseEditFrames.TcfBaseEditFrame.

We then subclass that in cfEditFrames.TcfEditFrame = class(TcfBaseEditFrame).
This is where we add our components (in our example a TActionList and TImageList)

When registering these in a package we added

RegisterCustomModule(TcfBaseEditFrame, TWinControlCustomModule);

We then ensure this package is in our project group and there is no problem opening up newly subclassed frames.

One last note, from memory it was important that the descendant frame (TcfEditFrame) was the one that added components. You couldn't add components in TcfBaseEditFrame and properties in 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.

You need to install this design time package. You could then have a frame in another project like the following

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

You should be able to open up EditFrameDescendant, edit its amazing "NewFormProperty" and add actions to to its action list. Works for me....Good luck

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文