Visual [Compound] Component Design with TFrame [Edit]

- VCL for Win32 for CodeGear RAD Studio 2009 (Update 2, Version 12.0.3210.17555)
- CodeGear RAD Studio 2007 (December 2007 Update,  Version 11.0.2902.10471) 
- Borland Developer Studio 2006 (Update 2, Version 10.0.2558.35231)
I am trying to create a visual component with child controls . . . imagine if you will a control with a textbox and a button.  I would like to be able to visually design this control as if I were designing a form, being able to use the object browser to specify child-control property values and assign child-control events.  Any properties or events that I wanted exposed to containers of this new control/component, I would expect to have to manually create via public/published properties.
I am able to accomplish most of this as follows (sorry for perhaps stating the obvious):
1) Create a new package
2) Add a frame to the package, painting my controls on the frame and coding as necessary
3) Add the RegisterComponents code, and then 
4) Install the package
I am then able to create a new project and paint this new component on a form.
My problem is that in the project that uses the new component, not only to I have access to the [TFrame] components properties and events, but I also can access the child compents and their properties and events.  Additionally, I can even individually select the child compnents and delete them, move them, etc. (all within the project that is using the compnent).
Is there anyway that I can protect/hide the childe compnents and their events and only expose that I choose (via new publlic properties on the TFrame component)?  I have tried making the controls private, but they are still accessible at design time from the project that uses the compnent and the project will not run.  Additionally, if I make the child-control events private, then I receive errors that the events are missing when I attempt to paint the TFrame component on a separate project's form.
Chances are I'm lacking in a core understanding of how Delphi components work.  The fact that TFrame (or TCustomFrame) are not available options when choosing Component > New VCL Component... perhaps should have been my first clue that what I want to do is not possible (or at least not advisable).
If you have not guessed, I come from a Microsoft Visual Studio background and am trying to duplicate the UserControl functionality.
Any information or direction would be greatly appreciated.
Following is sample code of a TFrame component with a single button:
unit untTFrameComponent;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
  Dialogs, StdCtrls;
type
  TFrameComponent = class(TFrame)
    btnButton: TButton;
    procedure btnButton_Click(Sender: TObject);
  public
    { Public declarations }
  end;
procedure Register();
implementation
{$R *.dfm}
procedure Register();
begin
  RegisterComponents('Local Components', [TFrameComponent]);
end;
procedure TFrameComponent.btnButton_Click(Sender: TObject);
begin
  MessageDlg('TFrameCompnent.btnButton_ButtonClick', mtCustom, [mbOK], 0);
end;
end.
Thanks!
Jonathan Weekes
Edited by: Jonathan Weekes on Feb 26, 2009 10:55 AM
0
Jonathan
2/26/2009 4:55:42 PM
📁 public.delphi.vcl.writing
📃 510 articles.
⭐ 0 followers.

💬 21 Replies
👁️‍🗨️ 2781 Views


Jonathan Weekes :
> I am trying to create a visual component with child controls . . . imagine if you will a control with a textbox and a button.  I would like to be able to visually design this control as if I were designing a form, being able to use the object browser to specify child-control property values and assign child-control events.  Any properties or events that I wanted exposed to containers of this new control/component, I would expect to have to manually create via public/published properties.
> <SNIP> 
> Jonathan Weekes
You did not say which version of Delphi you are using, you may look
http://cc.codegear.com/item.aspx?id=24236
This works for Delphi 6 - 2006
0
Gokhan
2/23/2009 9:43:47 AM
"Jonathan Weekes" wrote in message news:83652@forums.codegear.com...
....
>
> Is there anyway that I can protect/hide the childe compnents and their 
> events and only expose that I choose (via new publlic properties on the 
> TFrame component)?  I have tried making the controls private, but they are 
> still accessible at design time from the project that uses the compnent 
> and the project will not run.  Additionally, if I make the child-control 
> events private, then I receive errors that the events are missing when I 
> attempt to paint the TFrame component on a separate project's form.
>
What you are doing seems perfectly correct and what you want is possible. 
The problem (!) you are experiencing results from the "inline" keyword in 
the .dfm of you form that has the frame on. Sorry, haven't time to figure 
out exactly when and where the rules are applied, but generally (I think) if 
you right-click on a frame and "add to palette", then drop it onto a form, 
Delphi uses "inline", but if it is compiled into a separate design-time 
package the frame will be inserted as an "object" on the form with the 
effects you desire. Occasionally I have found that frames revert back to 
"inline" though (!?) - but you will quickly get run-time errors.
To fix:
Edit your .dfm (press Alt+F12) on the form. Look for:
inline FrameComponent1 : TFrameComponent
  Left = 6
  Top = 6
  Width = 211
  Height = 182
  inherited btnButton: TButton
    Width = 75
    Height = 25
   end
end
Change to "object" and remove the inherited controls.
object FrameComponent1 : TFrameComponent
  Left = 6
  Top = 6
  Width = 211
  Height = 182
