ORDA – Genealogy – Episode Four (by request !)

In the first, second, and third episodes of this series, we demonstrated the power of ORDA and how simple it is to manage related persons with a single , highly recursive table.

Today’s episode is focused on the user interface to help you create nice looking lists and grids!

Download Genealogy – Episode four

List boxes and styled text

Listboxes based on arrays and selections allow displaying styled text columns. This begs the question, “Can i do the same with list boxes based on collections and entity selections?”

As you may have guessed the answer is yes, but you may not know how to do it!

Of course, if the attributes of your entities are already styled, then “This.myStyledAttribute” will do the job without any programming (as long as the “multi-style” property of the column is checked). But when you have several attributes (name, firstname, zip code, phone, etc.), your goal is more…

to display this…                                        …rather than this !


Magic? not really, but STILL easy

In both of the above cases, we have a list box based on an entity selection created using DS (see previous episodes). Rather than display a single attribute (ex: This.name) or even an expression like «This.Firstname+” “+This.Lastname”, the column expression is a 4D method that returns a full text.

In one, the text is styled. In the other, it’s not.

Simple text (not styled)

$text:=$text+This.Title+" "+This.Firstname+" "+This.Lastname+Char(Carriage return)
$text:=$text+This.Address+Char(Carriage return)

$text:=$text+"email: "+This.email+Char(Carriage return)

Styled text based on a template

The trick here is to create a styled text template. To do so, just use a styled text area as shown on the left in sample below.

The area on the right is shown to remind you that styled text is based on span tags. You need to avoid span tags within your own tags. The string “[Lastname]” must be retrieved using the command replace string. This isn’t possible with [Lastname] or [Lastname] (i.e., the first char in different style of different color).


$text:=Replace string($text;"[title]";This.Title)
$text:=Replace string($text;"[firstname]";This.Firstname)

$text:=Replace string($text;"[cell]";This.Cell)
$text:=Replace string($text;"[email]";This.email)

Entity selections vs Collections

“What? Why should I compare these? They’re completely different!”

“Yes, you’re right. But wait…”

List boxes based on entity selections are great. They can hold thousands of entities, display them very quickly and nicely with as many columns as needed. They can display either compact data (as seen above) or separate attributes, etc.

The only thing they can’t do is display grids (matrix) of data. They can’t display a group of people from 1 to 100 in a first column,  then continue the group from 101 to 200 in a second column, etc. To be honest, list boxes based on a collection can’t do that either, however there is a nice way to do it with very little programming.

Let’s say our goal is to display a group of people like this:

blankThe list above shows only 9 persons, but there are more in the entity selection, let’s say there’s 28.

just a little math

The first thing we need to do is to find a way to have a unique number from 0 to 28 for each cell. Either from left to right, row by row; from top to bottom, or column by column.


To do this, just call a function with two parameters: the row number and the column number. In this case, the NumberOfColumns is 3 ($colNum from 0 to 2) and the needed NumberOfRows is 10  ($rowNum from 0 to 9). Knowing that, the calculation is easy:
Case “A”: $rowNum+($colNum*NumberOfRows)
Case “B”: ($rowNum*NumberOfColumns) + $colNum

The list box itself will display a collection filled with numbers from 0 to 9 that will represent the row numbers. Now it’s even more simple; No need to send the row number to the function. Each time the function is called, the row number is represented by This.value! So you just need to send the column number to the function so it can calculate the ID according to the row number!

And guess what ? That’s it! Rather than returning the calculated $i as a longint, just return the information you need like myEntity[$i].name as a text! (just be sure that the value of $i is lower than myEntities.length)

Code example for case “A”:

The expressions for the columns will be

CalcID(“vertical”;0) for 1st column
CalcID(“vertical”;1) for 2nd column
CalcID(“vertical”;2) for 3nd column
(etc; if more than 3 columns)

Now let’s see what the CalcID method looks like!

$rownum:=This.value // This.value is the current value of the collection (0,1,2,3,...)
$colNum:=$2         // $2 is given by the expression
If ($1="vertical")
   $0:=$rowNum+($colNum*numberOfRows) // numberOfRows is a previously calculated value (ex: 10)
   $0:=($rowNum*numberOfCols)+$colNum // numberOfCols is a previously calculated value (ex: ")
End if 

one more trick …

There’s no need to create individual functions per type of result you need. Simply send a parameter that will determine what you need in return and make your function return an object with the requested attribute filled!

Rather than creating three methods (getName, getPicture, getBirthday to return a text, a picture and a date) and typing these into column expressions…

getName(0)       // getName returns a text
getPicture(0)    // getPicture returns a picture
getBirthday(0)   // getBirthday returns a date

… create a single getAttributemethod that will return an object filled according to the parameter that you sent.

Question: Give me the name of the person!
Answer: Here’s an object with a name attribute containing what you asked for!

Here’s an example of the getAttribute method:

$result:=New object()
Case of
: ($1="name")
 $result.name:=contact.name          // name is requested: returned as attribute of $result
: ($1="portrait")
 $result.portrait:=contact.portrait  // portrait is requested: returned as attribute of $result
: ($1="birthday")
 $result.birthday:=contact.birthday  // birthday is requested: returned as attribute of $result
End case

Once this method is written, you can call it in column expressions:

getAttribute("name").name           // getAttribute returns an object
getAttribute("portrait").portrait   // getAttribute returns an object
getAttribute("birthday").birthday   // getAttribute returns an object

Have fun and let your imagination run wild!


Roland Lannuzel
• Product Owner & 4D Expert •After studying electronics, Roland went into industrial IT as a developer and consultant, building solutions for customers with a variety of databases and technologies. In the late 80’s he fell in love with 4D and has used it in writing business applications that include accounting, billing and email systems.Eventually joining the company in 1997, Roland’s valuable contributions include designing specifications, testing tools, demos as well as training and speaking to the 4D community at many conferences. He continues to actively shape the future of 4D by defining new features and database development tools.