The NSListView control exposes the Windows ListView control for the
NSBasic applications. For that purpose it has internal object structure in
which every ListView element is exposed to the application as an object
with appropriate methods and properties.
IMPORTANT: The NSListView object does not currently work from the NS Basic ToolBox. To use this control, use an AddObject statement in your Form_Load routine and set the properties you need in code. See the examples for sample code.
The example lines below assume you have AddObject "NSBasic.comctl.ListView",
"MyListView", ...
Click on the object of interest to go to the page describing it.
The control's architecture may look complex at first, but in fact the
usage of the control is quite simple. Each element is accessible through
an object named after the element it represents thus enabling you to write
code without checking the documentation for each line you write. To help
you follow the naming convention the ListView terminology is listed below.
The same terminology is used in MSDN and the documentation of other
ListView ActiveX controls - if you have ever used one you will find it
familiar.
All the Items, columns and SubItems can be accessed, changed and
otherwise managed no matter the current view. However the columns and the
sub items will be visible only in report view.
The NS BASIC ListView control is easy to use despite the number of the
objects in it. Their actual purpose is to represent each element in the
control i a convenient way and thus enable you work with it in intuitive
way.
The ListView is a control that can be used in various ways - it can
present simple pictured list of items with text labels, but it can also
work like a table/grid and display data in columns with sorting and other
extras. Furthermore the control can expose all the items in it as
checkboxes, the items (in report/grid view) can be indented and the
control may become quite similar to a tree view. All this makes it the
control of choice for many tasks. Depending on the application the control
can be pre-configured for a certain view only (e.g. as a grid or as icon
view), but sometimes it is convenient to enable the user to change between
2 or more views. The classic example is the Windows Explorer in which the
files can be viewed as big/small icons or in a report view with detailed
information about the file in each row.
So, where to begin? We will begin with a simple icon view in mind, but
we will then move to a report view. One of the most important features of
the control is that all the elements are accessible and creatable no
matter if the current view shows them or not. For example you can perform
tasks that will fill information for the report view while the list is
shown as icons - the data you add in the sub items will not be visible
until the view is changed to report. This means that the application can
always assume that the control is in report/grid view and manage its
contents in this manner. So, if the task the application is performing
deserves a report view you can think in its terms no matter if it is
allowed to change the way the user views the data (thus hiding/unhiding
parts of it when the view is changed).
NSListView1.ListItems.Add 1,"Item1
Key","My item 1"
NSListView1.ListItems.Add 2,"Item2 Key","My item 2"
NSListView1.ListItems.Add , ,"My item 3"
We add the items through the ListItems
collection. The control has many elements and that is why we need to be
specific (For example we may add also columns or images).
All the arguments of the ListItems.Add method are optional, thus (in
the 3-d line above) we decided to omit most of the item parameters. This
is what the control will look like after these lines
:
There are no icons? Well, lets load some:
NSListView1.Icons.LoadBitmap "bigicons1.bmp"
' note that you may need to specify the full path to the bitmap in the
real world
By default the items use the first icon from the loaded image. The
image we loaded above actually looks like this:
The image list is a collection of images all with the same size. When a
bitmap is loaded it expects there 1 or more images of the size currently
configured. The images are to be ordered from left to right in the
bitmap. By default the Icons image list is configured for 32x32
pixel images.
Now lets change the item images:
NSListView1.ListItems("Item1
Key").Icon = 1
NSListView1.ListItems("Item2 Key").Icon = 2
NSListView1.ListItems(3).Icon = 3
Above we have set keys for some of the items. This means that we can
do the same this way:
NSListView1.ListItems(1).Icon = 1
NSListView1.ListItems(2).Icon = 2
NSListView1.ListItems(3).Icon = 3
So, the keys can be used as item name which can be specified instead
of an index. This can be quite useful when the data in the control has
no definite order.
Let's now do all this again, but in much simpler way - saving
some typing work:
NSListView1.Icons.LoadBitmap
"bigicons1.bmp"
Set itm = NSListView1.ListItems.Add(1,"Item1 Key","My
item 1")
itm.Icon = 1
Set itm = NSListView1.ListItems.Add(2,"Item2 Key","My
item 2")
itm.Icon = 2
Set itm = NSListView1.ListItems.Add( , ,"My item 3")
itm.Icon = 3
The Add method returns the just created item, thus giving us
chance to do something more with its properties. We can also use the
rest of the optional parameters of the Add method and eliminate
the need to specify the item's image on a separate line:
NSListView1.ListItems.Add 1,"Item1
Key","My item 1", 1
NSListView1.ListItems.Add 2,"Item2 Key","My item 2",
2
NSListView1.ListItems.Add ,
,"My item 3", 3
NSListView1.Icons.LoadBitmap "bigicons1.bmp"
Let's talk about line 3. It does not specify an index for the created
item - where it will be put? The answer is it will be appended at the
end of the ListItems collection. Thus skipping the index is equivalent
to:
NSListView1.ListItems.Add
NSListView1.ListItems.Count, ,"My item 3", 3
Therefore when we add the items sequentially or the order does not
matter to the application we can just skip the index.
Now suppose we want to cycle through all the items and extract some
information from them. For the example we will use their Text property.
We can do this in two ways:
' Normal cycle
For I = 1 to NSListView1.ListItems.Count
MsgBox NSListView1.ListItems(I).Text
Next
or
' Enumeration
For Each I In NSListView1.ListItems
MsgBox I.Text
Next
Which way is better? Actually neither is better than the other, the
latter needs a bit less code, but aside of that all depends on any other
needs that you may have. The second technique is called enumeration.
The control exposes enumerations in many places - for the items, for the
columns, for the sub items of each item, for the items/sub items in each
column and so on. Generally the enumeration is more effective especially
if you enumerate sub items (discussed below).
Before continue let's change the view. Firs to small icons:
NSListView1.View = 2
Again no icons, what are we missing? All the other views except Icon
view use the SmallIcons image list. We have nothing loaded in it
yet, so no icons appear in the control. Let's load another bitmap - with
smaller images (16x16 by default):
NSListView1.SmallIcons.LoadBitmap "smallicons1.bmp"
the bitmap looks like this:
and the control after the above line:
Now let's switch to report view and see what we can do with the
control in its most powerful form.
NSListView1.View = 1
What we see? There is one column with empty caption and all the items
are listed one under another in it. The SmallIcons are used. Can we put
some caption to that column? Yes:
NSListView1.Columns(0).Text = "Items"
We can also set an icon to that column:
NSListView1.Columns(0).Icon = 1
But remember the columns have separate image list from which their
icons are fetched and we have nothing in it yet. so to make the above
two lines work we should:
NSListView1.ColumnHeaderIcons.LoadBitmap "smallicons1.bmp"
NSListView1.Columns(0).Icon = 1
NSListView1.Columns(0).Text = "Items"
By default the size of the header icons is 16x16 - the same as for
the small icons, but you can change that (discussed later).
What is important about the report view? It looks like a table or let
say a grid control. The NSListView is especially designed to make
table-like usage convenient and natural. Now is the time to note some
characteristics of the NSListView control. If you have some experience
with other ListView controls or with the native Windows control (using C
for example) it is very likely that you have some troubles especially if
you have attempted dynamic changes of the columns and the items.
NSListView hides virtually all the sensitive details and you can safely
assume the report view a table/grid. To refine the table abstraction let
say that the Items we discussed above play the role of rows and the
columns are just columns. Thus the first column is special - it cannot
be deleted, the items are listed in it, the column can be changed, but
it is always present.
That's why the index of the first column is 0 while everything else
in the control has 1-based indices. Furthermore the Columns.Count
returns only the count of the columns you added without this first
column which we will call later column 0. Thus the columns can be
assumed to have 1-based indices if you count only the columns you can
freely add and delete.
Let's add 3 columns:
NSListView1.Columns.Add , "Column1
Key","Column 1", 50
NSListView1.Columns.Add , "Column2 Key","Column 2",
50
NSListView1.Columns.Add , "Column3 Key","Column 3",
100, 2, 4
If we want to cycle through them we can do it like this:
For I = 1 To NSListView1.Columns.Count
NSListView1.Columns(I).Text = "My Column " & I
Next
changing their caption text a little. As we said above this cycle
will cycle only through the columns we added and will not include the
fixed column 0. If we want to include it into the cycle we should change
it:
For I = 0
To NSListView1.Columns.Count
NSListView1.Columns(I).Text = "My Column " & I
Next
Now lets use enumeration instead of an index cycle:
For Each col In NSListView1.Columns
col.Text = "Column " & col.Index
Next
We end up with something like this
Both cycles do the same work. When working with columns it is more
likely that an index cycle will be better choice because for columns we
will need more often to know the exact index. However, we shall see
later that the enumerations are generally more convenient for almost
everything else.
Back to the columns. The Columns.Add method has these parameters:
.Add index, Key, Text, Width, Alignment, Icon
All the arguments are optional, but you must be aware of the specific
behavior of the columns about the icons. If no
icon is specified when the column is created (added) no icon can be ever
set to that column. This behavior of the native Windows
control is preserved in the ActiveX for a reason - the icons even empty
occupy some space. And this is the only way to get rid of that space.
So, if the space in the column's caption is more precious than the
opportunity to show icons in the column's header - omit the image
argument, but remember that if you assign something to the Icon property
of that column nothing will happen. To illustrate that lets cycle
through the columns again and try to assign images:
For I = 0
To NSListView1.Columns.Count
NSListView1.Columns(I).Icon = I - 1
Next
Nothing will change no matter that we actually have 4 icons with
indices 1-4. If we change the code with which the columns have been
created and specify some initial image the cycle will have effect.
What is the key for? Like with the items it can be used instead of the
index:
Set mycolumn = NSListView1.Columns("Column2 Key")
will return the same as:
Set mycolumn = NSListView1.Columns(2)
because we keyed that column "Column2 Key"
Now let's do something with the "cells" of the table or in the terms of
the list view - the sub items. Providing the columns are not reordered
(see the Column.Position property) the column 0 (the left most one)
contains the items which in the other view modes (icon, small icons and
list) are the only visible elements in the work area of the control. To
show something in the other columns we need to set some sub-items of
some items. Lets do it randomly, because such a code will make use of
many of the involved features:
Dim Itm, J, o
For Each Itm In NSListView1.ListItems
Randomize
J = Int(NSListView1.Columns.Count * Rnd) + 1
Itm(J).Text = "SubItem " & J
Itm(J).Icon = Int(4 * Rnd) + 1
Next
And the result will be something like this:
What the code does: It cycles through all the items in the control.
Thinking of them as rows you can say it is a cycle through all the rows.
For each row a random column is chosen, but never column 0 because it
contains the items themselves. And through the item its subitems are
accessed. The items are ListItem objects and
their SubItems property gives us access to their sub-items which are SubItem
objects. The SubItems property actually holds a SubItems
collection object thus one of the highlighted lines above can be written
this way:
Itm.SubItems.Item(J).Text = "SubItem
" & J
However the SubItems is a default property of the ListItem object and
the Item property of the SubItems collection is its default property and
this enables us use the shorter syntax:
Itm(J).Text = "SubItem " & J
which exposes the item's sub-items as if they are its array
elements.
The NSListView attempts to expose the list view functionality in a
way that enables you to work with it as with a grid. Of course you
should remember that it is not a grid, but the resemblance is very
close. So there is one more trick here - the ColumnItems collection. It
enables us to access the items or the sub-items from a column. So lets
go through all the columns (this time including the column 0 and access
every item or sub-item in each column:
Dim c,I,J
For I = 0 to NSListView1.Columns.Count
Set c = NSListView1.Columns(I)
For J = 1 To NSListView1.ListItems.Count
c.ColumnItems(J).Text = "Col=" & I
& ".Row=" & J
Next
Next
And the result will be pretty much:
Now, how to handle events? All the events fired by the control
have the same syntax, for instance to handle the OnClick event you need
a routine like this:
Sub NSListView1_OnClick(oEvent)
........
End Sub
All the events carry one argument - oEvent in the above
example. The argument is not a simple value but an object - NSListViewEvent.
The object has a few properties among which the Item, SubItem
and Column are most interesting. Some or all of these 3 contain
references to a ListItem object (Item property),
SubItem object (SubItem property) and Column
object (Column property). What means some or all then?
The different events concern different elements of the control. For
example the user may have clicked an item or a sub-item. The event is
the same - OnClick, but the objects causing it may be different. This
applies to many of the other events. It is convenient to provide the
event handling code with references to all the objects related to the
event and thus free the code of the need to deduce them from minimal
data (index for example). In the above OnClick event handler we can use:
oEvent.Column.Text = "Clicked !!!"
This line will change the caption of the column in which an item or
sub-item has been clicked. As you can see the event handler does not
need to deduce which column is this - it has a reference to it
ready-to-use. Still, there is one more thing to be aware of - depending
on the event and how exactly it has occurred the related objects may be
different. For instance if an item has been clicked there is no relevant
SubItem. Thus in an OnClick event handler that works with SubItem's it
may happen that an item has been clicked and there is no relevant
sub-item to the event. To find one of these 3 properties contains
something one can use code like this:
If Not oEvent.SubItem Is Nothing Then
' There is a SubItem in this event - do something with it
End If
The same technique is used for any of these 3 properties.