views:

808

answers:

6

Hi,

In Delphi 2009, In one of my projects, I have a custom frame with some controls on it which I want to use as the base class for some other controls. I want to register this frame as an IDE wizard to be available in New Items list. When I add my newly added item (my custom frame) to a project, I expect it to:

  1. Show all the properties and events I added to the custom frame in object inspector.
  2. Derive the newly created frame from my custom frame rather than TFrame.

Ok, to make it show my properties and events in Object Inspector, I register a custom module into IDE. It doesn't work properly for frames. Fortunately somebody mentioned this on StackOverflow, and an answer is given to this:

http://stackoverflow.com/questions/289672/showing-tframe-descendants-additional-properties-on-the-object-inspector

Then, to make it load DFM of my custom frame, I added InitInheritedComponent to the constructor of my custom frame. Something like this:

constructor TMyFrame.Create(AOwner: TComponent); override;
begin
  inerited;
  if (ClassType <> TMyFrame) and not (csDesignInstance in ComponentState) then
  begin
    if not InitInheritedComponent(Self, TMyFrame) then
      raise EResNotFound.CreateFmt('Resource %s not found', [ClassName]);
  end;
end;

It doesn't work! It still creates an empty frame in designer rather than my own frame. If I don't register custom module into IDE, it shows my frame correctly even without needing InitInheritedComponent, but additional properties are not shown in Object Inspector!

if I change constructor source to this (Replacing TMyFrame with TFrame):

constructor TMyFrame.Create(AOwner: TComponent); override;
begin
  inerited;
  if (ClassType <> TFrame) and not (csDesignInstance in ComponentState) then
  begin
    if not InitInheritedComponent(Self, TFrame) then
      raise EResNotFound.CreateFmt('Resource %s not found', [ClassName]);
  end;
end;

The frame is added to the designer correctly, and additional properties are visible in Object Inspector, but running the application fails, because it complains that the components on the frame already exist.

So, my question is: what is the solution for having a Delphi IDE wizard which creates a derived frame from a custom frame (not form) with DFM, and shows its additional properties in Object Inspector?

BTW, I don't want to build the controls in the frame at runtime, because I need them to be available in design time too.

I hope somebody can make this thing clear to me.

Regards

EDITED:

These frames are actually used as pages for a wizard component. My wizard component creates them at runtime. I want the user to have an option in "New Item" menu to add a wizard page to project, and design its layout in IDE designer, and register it with my wizard component to be shown in the wizard. I am inheriting a base class from TFrame because my wizard pages should have some mandatory controls and some custom properties and events.

+1  A: 

Suspect this may have been fixed with Update 3, as I have no problems doing what you describe. I just created a TFrame descendent, added two properties (boolean and an event), added a panel etc. Created a new package with said frame registered, installed package, added package path to search path.

Then I created a new vcl app, created new form, added my new frame component and it is editable as per normal and both new properties are available in the Object Inspector.

If that does not work, I saw a workaround listed in QC 5230

Just Jules
+3  A: 

I have fairly extensively explored using TFrames (and their related inheritance) as a base for component development, and could elaborate on what I've found if it would be useful, but I have never had need of using RegisterCustomModule -- I just develop from TFrames directly, using normal frame inheritance, and then register the resulting "final" versions in a standard component registration unit. This seems to allow the best of both worlds (visual development + inheritance, plus component palette + object inspector capabilities).

There are a number of little tricks to it though, and snags to watch for, such as how you name the TFrame itself, making sure the DFM files use "object" or "inherited" properly on the first line, and, in general I have found it very beneficial for stability of complex inheritance trees to create a "base frame" that inherits from TFrame, but adds NOTHING to it... and then inherit all the others from there. (This seems particularly true when adding published properties etc).

Tell me more about why in particular you are wanting to use an IDE Wizard, and maybe if that is not cast in stone as an approach, I can be of more help.

Jamo
Thanks for the answer. I already tried registering my frame as a component rather than a custom module, but in that case, user won't be able to modify any of the child controls on the frame in IDE designer. I want the user to be able to edit child controls as well, but not delete them.
vcldeveloper
Some good points. When using multiple levels of frame inheritance it is important to open the base frame(s) in the IDE first. Otherwise, Delphi will have a heartburn since it has no knowledge of custom frames that are not otherwise contained in designtime package. This behavior provides some interesting insight into how the IDE designers work under the hood.
David Taylor
I edited my original post to explain why I need it this way.
vcldeveloper
A: 

Fro om my perspective Frames can be extremely useful tool for Delphi developer. However – I strongly recommend you place frames on forms (or on other frames) dynamically (eg from code not from forms designer). Frames on forms are being put info DFM and if you accidently move some buttons in such a statically placed frame, Delphi will put information about the change into forms DFM. Later changes to position of this button on the actual frame sometimes will not be transferred to placed frame.

