Jump to content
limelect

Create component ERROR in expert in D10.2.3

Recommended Posts

Using D10.2.3

 

function TForm1.GxOtaGetCurrentModule: IOTAModule;
var
  ModuleServices: IOTAModuleServices;
begin
  ModuleServices := BorlandIDEServices as IOTAModuleServices;
  Assert(Assigned(ModuleServices));
  Result := ModuleServices.CurrentModule;
end;

function TForm1.GxOtaGetFormEditorFromModule(const Module: IOTAModule): IOTAFormEditor;
var
  i: Integer;
  Editor: IOTAEditor;
  FormEditor: IOTAFormEditor;
begin
  Result := nil;
  if not Assigned(Module) then
    Exit;
  for i := 0 to Module.GetModuleFileCount - 1 do
  begin
    Editor := GxOtaGetFileEditorForModule(Module, i);
    if Supports(Editor, IOTAFormEditor, FormEditor) then
    begin
      Assert(not Assigned(Result));
      Result := FormEditor;
      // In order to assert our assumptions that only one form
      // is ever associated with a module, do not call Break; here.
    end;
  end;
end;

function TForm1.GxOtaGetFileEditorForModule(Module: IOTAModule; Index: Integer): IOTAEditor;
begin
  Assert(Assigned(Module));
  Result := Module.GetModuleFileEditor(Index);
end;


procedure TForm1.Button2Click(Sender: TObject);
var
  CurrentModule: IOTAModule;
  CurrentForm: IOTAFormEditor;
   lComponent: IOTAComponent;
begin
    CurrentModule := GxOtaGetCurrentModule;
  Assert(Assigned(CurrentModule));
   CurrentForm := GxOtaGetFormEditorFromModule(CurrentModule);
   if CurrentForm.GetSelCount > 0 then
    lComponent := CurrentForm.GetSelComponent(0)
  else
    lComponent := CurrentForm.GetRootComponent;
      if not lComponent.IsTControl then
  begin
    ShowMessage('The Component of Selected is not Container Control!');
    exit;
  end;
                   //CurrentForm.GetSelComponent(0).GetComponentType;   
   Label2.Caption:=lComponent.GetComponentType; <<<<< Tpanel or Tform
  Label3.Caption:= CurrentForm.FileName;  <<<  file.dfm
  //   CurrentForm.CreateComponent(lComponent, 'TADOtable', 0, 0, 50, 50);  <<<< This is OK

 

///THIS IS WHERE THE ERROR IS
  CurrentForm.CreateComponent({CurrentForm.GetSelComponent(0)}lComponent, 'TLabel', 0, 0, 50, 50);<<<< This is an ERROR
 end;

 

ERROR = Class TLabel is not applicable to this module

Any help?

Edited by limelect

Share this post


Link to post

I assume that manually dropping a TLabel onto the component works?

Do you have a test case project that shows this error so we can debug and investigate?

Share this post


Link to post

@David Hoyle I did not want to bother you However i will send you the source.

I used an old D6 project called ComponentSearch

the project called cs very simple.

It has a few pas so it is useless to put it here

Thanks

Edited by limelect

Share this post


Link to post

I would like to thanks @David Hoyle for his personal communication.

 

It seems that  "CurrentForm.CreateComponent" in another variation too

give this ERROR "Class TLabel is not applicable to this module".

Although the variables are correct.

Using TADOtable instead of Tlabel is working.

Any idea?

 

 

 

 

Edited by limelect

Share this post


Link to post

 Delphi and Embarcadero.

"CurrentForm.CreateComponent" works on D7 !!!

it creates the component.

Can someone explain why?

Is it a bug in Delphi?

 

Share this post


Link to post

I finally have a solution with the help of GEXPERT  @dummzeuch source

It says "// If this starts crashing try IDesigner.CreateComponent"

So I did

 

var

IDs:  IDesigner;
 

change create component to 

 

ids := (CurrentForm as INTAFormEditor).FormDesigner;
if Assigned(IDs) then
  IDs.CreateComponent(TComponentClass( Tlabel),GxOtaGetNativeComponent(lComponent),0,0,50,50) ;
 I hop i did OK

Any further help will be appritiated 

 

