Jump to content
Bart Verbakel

Use of dynamic control names

Recommended Posts

Hello all,

 

I have a question, but i don't know exactly how to explain the problem...

 

Is it possible to use "dynamic" control names to set their properties?

 

For example:

 

I have a form with 5 buttons (Button1, Button2...Button5)

 

Instead of using following code:

  Button1.Enabled:=True

  Button2.Enabled:=True

  Button3Enabled:=True

  Button4.Enabled:=True

  Button5.Enabled:=True

 

I want to use something like:

  for k:=1 to to 5 do

 

 (Edit-k-).enabled=True;

 

I hope you understand my question :)

 

With kind regards,

Bart

 

 

 

 

Share this post


Link to post

In C++ this is easy to do, provided you ensure that the five buttons are all in order in the header file for the form class. You then declare a pointer to the first button and then iterate through the series of buttons, incrementing the pointer each time until you reach the last button. Not sure of the equivalent in Delphi but I suspect there is one! I use this technique a lot.

 

I listen to (and value) my learned colleagues advice here. It is correct. I have ran into this packing problem with structures / classes. This approach is "dodgy". As recommended below : need to create an array of pointers and iterate through this. Thanks to Remy and Sherlock for the benefit of their expertise.

 

Edited by Roger Cigol
Less than ideal advice !
  • Sad 1

Share this post


Link to post

FindComponent

 

Or better set array of these buttons at form create and then loop through the array

Edited by Fr0sT.Brutal
  • Like 4

Share this post


Link to post

One of the various ways is to "cycle" within the controls of the parent component: to identify the affected control you could give a value to the tag property during the design phase (for example from 1000 to the first Button, 1001 to the second, etc. ..), then this will be the code assuming you are looping through the controls of a Form (Form1):

var CB: TControl;

       for var i := 0 to Form1.ControlCount-1 do
         begin
           CB := Form1.Controls[i];
           if (CB is TButton) and ((CB as TButton).Tag > 1000) then
             begin
               (CB as TButton).Enabled := True;
             end;
         end;

 

Edited by DelphiUdIT

Share this post


Link to post
1 hour ago, Roger Cigol said:

In C++ this is easy to do, provided you ensure that the five buttons are all in order in the header file for the form class. You then declare a pointer to the first button and then iterate through the series of buttons, incrementing the pointer each time until you reach the last button. Not sure of the equivalent in Delphi but I suspect there is one! I use this technique a lot.

Don't do that! You are invoking undefined behavior.  You can only iterate using a pointer like that if the values are in an array, Otherwise, you can't be sure the compiler is not adding padding between them inside the class.  If you want to use a pointer to iterate the members, you have to put them into an array first and iterate that instead.

Edited by Remy Lebeau
  • Like 1

Share this post


Link to post
53 minutes ago, DelphiUdIT said:

One of the various ways is to "cycle" within the controls of the parent component

That "works" but may be overkill, as you would potentially be iterating through a lot of other controls that you are not interested in.

Share this post


Link to post
1 hour ago, Bart Verbakel said:

Is it possible to use "dynamic" control names to set their properties?

I do use lists, i create a list lets, say i want a bunch of controls to be enabled or disabled when a specific value is right/wrong in an edit, register these controls once and use helper function or any other method you like, to enable or disable them,

lists are great for grouping. you can hide and show also.

  • Like 1

Share this post


Link to post

So inside an application with many forms and Controls you could build a list of Controls addresses by searching by form name and then component name and use list later with getparentform is easy to bringtofront and show needed controls.  Or realizing the list when custom controls or even standard controls are loaded at runtime.  

  • Like 1

Share this post


Link to post

First of all, keeping the default names for the buttons has never been best practice.

 

While suggesting to have descriptive names for the buttons, this nevertheless is another way to iterate over any group of buttons:

  for var btn in [Button1, Button2, Button3, Button4, Button5] do
    btn.Enabled := True;

And, no, you can't write [Button1..Button5] here.

  • Like 7

Share this post


Link to post
38 minutes ago, Uwe Raabe said:

And, no, you can't write [Button1..Button5] here

Put in a FlowPanel to make visual collection and Buttons are listed as FlowPanel.Controls.  🚋

 

42 minutes ago, Uwe Raabe said:

While suggesting to have descriptive names for the buttons

Here's sample based on Cantu's 10.4 guide.  Button renamed Karma operates two checkboxes with descriptive event handlers. 

Type
  TKarma = 0..10;
const
  KarmaRatingCutoff = 7; //allow good Karma when good most of the time was 8
var
  Goody: Boolean = False;
  KarmaRange: TKarma = High(TKarma);
  KarmaNow: TKarma = 0;
