Registering a custom Frame

2019-02-07 09:38发布

问题:

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:

Showing TFrame descendant's 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:

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.



回答2:

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!



回答3:

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



回答4:

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.



回答5:

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.