Jump to content
aehimself

Custom component on a VCL frame

Recommended Posts

Hello,

 

Let's imagine the following scenario. I made a custom component which resides on a VCL frame. First of all, it's a lot easier to design something if you can see it (hence no TPanel) and some components require a form or a frame as a parent.

It looks like this:

 

image.png.f0bc1ba39665a7b121ae6d93003f7307.png

 

Install it to the IDE and create a new application. Place this component on the form.

 

Issue #1:

You can not select individual components, any click with the mouse will select the whole frame instead. This is not a big thing, as you still can select its child components in the Structure pane.

 

image.thumb.png.2404ca71b9644df94dfb7c5ef75160a1.png

 

Now, save your project in an empty git repository and commit once, so you can compare the differences. Load up the project again, select the TEdit and enter something as it's Text property:

 

image.png.9c674cf747f64509a3993033994d8426.png

 

Save your project again and check the differences in the DFM:

image.png.dbbaf85df1494669a9d6e3940cbe8d03.pngimage.png.27ad45d26c8a748c15dcbb3dc6e65783.png

 

Issue #2:

All property changes in the components children are simply discarded.

I can get around this one too by setting these in runtime, but it's less convenient that directly in the IDE.

 

I suppose this is a limitation, I shouldn't design my custom component on a frame. How else can I achieve graphical designability in the IDE and how can I force for example a dxBarManager toolbars to stay within the components boundaries?

I'm also interested about the reason why these things happen, if someone knows by heart.

 

Cheers!

Share this post


Link to post

I don't know the answer. But many programmers use Subform because of problems with Frame. They have no problem with that. Me neither. Maybe it's better to go the Subform route.

Share this post


Link to post

I have the same problem. The frame should be inline but I also have a frame that Delphi uses as object and then you cannot change the properties on the frame. 

 

I have no solution spent hours to research the problem. (You cannot just change the DFM).

Share this post


Link to post
5 hours ago, aehimself said:

Issue #1:

You can not select individual components, any click with the mouse will select the whole frame instead.

You can add a message handler to the Frame class to handle the CM_DESIGNHITTEST message, setting the message's result to 1 if the mouse is over a child control within the Frame's client area.  That will allow the child control to handle the mouse click.

Edited by Remy Lebeau
  • Thanks 1

Share this post


Link to post
8 hours ago, Uwe Raabe said:

Can you show the code for the custom component?

Sure. There is no code whatsoever:

unit TempComponent;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  Vcl.ExtCtrls;

type
  TMyTempComponent = class(TFrame)
    Edit1: TEdit;
    Button1: TButton;
    RadioGroup1: TRadioGroup;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

Procedure Register;

implementation

Procedure Register;
Begin
  RegisterComponents('Temp component', [TMyTempComponent]);
End;

{$R *.dfm}

end.


 

Share this post


Link to post

just being the smart*ss, why are you mixing composite components with frames? can't you just put a frame onto an another frame?

Share this post


Link to post
4 minutes ago, Attila Kovacs said:

just being the smart*ss, why are you mixing composite components with frames? can't you just put a frame onto an another frame?

This is why 🙂

12 hours ago, aehimself said:

I suppose this is a limitation, I shouldn't design my custom component on a frame. How else can I achieve graphical designability in the IDE and how can I force for example a dxBarManager toolbars to stay within the components boundaries?

 

Share this post


Link to post

@aehimself I don't get it. I just created a new VCL Form Application plus designed two new frames, then added frame2 to frame1, without creating a custom component.

What is the concept behind creating a compound component from a frame?

You can also add code to the base frame and override it in the other places.

I don't know any Bar Managers except Gunther, but to make a component fill up a given space is normally done with alignment.

 

Share this post


Link to post
13 hours ago, Remy Lebeau said:

You can add a message handler to the Frame class to handle the CM_DESIGNHITTEST message, setting the message's result to 1 if the mouse is over a child control within the Frame's client area.  That will allow the child control to handle the mouse click.

I'm experimenting with this but I can not seem to get it to work.

