Recent

Author Topic: [SOLVED] Iterate through similarly named components ?  (Read 5737 times)

rautamiekka

  • New member
  • *
  • Posts: 8
[SOLVED] Iterate through similarly named components ?
« on: November 22, 2016, 01:20:49 am »
[ NOOB ALERT ]

... instead of writing nearly identical code for hundreds of lines. If I can help it, I don't wanna have all that nearly same code over and over again.

I'm porting a graphical program of mine to FPC 3.0.0 + Lazarus 1.6.2. The program uses 1+3 forms and those 3 have loads of checkboxes, textboxes and command buttons, with 1 each on same line, "grouped". Also has a load of command buttons which toggle a specific bunch, all of them, or reversing the states, of checkboxes, ending with another command button.

The name for the checkboxes is chkNormal# where # is number starting from 0. txtNormal# for textboxes (same syntax). cmdNormal# for the buttons. The other toggling ones have different names but the code is nearly the same for all of them, calling the same procedure.

The checkboxes control the textbox and the command button next to it by toggling their enabled property.

The program being designed in Visual Basic 6, I took advantage of component indexing, where you have multiple components with identical name but different index number. Whenever over 3 (2 in FPC) sequential checkboxes needed to be toggled, the code just iterated through every checkbox according to the name and index.

Once the last button is pressed, it checks each checkbox's state, complains if all are disabled, otherwise reads each textbox's contents and performs some calculation.

I've been unable to find a way to that, or something else which yields the same result. This is the code I barely found: http://stackoverflow.com/questions/18402124/free-pascal-use-for-loop-to-determine-visibility-of-component-based-on-status-of/18402550 and what little I find suggests I'm using wrong way to do that.

This is the adapted, stripped code:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.         Classes , Sysutils , Forms , Controls , Graphics ,
  9.         Dialogs , StdCtrls , ExtCtrls ;
  10.  
  11. type
  12.    
  13.     { TfrmNormalSpells }
  14.  
  15.     TfrmNormalSpells = class(Tform)
  16.         chkNormal0: Tcheckbox;
  17.         cmdNormal0: Tbutton;
  18.         txtNormal0: Tedit;
  19.         cmdAllDisable: Tbutton;
  20.         cmdAllEnable: Tbutton;
  21.         procedure cmdAllDisableClick(Sender: Tobject);
  22.         procedure cmdAllEnableClick(Sender: Tobject);
  23.     private
  24.         { private declarations }
  25.     public
  26.         { public declarations }
  27.     end;
  28.  
  29. var
  30.     frmNormalSpells: TfrmNormalSpells;
  31.  
  32. implementation
  33.  
  34. {$R *.lfm}
  35. procedure TfrmNormalSpells.cmdAllEnableClick(Sender: Tobject);
  36. begin
  37.     EnableDisableSpells(true, 'all', Self);
  38. end;
  39.  
  40. procedure TfrmNormalSpells.cmdAllDisableClick(Sender: Tobject);
  41. begin
  42.     EnableDisableSpells(false, 'all', Self);
  43. end;
  44.  
  45. procedure EnableDisableSpells(ToEnable:boolean; strSpellType:string; AOwner:TComponent);
  46.     var
  47.         i:integer;
  48. begin
  49.     for i := 0 to intSpellCount do //§intSpellCount§, 52, is also the count of those checkboxes, textboxes and command buttons. It's const-declared in the unit's start.
  50.         TCheckBox(AOwner.FindComponent('chkNormal' + IntToStr(i))).Checked := not ToEnable;
  51. end;

Although FPC/Laz doesn't say anything being wrong, exactly nothing happens when I click either of those buttons, especially the AllEnable one. I confirmed all that exists, by hovering on the names. The only messages Laz gives are the Sender parts in those 2 procedure definitions not being used. That might explain why this doesn't work.
« Last Edit: November 22, 2016, 12:33:49 pm by rautamiekka »

rautamiekka

  • New member
  • *
  • Posts: 8
Re: Iterate through similarly named components ?
« Reply #1 on: November 22, 2016, 01:39:02 am »
Got it, I removed
1) the parameters for the buttons
2) the usage of Self
3) all TComponent refs, replacing AOwner with the form's name. I have other issues now, but they're different story.

Randomly found this thread which showed different ways to use this thing: http://www.computerworld.dk/eksperten/spm/779297

molly

  • Hero Member
  • *****
  • Posts: 2330
Re: Iterate through similarly named components ?
« Reply #2 on: November 22, 2016, 01:49:33 am »
Your snippet as shown, works for me without any problems.

That can only mean that you have some other things lurking in there that influences behaviour.

1) i have no idea what you meant by 'parameters of the buttons', let alone how to remove them  :-[
2) The usage of self inside the button event is not necessary. All they do is point to the button that actually invoked the event (assuming you attached those events to their corresponding buttons).
3) Self corresponds to the form (at least when you run your code in the context of the form), so supplying self to EnableDisableSpells (if even as being declared as AOwner: TComponent) should work.

In addition to that, always assume the worst, so
Code: [Select]
TCheckBox(AOwner.FindComponent('chkNormal' + IntToStr(i))).Checked := not ToEnable;
Is not assuming the worst case scenario but forces the found component (is it actually found?) to be a checkbox (is it actually a checkbox, or did you name one of your components wrongly by accident ?).

If assigned(foundcompoenent) and if FoundComponent is TCheckBox usage would be better.