For me, it worked on  D10.2.3  this is what I needed.

 

 

 

  • Like 2

Share this post


Link to post

@limelect Just had a thought and tried the following and had some success... but need to go get something to eat...

You must use a Package and that package must be set to Design Time only.

Doing that I've managed to insert a TLabel and TEdit successfully but the add-in crashed exiting the IDE, something to do with the form I was using... need food thought 🙂

 

Share this post


Link to post

@limelect Just checked that error I was getting has nothing to do with the plug-in, the IDE does it all on its own without the plug-in being invoked but guess what, the exact same code in another session fails.

Share this post


Link to post

Okay, I'm leaning towards the issue being FMX verse VCL. I iterated all the packages below and picked out all the components and found duplicates between the frameworks.

image.thumb.png.70a94052da6b7c6f0e8bde593ed4d919.png

So I think GetClass() is looking up in FMX instead of VCL. Since it's now failing, selecting a VCL only component does nothing as GetClass() cannot find the component (which does not exist in FMX).

Getting a bit late now but I know that on occasions the IDE prompts you to select which framework you are using (FMX or VCL) and I need to find out how we influence this determination (starting a new VCL app didn't work but then again I didn't save it).

Share this post


Link to post

Well I think GetClass() is finding FMX controls not VCL ones which is why this is not working (despite the designer type and framework type being VCL).

image.thumb.png.0de214e520a287ab9ab8433baee290ce.png

Had enough for now. Might look later in the week if time permits.

image.png

Share this post


Link to post

@David Hoyle

if I use  this  IDs.CreateComponent(TComponentClass( Tlabel),GxOtaGetNativeComponent(lComponent),0,0,50,50) ;

Tlabel not as a string it needs USES to work. Using TADOtable need USES. But it works.

 

So I tried GetClass instead and it got me back to the ERROR;

 

It seems that the main problem is the difference between  VISUAL AND NON VISUAL.

Since TADOtable workers in any configuration but Tlabel maks

the ERROR 'not applicable to this module'

So the question is if not this module then which one.

 

In my case, I use the dfm/pas source  Label3.Caption:= CurrentForm.FileName; <<<< DFM

CurrentForm is derived from CurrentModule.

Label4.Caption:=CurrentModule.FileName; <<<< PAS

 

The objective is to place ANY component on a control be Form, Panel, or else.

 

I did not get to the point of differentiating between VCL and FMX.

Right now I am trying only to use VCL.

 

And lastly the original source workers on D7 so there is

a difference between the Delphi version although the sources (DesignIntf and more)  seem to be the same.

 

Edited by limelect

Share this post


Link to post

@David Hoyle You made me think and I just found that 

1. It works on FMX 

2 I did not do an extensive check but it seems to work with VISUAL and NON VISUAL.

 

So why the difference?

Edited by limelect

Share this post


Link to post

Thanks, everybody. After trying to solve my problem with ALL "CreateComponent"  (tried a few) it seems

that I do not have a solution for VCL.

I hope maybe @David Hoyle will find a solution?

 

So for my last question for this is it possible to distinguish 

, when I have a component name as a string, if it is for VCL of FMX.

 

I know that fact from the registry but that not good enough 

since NOT all components are written there.

 

The components list names are taken from   PackageServices := BorlandIDEServices as IOTAPackageServices;
 

 

 

Share this post


Link to post
On 9/6/2020 at 11:57 PM, limelect said:

And lastly the original source workers on D7

FMX didn't exist yet in D7, there was only VCL.

7 hours ago, limelect said:

So for my last question for this is it possible to distinguish 

, when I have a component name as a string, if it is for VCL of FMX.

Not easily, and not always.

 

If a component's class name is fully qualified, you could look at the prefix to see if it begins with 'Vcl.' vs 'Fmx.' but that only works for components that are distinctly VCL or FMX, it will not work for classes that are shared by both frameworks, ie classes whose qualified names begin with 'System.', 'Data.', 'Xml.', etc.

 

Otherwise, you could drill into each component's RTTI to see if they derive from Vcl.Controls.TControl vs Fmx.Controls.TControl, but again that only works for VISUAL components that are distinctly VCL vs FMX, but will not work for NON-VISUAL components that derive from TComponent and not from TControl.

 

