List views are extremely flexible controls that allow information to
be displayed in a variety of ways. In this section, you will learn
How to use image lists with the list view control
How to switch between different display styles in a list view control
How to allow the user to edit individual list items
What Is a List View Control?
List view controls are one of the new common controls first released with
Windows 95. A list view control is used to display information and an associated
icon in one of four different formats:
Icon view displays rows of 32x32 pixel icons.
Small icon view displays rows of smaller 16x16 pixel icons.
List view displays small icons and list items arranged in a column.
Report view displays items and their associated icons, along with subitems
that are arranged in columns.
When you use a list view control, you can provide a menu or other method
to enable the user to switch between the different viewing modes.
The Windows Explorer uses a list view control and offers all four view
styles. The Explorer is shown in Figure 18.1 with the contents of the C:\
directory contained in a large icon view.
figure 18.1
The Windows Explorer uses a list view control.
List views are very popular with users because they offer several different
ways to display information. When you allow the user to switch between
view styles, the list view control puts the user in charge of how information
is displayed.
List view controls can be used to associate different icons with items
stored in the list view control, as the Explorer does for filenames. The
user is free to select between different sized icons, or even the report
view, which can display extra information about each item. List view controls
also support drag-and-drop operations, which enable the user to easily
move items to or from a list view control.
List View Control Properties
The list view control's properties are set using the Properties dialog
box in the same way other controls are. Some of the properties available
for list view controls are also available for list boxes. The list view
control property options include the following:
ID is used for the list view control resource ID. A default resource
ID, such as IDC_LIST1, is supplied by Developer Studio.
Visibleis used to indicate that the control is initially
visible. This check box is usually checked.
Disabledis used to indicate that the list should be initially
disabled. This check box is usually cleared.
Groupis used to mark the first control in a group. This
check box is usually cleared.
Tab Stopindicates that this control can be reached by pressing
Tab on the keyboard. This check box is usually checked.
Help IDindicates that a context-sensitive help ID should
be created for this control. This check box is normally cleared.
Viewspecifies the initial view used by the list view control.
Possible values are Icon, Small Icon, List, or Report.
Align specifies whether the items are aligned to the top or left
sides of the control. This property applies only in the icon or small icon
views.
Sort enables items to be sorted based on their labels as they are
entered into the list view control.
Auto Arrange specifies that items should be kept arranged when viewed
in the icon or small icon views.
Single Selectionenables a single list view item to be selected.
No Label Wrapspecifies that each item label must be displayed
on a single line rather than wrapped, as is the standard behavior.
Edit Labelsenables labels to be edited by the user. If this
property is enabled, you must handle edit notification messages sent by
the control.
Owner Draw Fixedindicates that the owner of the control,
instead of Windows, is responsible for drawing the control.
No Scrolldisables scrolling.
No Column Header removes the header control that is usually included
in the report view.
No Sort Header disables the sorting function that is available through
the header control.
Share Image List indicates that the image list used by the list
view control is shared with other image lists. You must destroy the image
list manually after the last list view control has been destroyed.
Show Selection Always indicates that a selected item is always highlighted,
even if the list view control doesn't have the focus.
Borderindicates that a border should be drawn around the
control.
A List View Control Example
As an example of how the basic properties of list view controls are used,
create a dialog box-based application. This program displays a list view
control containing three items. You can use radio buttons to switch between
the different view styles.
Use AppWizard to create a dialog box-based application named ListEx.
Feel free to accept any of the options offered by AppWizard; this example
works with any AppWizard parameters.
Adding Controls to the ListEx Dialog Box
You must add a total of five controls to the ListEx main dialog box: four
radio buttons and one list view control. Add the controls to the dialog
box as shown in Figure 18.2.
figure 18.2
The ListEx main dialog box in the dialog editor.
Properties for the list view and radio button controls are listed in
Table 18.1. All properties that aren't listed should be set to the default
values.
Table 18.1. Property values for controls in the ListEx
main dialog box.
Control
Resource ID
Caption
Icon view radio
IDC_RADIO_ICON
&Icon
Small radio
IDC_RADIO_SMALL
&Small
List radio
IDC_RADIO_LIST
&List
Report radio
IDC_RADIO_REPORT
&Report
List view control
IDC_LIST
none
Use ClassWizard to associate a CListCtrl member variable with
IDC_LIST, using the values from Table 18.2.
Table 18.2. Values for a new CListCtrl member variable
in CListExDlg.
Control ID
Variable Name
Category
Type
IDC_LIST
m_listCtrl
Control
CListCtrl
Next, use ClassWizard to create message-handling functions that are
called when the radio buttons are selected. Add a total of four member
functions to the CListExDlg class, using the values from Table
18.3.
Table 18.3. Values for new CListCtrl member functions
in CListExDlg.
Object ID
Class Name
Message
Function
IDC_RADIO_ICON
CListExDlg
BN_CLICKED
OnRadioIcon
IDC_RADIO_SMALL
CListExDlg
BN_CLICKED
OnRadioSmall
IDC_RADIO_LIST
CListExDlg
BN_CLICKED
OnRadioList
IDC_RADIO_REPORT
CListExDlg
BN_CLICKED
OnRadioReport
Associating Image Lists with a List Control
The images displayed in the list view next to each item are stored in image
lists that are associated with the list view control. Constructing and
managing image lists was discussed in Section 17, "Using Image Lists and
Bitmaps." An image list is added to a list view control with the SetImageList
function:
Two parameters are passed to the list view control: the address of the
image list and a style parameter that indicates the type of images stored
in the image list. There are three image list types.
LVSIL_NORMAL is used for the image list used in the icon view.
LVSIL_SMALL is used for the image list used in the small icon
view.
LVSIL_STATE is used for optional state images, such as check marks.
Just a Minute: After the
image list control has been added to the list view control, the list view
control takes responsibility for destroying the image list.
CAUTION: If the Share image
list property has been selected, the list view control will not destroy
the image list. You must destroy it yourself after the list view has been
destroyed.
If you don't destroy the image list, you will create a memory leak.
If you destroy the image list too early, the list view control will behave
unpredictably.
Creating the Image Lists
You must create two bitmaps for the ListEx application. One bitmap is used
for the large icon bitmap and one for the small icon bitmap. The two bitmaps
are shown in Figure 18.3. Each of the bitmaps contains two balls of the
same size, and each ball is a different color.
figure 18.3
Bitmaps used for the ListEx image lists.
The properties for the two bitmaps are provided in Table 18.4.
Table 18.4. Properties for the ListEx image list
bitmaps.
Resource ID
Width
Height
Background
IDB_BALLS
64
32
White
IDB_SM_BALLS
32
16
White
Adding Items to a List View Control
The LV_ITEM structure is used to represent an item in a list view
control. This structure is used when adding, modifying, or fetching list
view items. The data members for the LV_ITEM structure include
the following:
mask is used to indicate which members are being used for the
current function call. Possible values for this member are given later
in this section.
item contains the List View index of the item referred to by this
structure. The first item contained in the list view control has an index
of zero, the next has an index of one, and so on.
iSubItem contains the index of the current subitem. A subitem
is a string that is displayed in a column to the right of an item's icon
and label in the report view. The first subitem has an index of one. The
zero index is used by the actual list view item.
state and stateMask contain the current state of the
item and the valid states of the item.
pszText contains the address of a string that is used as the item's
label. This member must be assigned the LPSTR_TEXTCALLBACK value
if a callback function is used to set the item's text.
cchTextMax specifies the size of the buffer provided in the pszText
member if the structure is receiving item attributes. Otherwise, this member
is not used.
iImage contains the image list index for this item.
The LV_ITEM structure's mask member is used to indicate
which parts of the structure are valid or should be filled in. It can be
one or more of the following values:
LVIF_TEXT indicates that the pszText member is valid.
LVIF_IMAGE indicates that the iImage member is valid.
LVIF_PARAM indicates that the lParam member is valid.
LVIF_STATE indicates that the state member is valid.
Time Saver: When more than
one value is needed, combine them using the C++ OR|
operator:
listItem.mask = LVIF_TEXT | LVIF_IMAGE;
Inserting a List View Item
The InsertItem function is used to add an item to a list view
control:
m_listItem.InsertItem( &listItem );
A pointer to an LV_ITEM structure is passed as the parameter to
InsertItem. LV_ITEM data members are filled with data
for the new item before it is inserted.
Unlike the other three list view styles, the report view displays additional
information for each item contained in the list. The extra items are subitems
that are arranged in columns. Each list view item must have the same number
of subitems. For example, in the Windows Explorer, the subitems are used
for file information such as file size, file type, and modified date.
Columns for subitems are added to a list view control in two steps:
first, the LV_COLUMN data structure is initialized and then the
columns are added. List view columns are inserted using LV_COLUMN
structures and the InsertColumn function. The LV_COLUMN
structure has the following members:
mask indicates the member variables that are used for the current
function call. Values for the mask member variable are discussed at the
end of this section.
fmt specifies the alignment used for the column. There are three
possible values: LVCFMT_LEFT, LVCFMT_RIGHT, and LVCFMT_CENTER.
The first column must use the LVCFMT_LEFT value.
cx specifies the width of the column in pixels.
pszText points to a string containing the column text. If the
structure is used to fetch information, this member holds the address of
the buffer that contains the column heading text.
cchTextMax stores the size of the buffer that is pointed to by
pszText. This member is used only when receiving data.
iSubItem specifies the column number.
The mask member variable is used to specify which member values are valid.
Possible values include the following:
LVCF_FMT indicates that the fmt member is valid.
LVCF_SUBITEM indicates that the iSubItem member is valid.
LVCF_TEXT indicates that the pszText member is valid.
LVCF_WIDTH indicates that the cx member is valid.
After you fill in the values for an LV_COLUMN structure, the column
is added to the list view control using the InsertColumn function:
m_listCtrl.InsertColumn( nColumn, &listColumn );
Determining Which Items Are Selected
Unlike a list box control, no single message or function exists to determine
which items are selected in a list view control. Instead, you must use
the CListCtrl::GetNextItem function, as in this example:
int nSel = m_listCtrl.GetNextItem( -1, LVNI_SELECTED );
This code returns the index of the first selected item in the list view
control. GetNextItem has two parameters: the start item and a
search flag. If the start item is -1, the search starts with the
first item. The flag variable can include one geometric value and one state
value. The following are the geometric values:
LVNI_ABOVE: Searches for an item above the start item.
LVNI_ALL: Searches for the next indexed item. This is the default
value.
LVNI_BELOW: Searches for an item below the start item.
LVNI_TOLEFT: Searches for an item to the left of the start item.
LVNI_TORIGHT: Searches for an item to the right of the start item.
The following are the possible state values:
LVNI_DROPHILITED: Searches for an item that has the LVIS_DROPHILITED
state flag set.
LVNI_FOCUSED: Searches for an item that has the LVIS_FOCUSED
state flag set.
LVNI_HIDDEN: Searches for an item that has the LVIS_HIDDEN
state flag set.
LVNI_MARKED: Searches for an item that has the LVIS_MARKED
state flag set.
LVNI_SELECTED: Searches for an item that has the LVIS_SELECTED
state flag set.
If no item can be found that matches the search parameters, -1
is returned. Otherwise, the index of the first list item that satisfies
the criteria is returned.
Modifications to the CListExDlg Class
The CListExDlg class must have two small modifications made to
its class declaration:
You must add two CImageList member variables for the list view
control.
You must add a SetListView member function. This function handles
switching between different list view styles.
Add the source code provided in Listing 18.1 to the implementation section
of the CListExDlg class declaration.
TYPE: Listing 18.1. Changes to the CListExDlg class
declaration.
The CListExDlg::OnInitDialog member function is called when the
main dialog box is initialized. Add the source code in Listing 18.2 to
the OnInitDialog function, just after the // TODO comment
provided by AppWizard.
TYPE: Listing 18.2. Changes to the CListExDlg class
implementation.
The source code provided in Listing 18.2 creates two image lists for the
list view control and then creates the list view's column headers. After
the columns are created, the three list items are inserted into the list
view. The SetItemText function is used to add subitem text strings
to each list item--in this case, the name of the professional football
and baseball teams for each city.
Changing the Current View for a List View Control
Switching views in a list view control requires just a few lines of code.
The current view style is stored in a structure maintained by Windows.
This information can be retrieved using the GetWindowLong function:
A GWL constant that specifies the type of information requested--in
this case, GWL_STYLE
The return value from GetWindowLong contains all the Windows style
information for the list view control. If you are interested in the current
view, the unnecessary information should be masked off using LVS_TYPEMASK.
dwOldStyle &= ~LVS_TYPEMASK; // Mask off extra style info
After the mask has been applied, the style information is one of the following
values:
LVS_ICON
LVS_SMALLICON
LVS_LIST
LVS_REPORT
To change to another view, you use the SetWindowLong function.
When applying a new list view style, you must make sure that the style
bits that are not associated with the list view style are left undisturbed.
This is usually done in the following four steps:
1. Get the existing window style bit information using GetWindowLong.
2. Strip off the list view style information, leaving the other style
information intact.
3. Combine a new list view style with the old style information.
4. Apply the new style information using SetWindowLong.
These steps are often combined into a few lines of code in the following
way:
The source code provided in Listing 18.3 is used to switch between list
view styles. When a radio button is selected, its message-handling function
is called. Each message-handling function passes a different list view
style parameter to the SetListView member function. The SetListView
function uses the SetWindowLong function to change the list view
to the selected style.
TYPE: Listing 18.3. Functions used to change the
control's view style.
Compile and run the ListEx sample program. The list view initially displays
its contents in the icon view. Try using the radio buttons to switch between
views. When the report view is displayed, use the header control to change
the spacing between columns. Figure 18.4 shows the ListEx program running.
figure 18.4
The ListEx sample program shows subitems and small icons in Report
view.
Congratulations! You now have an example of a basic list view control.
In the next few sections, you will add label editing and drag-and-drop
functionality.
Editing Items in Place
The list view control offers a built-in edit control that you can use to
edit items contained in the control. You can add this feature to the ListEx
example in a few minutes. In order to take advantage of this capability,
the list view control must have the LVS_EDITLABELS style, which
is set by clicking the Edit Labels check box on the list view's property
page.
In addition to setting the list view control's style, you must handle
two notification messages that relate to label editing:
LVN_BEGINLABELEDIT is sent just before editing begins.
LVN_ENDLABELEDIT is sent after editing has been completed.
When your application receives the LVN_BEGINLABELEDIT message,
you have the opportunity to allow or prevent editing of the label. As with
other notification messages, one of the parameters passed to your handler
for the notification messages is *pResult. You should set this
value to FALSE or zero to allow the label edit to begin, or TRUE
to prevent the label edit from starting.
Time Saver: To access the
edit control used to edit the label, use the GetEditControl member
function in the CListCtrl class.
In most cases, you respond to the LVN_ENDLABELEDIT message
and update the application's data. To allow a change, set *pResult
to TRUE; to reject a label change, set *pResult to FALSE.
CAUTION: If you allow the
user to change a label, you might need to update the underlying data. There
are two cases in which you should not update your data: if the LV_ITEM
pszText member passed with the message is NULL, or if the
LV_ITEM iItem member is set to -1. In these cases, the
user has canceled the label-editing operation.
To add label editing to the ListEx example, begin by opening the IDD_LISTEX_DIALOG
dialog box resource. Check the Edit Labels check box in the styles property
sheet for the list control. Next, add message-handling functions for the
label editing messages, using the values from Table 18.5.
Table 18.5. Values for drag-and-drop functions in
CListExDlg.
Object ID
Class Name
Message
Function
IDC_RADIO_LIST
CListExDlg
LVN_BEGINEDIT
OnBeginlabeleditList
IDC_RADIO_LIST
CListExDlg
LVN_ENDEDIT
OnEndlabeleditList
Listing 18.4 shows the source code for the message-notification handlers
to be added to the CListExDlg class.
TYPE: Listing 18.4. Handling the LVN_ENDLABELEDIT
and LVN_ENDLABELEDIT messages.
Just a Minute: Remember
that the Edit control updates only what is displayed in the list view control;
you must update your document or other database in response to LVN_ENDLABELEDIT.
DO/DON'T: DO set the Edit labels property for the list control.
DO handle the LVN_BEGINLABELEDIT message and set *pResult
to FALSE to allow an edit to begin.
DO handle the LVN_ENDLABELEDIT message and set *pResult
to TRUE to allow an edit to be completed.
DO remember to update your application's data after an edit
has been completed.
DON'T forget to test iItem and pszText to
see whether the user has canceled the edit operation.
Summary
In this section, you learned about the list view control and how it is
used in an MFC-based program. You have also learned how this control interacts
with image lists and header controls, and you built a sample application
to demonstrate how a list view control is used.
Q&A
Q How can I limit the amount of text the user can enter when editing
a label?
A When handling the LVN_BEGINLABELEDIT message, set
the maximum text limit by calling the edit control's LimitText
member function:
Q In a multiple selection list view, how can I determine which items
have been selected?
A As discussed earlier this section, you can use CListCtrl's
GetNextItem member function to determine the first selected item
in a list view control. To determine all selected items, you must use a
while loop to continue to search for selected items until you
reach the end of the list, like this:
The Workshop is designed to help you anticipate possible questions, review
what you've learned, and begin thinking ahead to put your knowledge into
practice. The answers to the quiz are in Appendix B, "Quiz Answers."
Quiz
1. What styles are available for the list view?
2. What are the sizes of the icons used by the list view control?
3. What messages must be handled when the user edits a label?
4. How do you discover that the user has canceled a label edit?
5. What Windows function is used to change list view styles?
6. What steps are required to add a column to a list view?
7. How do you add text for a list view subitem?
8. What is the return value when the InsertItem function fails?
Exercises
Make it possible for a user to add new items to the list view control
in the ListEx project.
2. Add a pop-up menu to the ListEx project that enables the user to
select which list view style should be used. Display the pop-up menu when
the user right-clicks the list view control.