注册自定义表单,以便我可以从多个项目继承它,而无需将表单复制到对象存储库文件夹
我有一个需要在多个项目中继承的自定义框架。该框架包含一些代码和一些组件,它驻留在磁盘上的某个位置,在它自己的项目目录中。我不想将其复制到对象存储库文件夹,这对我来说似乎不合适:我最终会得到该表单的两份副本,一份位于我的 Mercurial 支持的存储库中, Delphi 的对象存储库中的一个。绝对不是一个好主意。
我想要的是将我的框架放在一个包中,并让该包执行使 IDE 知道该框架所需的所有操作,并允许 IDE 创建给定框架的新同级框架,而无需实际将该框架添加到每个项目中。
到目前为止我所做的、遇到的问题、尝试的解决方案:
- 我将框架添加到包中,使用
RegisterClass
和RegisterNoIcon 注册我的框架。
< em>问题:当我进入其他项目并尝试打开派生框架进行编辑时,它说找不到我的原始框架。 - 为了解决问题“1”,我想我必须将我的框架注册为自定义模块。所以我调用了RegisterCustomModule(TMyFrameName, TCustomModule)。 问题:我从“其他”项目中打开一个派生框架,IDE 没有在我的原始框架上创建组件,并且 IDE 抱怨缺少一个“继承”组件。
- 为了修复“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:
- I added my frame to a package, registered my frame using both
RegisterClass
andRegisterNoIcon.
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. - 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. - 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这个问题有几个方面:
允许不同的项目分支使用您自己的包的不同版本
-- 删除所有不是您自己的包的值。
-- 将您自己的包的值从例如
D:\\Whatever\\Version1\xxx.bpl
更改为$(MyLib)\\xxx.bpl
。在设计时使用包中的框架
Register;
过程添加到 LibraryFrame 单元,并将RegisterComponents('MyFrames', [TLibraryFrame]);
放入其实现中。从包中的框架继承
既然以上所有内容均已设置,则包中的框架仍然无法从“创建”它的库之外的任何其他项目中继承。然而,现在使其可用于继承相当简单。只需将
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:
Enabling different project branches to use different versions of your own packages
-- 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
.Using frames from a package at design time
Register;
procedure to the LibraryFrame unit, and putRegisterComponents('MyFrames', [TLibraryFrame]);
in its implementation.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'
toMyOwnFrame_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.
呃,如果只是将要重用的框架添加到要重用的项目中,有什么问题吗?
例如
编辑
如果您不想在 dpr 中包含框架的硬编码路径,则始终可以使用 IDE 的环境变量。
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.
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.
当我们为我工作的最后一家公司创建构建流程时,我们想做完全相同的事情。我们使用 subversion 并为我们的共享组件创建了一个项目,其中包含一个 (Finalbuilder) 项目来构建我们所有的共享包。
然后我将使用与 Marjan 的 (MyLib) 变量类似的技术。并使用最新的组件集从批处理文件启动 Delphi。
最后我们发现没有必要。我们的主框架的已发布属性很少更改,因此我们只使用了一组已安装的软件包,例如
c:\BDS\Components\D12\Bpl(这可能因开发人员而异),
但位于
c: 中的代码: \BDS\MyProject\共享
编译时始终链接,因为它位于构建项目
c:\BDS\MyProjectExperimental 时的相对搜索路径上
在构建时也会使用正确的分支代码。
实际上,我们通过使用 CodeSmith(代码生成器)生成框架来解决存储库问题,额外的好处是它们将被放置在具有正确命名约定的正确文件夹中(以及任何分支特定的更改)。我们这样做是为了节省时间,但我们(偶然)完全避免了这个问题。
CodeSmith 模板花了一点时间来设置第一帧,但随后我们轻松地对其进行调整以创建其他子类,而无需太多的大脑循环(对于诸如 dataModules 之类的东西)。
cfEditFrame.dfm.cst
cfEditFrame.cst - 我们必须调用的一个脚本来生成一个新的子类框架
可能还有其他源代码生成器可以同样完成此操作,甚至更好。我们有 CodeSmith,因此使用了它。希望以上文件格式正确。我是一个新手,希望 html 之类的代码的渲染是正确的。
至于您想要继承的这些框架上的属性和组件,您需要做一件奇怪的事情。您需要分两步完成。首先,您需要在第一个类中添加框架属性,然后在其子类中添加组件。
例如,我们在 cfBaseEditFrames.TcfBaseEditFrame 中添加自定义属性。
然后我们在 cfEditFrames.TcfEditFrame = class(TcfBaseEditFrame) 中对其进行子类化。
这是我们添加组件的地方(在我们的示例中是 TActionList 和 TImageList)。
在包中注册这些组件时,我们添加了
RegisterCustomModule(TcfBaseEditFrame, TWinControlCustomModule);
然后,我们确保该包位于我们的项目组中,并且打开新的子类框架没有问题。
最后一点,根据记忆,后代框架 (TcfEditFrame) 是添加组件的框架,这一点很重要。您无法在 TcfBaseEditFrame 中添加组件,也无法在 TcfEditFrame 中添加属性。
BaseEditFrames.pas
BaseEditFrames.dfm
EditFrames.pas
EditFrames.dfm
FramePackage.dpk
您需要安装此设计时包。然后,您可以在另一个项目中拥有一个框架,如下所示
EditFrameDescendants.pas
EditFrameDescendants.dfm
您应该能够打开 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
cfEditFrame.cst - The one script we had to call to generate a new subclassed frame
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
BaseEditFrames.dfm
EditFrames.pas
EditFrames.dfm
FramePackage.dpk
You need to install this design time package. You could then have a frame in another project like the following
EditFrameDescendants.pas
EditFrameDescendants.dfm
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