end
Press Alt+F12 to re-create the form and allow Delphi to remove any invalid 
declarations.
Good luck,
Andrew
0
Andrew
2/23/2009 3:26:12 PM
Thanks for the replies, G?khan and Andrew.  I'm investigating your comments now and update update soon.
I am working with a VCL for Win32 project in CodeGear RAD Studio 2009 (Update 2, Version 12.0.3210.17555).  Testing in 2007 and 2006 now (and checking out G?khan's linked project and the Andrew's inline change).
Thanks again!
Jon
0
Jonathan
2/23/2009 3:40:30 PM
> {quote:title=Andrew Venmore wrote:}{quote}
>
> To fix:
> 
> Edit your .dfm (press Alt+F12) on the form. Look for:
> 
> inline FrameComponent1 : TFrameComponent
>   Left = 6
>   Top = 6
>   Width = 211
>   Height = 182
>   inherited btnButton: TButton
>     Width = 75
>     Height = 25
>    end
> end
> 
> Change to "object" and remove the inherited controls.
> 
> object FrameComponent1 : TFrameComponent
>   Left = 6
>   Top = 6
>   Width = 211
>   Height = 182
> end
Thanks for the suggestion, *Andrew*, but the .dfm for the form that references my component (TFrameComponent) is already using "object" in the reference/declaration (not sure of the proper term here) and there are no inherited controls listed.

