For..next / dynamic properties

  1. 2 weeks ago

    Thomas H

    Nov 28 Stuttgart, Germany

    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

    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.

  2. Marc Z

    Nov 28 Pre-Release Testers, Xojo Pro, XDC Speakers Oregon

    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.

  3. Art G

    Nov 28 Prescott AZ

    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
  4. Thomas H

    Nov 29 Stuttgart, Germany

    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

  5. Louis D

    Nov 29 Pre-Release Testers, Xojo Pro Montreal, QC, Canada
    Edited 2 weeks ago

    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)

    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

    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.

  6. Dave S

    Nov 29 San Diego, California USA
    Edited 2 weeks ago

    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

  7. Louis D

    Nov 29 Pre-Release Testers, Xojo Pro Montreal, QC, Canada

    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:

    ' récupérer 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.Prénom = 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.Prénom = ""
        spartner.Nom = "" 
        spartner.Tel1 = "" 
        spartner.courriel1 = "" 
        spartner.role = "" 
        
      Wend
    End If

    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!

  8. Greg O

    Nov 29 Xojo Inc

    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.

  9. Louis D

    Nov 29 Pre-Release Testers, Xojo Pro Montreal, QC, Canada

    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.

  10. Thomas H

    Nov 30 Stuttgart, Germany

    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

  11. last week

    Thomas H

    Dec 5 Stuttgart, Germany

    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

  12. Thomas H

    Dec 5 Stuttgart, Germany

    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

  13. Louis D

    Dec 5 Pre-Release Testers, Xojo Pro Answer Montreal, QC, Canada

    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.

  14. Thomas H

    Dec 5 Stuttgart, Germany

    Thanks for this idea. Was looking for something like this, but been "blind", I think...
    I'll check out with your "2"...

or Sign Up to reply!