A list control consists of using one of four views to display a list of items. The list is typically equipped with icons
that indicate what view is displaying. There are four views used to display items:
- Icons: The control displays a list of items using icons
with a 32x32 pixels size of icons. This is the preferred view when the
main idea consists of giving an overview of the items
- Small Icons: Like the other next two views, it uses 16x16
pixel icons to display a simplified list of the items. Once more, no
detail is provided about the items of this list. The list is organized
in disparate columns with some on top of others. If the list is supposed
to be sorted, the alphabetical arrangement is organized from left to
right.
- List: This list, using small icons, is also organized in
columns; this time, the columns are arranged so that the first column
gets filled before starting the second. If the list is sorted, the
sorting is arranged in a top-down manner.
- Report: This view displays arranged columns of items and provides as many details as the list developer had arranged it.
|
|
Practical Learning: Introducing the List Control
|
|
- Start Microsoft Visual C++ or Visual Studio and create an MFC Application named
DeptStore2
- Create it as Dialog Based
- Delete the TODO line and the OK button
- Change the Caption of the Cancel button to Close
A list control is implemented in the MFC library by the CListCtrl class. At design time, to create a list control, on the Controls toolbox, click the List Control button
and click the desired area on a dialog box or a form. Normally, you
should expand its dimensions beyond the default assigned because a list
control is usually used to display its items on a wide rectangle.
To programmatically create a list control, declare a variable or a pointer to
CListCtrl. To initialize the control, call its Create() method. Here is an example:
BOOL CPictureDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: Add extra initialization here
CListCtrl *lstCtrl = new CListCtrl;
lstCtrl->Create(WS_CHILD | WS_VISIBLE,
CRect(10, 10, 320, 280), this, 0x285);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
As mentioned already, a list control can display its items in
one of four views. To specify the desired view at design time, on the
Properties window, select a value from the View combo box. The default
value is Icon. The possible values are:
|
|
Icon: To get this value when programmatically creating the control, add the LVS_ICON style: |
|
|
Small Icon: This is the same as adding the LVS_SMALLICON style to a dynamic list control |
List: You can get the same result when creating the control
with code by adding the LVS_LIST style: |
|
|
Report: This view displays the items in explicit columns. It is the same as adding the
LVS_REPORT style |
Besides the regular styles, the Win32 library provides extended styles for a list control. To apply an extended style, call the
CListCtrl::SetExtendedStyle() method. Its syntax is:
DWORD SetExtendedStyle(DWORD dwNewStyle);
When calling this method, pass the desired extended style or a combination of these styles as argument. Some of the values are:
LVS_EX_CHECKBOXES: The items of the control will display a check box on their left side:
LVS_EX_FULLROWSELECT: This style allows the whole row of a Report view to be selected instead of just the item:
LVS_EX_GRIDLINES: The control’s items in Report view will display
with horizontal grid lines that separate items and vertical grid lines
that separate columns items or categories:
LVS_EX_TRACKSELECT: When this style is set, if the user positions
the mouse on an item for a few seconds without clicking, the item would
be automatically selected.
The items of a list control can display only
within the control, if there are too many of them or the total width of
the items is larger than the control can display, it would be equipped
with either a vertical scroll bar, a horizontal scroll bar, or both. If
you want to prevent scroll bars from displaying even if the list’s items
go beyond the allocated rectangle, set the No Scroll property to True
or create the control with the
LVS_NOSCROLL style.
Once the list has been created, the user can
select an item by clicking it. To select more than one item, the user
can press and hold either Ctrl for random selection or Shift for range
selection. Here is an example of a random selection:
If you do not want the user to be able to
select more than one item at a time, set the Single Selection property
to True or create the control with the
LVS_SINGLESEL style.
Any item that is selected is highlighted. When
the user clicks another control or another application, you can decide
whether you want the item(s) selected to still show its (their)
selection. State. This characteristic is controlled at design time by
the Show Selection Always property. By default, it is set to False,
meaning that when the control looses focus or its parent application is
deactivated, an item that is selected would not show it. Otherwise, you
can set this property to True to indicate the selected item even when
the control is not active. This property is available through the
LVS_SHOWSELALWAYS style.
When creating the list, its items are sorted
in alphabetical order using the items text as reference. Even if you add
items later on, they are inserted in the appropriate order. This
sorting feature is controlled at design time by the Sort combo box box.
By default, the items of a list control are sorted in alphabetical order
using the Ascending value or the
LVS_SORTASCENDING style. If you want items to be sorted in
reverse alphabetical order, set this property to Descending or create
the control with the
LVS_SORTDESCENDING style.
|
Practical Learning: Creating a List Control
|
|
- On the Toolbox, click the List Control and click the dialog box
Change its ID to IDC_STORE_ITEMS
- Right-click the list control and click Add Variable…
- Set the Variable Name to m_StoreItems
- Click Finish
|
After visually adding or dynamically creating a list control, the next
action you probably would take is to populate the control with the
desired items. This can be taken care of by calling the
CListCtrl::InsertItem() method. One of its syntaxes is as follows:
int InsertItem(const LVITEM* pItem );
This version requires an LVITEM pointer as argument. The LVITEM structure is defined as follows:
typedef struct _LVITEM {
UINT mask;
int iItem;
int iSubItem;
UINT state;
UINT stateMask;
LPTSTR pszText;
int cchTextMax;
int iImage;
LPARAM lParam;
#if (_WIN32_IE >= 0x0300)
int iIndent;
#endif
} LVITEM, FAR *LPLVITEM;
The mask member variable is used to specify the types of values you want to set for the current item.
The value of iItem specifies the index of the item that is being changed. The first item would have an index of 0. The second would be 1, etc. The
iSubItem member variable is the index of the sub item for the current value. If the current item will be the leader, the
iSubItem is stored in a 0-based array. If it is a sub item, then it is stored in a 1-based array.
The pszText member variable is the string that will display as
the item. You can specify the length of this text by assigning a value
to the
cchTextMask variable.
After initializing the LVITEM variable, you can pass it to the InsertItem() method to add it as a new item to the list. Here is an example that creates items and displays as a List view:
BOOL COthersDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: Add extra initialization here
LVITEM lvItem;
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 0;
lvItem.iSubItem = 0;
lvItem.pszText = "Sandra C. Anschwitz";
m_List.InsertItem(&lvItem);
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 1;
lvItem.iSubItem = 0;
lvItem.pszText = "Roger A. Miller";
m_List.InsertItem(&lvItem);
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 2;
lvItem.iSubItem = 0;
lvItem.pszText = "Marie-Julie W. Gross";
m_List.InsertItem(&lvItem);
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 3;
lvItem.iSubItem = 0;
lvItem.pszText = "Ella Pius Roger";
m_List.InsertItem(&lvItem);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
The state member variable of the LVITEM structure specifies what
to do with the new item. For example, once the item has been added, you
may want to prepare it for deletion prior to a cut-and-paste operation,
in which case you would give it a value of
LVIS_CUT. If the item is involved in a drag-and-drop operation, you can assign it a state value of
LVIS_DROPHILIGHTED. To give focus to the item, set its state value to LVIS_FOCUSED. An item with an
LVIS_SELECTED state value will be selected.
Besides the above version of the CListCtrl::InsertItem() method, the CListCtrl class provides this other version:
int InsertItem(int nItem, LPCTSTR lpszItem);
This is a good simplification of the earlier version. The nItem argument is the index of the new item to de added. Like the
LVITEM::iItem member variable, the value of this argument is 0 if the item will be the leader. The
lpszItem value is the string that will be used to lead the current item.
|
Practical Learning: Populating a List Control
|
|
- To create a new dialog box, on the main menu, click Project -> Add Resource…
- In the Add Resource dialog box, click Dialog and click New
- Change the ID of the dialog box to IDD_STOREITEMS_DLG
- Design the dialog box as follows:
|
Control |
ID |
Caption |
Static Text |
|
Item #: |
Edit Control |
IDC_ITEMNUMBER |
|
|
- Right-click the dialog box and click Add Class…
- Set the Class Name to CNewStoreItemDlg and base it on CDialog
- Click Finish
- Add a CString Value Variable associated with the Item # edit control and name it m_ItemNumber
- Display the first dialog box. Under the list control, add a Button and set its properties as follows:
Caption: New Item
ID: IDC_NEWITEM
- Double-click the new button to generate its OnBnClicked event
- In the top section of the file, type:
// DeptStore2Dlg.cpp : implementation file
//
#include "stdafx.h"
#include "DeptStore2.h"
#include "DeptStore2Dlg.h"
#include ".\deptstore2dlg.h"
#include "NewStoreItemDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
|
- Implement the event as follows:
void CDeptStore2Dlg::OnBnClickedNewitem()
{
// TODO: Add your control notification handler code here
CNewStoreItemDlg dlg;
srand( (unsigned)time(NULL) );
char strNumber[20];
int number1 = rand() % 999;
int number2 = rand() % 999;
sprintf(strNumber, "%d-%d", number1, number2);
dlg.m_ItemNumber = strNumber;
if( dlg.DoModal() )
{
LVITEM lvItem;
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 0;
lvItem.iSubItem = 0;
lvItem.pszText = strNumber;
this->m_StoreItems.InsertItem(&lvItem);
}
}
|
- Execute the application and create a few item numbers
|
Whether you use the first or the second version, the InsertItem()
method allows you to create the item that will display for the Icon,
the Small Icon, or the List views of the control. If you plan to display
the list in Report view (or to allow the user to transition from
various views) and you want to provide more information for each item,
you must “create” a report of information for each item.
Among the possible views available for a list
control, one of them can display columns. This view is called the report
view. This view is not required for a list view but it is the only one
that provides more detailed information about the items of the list. If
you plan to display that view on your list control, then you should
create columns. (Alternatively, you can omit creating columns and
instead provide headers of columns separately, which can be done using
the
CHeaderCtrl class. Otherwise, the list control provides the means of creating columns for its report view.)
To create the column(s) of a list control, you can use the
CListCtrl::InsertColumn() method. One of its syntaxes is:
int InsertColumn(int nCol, const LVCOLUMN* pColumn);
The nCol argument is the index of the column that this call will create.
The pColumn argument is an LVCOLUMN pointer. This structure is defined as follows:
typedef struct _LVCOLUMN {
UINT mask;
int fmt;
int cx;
LPTSTR pszText;
int cchTextMax;
int iSubItem;
#if (_WIN32_IE >= 0x0300)
int iImage;
int iOrder;
#endif
} LVCOLUMN, FAR *LPLVCOLUMN;
The mask member variable is used to specify what attribute of the column you want to define with this
LVCOLUMN variable.
The fmt member variable formats the text of the column. For
example, it can be used to align the text of the column to the left (the
default) (LVCFMT_LEFT), the center (LVCFMT_CENTER), or the right (LVCFMT_RIGHT).
If you do not set a value for this member variable, the text will be
aligned to the left. If you plan to set a value for this variable, then
add the
LVCF_FMT value for the mask member variable.
The cx variable is used to specify the width occupied by the
text of the column. If you do not set a value for this property, the
column would initially appear so narrow its text would not display.
Therefore, unless you have a good reason to omit it, you should always
specify the value of this variable. If you plan to set a value for this
property, then add the
LVCF_WIDTH value to the mask member variable.
The pszText is the string that will appear as the text of the
column. Just like all the other member variables, this one is not
required but, besides the rectangle that limits the column header, this
member is probably the most important characteristic of a column because
it informs the user as to what this column is used for. The string of
this variable can be provided as a null-terminated value. Like all other
strings used in an MFC application, this string can also be a value of a
String Table item. It can also be retrieved from an array of strings.
To set a value for this member variable, add the
LVCF_TEXT value to the mask variable. The length of this string can be specified by assigning a value to the
cchTextMax member variable.
After initializing the LVCOLUMN variable, pass it as the CListCtrl::InsertColumn() second argument. Here is an example:
BOOL COthersDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: Add extra initialization here
LVCOLUMN lvColumn;
int nCol;
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 120;
lvColumn.pszText = "Full Name";
nCol = m_List.InsertColumn(0, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 100;
lvColumn.pszText = "Profession";
m_List.InsertColumn(1, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 80;
lvColumn.pszText = "Fav Sport";
m_List.InsertColumn(2, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 75;
lvColumn.pszText = "Hobby";
m_List.InsertColumn(3, &lvColumn);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
The iOrder member variable is used to identify the column addressed by the
LVCOLUMN variable.
Besides, or instead of, the above version of the CListCtrl::InsertColumn() method, you can use the following version to create columns for the control:
int InsertColumn(int nCol,
LPCTSTR lpszColumnHeading,
int nFormat = LVCFMT_LEFT,
int nWidth = -1,
int nSubItem = -1);
This version simplifies the first a little bit. The nCol argument is the index of the column that will be configured. The second argument,
lpszColumnHeading, is the string that will be displayed on the column header. It follows the same rules as the
LVCOLUMN::pszText member variable.
The optional nFormat argument is used to specify the horizontal alignment of the
lpszColumnHeading text. It can be set to LVCFMT_LEFT for left alignment (the default),
LVCFMT_CENTER for center alignment, or LVCFMT_RIGHT for right alignment. If you do not specify this argument, the text would be aligned to the left. The
nWidth argument is used to set the width of the column
header in pixels. If you do not want to specify this argument, pass it
at –1. The nSubItem is used to set the index of the sub item used on the
current column.
With the columns configured, you must provide a string that will be
displayed under a particular column header for an item. To do this, you
must first specify which item will use the information you are going to
add. The
InsertColumn() method returns an integer that is the index
of the new item. You can use this returned value to identify the column
whose information you are adding. Then to specify a string for each
column of the current item, call the
CListCtrl::SetItemText() method. Its syntax is:
BOOL SetItemText(int nItem, int nSubItem, LPTSTR lpszText);
The nItem argument is the index of the column whose information you are adding. It can be the return value of a previously called
InsertColumn(). The pieces of information for each item are
stored in a 0-based array. The index of the current sub item is
specified using the nSubItem argument. The
lpszText is the actual text that will display under the column for the current item.
Here is an example:
|
BOOL COthersDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: Add extra initialization here
LVCOLUMN lvColumn;
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 120;
lvColumn.pszText = "Full Name";
m_List.InsertColumn(0, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 75;
lvColumn.pszText = "Profession";
m_List.InsertColumn(1, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 80;
lvColumn.pszText = "Fav Sport";
m_List.InsertColumn(2, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 75;
lvColumn.pszText = "Hobby";
m_List.InsertColumn(3, &lvColumn);
LVITEM lvItem;
int nItem;
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 0;
lvItem.iSubItem = 0;
lvItem.pszText = "Sandra C. Anschwitz";
nItem = m_List.InsertItem(&lvItem);
m_List.SetItemText(nItem, 1, "Singer");
m_List.SetItemText(nItem, 2, "HandBall");
m_List.SetItemText(nItem, 3, "Beach");
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 1;
lvItem.iSubItem = 0;
lvItem.pszText = "Roger A. Miller";
nItem = m_List.InsertItem(&lvItem);
m_List.SetItemText(nItem, 1, "Footballer");
m_List.SetItemText(nItem, 2, "Tennis");
m_List.SetItemText(nItem, 3, "Teaching");
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 2;
lvItem.iSubItem = 0;
lvItem.pszText = "Marie-Julie W. Gross";
nItem = m_List.InsertItem(&lvItem);
m_List.SetItemText(nItem, 1, "Student");
m_List.SetItemText(nItem, 2, "Boxing");
m_List.SetItemText(nItem, 3, "Programming");
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 3;
lvItem.iSubItem = 0;
lvItem.pszText = "Ella Pius Roger";
nItem = m_List.InsertItem(&lvItem);
m_List.SetItemText(nItem, 1, "Architect");
m_List.SetItemText(nItem, 2, "Ping-Pong");
m_List.SetItemText(nItem, 3, "Songo");
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
|
Practical Learning: Implementing a Detail View of a List Control
|
|
- Display the first dialog box and click the list control on it
- In the Properties window, change its View property to Report
- To create the columns of the list control, access the OnInitDialog() event of the main dialog box and change it as follows:
BOOL CDeptStore2Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
LVCOLUMN lvColumn;
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_CENTER;
lvColumn.cx = 60;
lvColumn.pszText = "Item #";
this->m_StoreItems.InsertColumn(0, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 100;
lvColumn.pszText = "Category";
this->m_StoreItems.InsertColumn(1, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 160;
lvColumn.pszText = "Item Name";
this->m_StoreItems.InsertColumn(2, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 80;
lvColumn.pszText = "Size";
this->m_StoreItems.InsertColumn(3, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_RIGHT;
lvColumn.cx = 60;
lvColumn.pszText = "Unit Price";
this->m_StoreItems.InsertColumn(4, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_RIGHT;
lvColumn.cx = 30;
lvColumn.pszText = "Qty";
this->m_StoreItems.InsertColumn(5, &lvColumn);
this->m_StoreItems.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
return TRUE; // return TRUE unless you set the focus to a control
}
|
- To prepare creating a complete item, access the second dialog box and change its design as follows:
|
Control |
Caption |
ID |
Other Properties |
Static Text |
Category: |
|
|
Combo Box |
|
IDC_CATEGORIES |
Data: Babies;Teens;Women;Men;Miscellaneous |
Static Text |
Item Name: |
|
|
Edit Control |
|
IDC_ITEMNAME |
|
Static Text |
Item Size: |
|
|
Edit Control |
|
IDC_ITEMSIZE |
|
Static Text |
Qty: |
|
|
Edit Control |
|
IDC_QUANTITY |
|
Static Text |
Unit Price: |
|
|
Edit Control |
|
IDC_UNITPRICE |
|
Static Text |
Item #: |
|
|
Edit Control |
|
IDC_ITEMNUMBER |
|
Button |
OK |
IDOK |
|
Button |
Cancel |
IDCANCEL |
|
|
- Create the CString Value Variables associated with the controls as follows:
ID |
Value Variable |
IDC_CATEGORIES |
m_Categories |
IDC_ITEMNAME |
m_ItemName |
IDC_ITEMSIZE |
m_ItemSize |
IDC_QUANTITY |
m_Quantity |
IDC_UNITPRICE |
m_UnitPrice |
- Access the main dialog box. Double-click its New Item button and change its implementation as follows:
void CDeptStore2Dlg::OnBnClickedNewitem()
{
// TODO: Add your control notification handler code here
CNewStoreItemDlg dlg;
srand( (unsigned)time(NULL) );
char strNumber[20];
int number1 = rand() % 999;
int number2 = rand() % 999;
sprintf(strNumber, "%d-%d", number1, number2);
dlg.m_ItemNumber = strNumber;
if( dlg.DoModal() )
{
LVITEM lvItem;
int nItem;
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 0;
lvItem.iSubItem = 0;
lvItem.pszText = strNumber;
nItem = this->m_StoreItems.InsertItem(&lvItem);
this->m_StoreItems.SetItemText(nItem, 1, dlg.m_Category);
this->m_StoreItems.SetItemText(nItem, 2, dlg.m_ItemName);
this->m_StoreItems.SetItemText(nItem, 3, dlg.m_ItemSize);
this->m_StoreItems.SetItemText(nItem, 4, dlg.m_UnitPrice);
this->m_StoreItems.SetItemText(nItem, 5, dlg.m_Quantity);
}
}
|
- Execute the application and create a few items as follows (let the computer generate the item numbers):
Category |
Item Name |
Size |
Qty |
Unit Price |
Women |
Cashmere Lined Glove |
8 |
12 |
115.95 |
Miscellaneous |
Chocolate Gift Box |
Medium |
5 |
45.00 |
Men |
Trendy Jacket |
Medium |
8 |
45.85 |
Women |
Stretch Flare Jeans |
Petite |
6 |
27.75 |
Women |
Belted Sweater |
Large |
10 |
15.95 |
Teens |
Girls Classy Handbag |
One Size |
4 |
95.95 |
Women |
Casual Dress Shoes |
9.5M |
16 |
45.95 |
Babies |
Infant Girls Ballerina Dress |
2M |
14 |
22.85 |
Teens |
Girls Velour Dress |
10 |
8 |
12.55 |
Women |
Lace Desire Panty |
M |
22 |
7.15 |
Teens |
Boys Hooded Sweatshirt |
M (7/8) |
16 |
42.75 |
Men |
Classic Pinstripe Suit |
38 |
8 |
145.90 |
|
|
- Close the dialog box and return to your programming environment
|
You can create a list control that displays its items in a single view
or you can allow the user to change from one view to another. As
mentioned already, at design time or when programmatically creating the
list control, you can set the initial view using either the View combo
box to select a view’s value or by adding one of the view styles. If you
want to display only that initial view, you can stop there. Otherwise,
you can provide a means of changing views.
Because the view displayed on a list control
is part of its style, in order to programmatically change its view mode,
you can first retrieve the control’s style using the
GetWindowLong() function. The GetWindowLong()
function only retrieves the current style of the control. You may need
to check it first before changing it. This can be done by ANDing the
value of the
GetWindowLong() function with the LVS_TYPEMASK constant. After checking the view of the control, you can then change its style by calling the
SetWindowLong() function. Here is an example:
void COthersDlg::OnIconBtn()
{
// TODO: Add your control notification handler code here
LONG mListStyle = GetWindowLong(m_List.m_hWnd, GWL_STYLE);
mListStyle &= ~LVS_TYPEMASK;
mListStyle |= LVS_ICON;
SetWindowLong(m_List.m_hWnd, GWL_STYLE, mListStyle);
}
void COthersDlg::OnSmallIconBtn()
{
// TODO: Add your control notification handler code here
LONG mListStyle = GetWindowLong(m_List.m_hWnd, GWL_STYLE);
mListStyle &= ~LVS_TYPEMASK;
mListStyle |= LVS_SMALLICON;
SetWindowLong(m_List.m_hWnd, GWL_STYLE, mListStyle);
}
void COthersDlg::OnListBtn()
{
// TODO: Add your control notification handler code here
LONG mListStyle = GetWindowLong(m_List.m_hWnd, GWL_STYLE);
mListStyle &= ~LVS_TYPEMASK;
mListStyle |= LVS_LIST;
SetWindowLong(m_List.m_hWnd, GWL_STYLE, mListStyle);
}
void COthersDlg::OnReportBtn()
{
// TODO: Add your control notification handler code here
LONG mListStyle = GetWindowLong(m_List.m_hWnd, GWL_STYLE);
mListStyle &= ~LVS_TYPEMASK;
mListStyle |= LVS_REPORT;
SetWindowLong(m_List.m_hWnd, GWL_STYLE, mListStyle);
}
|
Practical Learning: Changing the View of a List Control
|
|
- In the Class View, expand DeptStore2. Right-click CDeptStore2Dlg -> Add -> Add Function…
- Set the Return Type to DWORD and the Function Name to GetViewType
- Click Finish and implement the method as follows:
DWORD CDeptStore2Dlg::GetViewType(void)
{
return (GetStyle() & LVS_TYPEMASK);
}
|
- In the Class View, right-click CDeptStore2Dlg -> Add -> Add Function…
- Set the Return Type to void and the Function Name to SetViewType
- Set the Parameter Type to DWORD
- In the Parameter Name, type dwViewType and click Add
- Click Finish and implement the method as follows:r file of the first dialog box and declare two methods as follows:
void CDeptStore2Dlg::SetViewType(DWORD dwViewType)
{
DWORD dwCurType;
HWND hWnd;
hWnd = this->m_StoreItems;
GetSafeHwnd();
dwCurType = ::GetWindowLong(hWnd, GWL_STYLE);
dwCurType &= ~LVS_TYPEMASK;
dwViewType |= dwCurType;
::SetWindowLong(hWnd, GWL_STYLE, dwViewType);
}
|
- Access the first dialog box and add four buttons to it as follows:
|
Button ID |
Caption |
IDC_LARGE |
Large |
IDC_SMALL |
Small |
IDC_LIST |
List |
IDC_DETAILS |
Details |
|
- Double-click the Large button and implement its OnBnClicked event as follows:
void CDeptStore2Dlg::OnBnClickedLarge()
{
// TODO: Add your control notification handler code here
SetViewType(LVS_ICON);
}
|
- Return to the dialog box. Double-click the Small button and implement its event as follows:
void CDeptStore2Dlg::OnBnClickedSmall()
{
// TODO: Add your control notification handler code here
if( GetViewType() != LVS_SMALLICON)
SetViewType(LVS_SMALLICON);
}
|
- Return to the dialog box. Double-click its List button and implement the event as follows:
void CDeptStore2Dlg::OnBnClickedList()
{
// TODO: Add your control notification handler code here
if( GetViewType() != LVS_LIST)
SetViewType(LVS_LIST);
}
|
- Return to the dialog box and double-click the Details button to implement its event as follows:
void CDeptStore2Dlg::OnBnClickedDetails()
{
// TODO: Add your control notification handler code here
if( GetViewType() != LVS_REPORT)
SetViewType(LVS_REPORT);
}
|
- Save all
|
A list control can be configured to display pictures that accompany
either the columns, the list items, or both. If you want to display a
bitmap on the column, you should declare and initialize a
CImageList variable. Then call the CListCtrl::SetImageList() method and pass it as argument. If you plan to do this, and if you are using the first version of the
CListCtrl::InsertColumn() method that takes an LVCOLUMN pointer as argument, then add the
LVCF_IMAGE value to the mask variable and add the LVCFMT_IMAGE value to the fmt variable. To specify the image that will display on the column header, assign its index to the value of the
iImage variable. Here is an example:
BOOL COthersDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: Add extra initialization here
LVCOLUMN lvColumn;
CImageList *ImgHeaders = new CImageList;
ImgHeaders->Create(16, 16, ILC_MASK, 1, 1);
ImgHeaders->Add(AfxGetApp()->LoadIcon(IDI_UP));
ImgHeaders->Add(AfxGetApp()->LoadIcon(IDI_LOSANGE));
m_List.SetImageList(ImgHeaders, LVSIL_SMALL);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_IMAGE;
lvColumn.fmt = LVCFMT_LEFT | LVCFMT_IMAGE;
lvColumn.cx = 120;
lvColumn.pszText = "Full Name";
lvColumn.iImage = 0;
m_List.InsertColumn(0, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 100;
lvColumn.pszText = "Profession";
m_List.InsertColumn(1, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_IMAGE;
lvColumn.fmt = LVCFMT_LEFT | LVCFMT_IMAGE;
lvColumn.iImage = 1;
lvColumn.cx = 80;
lvColumn.pszText = "Fav Sport";
m_List.InsertColumn(2, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 75;
lvColumn.pszText = "Hobby";
m_List.InsertColumn(3, &lvColumn);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
To use bitmaps or icons on a list control’s
items, you should first create bitmaps or icons. If you do not plan to
use the Report view and you want to use bitmaps, you can create a long
bitmap that will be made of small pictures of the same size. Each
picture will be used for each item. Normally, each picture should have a
size of 16x16 or less. An example would be:
This bitmap is made of 6 pictures of the same
dimensions. If you do not plan to use the Report view and you plan to
use icons, create each icon with a 16x16 size.
If you plan to display the control’s items in Report view and you want
to use bitmaps, you can create a long bitmap that will made of small
pictures of the same size and each picture will be used for each item.
Each picture should have a size of 32x32.
If you plan to display the control’s items in Report view and other
views, if you want to use bitmaps, you should create two long bitmaps.
One would be made of pictures that are 16x16 size. Such pictures would
be used for the Small Icon, the List, and the Report views. You should
also create the second bitmap made of pictures of 32x32 size. These
pictures would be used for the List view.
After creating the bitmap and/or icons, you should declare a variable or a pointer to
CImageList class and initialize it using the CImageList::Create() method. To make the image list available to the list control, call the
CListCtrl::SetImageList() method. Its syntax is:
CImageList* SetImageList(CImageList* pImageList, int nImageList);
The pImageList argument is a CImageList
variable or pointer previously initialized. The nImageList is a flag
that specifies the type of image list used. It can have one of the
following values:
Value |
Description |
LVSIL_NORMAL |
The image list is made of large bitmap or icons, typically 32x32 |
LVSIL_SMALL |
The image list is made of small bitmap or icons, typically 16x16 |
LVSIL_STATE |
The image list is made of pictures that will be used as mask |
To associate a picture with a list item, you can use one of the following versions of the
CListCtrl::InsertItem() methods:
int InsertItem(int nItem, LPCTSTR lpszItem, int nImage );
int InsertItem(UINT nMask, int nItem, LPCTSTR lpszItem, UINT nState, UINT nStateMask,
int nImage, LPARAM lParam );
The nImage argument is the index of the image used for the item.
The nMask argument is the same as the LVITEM::mask member variable introduced earlier. It is simply used to specify what information you need to specify on the item.
The nState argument is the same as the LVITEM::state
member variable introduced earlier and used to specify what to do with
the item, whether to select it, give it focus, getting it ready for a
cut-and-paste operation, or highlighted for a drag-and-drop operation.
The nStateMask argument is used in
conjunction with the nState argument. It is used to specify what exact
type of information, defined by the state flag, that will be changed or
retrieved on the item.
The lParam argument, as well as the lParam member variable of the
TVITEM structure are used to perform a specific operation on the item, such as involving it in a sort algorithm or finding an item.
|
Practical Learning: Associating Bitmap With a List Control’s Items
|
|
- To create bitmap, on the main menu, click Project -> Add
Resource... In the Add Resource dialog box, click Bitmap and click New
- In the Properties window, change the ID to IDB_LARGEIMG
- Set the Height to 32 and the Width to 160
- Design the bitmap as follows:
- To create another bitmap, on the main menu, click Project
-> Add Resource... In the Add Resource dialog box, click Bitmap and
click New
- In the Properties window, change the ID to IDB_SMALLIMG
- Set the Height to 16 and the Width to 80
- Design the bitmap as follows:
- Access the header file of the main dialog box and declare two CImageList variables as follows:
private:
CImageList m_SmallImg;
CImageList m_LargeImg;
};
|
- Access the OnInitDialog() method of the same class and initialize the variables as follows:
BOOL CDeptStore2Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
LVCOLUMN lvColumn;
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_CENTER;
lvColumn.cx = 60;
lvColumn.pszText = "Item #";
this->m_StoreItems.InsertColumn(0, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 100;
lvColumn.pszText = "Category";
this->m_StoreItems.InsertColumn(1, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 160;
lvColumn.pszText = "Item Name";
this->m_StoreItems.InsertColumn(2, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 80;
lvColumn.pszText = "Size";
this->m_StoreItems.InsertColumn(3, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_RIGHT;
lvColumn.cx = 60;
lvColumn.pszText = "Unit Price";
this->m_StoreItems.InsertColumn(4, &lvColumn);
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_RIGHT;
lvColumn.cx = 30;
lvColumn.pszText = "Qty";
this->m_StoreItems.InsertColumn(5, &lvColumn);
this->m_StoreItems.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
m_SmallImg.Create(IDB_SMALLIMG, 16, 1, #ffffff);
m_LargeImg.Create(IDB_LARGEIMG, 32, 1, #fffff5);
m_List.SetImageList(&m_SmallImg, LVSIL_SMALL);
m_List.SetImageList(&m_LargeImg, LVSIL_NORMAL);
return TRUE; // return TRUE unless you set the focus to a control
}
|
- To use the images, change the OnBnClicked event of the New Item button as follows:
void CDeptStore2Dlg::OnBnClickedNewitem()
{
// TODO: Add your control notification handler code here
CNewStoreItemDlg dlg;
srand( (unsigned)time(NULL) );
char strNumber[20];
int number1 = rand() % 999;
int number2 = rand() % 999;
sprintf(strNumber, "%d-%d", number1, number2);
dlg.m_ItemNumber = strNumber;
if( dlg.DoModal() )
{
LVITEM lvItem;
int nItem;
int imgNbr;
if( dlg.m_Category == "Babies" )
imgNbr = 0;
else if( dlg.m_Category == "Teens" )
imgNbr = 1;
else if( dlg.m_Category == "Women" )
imgNbr = 2;
else if( dlg.m_Category == "Men" )
imgNbr = 3;
else // if( dlg.m_Category == "Miscellaneous" )
imgNbr = 4;
lvItem.mask = LVIF_IMAGE | LVIF_TEXT;
lvItem.iItem = 0;
lvItem.iSubItem = 0;
lvItem.pszText = strNumber;
lvItem.iImage = imgNbr;
nItem = this->m_StoreItems.InsertItem(&lvItem);
this->m_StoreItems.SetItemText(nItem, 1, dlg.m_Category);
this->m_StoreItems.SetItemText(nItem, 2, dlg.m_ItemName);
this->m_StoreItems.SetItemText(nItem, 3, dlg.m_ItemSize);
this->m_StoreItems.SetItemText(nItem, 4, dlg.m_UnitPrice);
this->m_StoreItems.SetItemText(nItem, 5, dlg.m_Quantity);
}
}
|
- Execute the application
|
|