> {quote:title=G?khan Ers#mer wrote:}{quote}
> 
> You did not say which version of Delphi you are using, you may look
> 
> http://cc.codegear.com/item.aspx?id=24236
> 
> This works for Delphi 6 - 2006
The original post was based on VCL for Win32 for CodeGear RAD Studio 2009 (Update 2, Version 12.0.3210.17555), but I can reproduce the (or my) problem in 2007 and 2006.
Thanks, *G?khan*, for the link to the custom containers project.  Unfortunately, I am having the same problem with it as I am with my project: 
 - All published [ancestor] TFrame properties and events are automatically exposed 
 - More importantly (of greater concern), all child components and their published properties and events are exposed
 - Most importantly (and disturbing), from the project that uses the component, I am able to modify the child component properties (i.e., position, size, visiblity) and code against (assign) their events.
*I would like to HIDE the child compopnents from the project/container that uses the compound component I am creating, and selectively expose properties and events.  If possible, I would also like to selectively expose (or hide) the component's TFrame ancestor properties and events*.
Fortunately, this is an inhouse component and will likely only be local to one project; however, I would really like to understand this and do it the right way.
Any other ideas?
0
Jonathan
2/24/2009 10:01:41 PM
Hi Jonathan,
What do the DPK files for your runtime and design-time component packages 
look like?  Also, what does the RegisterComponents call look like, and where 
is it located?  In the component unit, or in a separate registration unit.
From your description, it sounds like you are "dropping" your frame 
component the same way one normally drops an inline frame onto a form.  That 
is, using the Frames component icon on the Standard page instead of clicking 
on the component icon in the page that you specified in the 
RegisterComponents call.
One more thing to check, does your code make any calls to SetSubComponent? 
This will also cause the behavior you are describing.
Ray


> - All published [ancestor] TFrame properties and events are automatically 
> exposed
> - More importantly (of greater concern), all child components and their 
> published properties and events are exposed
> - Most importantly (and disturbing), from the project that uses the 
> component, I am able to modify the child component properties (i.e., 
> position, size, visiblity) and code against (assign) their events.
>
> *I would like to HIDE the child compopnents from the project/container 
> that uses the compound component I am creating, and selectively expose 
> properties and events.  If possible, I would also like to selectively 
> expose (or hide) the component's TFrame ancestor properties and events*.
>
> Fortunately, this is an inhouse component and will likely only be local to 
> one project; however, I would really like to understand this and do it the 
> right way.
>
> Any other ideas?
0
Ray
2/25/2009 6:15:25 AM
> Thanks, *G?khan*, for the link to the custom containers project.  Unfortunately, I am having the same problem with it as I am with my project: 
> 
>  - All published [ancestor] TFrame properties and events are automatically exposed 
>  - More importantly (of greater concern), all child components and their published properties and events are exposed
>  - Most importantly (and disturbing), from the project that uses the component, I am able to modify the child component properties (i.e., position, size, visiblity) and code against (assign) their events.
> 
> *I would like to HIDE the child compopnents from the project/container that uses the compound component I am creating, and selectively expose properties and events.  If possible, I would also like to selectively expose (or hide) the component's TFrame ancestor properties and events*.
> 
> Fortunately, this is an inhouse component and will likely only be local to one project; however, I would really like to understand this and do it the right way.
> 
> Any other ideas?
I think, there is no way which you can design your compound component 
RAD way and hide child components. Delphi's streaming system will not 
work without published properties. So I can suggest you installing 
GExperts, (http://gexperts.org) visually design your frame/panel, select 
all components on frame, select "Components to Code" from GExperts menu, 
paste this code into your frame's constructor. Move child components 
references from published to private. Add properties/events you want and 
registration code. Install your new compound component. Enjoy :)
Sorry, I dont know a better way than this.
0
Gokhan
2/25/2009 9:16:36 AM
"G?khan Ers#mer" <sky_khan@y.a.h.o.o.com> wrote in message 
news:85178@forums.codegear.com...
>>...
> I think, there is no way which you can design your compound component
> RAD way and hide child components. Delphi's streaming system will not
> work without published properties. So I can suggest you installing
>
Yes, it can be easily done and I use it on a daily basis. The frame must be 
registered as a component in a separate design-time package and it works 
exactly how you would expect it to.
I agree with Ray - there is something missing in the information on how your 
design-time package/RegisterComponents/form is configured.
Andrew
0
Andrew
2/25/2009 12:32:13 PM
Andrew Venmore yazmış:
> "G?khan Ers#mer" <sky_khan@y.a.h.o.o.com> wrote in message 
> news:85178@forums.codegear.com...
>>> ...
>> I think, there is no way which you can design your compound component
>> RAD way and hide child components. Delphi's streaming system will not
>> work without published properties. So I can suggest you installing
>>
> 
> Yes, it can be easily done and I use it on a daily basis. The frame must be 
> registered as a component in a separate design-time package and it works 
> exactly how you would expect it to.
> 
> I agree with Ray - there is something missing in the information on how your 
> design-time package/RegisterComponents/form is configured.
Question is belong to Jonathan, not me.
> 
> Andrew
Yes,you can easily design a frame and register it with a design-time 
package. BUT, your frame's child components will be published properties 
of this class. So these child components and their properties/events 
will be accessible from code/code completion when you use your compound 
component on a form.
e.g:
TForm1 = class(TForm)
   MyCompoundComponent: TMyCompoundComponent;
....
procedure TForm1.Create(aOwner:TComponent);
begin
   inherited;
   MyCompoundComponent.Edit1.OnClick:=...
end;
I was thinking Jonathan does not want these child components is being 
accessible outside of TMyCompoundComponent or its derivatives code. So, 
either I did misunderstand Jonathan's request or you know a way to 
visually design a frame and register it as a component without 
publishing their child components ?
Gokhan Ersumer
0
Gokhan
2/25/2009 1:36:10 PM
"G?khan Ers#mer" <sky_khan@y.a.h.o.o.com> wrote in message 
news:85238@forums.codegear.com...
>>
> ...
> Question is belong to Jonathan, not me.
>
Yes, sorry, was trying to talk to two people at once.
> ...
> I was thinking Jonathan does not want these child components is being
> accessible outside of TMyCompoundComponent or its derivatives code. So,
> either I did misunderstand Jonathan's request or you know a way to
>
I think that I may have misunderstood the request. I understood that the 
child controls were accessible at design-time, selectable and properties 
available in the object inspector. You are quite correct that hiding them 
from programmatic access is not possible. Sorry.
Andrew
0
Andrew
2/25/2009 3:02:30 PM
Hi Andrew,
I do not believe that you have misunderstood the original post.  The problem 
was that the sub-components were selectable in the Form Designer and thus 
could be moved, modified, or even deleted.  This suggests that it was not 
dropped on the form as a component, but as an inline frame, or that calls to 
SetSubComponent have been specifically made to surface the sub-components in 
the form designer.
Ray

> I think that I may have misunderstood the request. I understood that the
> child controls were accessible at design-time, selectable and properties
> available in the object inspector. You are quite correct that hiding them
> from programmatic access is not possible. Sorry.
>
> Andrew
0
Ray
2/25/2009 4:29:23 PM
Hi Jonathan,
First of all, you can indeed accomplish the behavior you want using TFrame 
to visually design your components.  I do it quite often and GExperts is not 
needed to accomplish this.
The first thing that I suggest doing is to modify your DPK file and change 
the contains clause to the following:
contains
  untTFrameComponent in 'untTFrameComponent.pas';
The {FrameComponent: TFrame} comment is typically present in project files, 
and is processed by the FormDesigner and Project Manager. I suspect that 
this is what is causing your problems.  This was most likely added when you 
added the frame to your package using the IDE.  I manually create my 
component DPKs and so I don't have this extra comment in my package files.
Ray

> - I created and registered the comonent in a separate design-time package.
>
> - There is very little in the .dpk:
>
>> requires
>>   rtl,
>>   vcl;
>>
>> contains
>>   untTFrameComponent in 'untTFrameComponent.pas' {FrameComponent: 
>> TFrame};
>
> - The RegisterComponents call is simply
>
>> RegisterComponents('Local Components', [TFrameComponent]);.
>
>  It is included in the TFrameComponent's unit, which is the only file in 
> the compnent's design-time package.  I think this is shown in my original 
> post above.
>
> - When painting the component on a form, I am double clicking on my 
> installed component (TFrameComponent) from the Tool Pallette.  I am not 
> familliar with SetSubComponent and am not explicitly or intentionally 
> calling it.
>
> (hopefully I answered all the questions ;p)
>
> I have about come to the conclusion that the behavior I am wanting (only 
> selectively exposing the component's child components' properties and 
> events) is only possible using something like GExperts as G?khan suggested 
> or through creating an ActiveX/COM component, similar to .OCX's I used to 
> create in Visual Studio.  I'm not sure I'm ready to go there with Delphi, 
> though.
>
> Unless there is another recommended way of visually designing and creating 
> compound compnents and if it is considered "best practice" to have all the 
> child components' published events and properties exposed to clients, to 
> be accessed and manipulated as desired, then I guess I am OK with 
> deploying compnents like this; however, especially when designing data 
> related components, this certainly does not seem very safe to me.
>
> Thank you all so much for your responses and time spent thinking about 
> this . . . and I sure hope I'm not filling up your inboxes with watch 
> emails everytime I edit this post.
>
> Edited by: Jonathan Weekes on Feb 25, 2009 12:55 PM
0
Ray
2/25/2009 7:17:40 PM
G?khan, your understanding is correct (as follows):
> {quote:title=G?khan Ers#mer wrote:}{quote}
> . . . Jonathan does NOT want these child components [to be]
> accessible outside of TMyCompoundComponent or its derivatives code.
Additionally, Ray is correct in saying . . . 
> {quote:title=Ray Konopka wrote:}{quote}
> I do not believe that you have misunderstood the original post. The problem 
> was that the sub-components were selectable in the Form Designer and thus 
> could be moved, modified, or even deleted.
- I created and registered the comonent in a separate design-time package.
- There is very little in the .dpk:
  
> requires
>   rtl,
>   vcl;
>
> contains
>   untTFrameComponent in 'untTFrameComponent.pas' {FrameComponent: TFrame};
- The RegisterComponents call is simply   
> RegisterComponents('Local Components', [TFrameComponent]);.  
  It is included in the TFrameComponent's unit, which is the only file in the compnent's design-time package.  I think this is shown in my original post above.
- When painting the component on a form, I am double clicking on my installed component (TFrameComponent) from the Tool Pallette.  I am not familliar with SetSubComponent and am not explicitly or intentionally calling it.
(hopefully I answered all the questions ;p)
I have about come to the conclusion that the behavior I am wanting (only selectively exposing the component's child components' properties and events) is only possible using something like GExperts as G?khan suggested or through creating an ActiveX/COM component, similar to .OCX's I used to create in Visual Studio.  I'm not sure I'm ready to go there with Delphi, though.
Unless there is another recommended way of visually designing and creating compound compnents or if it is considered "best practice" to have all the child components' published events and properties exposed to clients, to be accessed and manipulated as desired, then I guess I am OK with deploying compnents like this; however, especially when designing data related components, this certainly does not seem very safe to me.
Thank you all so much for your responses and time spent thinking about this . . . and I sure hope I'm not filling up your inboxes with watch emails everytime I edit this post.
Edited by: Jonathan Weekes on Feb 25, 2009 12:55 PM
0
Jonathan
2/25/2009 11:04:34 PM
Hi Jonathan,
Given your description, then there must be something going on in your 
component code that is causing the behavior. As I noted before, I have 
created several Frame-based components and the main advantage of them is 
that you can prevent users of the component from selecting the individual 
sub-components.
In order to determine what is causing the behavior you are seeing, I would 
suggest that you send me the code for your frame component, along with the 
DPK files.  I'd be curious to see if I can reproduce the problem on my 
system.  Please feel free to send the source to support@raize.com and I'll 
take a quick look.
Ray

> Hi again, Ray.  As you suggested, I removed the {TFrameCompnent: TFrame} 
> comment from the contains clause.  Unfortunately, I see no change in 
> behavior; from the client project I am still able to access the child 
> compnents' properties and events via the object browser and code.
>
> Not entirely unexpected (and with or without the above change), changes 
> made (from the client project) to the child compnents via the object 
> browser are not persistent and are reset when the project is run or when 
> the form is reloaded in design mode.  Additionally, although I am able to 
> access the child component events in the object browser and enter code for 
> them, these events do not fire at run-time.
>
> Regardless, the child components' properties and events are accessible 
> form the code and may be altered and asigned programmatically, which is 
> not desired--i.e. TFrameComponent.btnButton.Caption := 'EXPOSED' or 
> TFrameComponent.btnButton.OnClick := self.Button1Click.
>
> Edited by: Jonathan Weekes on Feb 25, 2009 4:57 PM
0
Ray
2/26/2009 4:08:31 AM
Hi Jonathan,
The more I think about your issue, another question that comes to mind is 
just how you have setup your client project.  That is, are you doing 
anything with the package that you put the frame component into? For 
example, are you adding the frame component package to the client project?
I just have a feeling that there is something that you have done in setting 
this up that is causing the problems.
Ray

> Hi again, Ray.  As you suggested, I removed the {TFrameCompnent: TFrame} 
> comment from the contains clause.  Unfortunately, I see no change in 
> behavior; from the client project I am still able to access the child 
> compnents' properties and events via the object browser and code.
>
> Not entirely unexpected (and with or without the above change), changes 
> made (from the client project) to the child compnents via the object 
> browser are not persistent and are reset when the project is run or when 
> the form is reloaded in design mode.  Additionally, although I am able to 
> access the child component events in the object browser and enter code for 
> them, these events do not fire at run-time.
>
> Regardless, the child components' properties and events are accessible 
> form the code and may be altered and asigned programmatically, which is 
> not desired--i.e. TFrameComponent.btnButton.Caption := 'EXPOSED' or 
> TFrameComponent.btnButton.OnClick := self.Button1Click.
>
> Edited by: Jonathan Weekes on Feb 25, 2009 4:57 PM
0
Ray
2/26/2009 4:21:49 AM
Ray -  I have just sent the code to you.  Thank you!!  - Jon
0
Jonathan
2/26/2009 4:54:02 PM
Hi Jonathan,
Thanks for sending the component source code.  I rebuilt the component 
package and loaded it into Delphi.  I then created a new application and 
dropped an instance of the component onto the form.
The component worked as I expected.  That is, I was not able to select any 
of the individual controls by clicking on them in the Form Designer.  Also, 
I did not see any of the sub-components listed in the Object Inspector, 
which is also what I expected.
However, I believe I finally understand what you are running into.  The 
sub-components *DO* show up in the Structure Pane.  And as you have pointed 
out, when you select them in the Structure Pane, you can then edit their 
properties in the Object Inspector.  I was also very surprised to see that 
you can delete the controls as well, which can cause all kinds of problems 
in the Form Designer.  Clearly, this is not desired behavior.
Fortunately, it is possible to alter the behavior of the Structure Pane by 
registering a new sprig class for your frame component.  First, you need to 
modify the component package as follows:
package TFrame2006;
{$R *.res}
(*
Other compiler directives deleted
*)
requires
  rtl,
  vcl,
  DesignIDE;     // Add this line
contains
  untTFrame2006 in 'untTFrame2006.pas';
end.

Next, you need to modify the Register procedure in your component unit to 
the following:
uses
  TreeIntf;
procedure Register();
begin
  // Register component and add to Tool Palette
  RegisterComponents('TFrame Component Test', [TFrame2006]);
  RegisterSprigType( TFrame2006, TComponentSprig );
end;

By default, the Delphi IDE uses a special sprig class when displaying 
controls that can contain other controls such as a TPanel, a TForm, or a 
TFrame.  The RegisterSprigType call above forces the simple TComponentSprig 
to be used in the Structure Pane.  After rebuilding the package and 
reloading into Delphi, you will notice that you can no longer select the 
sub-components of the frame component in the Structure Pane.
Ray
0
Ray
2/26/2009 8:27:07 PM
Yes, your new understanding of my problem is correct: from the client project, I am [unfortunately] able to select the component's sub-components from the Structure Pane.  Sorry for not making that more clear before.
I tried registering the component using the simple TSprigComponent as you suggested and had mixed results.  
I updated the package as you suggested, adding DesignIDE to the package/project file's requires clause and adding the TreeIntf to the component unit's uses clause as well as adding the RegisterSprigType to the component's Register procedure.  This compliled and installed successfully.  I then opened the client project that uses the compent and indeed the sub-components were no longer accessible from the Structure Pane.
Unfortunately, when I attempted to compile the client project, I received an error that the unit for the component could not find TreeIntf.dcu.  I could find only a TreeIntf*.pas* file, in C:\Program Files\Borland\BDS\4.0\source\ToolsAPI (no .dcu); I added this path to the client project's search path, but upon compiling the client project I then receive an error that the DesignEditor unit could not find Proxies.dcu.  I could find only a Proxies*.hpp* file, in C:\Program Files\Borland\BDS\4.0\include\vcl;
 I added this to the client project's search path, but still got the "File not found: 'Proxies.dcu'" when compiling the client project.  Same results in both 2006 and 2009.  Also, it bothers me that I should have to add these references to all projects that reference the component.
Perhaps there is a problem with my installations of 2006 and 2009, but more than likely it has to do with my unclear understanding of package/.bpl installations and why a the source unit is still necessary when you have already successfully created the .bpl.  
I'll continue researching to see what I can figure out.
Thank you again for your assistance and direction!
> {quote:title=Ray Konopka wrote:}{quote}
> Hi Jonathan,
> 
> Thanks for sending the component source code.  I rebuilt the component 
> package and loaded it into Delphi.  I then created a new application and 
> dropped an instance of the component onto the form.
> 
> The component worked as I expected.  That is, I was not able to select any 
> of the individual controls by clicking on them in the Form Designer.  Also, 
> I did not see any of the sub-components listed in the Object Inspector, 
> which is also what I expected.
> 
> However, I believe I finally understand what you are running into.  The 
> sub-components *DO* show up in the Structure Pane.  And as you have pointed 
> out, when you select them in the Structure Pane, you can then edit their 
> properties in the Object Inspector.  I was also very surprised to see that 
> you can delete the controls as well, which can cause all kinds of problems 
> in the Form Designer.  Clearly, this is not desired behavior.
> 
> Fortunately, it is possible to alter the behavior of the Structure Pane by 
> registering a new sprig class for your frame component.  First, you need to 
> modify the component package as follows:
> 
> package TFrame2006;
> 
> {$R *.res}
> (*
> Other compiler directives deleted
> *)
> 
> requires
>   rtl,
>   vcl,
>   DesignIDE;     // Add this line
> 
> contains
>   untTFrame2006 in 'untTFrame2006.pas';
> 
> end.
> 
> 
> Next, you need to modify the Register procedure in your component unit to 
> the following:
> 
> uses
>   TreeIntf;
> 
> procedure Register();
> begin
>   // Register component and add to Tool Palette
>   RegisterComponents('TFrame Component Test', [TFrame2006]);
>   RegisterSprigType( TFrame2006, TComponentSprig );
> end;
> 
> 
> By default, the Delphi IDE uses a special sprig class when displaying 
> controls that can contain other controls such as a TPanel, a TForm, or a 
> TFrame.  The RegisterSprigType call above forces the simple TComponentSprig 
> to be used in the Structure Pane.  After rebuilding the package and 
> reloading into Delphi, you will notice that you can no longer select the 
> sub-components of the frame component in the Structure Pane.
> 
> Ray
Edited by: Jonathan Weekes on Feb 27, 2009 12:01 PM
0
Jonathan
2/27/2009 6:02:09 PM
Hi Jonathan,
The compiling problem you are now facing is a result of creating a component 
package that is serving as both a runtime package *and* a design package.
In order to compile correctly, you will need to create two packages.  The 
first package will be a runtime only package and will only contain the frame 
component unit.  The Register procedure along with the reference to TreeIntf 
will need to be moved to a separate registration unit.  Also, the DesignIDE 
reference in the requires clause of the runtime package will need to be 
removed.
The design package will contain the registration unit (which contains the 
Register procedure), and the requires clause of the design package will list 
the DesignIDE package.  Also, the design package will also require the 
runtime package you created above.
For example,
// Runtime package
package TFrame2006;
{$R *.res}
(*
other compiler directives
*)
{$RUNONLY}
requires
  rtl,
  vcl;
contains
  untTFrame2006 in 'untTFrame2006.pas';
end.

// Design Package
package TFrame2006_Design;
{$R *.res}
{$R TFrame2006_Reg.dcr}
{$ALIGN 8}
(*
other compiler directive
*)
{$DESIGNONLY}
requires
  vcl,
  DesignIDE,
  TFrame2006;
contain
  TFrame2006_Reg;
end.

If you decide to create a component icon for the frame component, it should 
be placed in a dcr file that is linked into the design package. This is what 
the {$R TFrame2006_Reg.dcr} statement is for.
Also, the TFrame2006_Reg unit is the registration unit that simply contains 
the Register procedure and registers both the frame component and the sprig. 
Also note that the design package is dependent on the runtime package.
Ray


> Yes, your new understanding of my problem is correct: from the client 
> project, I am [unfortunately] able to select the component's 
> sub-components from the Structure Pane.  Sorry for not making that more 
> clear before.
>
> I tried registering the component using the simple TSprigComponent as you 
> suggested and had mixed results.
>
> I updated the package as you suggested, adding DesignIDE to the 
> package/project file's requires clause and adding the TreeIntf to the 
> component unit's uses clause as well as adding the RegisterSprigType to 
> the component's Register procedure.  This compliled and installed 
> successfully.  I then opened the client project that uses the compent and 
> indeed the sub-components were no longer accessible from the Structure 
> Pane.
>
> Unfortunately, when I attempted to compile the client project, I received 
> an error that the unit for the component could not find TreeIntf.dcu.  I 
> could find only a TreeIntf*.pas* file, in C:\Program 
> Files\Borland\BDS\4.0\source\ToolsAPI (no .dcu); I added this path to the 
> client project's search path, but upon compiling the client project I then 
> receive an error that the DesignEditor unit could not find Proxies.dcu.  I 
> could find only a Proxies*.hpp* file, in C:\Program 
> Files\Borland\BDS\4.0\include\vcl;
> I added this to the client project's search path, but still got the "File 
> not found: 'Proxies.dcu'" when compiling the client project.  Same results 
> in both 2006 and 2009.  Also, it bothers me that I should have to add 
> these references to all projects that reference the component.
>
> Perhaps there is a problem with my installations of 2006 and 2009, but 
> more than likely it has to do with my unclear understanding of 
> package/.bpl installations and why a the source unit is still necessary 
> when you have already successfully created the .bpl.
>
> I'll continue researching to see what I can figure out.
>
> Thank you again for your assistance and direction!
>
>> {quote:title=Ray Konopka wrote:}{quote}
>> Hi Jonathan,
>>
>> Thanks for sending the component source code.  I rebuilt the component
>> package and loaded it into Delphi.  I then created a new application and
>> dropped an instance of the component onto the form.
>>
>> The component worked as I expected.  That is, I was not able to select 
>> any
>> of the individual controls by clicking on them in the Form Designer. 
>> Also,
>> I did not see any of the sub-components listed in the Object Inspector,
>> which is also what I expected.
>>
>> However, I believe I finally understand what you are running into.  The
>> sub-components *DO* show up in the Structure Pane.  And as you have 
>> pointed
>> out, when you select them in the Structure Pane, you can then edit their
>> properties in the Object Inspector.  I was also very surprised to see 
>> that
>> you can delete the controls as well, which can cause all kinds of 
>> problems
>> in the Form Designer.  Clearly, this is not desired behavior.
>>
>> Fortunately, it is possible to alter the behavior of the Structure Pane 
>> by
>> registering a new sprig class for your frame component.  First, you need 
>> to
>> modify the component package as follows:
>>
>> package TFrame2006;
>>
>> {$R *.res}
>> (*
>> Other compiler directives deleted
>> *)
>>
>> requires
>>   rtl,
>>   vcl,
>>   DesignIDE;     // Add this line
>>
>> contains
>>   untTFrame2006 in 'untTFrame2006.pas';
>>
>> end.
>>
>>
>> Next, you need to modify the Register procedure in your component unit to
>> the following:
>>
>> uses
>>   TreeIntf;
>>
>> procedure Register();
>> begin
>>   // Register component and add to Tool Palette
>>   RegisterComponents('TFrame Component Test', [TFrame2006]);
>>   RegisterSprigType( TFrame2006, TComponentSprig );
>> end;
>>
>>
>> By default, the Delphi IDE uses a special sprig class when displaying
>> controls that can contain other controls such as a TPanel, a TForm, or a
>> TFrame.  The RegisterSprigType call above forces the simple 
>> TComponentSprig
>> to be used in the Structure Pane.  After rebuilding the package and
>> reloading into Delphi, you will notice that you can no longer select the
>> sub-components of the frame component in the Structure Pane.
>>
>> Ray
>
> Edited by: Jonathan Weekes on Feb 27, 2009 12:01 PM
0
Ray
2/27/2009 7:35:54 PM
Thank you, Ray!  Creating the separate run-time and design-time packages, in addition to using the RegisterSprigType, is the answer to not allowing selection of a custom component's child components via the Structure Pane.  Although the child components are still accessible in the code, which is less than ideal, I have at least made significant progress--and learned quite a lot in the meantime.
Now, after all of this, I am reconsidering whether or not I want to use components.  My goal was to achieve code re-use with minimal complexity; creating and installing design- and run-time packages for each component and keeping all the files together and in-synch for installation on mutltiple devolopers' computers could prove to be quite a challenge.  
Prior to looking into actual custom components, I was creating pseudo-components on a TForm and then programmatically inserting the form into a container on the main form at run-time.  This actually worked quite well; however, it required wiring events in both the host and the client.  With actual custom components, the component's exposed events were "pre-wired" for the client.  I think manually wiring the client will likely be easier and certainly require fewer files to keep track of (.pas & .dfm vs. .p
as, .dfm, and two .dpk's).  I welcome any comment on the TForm vs. component implentation.
Although I possibly will not use this on this project, I am certain it will be very useful in the future (for both in-house designed and 3rd-party components).  Thank you so much again for all of your help, Ray (and G?hkan and Andrew).
Jonathan Weekes

> {quote:title=Ray Konopka wrote:}{quote}
> Hi Jonathan,
> 
> The compiling problem you are now facing is a result of creating a component 
> package that is serving as both a runtime package *and* a design package.
> 
> In order to compile correctly, you will need to create two packages.  The 
> first package will be a runtime only package and will only contain the frame 
> component unit.  The Register procedure along with the reference to TreeIntf 
> will need to be moved to a separate registration unit.  Also, the DesignIDE 
> reference in the requires clause of the runtime package will need to be 
> removed.
> 
> The design package will contain the registration unit (which contains the 
> Register procedure), and the requires clause of the design package will list 
> the DesignIDE package.  Also, the design package will also require the 
> runtime package you created above.
> 
> For example,
> 
> // Runtime package
> 
> package TFrame2006;
> 
> {$R *.res}
> (*
> other compiler directives
> *)
> {$RUNONLY}
> 
> requires
>   rtl,
>   vcl;
> 
> contains
>   untTFrame2006 in 'untTFrame2006.pas';
> 
> end.
> 
> 
> // Design Package
> 
> package TFrame2006_Design;
> 
> {$R *.res}
> {$R TFrame2006_Reg.dcr}
> {$ALIGN 8}
> (*
> other compiler directive
> *)
> {$DESIGNONLY}
> 
> requires
>   vcl,
>   DesignIDE,
>   TFrame2006;
> 
> contain
>   TFrame2006_Reg;
> 
> end.
> 
> 
> If you decide to create a component icon for the frame component, it should 
> be placed in a dcr file that is linked into the design package. This is what 
> the {$R TFrame2006_Reg.dcr} statement is for.
> 
> Also, the TFrame2006_Reg unit is the registration unit that simply contains 
> the Register procedure and registers both the frame component and the sprig. 
> Also note that the design package is dependent on the runtime package.
> 
> Ray
0
Jonathan
2/27/2009 11:43:08 PM
Hi Jonathan,
You're welcome.  Creating components is a great way to re-use code.  It may 
seem like a little extra overhead when you are dealing with just one 
component.  But your runtime package and design-time package can contain 
multiple components.  That is, you do not need to create a separate set of 
packages for each component you create.
However, even if you do create separate packages, the package structure is 
essentially the same for each one.  Once you create a set, it's a snap to 
create another.
As for accessing the sub-components from within your code, this is the price 
you pay for being able to visually design the composite.  The Form/Frame 
Designer needs to access the sub-components, and thus the controls are 
published.  You could probably try to create a dummy ReadOnly property to 
prevent a user from doing anything with the control, but frankly I do not 
think this is worth it.
Good Luck to you.
Ray

> Thank you, Ray!  Creating the separate run-time and design-time packages, 
> in addition to using the RegisterSprigType, is the answer to not allowing 
> selection of a custom component's child components via the Structure Pane. 
> Although the child components are still accessible in the code, which is 
> less than ideal, I have at least made significant progress--and learned 
> quite a lot in the meantime.
>
> Now, after all of this, I am reconsidering whether or not I want to use 
> components.  My goal was to achieve code re-use with minimal complexity; 
> creating and installing design- and run-time packages for each component 
> and keeping all the files together and in-synch for installation on 
> mutltiple devolopers' computers could prove to be quite a challenge.
>
> Prior to looking into actual custom components, I was creating 
> pseudo-components on a TForm and then programmatically inserting the form 
> into a container on the main form at run-time.  This actually worked quite 
> well; however, it required wiring events in both the host and the client. 
> With actual custom components, the component's exposed events were 
> "pre-wired" for the client.  I think manually wiring the client will 
> likely be easier and certainly require fewer files to keep track of (.pas 
> & .dfm vs. .p
> as, .dfm, and two .dpk's).  I welcome any comment on the TForm vs. 
> component implentation.
>
> Although I possibly will not use this on this project, I am certain it 
> will be very useful in the future (for both in-house designed and 
> 3rd-party components).  Thank you so much again for all of your help, Ray 
> (and G?hkan and Andrew).
>
> Jonathan Weekes
>
>
>> {quote:title=Ray Konopka wrote:}{quote}
>> Hi Jonathan,
>>
>> The compiling problem you are now facing is a result of creating a 
>> component
>> package that is serving as both a runtime package *and* a design package.
>>
>> In order to compile correctly, you will need to create two packages.  The
>> first package will be a runtime only package and will only contain the 
>> frame
>> component unit.  The Register procedure along with the reference to 
>> TreeIntf
>> will need to be moved to a separate registration unit.  Also, the 
>> DesignIDE
>> reference in the requires clause of the runtime package will need to be
>> removed.
>>
>> The design package will contain the registration unit (which contains the
>> Register procedure), and the requires clause of the design package will 
>> list
>> the DesignIDE package.  Also, the design package will also require the
>> runtime package you created above.
>>
>> For example,
>>
>> // Runtime package
>>
>> package TFrame2006;
>>
>> {$R *.res}
>> (*
>> other compiler directives
>> *)
>> {$RUNONLY}
>>
>> requires
>>   rtl,
>>   vcl;
>>
>> contains
>>   untTFrame2006 in 'untTFrame2006.pas';
>>
>> end.
>>
>>
>> // Design Package
>>
>> package TFrame2006_Design;
>>
>> {$R *.res}
>> {$R TFrame2006_Reg.dcr}
>> {$ALIGN 8}
>> (*
>> other compiler directive
>> *)
>> {$DESIGNONLY}
>>
>> requires
>>   vcl,
>>   DesignIDE,
>>   TFrame2006;
>>
>> contain
>>   TFrame2006_Reg;
>>
>> end.
>>
>>
>> If you decide to create a component icon for the frame component, it 
>> should
>> be placed in a dcr file that is linked into the design package. This is 
>> what
>> the {$R TFrame2006_Reg.dcr} statement is for.
>>
>> Also, the TFrame2006_Reg unit is the registration unit that simply 
>> contains
>> the Register procedure and registers both the frame component and the 
>> sprig.
>> Also note that the design package is dependent on the runtime package.
>>
>> Ray
0
Ray
2/28/2009 3:20:23 AM
> If possible, I would also like to selectively expose (or hide) the component's TFrame ancestor properties and events
If would be possible ho inherit from TCustomFrame instead of TFrame, and continue have the ability to use the designer to design your frame, alf of this was solved. As this is not possible (as far as I know) you can, at least, hide the frame properties from object inspector using the method "UnlistPublishedProperty".
This will only hide them from the inspector, but the frame properties will still be accessible via code, and stored in the .DFM file.
Example:
procedure Register;
begin
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'Align');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'AlignWithMargins');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'Anchors');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'AutoScroll');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'AutoSize');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'BiDiMode');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'Color');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'Constraints');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'Ctl3D');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'Cursor');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'DockSite');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'DragCursor');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'DragKind');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'DragMode');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'Font');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'HelpContext');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'HelpKeyword');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'HelpType');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'Hint');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'HorzScrollBar');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'Margins');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'Padding');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'ParentBackGround');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'ParentBiDiMode');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'ParentColor');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'ParentCtl3D');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'ParentFont');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'ParentShowHint');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'PopupMenu');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'ShowHint');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'TabOrder');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'TabStop');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'VertScrollBar');
{Event's properties can't be hidden by this method? :-(
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnAlignInsertBefore');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnAlignPosition');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnCanResize');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnConstrainedResize');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnContextPopup');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnDblClick');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnDockDrop');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnDragOver');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnEndDock');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnEndDrag');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnEnter');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnExit');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnGetSiteInfo');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnMouseActivate');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnMouseDown');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnMouseEnter');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnMouseLeave');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnMouseMove');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnMouseUp');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnMouseWheel');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnMouseWheelDown');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnMouseWheelUp');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnResize');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnStartDock');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnStartDrag');
  DesignIntf.UnlistPublishedProperty(TYourFrameComponent, 'OnUnDock');
}
 RegisterSprigType(TYourFrameComponent, TComponentSprig);
end;
Hope this helps!
--
Regards,
Jeferson Oliveira
Brazil
0
Jeferson
2/23/2010 12:21:55 PM
Reply: