Best programming practises: the ListBox

When I started out with REALbasic / Xojo I used the ListBox both to store and display data. It worked but the code was cumbersome and hard to maintain and expand as everything tended to be hardwired like this:

ListBoxEmplyees(0, 0).cell = TextFieldName.text
ListBoxEmplyees(0, 1).cell = TextFieldFirstName.text
ListBoxEmplyees(0, 2).cell = TextFieldStreet.text
ListBoxEmplyees(0, 0).cell = TextFieldCity.text

Nowadays I don’t pass strings around anymore, I use objects like this:

// create new Employee object
dim Employee as new cEmployee

// set properties of Employee object
Employee.name = TextFieldName.text
Employee.FirstName = TextFieldFirstName.text
Employee.Street = TextFieldStreet.text
Employee.City = TextFieldCity.text

and then I simply pass around those objects:

// store Employee object in array
Window1.ListOfEmplyees.append Employee

// store Employee object in ListBox RowTag
// first create a new row in the ListBox
ListBoxEmplyees.addRow ""
ListBoxEmplyees.RowTag(ListBoxEmplyees.LastIndex) = Employee

Note that there is only ONE object, and that it is only a pointer to that object that is stored in the ListOfEmplyees and a RowTag of the ListBox

Now I want my objects to be able to draw themselves, so they need to have a Draw method. I could simply add a Draw method to the object itself, but a better way is to define an interface CanDrawItself with two methods DrawAsList and DrawAsBusinessCard, and pass a graphics object to them for them to draw themselves on:

Public Sub DrawAsList(g as Graphics)
  // actual drawing code
End Sub

Public Sub DrawAsBusinessCard(g as Graphics)
  // actual drawing code
End Sub

Of course you can have many more drawing methods.

The advantages of using an interface are manyfold. For one thing - and very unlike a subclass - you can add it to many different objects! So all you have to do now in the Listbox CellBackgroundPaint event is to pass the ListBox cell’s graphic parameter g to the draw method of the object in the RowTag like this (in pseudo-code … note the Casting):

if PopupDrawSelection.text = "List" then
  ObjectType( me.RowTag( row ) ).DrawAsList( g )
end if

if PopupDrawSelection.text = "Business Card" then
  ObjectType( me.RowTag( row ) ).DrawAsBusinessCard( g )
end if

and it will draw on the graphics of the cell.

Um. One moment? ObjectType? How do I know what kind of object is in the RowTag???

That’s the beauty of it: it doesn’t matter what type the object is because any object that implements the interface can be called as being of that type like this:

If row < Me.ListCount Then
  CanDrawItself( me.RowTag( row ) ).Draw( g )
end if

With this is becomes easy to make a ListBox like this (note that this ListBox uses just one column):

Note that there are THREE different types of rows: one is based on a class cHEADER, one on a class cDEPARTMENT, and one on a class cEMPLOYEE. Each of them implements the CanDrawItself interface and draws itself in a one column listbox according to the code in its draw method.

A longer version of this will be an article in the forthcoming issue of xDev magazine, and the code will be on GitHub afterwards …

7 Likes