Something similar to this:
Code: Pascal  [Select][+][-]
  1. procedure EnableDisableSpells(ToEnable:boolean; strSpellType:string; AOwner:TComponent);
  2. const
  3.   BaseName = 'chkNormal';
  4. var
  5.   i              : integer;
  6.   FoundCheckBox  : TCheckBox;
  7.   FindName       : String;
  8. begin
  9.   if strSpellType = 'all' then
  10.   begin
  11.     for i := 0 to Pred(intSpellCount) do
  12.     begin
  13.       FindName := BaseName + IntToStr(i);
  14.       FoundCheckBox := AOwner.FindComponent(FindName) as TCheckBox;
  15.       if assigned(FoundCheckBox)
  16.       then FoundCheckBox.Checked := not(ToEnable)
  17.       else raise Exception.CreateFmt('A component with name "%s" could not be found',[FindName]);
  18.     end;
  19.   end;
  20. end;
  21.  
« Last Edit: November 22, 2016, 02:32:56 am by molly »

bytebites

  • Hero Member
  • *****
  • Posts: 632
Re: Iterate through similarly named components ?
« Reply #3 on: November 22, 2016, 08:10:53 am »

rautamiekka

  • New member
  • *
  • Posts: 8
Re: Iterate through similarly named components ?
« Reply #4 on: November 22, 2016, 12:33:23 pm »
Your snippet as shown, works for me without any problems.
Odd, nothing happened for me, at least when I tried in the actual program without a test one. Also, I had somehow copied cmdAllEnableClick to every button's onClick event which caused them all to malfunction until I noticed that mistake. I think I had double-clicked the actual button before copypasting the button to form the other buttons. When I did I opened the lfm file in Notepad++ and completely removed the onClick event for every button except the actual one, then double-clicked each and added the proper code.

1) i have no idea what you meant by 'parameters of the buttons', let alone how to remove them
I meant this code, but it turned out to be a bad idea since other unwanted things started to happen, so I put them back. Laz however gives a hint in the message box saying Sender is unused, and those messages point to the procedure declarations in the type block.
Code: [Select]
Sender: Tobject
(is it actually a checkbox, or did you name one of your components wrongly by accident ?).
They are, just unimaginatively named 'xxxNormal' after the fact that form deals with the normal spellbook.

2) The usage of self inside the button event is not necessary. All they do is point to the button that actually invoked the event (assuming you attached those events to their corresponding buttons).
3) Self corresponds to the form (at least when you run your code in the context of the form), so supplying self to EnableDisableSpells (if even as being declared as AOwner: TComponent) should work.
That's how the original code went, turning out to do nothing. Removed AOwner: TComponent and replaced AOwner.FindComponent with frmNormalSpells.FindComponent as per that Danish article, works perfectly. Self however seems to be wrong thing to use since it doesn't point to the form (unlike Me in VB), evident by replacing the form's name with it in the EnableDisableSpells procedure, only to get Laz say it's an unknown identifier.

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: [SOLVED] Iterate through similarly named components ?
« Reply #5 on: November 22, 2016, 08:46:31 pm »
One approach when numerous very similar controls are needed in a GUI is to design a custom control that wraps what you need into a single entity, but give it  a flexible constructor so you can repeat the needed control as often as that particular use case demands.

The attached project is a demo of one such composite control that may be similar to the repeated checkbox+edit+button you described in your first post.
Code that searches for named components on a form is a maintenance nightmare, and not reusable elsewhere.

Frames are one answer to the reusability issue, but I have found them rather inflexible in practice, and find a custom wrapper component takes not much longer to write, and if well-designed gives flexibility and extensibility too.

The composite component demonstrated here is eminently reusable, and also fairly simple to adapt to other uses. Perhaps you need lots of TSpinEdits... etc. You could hack the code shown to a variety of different purposes.

rautamiekka

  • New member
  • *
  • Posts: 8
Re: [SOLVED] Iterate through similarly named components ?
« Reply #6 on: November 23, 2016, 10:35:07 pm »
Frames are one answer to the reusability issue, but I have found them rather inflexible in practice, and find a custom wrapper component takes not much longer to write, and if well-designed gives flexibility and extensibility too.
I did consider frames, but later figured it solves only half of my problems: 1 was this iteration. Sure they would make disabling easier, but I'd still need to iterate through them. I may be wrong but I don't see any ways for them to help with anything else.

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: [SOLVED] Iterate through similarly named components ?
« Reply #7 on: November 24, 2016, 12:20:01 am »
I did consider frames, but later figured it solves only half of my problems: 1 was this iteration. Sure they would make disabling easier, but I'd still need to iterate through them. I may be wrong but I don't see any ways for them to help with anything else.

Frames definitely have their uses where repeated boiler-plate UI layouts are needed. They can carry all sorts of built-in functionality in the interactions of the controls placed on them, functionality that only needs to be written and debugged once, then oft reused elsewhere.
If you find you are writing almost exactly the same UI code time after time, a frame may be just what you need.
As always, there are several ways to approach programming problems, and sometimes one solution seems best, sometimes another.

rautamiekka

  • New member
  • *
  • Posts: 8
Re: [SOLVED] Iterate through similarly named components ?
« Reply #8 on: November 24, 2016, 12:25:32 am »
I did consider frames, but later figured it solves only half of my problems: 1 was this iteration. Sure they would make disabling easier, but I'd still need to iterate through them. I may be wrong but I don't see any ways for them to help with anything else.

Frames definitely have their uses where repeated boiler-plate UI layouts are needed. They can carry all sorts of built-in functionality in the interactions of the controls placed on them, functionality that only needs to be written and debugged once, then oft reused elsewhere.
If you find you are writing almost exactly the same UI code time after time, a frame may be just what you need.
As always, there are several ways to approach programming problems, and sometimes one solution seems best, sometimes another.
Ok, I'll do some research.

 

TinyPortal © 2005-2018