For..next / dynamic properties

Hello to all,

this might be a typical beginners question, but I can’t help myself.
I try to do something with For…Next loop.
As an example (for web app):
I have a changing number of different boxes and want to set (dynamically) the properties (values):

Number of boxes As Integer…

For i As Integer = 1 To NumberOfBoxes
Length[i] = 3.00
Width[i] = 3.00
Height[i] = 3.00
Next

How can I add the “counter” to the property so I have "Length1, Length2, Length3… in the end?
And this brings me to the next question:
Also… can I create (session) properties the same way (“on the fly”), as I do not know the number of boxes I need in advance?
I did something similar in a desktop app years ago (unfortunately stopped working on Xojo after that, but try to get into again…)

Or is it better to it with “Classes”? I just started learning about this…

Any help appreciated.
Thanks. Thomas

You need to use arrays for your variables. An array is like a list, with each item stored in a separate container referenced by index number. You create an array like this:

dim myLength() as double

This creates an empty array with no items. To add items, you use the append keyword:

myLength.append 3.00

Once an item has been added, you can refer to it by number. The first item is 0, the second is 1, the third 2, etc. You can use the ubound command to return the last item in the array so that you don’t try to access an item that isn’t there.

if ubound(myLength) is <= 3 then msgBox str(myLength(3))

If you know the exact number of items your array will need, you can define that during its definition:

dim myLength(numberOfBoxes-1) as double

Note that I subtracted one from numberOfBoxes since arrays are zero-based (they start at zero).

With that established, your code works (with a few adjustments)

For i As Integer = 1 To NumberOfBoxes Length(i) = 3.00 Next

Does that make sense? You can read more about arrays in the Language Reference now that you know what you are looking for.

You could define a class called boxclass with properties of Length, Width, and Height (amongst other properties like the x and y location for example). Then create an array to hold your boxes, Boxes(). Then:

For Each b As boxclass in Boxes b.Length = 3.0 b.Width = 3.0 b.Height = 3.0 Next

To Mark and Art:
Thanks for your input… I already tried with Array, but:
Maybe I should have been more precise…

My dimensions of boxes come from a MySQL database. All boxes may have different dimensions.
I “read” them with a sql statement and have a recordset. But in the recordset there are different values like “ID” (Integer) , “dimensions” (Length, Width… as Double) and also “Name” AS String (like “Box 1”).
If I put it in an array, I have to convert all to strings, I think.
From recordset I have the total number of boxes (totalnumber=rs.recordcount)

But the array can only be of one type (“String” or “Double”…)?
And I would need a 2-dimensional array.
So I am stuck with creating some sort of “table” that can hold 2-dimensional data and will be flexible in number of items. So I think I will have to end up with Classes?

For example I will have 3 boxes with
ID Name Length Width X-Coordinate… maybe more

1 Box 1 100,00 200,00 80
2 Box 2 150,00 250,00 120
3 Box 3 180,50 320,40 180
…and so on (depending on the number of boxes in recordset.

This is what I intended to do with my initial For…Next…
Maybe you canhelp again, because I “do not see the wood because of the trees…” (…poorly translated from a german saying).

Thanks anyway for your thoughts
Thomas

So, you are reading from the database the box parameters. Perhaps what you need is something like this:

in a module, define structure myBoxStructure like this (as an example)

P1 as integer P2 as integer P3 as integer P4 as double P5 as string * 75

in a method of your window or webpage (or container)

[code]Dim myBox() as myboxStructure
Dim RS1 as recordset
Dim SQL as string = “Select * from boxparameters”

dim interimbox as myBoxStructure

RS1 = myconnection.SQLSelect(SQL)

If RS1.recordcount>0 then
’ initialize the interim box
interimbox.P1 = 0
interimbox.P2 = 0
interimbox.P3 = 0
interimbox.P4 = 0
interimbox.P5 = “”

'get the data
rs1.movefirst
while not RS1.EOF
'Assign the data to the interim box
interimbox.P1 = RS1.idxfield(1).value
interimbox.P2 = RS1.idxfield(2).value
interimbox.P3 = RS1.idxfield(3).value
interimbox.P4 = RS1.idxfield(4).value
interimbox.P5 = RS1.idxfield(5).value

’ append the box array
mybox.append(interimBox)
RS1.movenext
'reset the interim box before reading the next one
interimbox.P1 = 0
interimbox.P2 = 0
interimbox.P3 = 0
interimbox.P4 = 0
interimbox.P5 = “”
wend
[/code]

Now, you need to be careful when using structures especially if you need 32 and 64-bit executables. The same could be achieved with a class “mybaseBox” and an array of classes, say myBoxes() as new myBaseBox, where the structure elements described earlier are actually properties of the class.

I am writing all this on the fly without an IDE so there is surely a dozen little bugs in what I wrote, but you should get the general idea.

create a class that matches you table schema
create an Array of that class
load the array from you database

structures have a place… but I don’t think this is it

also… in the above example. by “resetting” Interimbox each time, you are destroying the data you just read… unless you use NEW inside the loop… you just have a pointer to the structure, and are continually over-writing

no Dave. the box array was updated first. You may not like this approach, and granted it may not be the best, but it does work very well. I use this appoach in many places. Here is an example of it working in prod just fine:

[code]’ rcuprer les partenaires
If Session.con.connect = True Then
RS1 = Session.Con.SQLSelect(sql)
Else
Dim B As Boolean
B = Session.SetSessionConn(Session.ZENV)
RS1 = Session.Con.SQLSelect(sql)
End If

If RS1.recordcount > 0 Then
RS1.Movefirst
While Not RS1.eof
spartner.PartId = rs1.field(“partid”).value
spartner.PartType = rs1.field(“parttype”).value
spartner.Prnom = DefineEncoding(rs1.field(“prenom”).value, encodings.utf8)
spartner.Nom = DefineEncoding(rs1.field(“nom”).value, encodings.utf8)
spartner.Tel1 = DefineEncoding(rs1.field(“tel1”).value, encodings.utf8)
spartner.courriel1 = DefineEncoding(rs1.field(“courriel1”).value, encodings.utf8)
If session.Langue = “FRA” Then
spartner.role = DefineEncoding(rs1.field(“desc_fr”).value, encodings.utf8)
Else
spartner.role = DefineEncoding(rs1.field(“desc_en”).value, encodings.utf8)
End If

Self.OthPartners.append(sPartner)

RS1.movenext

spartner.PartId = 0
spartner.PartType = 0
spartner.Prnom = ""
spartner.Nom = "" 
spartner.Tel1 = "" 
spartner.courriel1 = "" 
spartner.role = "" 

Wend
End If[/code]

Here, othPartners is a property of a webcontainer dimmed as zpartner. Spartner is also dimmed As zpartner; A lot of code is missing, this is just an extract of the method. Not also that usually, I have a table for descriptions where the object ID and language are used as key. in this specific case, I did it wrong. It is on my to-do list to have just one description field in the table, with a configuration table (or dynamic constant) to feed it. But the fact is, it works!

Just to be clear, while structures may work in your case, you’re likely to be limiting yourself down the line and setting yourself up for a major refactor because of the simple fact that structures cannot contain code any code and the elements can’t contain objects. If you needed to store a date, sure you could store it as a string, but then every time you want to use it as a date, you have to convert it.

For a new user that’s just getting started with Xojo, the right way to do this is to use a custom class. It’ll give you the most flexibility going forward and because everything that you work with in Xojo is a class, you’ll get more relevant experience doing so.

FWIW, the whole reason that Structures exist is for working with Declares. Just because you can use them instead of a class doesn’t mean that you should.

I was just served my own medicine… This is a sentence I use a lot.

Well, I will begin my refactoring now, as I need to bring an upgrade online in a few weeks. In other places, I do use classes. Using structures in this way is an old habit from VB6 and it is sooo comfortable. Time to start with a new habit, I suppose. Hopefully this is going to be useful to others also.

Hi to all and thank you for your time and thoughts!
I will try (my best) on classes over the weekend…
and leave this channel “open” and will report when it’s done.
Maybe another question will come up…?
Best regards. Thomas

Hello again,
just to report and with a new question:
I (think I) solved the problem with “For… Next” loop with class.

  • I created a class “ClsBox” with properties “length”, “width”, “leftX”, “leftY” (all Integer) in the Content section of the IDE.
  • in Webpage1 with SQL select and Recordset I can add all data to instances of ClsBox and these are all stored in an array “ClsBoxAll()” that holds all boxes with properties - where ClsBoxAll() is a property of Webpage1 (with type ClsBox).
    So far it seems to be fine.

After that I have to switch to Webpage2 and read data from ClsBoxAll().
So, if I want to use the instances from Webpage2, I always have to use “Webpage1.ClsBoxAll()…”?

And, as I have a Web app, is class “ClsBox” in “Contents” also Session based? I mean, if differnet people access the Web app, do they all have their “own” ClsBoxes?

Thanks again for your help and maybe further advice.
Thomas

by the way… maybe a good source for other beginners who will end up here…
I read through some “books” of Eugene Dakin…
If interested, you can get the books here

Link to website of scispec.ca

Thomas

If you must use the class array from multiple pages, then you have 2 main options options (and then a few others also…):

1- use only one page, and create webcontainers for all the pages in your current design. You can embed webcontainers in your current page programmatically when required. In this case, your array ClsBoxAll() can be a property of the page and is easily accessible from any of the webcontainers embedded in the page.
2- make ClsBoxAll() a property of the session. This is my preferred approach with data that must be shared by many process flows in my application. All webcontainers or all webdialogs or all webpages can access the array.

Another way would be to call a new page, passing as parameter whatever box you want to use in that page. That works very well also and I use this approach in some cases. It all depends on how much of the data must be reused frequently and how many roundtrips to the database you are saving this way.

You could also make ClsBoxAll() a property of a module, but I don’t use this approach much. Session or page work just fine.

Thanks for this idea. Was looking for something like this, but been “blind”, I think…
I’ll check out with your “2”…