procedure TMyTempComponent.HitTest(Var Msg: TCMDesignHitTest);
begin
//  ShowMessage('Edit1.BoundsRect.Top: ' + Edit1.BoundsRect.Top.ToString + sLineBreak +
//              'Edit1.BoundsRect.Left: ' + Edit1.BoundsRect.Left.ToString + sLineBreak +
//              'Edit1.BoundsRect.Bottom: ' + Edit1.BoundsRect.Bottom.ToString + sLineBreak +
//              'Edit1.BoundsRect.Right: ' + Edit1.BoundsRect.Right.ToString + sLineBreak + sLineBreak +
//              'Msg.XPos: ' + Msg.XPos.ToString + sLineBreak +
//              'Msg.YPos: ' + Msg.YPos.ToString);
  If PtInRect(Edit1.BoundsRect, Point(Msg.XPos, Msg.YPos)) Then
    Msg.Result := 1
  Else
    Msg.Result := 0;
end;

The event does not fire at all if I move the cursor over any child component (Edit, button or radiogroup) only when it is over the frame itself. I also tried adding

ControlStyle:= ControlStyle - [csDesignInteractive];

in the constructor.

 

I'll keep trying, but am I on the right track here? Maybe frames are just work... different? 🙂

 

Share this post


Link to post

I never had good luck with Frames...  With custom component development the issue is the IDE needs a restart after an "improvement" to the control.  Or uninstall the control and reinstall once you have a project group set up. to rebuild the control. 

 

In the IDE control X and control V the control to reflect the improvements. 

 

For a complex control I simply use a form that keeps all the controls events assigned.  By wrapping the form create with owner and parent and Event arguments the "Parent form" is not added to uses clause.  

rule 1 Do not use ShowModal that is the only danger I have found.

    constructor NewAux(const aName:string; bossForm: TForm;
                      APanel: TWinControl; aEM: TEventBoss;
                                        Headings: Tstrings;
                                      aClick: TnotifyEvent; 
                          Settings: TWindowFormSettingSet);

 

Example code.

  

 

Share this post


Link to post
19 hours ago, aehimself said:

I made a custom component which resides on a VCL frame.

I guess, that is not one of the use cases the developers had in mind when they designed the custom component system.

 

AFAIK, you can either have subcomponents created dynamically with their properties stored as sub-properties of the outer component or you can have a frame stored in the object repository with derived instances in your project.

 

 

Share this post


Link to post
On 10/16/2022 at 6:38 AM, aehimself said:

 


  If PtInRect(Edit1.BoundsRect, Point(Msg.XPos, Msg.YPos)) Then

The message's coordinates are expressed as screen coordinates, but a control's BoundsRect is expressed as client coordinates within the parent's client area.  So, you would need to normalize the two sets of coordinates to the same coordinate space before you can compare them.

 

Also, it turns out that TWinControl already handles CM_DESIGNHITTEST, by forwarding the message to any child control that is at the specified coordinates.  By default, TControl returns 0 for the message's result.  So, another option may be to have your Frame class subclass its child controls and have them unconditionally return 1 for CM_DESIGNHITTEST, instead of handling the message at the TFrame level.  Just a thought.

On 10/16/2022 at 6:38 AM, aehimself said:

I also tried adding


ControlStyle:= ControlStyle - [csDesignInteractive];

in the constructor.

That flag only affects right mouse clicks, by converting them into left mouse clicks.

On 10/16/2022 at 6:38 AM, aehimself said:

Maybe frames are just work... different? 🙂

Maybe. I've never tried to make a component out of a Frame before. Have you tried basing the component on TPanel instead?

Share this post


Link to post

the purpose of TFrame is to not register a custom component !

you create a frame, put some components, and voilà you can put this frame on any form of your project without registering anything.

 

when a custom component owns some subcomponents, there's more to do - that I don't have in mind yet - to support serialization to/from the DFM

Share this post


Link to post
1 hour ago, Paul TOTH said:

when a custom component owns some subcomponents, there's more to do - that I don't have in mind yet - to support serialization to/from the DFM

I don't know if all is necessary, but I at least it is sufficient:

  • have a field for the sub component exposed as a published read-property
  • create the instance in the constructor (Self as Owner is allowed omitting the Free in the destructor)
  • call SetSubComponent(True) after creation
  • set all properties as needed
  • don't forget to set Parent

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×