procedure TForm21.KarmaChange(Sender: TObject);
begin
  Screen.Cursor := crAppStart;   //calm the user with phone like spinner
  Karma.OnClick := nil;
  Bad.OnMouseDown := nil;
  Good.OnMouseDown := nil;       //snuff the event handlers
  Karma.Enabled := False;
  Bad.Enabled := False;
  Good.Enabled := False;         //prevent user input

  KarmaNow := Random(KarmaRange);

  Goody := KarmaNow > KarmaRatingCutoff; //direct assignment no ifs + Ranged to conserve the KarmaBase
  sleep(7);                        //
  Good.Checked := Goody;
  Bad.Checked  := not Goody;
  if Goody
      then
          Karma.Hint := 'Yaa'
      else
          Karma.Hint := 'Yuch';

  Karma.ShowHint := True;
  Screen.Cursor := crDefault;
//  Bad.OnMouseDown := BadmdEvent;
//  Good.OnMouseDown := GoodmouseEvent; restore one at a time to find bad actors
  Karma.onClick := KarmaChange;
  Karma.Enabled := True;

end;

 

Share this post


Link to post
3 hours ago, Remy Lebeau said:

That "works" but may be overkill, as you would potentially be iterating through a lot of other controls that you are not interested in.

You are right. But I normally use it with Panels, GroupBoxs, or such containers and inside them I put only few controls.

 

I use it for example to enable controls with various login and disable them after logout, I found it very simple, and manipulating the tag value only (1K = Login Level 1, 2K = Login Level 2, etc ...).

If I need to modify only some properties, I don't need to know the class of the controls ... eg. Enabled, Visible are common to all TControls so not needs explicit use of "(CB as TButton)" ... or "IF (CB is TButton)"

 

But like some others wrote there are more methods, surely better than this.

 

N.B.: very often I create a lot of controls at runtime, and in that way assigning a tag value is enough. I don't need list or static enumeration.

Share this post


Link to post

Another option would be to have a procedure like this:

procedure TButtons_SetEnabled(const _Buttons: array of TButton; _Value: Boolean);
var
  btn: TButton;
begin
  for btn in _Buttons do
     btn.Enabled := _Value;
end;

And call it like this:

TButtons_SetEnabled([Button1, Button2, Button3], True);

Not much of a typing saver, but not as repetitive.

 

Edit: Just noticed that Uwe already suggested a similar approach.

Edited by dummzeuch
  • Like 1

Share this post


Link to post
22 hours ago, Uwe Raabe said:

First of all, keeping the default names for the buttons has never been best practice.

 

While suggesting to have descriptive names for the buttons, this nevertheless is another way to iterate over any group of buttons:


  for var btn in [Button1, Button2, Button3, Button4, Button5] do
    btn.Enabled := True;

And, no, you can't write [Button1..Button5] here.

 

I learned something new.  This works as described..,, minimal code and easy to follow.  I added an addition in case I were to want a toggle type set of on/off controls for certain situations, but slightly more code: 

 

for var btn in [Button1, Button2, Button3, Button4, button5] do
    if btn.Enabled = true then btn.Enabled:=false else btn.Enabled:=true;

 

Edited by JohnLM

Share this post


Link to post

That could even be simplified to:

for var btn in [Button1, Button2, Button3, Button4, button5] do
  btn.Enabled := not btn.Enabled;

 

  • Like 1

Share this post


Link to post

Ah, I knew about the 'not' and used it in similar situations but not used often enough to remember exactly in this case. 

Share this post


Link to post

Thank you for al the replies. I have to do some more investigation to find out which solution is the best for me and write a simple application to check its behaviour.

 

Bart

 

Share this post


Link to post

If they are indeed "dynamic" controls -- ie, they're defined at run-time -- then the best way is to create an array of T or a TList<T> to contain them. If there are groups of them, then make a class or put them on a panel and make a list of the panels and iterate over the (known) controls on each panel.

 

I find myself doing this relatively often for both components put on the form at design time as well as at run-time.

Edited by David Schwartz

Share this post


Link to post

No,these are not dynamic controls that are defined at runtime. Maybe I choosed the wrong words for this topic.

The controls are defined at the design phase, I want to replace the "static" code

 

Edit1.text:='ABCD'

 

replaced by another code where the '1' is a variable.

 

so I can use some code like:

 

var sName:Array[1..10] of string

for k:=1 to 10 do

  sName[k]:=Edit[k].text;

 

I am now checking which solution mentioned above is the best for me

 

 

 

 

 

 

Edited by Bart Verbakel

Share this post


Link to post

There are even options to wrap some or even any property assignments into one container object and write something like ButtonsList.Enabled := True

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

×