Recent

Author Topic: [SOLVED]How to avoid duplicate component names when creating components?  (Read 668 times)

Lamb3234

  • New Member
  • *
  • Posts: 16
Hello,

In my form, the user is able to create and destroy TabSheets dynamically. Here is the code that names the TabSheets:

Code: Pascal  [Select][+][-]
  1. k := Form1.PageControl1.PageCount;
  2. NewTab.Name := 'TabSheet' + IntToStr(k);

But, of course, if the user chooses to destroy a tab, and creates another tab and the name already exists, the program crashes with this message:

Code: Pascal  [Select][+][-]
  1. Duplicate name: A component named "TabSheet3" already exists

What's a neat way to avoid this from happening?
« Last Edit: December 10, 2019, 11:56:53 am by Lamb3234 »

wp

  • Hero Member
  • *****
  • Posts: 7529
I don't given them a name at all. At runtime, normally there is no reason that components should have a non-empty value in the property "Name". This is not to be confused with the name of the variable - just keep the name of the variable if you must access a run-time created component later.


Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

lucamar

  • Hero Member
  • *****
  • Posts: 3019
The name is not really that usefull but if you really want to name them then use a simple counter, incrementing it anytime a new tab is created:

Code: Pascal  [Select][+][-]
  1. var
  2.   pagenum: Integer = 0;
  3.  
  4. ...
  5.  
  6. Inc(pagenum);
  7. NewTab.Name := 'TabSheet' + IntToStr(pagenum);

Though I don't remember its name ATM, there is a function somewhere in Lazarus which is used to create such names(*) (it's used e.g. by the designer when adding a control. I haven't yet found the need to use it and always use the simple "trick" shown above: you'll not run out of numbers for several millions of controls :)

Exception is if you realy need the name to reflect the tab's index. In that case, I'd change *all* the tab's names to use their index each time you add a new one. Not very elegant, but acceptable if made with dure care.

HTH

ETA:
(*) Found them: not a function but two complementary ones, in unit LCLProc: CreateFirstIdentifier and CreateNextIdentifier
« Last Edit: December 09, 2019, 08:18:06 pm by lucamar »
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 2.0.8/FPC 3.0.4 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

Lamb3234

  • New Member
  • *
  • Posts: 16
Thanks for both of your responses.

I think wp's solution of not using a name at all (which I never knew was possible) would work well, but in each of the tabs, a StringGrid is dynamically created too. The user has the option to add rows to the StringGrid - is there any way to reference which StringGrid to add rows to as being the StringGrid within the tab that's selected, and not the StringGrid in any other tabs?

Thanks for your time.

winni

  • Hero Member
  • *****
  • Posts: 1778
Hi!

If you take @lucamars solution and name the stringGrids along with the tabsheets then the names are uniq.

And the active page of the pagecontrol you get with:

Code: Pascal  [Select][+][-]
  1. pageNo := PageControl1.ActivePageIndex;

Winni

wp

  • Hero Member
  • *****
  • Posts: 7529
but in each of the tabs, a StringGrid is dynamically created too. The user has the option to add rows to the StringGrid - is there any way to reference which StringGrid to add rows to as being the StringGrid within the tab that's selected, and not the StringGrid in any other tabs?
This is what the Tag property is good for. Assign the pointer to the StringGrid to the Tag of the TabSheet, and maybe assign the pointer to the TabSheet to the Tag of the StringGrid (or use the child-parent relationship to find a grid's TabSheet). And do a few type-casts so that the compiler accepts everything.

Find a short demo in the attachment.
« Last Edit: December 09, 2019, 10:14:59 pm by wp »
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

Lamb3234

  • New Member
  • *
  • Posts: 16
Thanks wp, that's very helpful - all solved now!

lucamar

  • Hero Member
  • *****
  • Posts: 3019
Re: [SOLVED] How to avoid duplicate component names when creating components?
« Reply #7 on: December 10, 2019, 01:51:52 am »
An alternative I use frequently for this kind of tasks is to use a function that traverses the tab's Controls array property looking for the one that inherits from the target, as in:

Code: Pascal  [Select][+][-]
  1. function TMainForm.GridInTab(TheTab: TTabSheet): TStringGrid;
  2. var
  3.   i: Integer;
  4. begin
  5.   Result := Nil;
  6.   if Assigned(TheTab) then begin
  7.     for i := 0 to TheTab.ControlCount-1 do
  8.       if TheTab.Controls[i].InheritsFrom(TStringGrid) then begin
  9.         Result := TheTab.Controls[i] as TStringGrid;
  10.         Break;
  11.       end;
  12.   end;
  13. end;

Of course, that'll work if there's just one string grid per tab. To get the one for the current tab you would call it as, for example:

Code: Pascal  [Select][+][-]
  1. with GridInTab(ThePageControl.ActivePage) do begin
  2.   {... do something ...}
  3. end;

Of course wp's solution is a very good one, even better when there can be more than one grid (or descendant) inside the tab with one of them being the "master" to which other controls apply.
« Last Edit: December 10, 2019, 02:55:43 pm by lucamar »
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 2.0.8/FPC 3.0.4 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

Lamb3234

  • New Member
  • *
  • Posts: 16
Re: How to avoid duplicate component names when creating components?
« Reply #8 on: December 10, 2019, 02:08:43 am »
Thank you for your code lucamar, it's very educational to see multiple ways of doing things.

I'm still struggling with a bug that I've been trying to wrap my head around for hours now. On my form, the PageControl is controlled by a ListBox with the following code:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.ListBox1SelectionChange(Sender: TObject; User: boolean);
  2. var
  3.   NewTab: TTabSheet;
  4.   NewStringGrid: TStringGrid;
  5. begin
  6.   NewTab := PageControl1.ActivePage;
  7.   NewStringGrid := TStringGrid(NewTab.Tag);
  8.  
  9.   PageControl1.TabIndex := ListBox1.ItemIndex;
  10.  
  11. // problem code below
  12.   If NewStringGrid.RowCount = 1 then
  13.   begin
  14.     Button1.Enabled := False;
  15.   end
  16.   else
  17.     Button1.Enabled := True;
  18. end;                      

The problem with this code is, when I switch tab (using ListBox1SelectionChange), if the StringGrid within that tab has RowCount = 1, the Button1 does not disable, until I change the ListBox1Selection/tab again. It's as though this code is referencing the StringGrid prior to the change in selection/tab, rather than the StringGrid in the newly selected page. Does anyone know a fix to this issue?

Thank you.

winni

  • Hero Member
  • *****
  • Posts: 1778
Re: How to avoid duplicate component names when creating components?
« Reply #9 on: December 10, 2019, 02:58:06 am »
Hi!

You assign NewTab and NewStringGrid from the current page:

NewTab := PageControl1.ActivePage;
NewStringGrid := TStringGrid(NewTab.Tag);

Now you change the active page: (!!!)

PageControl1.TabIndex := ListBox1.ItemIndex;

The NewTab and the NewStringGrid are the components of the last active page!
And not of the now active page.

I think, that is not what you want!

Winni

Lamb3234

  • New Member
  • *
  • Posts: 16
Re: How to avoid duplicate component names when creating components?
« Reply #10 on: December 10, 2019, 11:56:35 am »
Darn, I feel really stupid now. Thanks winni!!!

 

TinyPortal © 2005-2018