[TIP] Hiding rows of a collection based listbox (you can't and that's OK)
In prior versions of 4D one of my favorite uses of array based listboxes is
for displaying lists of things and including a variable the user could type
into to filter the list as they typed. The technique involved two things:
the listbox had a 'hidden' array specified
the code for the search variable fired with the On After Edit form event
The code simply looped through the array being filtered and set the hidden
array depending on whether the string was found or not. It looks like this:
$search_str:=Get Edited Text+"@"
For($i;1;Size of array($nameArray))
I was updating a form that uses this trick to be a collection based
listbox. Collection based listboxes are so much more dynamic and flexible I
think it's worth the effort to convert them in most cases. But that's
another post. This post is about how to get the same effect since a
collection based listbox doesn't have the hidden array.
The first step was to identify what I was really trying to do. "I want to
hide some rows" I thought. Because that's what I did before. But really
what I want to do is filter the collection based on the search string.
Looking at it that way the first thought was, "well, I could just re-run
creating the collection." This can be too slow if the collection is built
from complicated conditions. And I wish I could use the query function
collections but it returns a new collection. That doesn't help me because I
already have the collection displayed in my listbox. And it has to be fast
or the users will hate it because it slows down typing.
So why not just change the listbox to show the collection resulting from
the query? I typically avoid this sort of thing because in the past it
meant duplicating the initial set of arrays in memory. But objects are
references to the data. So having two instances of the same collection does
not double the memory used. To try this out I added a new instance of the
collection like this:
Form.dataCollection:=Method_to_get_data // get the complete list of data
Form.displayCollection:=Form.dataCollection // set the list to the display
// each element of the collection is an object. The object has a
property, "name", that is being queried.
// this is the equivalent of the example I began with
Really, that's it. Because my display collection is selected from the data
collection any changes the user makes on the listbox also 'flow' or
'update' or 'are reflected in' the data collection. That's important to
remember - because the collections are references, including the listbox
column assignment, a change to the root element (in this case
Form.dataCollection) propagates to all other instances. If I didn't want
this I could modify my collection query to make a new, separate collection.
This heads off into the differences and similarities of collections and
entity selections and saving data which is more than I want to get into
I was updating a component that does stuff with method code (list the
methods, show the modified date, show the method attributes, etc.). Using
arrays I had to have an array for each data bit I wanted to display. With a
collection I create an object that has properties for each bit I want.
Takes about the same time to runs because the real work is getting the
code. But working with the collection is far easier. And the code to manage
all this on the form really shrank to about what you see above. There are
about 12 columns in the listbox. With arrays I had to identify each one
with a column and deal with all the array declarations. With a collection I
only need to declare it. The listbox columns set themselves to the correct
data type once I assign the data source.
To summarize - converting existing structures in 4D to utilize the new
language capabilities is as much about understanding what the actual task
is than about manipulating code. In this case the task is to display a
filtered list. The previous technique was to hide unwanted elements. The
new technique is to show the desired ones.
San Francisco, CA