And, of course, the situation may get more complicated in cases where users (despite being told not to) decide to mix VCL and FMX together into a single project.

Edited by Remy Lebeau

Share this post


Link to post

I need to go back over this at the weekend (no time at the moment due to the day job) but I seem to remember one of the techniques I tried required a fully qualified name which I think we can build from enumerating the packages and their components in the IDE.

  • Like 1

Share this post


Link to post

@David Hoyle Unfortunately I do not understand  "fully qualified name".

An example of "fully qualified name"?

What I get from the IDE is a component name as a string.

Is there another way?

The successful "CreateComponent" uses a component name, not a string, 

that needed a USES, which is not good.

 

Ok i read the explanation

So what you mean is to go from "TStream" to System.Classes.TStream
and then use that?

Edited by limelect

Share this post


Link to post

@limelect I mean Unit.Component or perhaps event Package.Unit.Component. At some point in the work I did last Saturday when trying to find a solution for you, I'm positive one of the solutions accepted this format as a string. I'll try again tomorrow.

Share this post


Link to post

@David Hoyle I did try 'Vcl.StdCtrls.TLable' it did not make any mistake but did not create a component.

 All the time I am trying with my base instruction

CurrentForm.CreateComponent(lComponent, 'Vcl.StdCtrls.TLabel', 0, 0, 50, 50); << this did not make a mistake.

however, the list of components is like TLabel and not Vcl.StdCtrls.TLabel.

The last few hours I tried from string to class and going from there but did not succeed

function FindAnyClass2(const Name: string): TClass;  >> this is ok but from here to IOTAComponent; i am again stack.

var
  😄 TClass;
  ctx: TRttiContext;
  typ: TRttiType;
begin
  ctx := TRttiContext.Create;
  typ := ctx.FindType(Name);
  if (typ <> nil) and (typ.IsInstance) then c := typ.AsInstance.MetaClassType;
  result:=c;
  ctx.Free;
end;
 

I really do not have a solution. I am an expert in real-time processes. Making software for big companies

but am not an expert in EXPERT.

Share this post


Link to post

Not sure if this will help, but you might try calling ActivateClassGroup(TControl) before calling CreateComponent() to create a TControl-derived component.  At least in DFM streaming, ActivateClassGroup() can be used to limit the class types that are looked at, to avoid ambiguities with VCL and FMX classes of the same name.

Edited by Remy Lebeau
  • Like 1

Share this post


Link to post

Alas @Remy Lebeau's suggestion has not worked in the 2 scenarios I have.

I will pick this up again later today or tomorrow as I'm sure I had some part of it working with qualified names last week.

  • Like 1

Share this post


Link to post

@Remy Lebeau you were right, I just didn't call it properly.

The below code works for me in 10.4.1...

  // Set the control framework
  ActivateClassGroup(Vcl.Controls.TControl); // <= THIS FIXES IT!!!!!!!!!
  
  // Get the generic form editor for the current module
  Module := TACTFUtilities.CurrentModule;
  FormEditor := TACTFUtilities.FormEditor(Module);

  // Get the simple class name from a dropdown, i.e. TLabel, TButton, TEdit, etc
  strClassName := edtComponentClassName.Text;
  Delete(strClassName, Pos('=', strClassName), strClassName.Length);

  // Insert the component with the selected component parent
  If Assigned(FormEditor) Then
    Begin
      Component := FormEditor.GetCreateParent;
      Component := FormEditor.CreateComponent(
        Component,
        strClassName,
        10, 10, 50, 28
      );
      FormEditor.MarkModified;
    End;

 

Share this post


Link to post

@David Hoyle @Remy Lebeau that did it

I just added 

ActivateClassGroup(Vcl.Controls.TControl);

 

@David Hoyle I could not tell everybody how much you help.

Thank you very much. And to you @Remy Lebeau that did it thanks.

 

What i did is adding

  if ExtractFileExt(CurrentForm.FileName)='.dfm' then
    ActivateClassGroup(VCL.Controls.TControl);
So i have it for both

 

Now @David Hoyle when you write the BLOG plz notify me. Thanks. It was a long road for both

of us.

Edited by limelect

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

×