Jump to content
Angus Robertson

Dropping component on form

Recommended Posts

When you drop a component on a form, the IDE automatically adds the unit to the uses clause. 

 

Is there a way for that component to get the IDE to also add a second unit on which it is dependent?

 

Angus

 

Share this post


Link to post
Posted (edited)

Yes there is. Have a look at TSelectionEditor and its RequiresUnits method:

 

https://docwiki.embarcadero.com/Libraries/Alexandria/en/DesignEditors.TSelectionEditor

 

Many 3rd party products do this (e.g. DevExpress) especially when components have event handlers containing types that are defined in another unit. Because adding such an event handler will otherwise break compiling and the user going on a hunt for the unit.

Edited by msohn

Share this post


Link to post

That is exactly what I need, and for that reason, but this if for dozens of existing components and I assume they would need to be changed to be derived from TSelectionEditor.  I also need compatibility with Delphi 2007 and later, ideally.

 

Angus

Share this post


Link to post
1 minute ago, Angus Robertson said:

but this if for dozens of existing components and I assume they would need to be changed to be derived from TSelectionEditor.

No, the actual components don't have to be derived from TSelectionEditor. That would probably be near to impossible, as it would require to use design-time code in the application. Instead you need to register a TSelectionEditor descendant for each of these components and implement the RequiresUnits as appropriate.

Share this post


Link to post
Posted (edited)

Yes, TSelectionEditor is the way to go.  When a unit is saved or compiled, the IDE first invokes any registered TSelectionEditor classes relevant to that unit, calling their RequiredUnits() methods to update the 'uses' clause.

 

Indy registers several TSelectionEditor classes for a few specific cases.  But, I want to point out 1 editor in particular: TIdBaseComponentSelectionEditor.  It is registered for TIdBaseComponent, which all Indy components are derived from.  TIdBaseComponentSelectionEditor iterates all Indy components on a Form, using RTTI to look for any event handlers that are assigned to those components, and then to determine the types of any parameters and return values of those event handlers, and the unit names that those types belong to.  It then reports those units to the IDE to add to the 'uses' clause.

 

Now, one would think this is something that the IDE itself should handle automatically for all components, but it doesn't, or at least not completely.  Which is why TIdBaseComponentSelectionEditor was created, instead of having to define a separate TSelectionEditor for each individual component that needs it.

 

I mention this because Indy has a LOT of components, so it makes sense to handle something like this in a central place.  So, if you don't want to write a bunch of smaller editors for multiple components in your library, you might consider a similar approach (although handling RTTI differences across multiple IDE versions can be a bit of a task).

Edited by Remy Lebeau
  • Like 1

Share this post


Link to post

ICS now only needs to add two units to meet almost all event handler type needs. 

 

Users of old compilers can not always get the same features as newer compilers, so may have to add uses manually, as everyone has for 25 years.  

 

There is an argument for only supporting new compilers, but I still have a commercial application using Delphi 2007, so support for that will not be dropped any time soon

 

Angus

 

Share this post


Link to post

Added a few lines to the ICS design package registration unit, and two extra units are now being added automatically when components are dropped on forms, or those forms are accessed in the IDE.  

 

Would not have  been possible without a Stack Overflow answer by Remy Lebeau, Aug 19, 2020:

 

Have your design-time package implement a class that inherits from TSelectionEditor and overrides its virtual RequiresUnits() method, and then register that class for your component using
RegisterSelectionEditor(). This way, whenever you place your component onto a Form/Frame/DataModule Designer at design-time, any additional units you report from RequiresUnits() will be added automatically to that unit's uses clause when the unit is saved.
 

uses
  ..., DesignIntf;

type
  TWebBrowserWrapperSelectionEditor = class(TSelectionEditor)
  public
    procedure RequiresUnits(Proc: TGetStrProc); override;
  end;

procedure TWebBrowserWrapperSelectionEditor.RequiresUnits(Proc: TGetStrProc);
begin
  inherited RequiresUnits(Proc);
  // call Proc() for each additional unit you want added...
  Proc('MyWrapperUnit');
  Proc('MyUtilityUnit');
end;

procedure Register;
begin
  RegisterComponents('My Wrappers', [TWebBrowserWrapper]);
  RegisterSelectionEditor(TWebBrowserWrapper, TWebBrowserWrapperSelectionEditor);
end;


 

Angus

 

 

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

×