Dynamic controls

Hi All,
I’m a hobby programmer writing Mac apps for my own use/amusement.

I’m doing something at the moment and decided to try creating “dynamic controls” (I’m not sure if that is the correct term). That is a series of controls that differ in number depending on particular circumstances.

I’ve got it working but, as there are quite a lot (labels, text boxes, buttons etc), the code is VERY long. I thought it would be possible to group them into/onto 1 group and then just create that, but can’t seem to work that out. I’ve tried using a group box, but the individual controls don’t create. I then considered a Container Control but the docs say they can’t be used in an array. Finally, I had the idea of a tab control and add the appropriate number of panels as required. The panels create ok, but I can’t figure out how to place controls onto them.

So, my questions are:
Is there a control that will group other controls so they are created together each time?,
Is there a way to add the controls to a tab panel after it has been created?

Finally, a “minor” issue which I’m sure is simple and I’m just missing it, what is the command to end a program running? Eg., user answers “no” to a questions that needs a “yes” to continue, so the program ends.

I appreciate any help (as you’ve always done before),
Thanks,
Barry

You can embed a ContainerControl on a panel of a TabPanel (or PagePanel).

http://documentation.xojo.com/api/deprecated/containercontrol.html#containercontrol-embedwithinPanel

Call the Quit() method.

Andrew,
Thanks for that. I’ve followed the link and got the idea. So my next question then, is how do I reference those controls?

For example if I create a container control with a text box on it. The TabPanel is named Tab, the Container is named Container and the text box is Text (I know, very original! … just for example sake)

I use the following to create 3 panels and embed the container:

for i As Integer = 1 To 3
Tab.AddPanel
Tab.CaptionAt(i) = i.ToText
var MyContainer As New Container
MyContainer.EmbedWithinPanel(RoundsTab, i)
Next

That works ok, but how do I reference the 3 text boxes. I’ve tried MyContainer(x).Text but am told I can’t use the Container as an array and I can’t figure out any other variant Tab(x)… Tab.panel(x)… etc.

Thanks,
Barry

BTW, thanks for the “Quit()” method :slight_smile:

Containers and windows have a method named Control() for iterating over the controls within. It returns a reference of datatype Control, which is the base class from which all GUI widget classes (such as TextField) inherit. Cast the Control reference as a TextField to access its Text property.

Dim t As TextField = TextField(MyContainer.Control(x))
t.Text = "Hello, world!"

Casting a control as the wrong subclass will raise an IllegalCastException, so be careful.

When you use ContainerControls, instead of setting them up as a Control Set (used to be call Control Array), you must maintain your own array of them. So you might do

for i As Integer = 1 To 3
   Tab.AddPanel
   Tab.CaptionAt(i) = i.ToText
   var MyContainer As New Container
   MyContainer.EmbedWithinPanel(RoundsTab, i)
   TabContainers.Append MyContainer
Next

Then to access them it would look like

var c as Container = TabContainer(RoundsTab.PanelIndex)
c.Text = "Hello World"
1 Like

Thanks Tim,
Have run that. Where does TabContainers come from. Do I have to declare that and what as?

yeah, sorry, that would be a property declared somewhere as an array of type Container.

TabContainer() as Container

You would be responsible for keeping it in sync with the number or tabs in the tabpanel and making sure that the tab panelindex aligns with the index in the array. If you can do that, then you have access to the appropriate container on that tab.

And it would be c.Text.Text = “Hello World”

Assuming your TextField is named Text as per above.

would be the same
MyContainer.MyTextField.Text = “Hello, world!”
you can also add computed propertys to access the inner controls.

Hey guys,
I really appreciate the help. I feel like I’m nearly there but then I have probs understanding some of this more complicated stuff. I understand arrays such as
var ABC() as string = array(1,2,3,4)
and then accessing it with
ABC(1) = 2 etc…

But I’m struggling with some of this.

What I have so far is:
(The tab panel is called RoundsTab, the container is TestContainer and it has a text field on it called TextField1)

var TabContainers() as TestContainer
for i As Integer = 1 To 5
RoundsTab.AddPanel
RoundsTab.CaptionAt(i) = i.ToText
var MyContainer As New TestContainer
MyContainer.EmbedWithinPanel(RoundsTab, i)
TabContainers.Append MyContainer
Next

var c as TestContainer
for i As Integer = 0 to RoundsTab.PanelCount - 1
c = TabContainers(RoundsTab.PanelIndex)
c.TextField1.Value = "Hello World " + i.ToText
Next

This results in 5 tabs with a text field on each but only the text field in tab 1 has “Hello World 5”. The others are blank.

I think I get the first block but it’s the second that is causing problems. I’ll have a look through the docs and try and figure it out, any help appreciated (also understand if you lose patience tho).

Barry

If you’re posting code it looks better (i.e., easier for the reader) if you select the code lines and click on </>, like this:

var c as TestContainer
for i As Integer = 0 to RoundsTab.PanelCount - 1
c = TabContainers(RoundsTab.PanelIndex)
c.TextField1.Value = "Hello World " + i.ToText
Next

I think you’re referencing the same tabcontainer each time. Should it not be:

for i As Integer = 0 to TabContainers.LastIndex
c = TabContainers(i)
c.TextField1.Value = "Hello World " + i.ToText
Next
1 Like

