Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
857 views
in Technique[技术] by (71.8m points)

delphi - Register custom form so I can inherit from it from multiple projects, without copying the form to the Object Repository folder

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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:WhateverVersion1" 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\Version1xxx.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:WhateverVersion1 folder.
  • Add a frame "LibraryFrame", save it in the D:WhateverVersion1 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:WhateverSecondFormReusingFrameLibraryFrame_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 ..FrameToBeReusedSecondLibraryFrame_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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
...