smok1
Yes, but I need the controls on the frame to be editable in design-time too. If I create them at run-time, I will lose that.
vcldeveloper
No, create controls on a frame during designtime. During runtime only place whole frame on the form.
smok1
+2  A: 

Yes it can be done!

I have done precisely what you are asking in both Delphi 2007 and 2009 and can say that it works well for both versions irrespective of update level. I did run into some interesting problems along the way, but the final solution was very straight forward in hind sight (after beating my head against a wall for a day). More about this below in my answer.

I placed my custom frame into a runtime package referenced by my component's designtime package. This was necessary to allow the frame to be used in applications using runtime packages and also to allow the same frame to be used in other custom component packages. The designtime package also contained a custom module expert to add the frame to the File -> New -> Other... -> "New Items" gallery and to generate custom default module code. That later part was mainly a nicety to save coding time during actual usage and was a royal pain to get right. I chalk that up to learning curve since I could not find a complete example that matched my situation.

Code from the designtime DPK:

requires
  DesignIDE,
  FramePageListD11R,
...
contains
  FramePageListPkgReg in 'FramePageListPkgReg.pas',

And from FramePageListPkgReg

implementation

{$R FramePageListIcons.res}

procedure Register;
  begin
    ...
    RegisterCustomModule(TBaseDisplayFrame, TCustomModule);
    RegisterPackageWizard(TDisplayFrameModuleExpert.Create);
  end;

My frame implementation had no visual components on the frame since my goal was to introduce virtual methods, published properties, events and to implement a custom interface for my dynamic frame handling logic. Here is the contents of the DFM file and part of the PAS file for reference. Please ignore the IDisplayFrameEvent interface since it is irrelevant to the current topic of discussion.

object BaseDisplayFrame: TBaseDisplayFrame
  Left = 0
  Top = 0
  Width = 500
  Height = 300
  HorzScrollBar.Smooth = True
  HorzScrollBar.Style = ssHotTrack
  HorzScrollBar.Tracking = True
  VertScrollBar.Smooth = True
  VertScrollBar.Style = ssHotTrack
  VertScrollBar.Tracking = True
  TabOrder = 0
end

TBaseDisplayFrame = class(TFrame, IDisplayFrameEvent)
private
  FOnPageShow : TDisplayFrameEvent;
  FOnPageHide : TDisplayFrameEvent;
  ...
published
  ...
  property OnPageShow: TDisplayFrameEvent read FOnPageShow write FOnPageShow;
  property OnPageHide: TDisplayFrameEvent read FOnPageHide write FOnPageHide;
end;

As for getting the DFM to load, I did not need any special coding. What I did find is that there is a bug in the Delphi IDE that foo bars the inclusion of the form into the package. I do not recall all of the details, but the problem was easily fixed by manually editing the generated DPK code. Here is the relevant part of the DPK for reference. I suspect this is your main problem. Pay careful attention to the comment part of the line since that is critical to DFM inclusion in the package.

contains
  BaseDisplayFrameModule in 'BaseDisplayFrameModule.pas' {BaseDisplayFrame: TFrame};

Once everything is working, you can create frames as usual and then modify the PAS file and DFM to inherit from the frame. As Jamo noted, do not forget to change "object" to "inherited" in the first line in the DFM. The only quirk I have seen is the name of event handlers on the inherited frame does not conform to typical Delphi naming conventions, but rather derives from the base frame class. This is purely cosmetic and I have not taken the time to figure out if there is an easy fix.

Best of luck!

David Taylor
Thanks. Actually my problem is that in my case I have both visual components and additional properties in my frame.My current solution is similar to yours, that is, I have my frame in a runtime package, and I register an IDE wizard for it in a design-time package which requires my runtime package.I also have that comment in DPK file. When I call RegisterCustomModule, I have my properties in IDE, but not my visual controls on the frame. When I remove RegisterCustomModule, I have my visual controls, but not my properties in Object Inspector!
vcldeveloper
Hmm.. I have forms working inside of component packages with controls placed, but have not tried this with frames. I will have to give this a try. It may also be possible for you to create and place the controls in code at frame creation though that might be very inconvenient.
David Taylor
I have verified this does not work for frames. There seems to be some special logic at work to make this work for forms.
David Taylor
+1  A: 

Given your actual objective, have you looked at possibly using embedded forms instead? I've played with doing similar things to what you're trying to accomplish, using AppControls TacEmbeddedForm, combined with Greatis Form Designer (now also available in a slightly different form as TMS Scripter Studio), with positive results. Nothing finished yet, but it looks like a promising route for providing some of the runtime customization features you are looking for.

Jamo
Yes, I've been considering using embedded forms instead frames. Maybe it is the only option available for now.Regards
vcldeveloper
A: 

Note for a frame to appear in "Frames" in the standard view and to appear in inheritable items in add new.. the unit must appear in the DPR of the project you are working in!!

roger