a suggestion if you review code parts, try to memory where the variables / data came from.
especially this came outside of the for loop → RoundsTab.PanelIndex

Hi everyone,
Thanks so much for the help, you are all terrific. I think I’ve now got a handle on nearly all of this. Just one aspect left. How do I reference the controls from outside their little “bubble”?

For example, I have now dropped the tab idea and am going with creating an array of the container controls & displaying them one under each other on a PagePanel.

This is an example of the code:
I have a container object called MasterGameContainer and it contains several other objects. This is the code to display 6 of them, one on top of the other:

var controlTop As integer = 200
var GameContainerArray() as MasterGameContainer
for i As Integer =  1 to 6

  var GameContainer As New MasterGameContainer
  GameContainer.EmbedWithinPanel(MainWindowGroup,0,20,controlTop)  
  GameContainerArray.Append GameContainer

  controlTop = controlTop + 70
Next


var tmpGameContainer As MasterGameContainer
for i As Integer = 1 to GameContainerArray.LastIndex
  tmpGameContainer = GameContainerArray(i-1)
  tmpGameContainer.HomeTeamLabel.Value = "something"
  tmpGameContainer.AwayTeamLabel.Value = "something else"
  tmpGameContainer.GroundLabel.Value = "more things"
  tmpGameContainer.HomeGoalText.value = "a text value"
  tmpGameContainer.HomeBehindText.value = "another text value"      
Next

That’s all good and works ok and I (think) I understand it. What I need, is how do I reference the info in the labels and/or text boxes? Under an array system, they would be (for example), HomeGoalText(6) and I would access/change the value by HomeTeamText(x).Value but I don’t know what I call it from inside the rest of the app.

Also, to access an event such as TextChanged there would be an index number to reference the particular text control. How do I reference these?

This has been great and I appreciate it. That bit of code above, replaced 221 lines of code!!
(I realise that’s probably not a surprise to you guys)

I apologise for the length of all this but, just to clarify the above.

If I were using an array of, say, 3 text boxes Text(3) and wanted to have the TextChanged event dynamically update a database with a table called Table and 3 records names DATA_1, DATA_2 and DATA_3, I could put an SQLite UPDATE command into the TextChanged event:

“UPDATE Table SET DATA_” + Index.totext

Index is obviously the index of the Text Box. How do I do that using the container array?

The key here is the concept of Scope. If you VAR an array inside a method or event, it only exists within that method. To access it outside the method, you need to give it a wider scope. In this case, you should make it a property of the window. Then any event / method in the window can access it.

or you can give the obj reference with a defined event. you can define a event from context menu.
you can also add a property Index to your container control.

see also Runtime — Xojo documentation
if you use dynamic controls
also useful
https://documentation.xojo.com/api/code_execution/addhandler.html
RemoveHandler — Xojo documentation
the registered methods always get the sender object too

Thanks guys.

Tim, I understand scope, are you suggesting I var the container as a property of the window or the app rather than just in the method that displays the objects? If that’s so, I still don’t get how I access the particular object in the “array” of containers.

MarkusR, thanks, I don’t understand most of that, but I will read into the docs you suggested. I’m sure a lot of the stuff I often look for is in there, it’s just finding it, can be the charm.

Barry

Without knowing more about your particular usage, it’s difficult to guide you. I looks like you’re putting a container on each tab of the tabpanel. If so, you’re safe to use the PanelIndex to index the array and access the appropriate container. In the end, it’s up to you do devise some way to keep the array in sync with the tabs, or create some other mechanism to know which container in the array you need.

Hi Tim,
Thanks for your patience. What I’m actually doing is writing an app to keep track of a sporting competition we run down here (think your gridiron). Each ContainerControl has labels for the date, each team and scores (we keep track of all the goals and then how many points that adds up to not just the points tally). It also has a label for the ground where it is played and the time.

There are 23 rounds of 9 games each. When I call up a round, I display 9 of those controls one one top of the other on a PagePanel.

It’s interesting that if writing something into one of the events on a control, it can see all the others on it’s container, which makes interacting with them easy. I’ve placed an invisible label on the containercontrol which I use to store the “game number” from the loop when setting them up. That allows each control to be able to recall the position it is in when writing out to the DB etc., but I can’t figure out how to access back “in” from outside.

It’s all good, I haven’t had to do the latter yet, but it irks me not knowing how (as I sense there must be a way).

Anyway, I REALLY appreciate all the help. Even up to here, as I said previously, I have swapped couple hundred lines for around 30 or 40. Now I’ve got a start, I’ll keep delving. Great thing about just being a hobby, is it’s not critical :slight_smile:

Barry

SUCCESS!!! - I know you’ll all sleep easier tonight. :slight_smile:

I changed the array that stores the ContainerControl to a property of the window - GameContainerArray() of type MasterGameContainer. Now, it appears I can access then control with:

  GameContainerArray(x).Label.Value (or TextBox etc).

Thank you all so much for your help.

Barry

2 Likes

over time you will find more solution approaches with learning by doing :slight_smile:
with computed propertys this row …

GameContainer.HomeTeamLabel.Value = "something"

would look like

GameContainer.HomeTeam = "something"

in the computed propertys you can use the label as storage direct.

with a method it would look

GameContainer.HomeTeam "something"

you can also use the containercontrol to save your data via a method there.
would be easier to access the data from inside.
GameContainerArray(x).SaveData(DB)