wxListCtrl
Official Classes | ![]() |
Archive • Containers • Controls • Data Structures • Database • Date & Time • Debug • Device Contexts • Dialogs • Document & Views • Drag & Drop • Events • Filesystem • Frames • Graphics • Grid Cell • Help • HTML • Logging • Miscellaneous • Networking • Printing • Sizers • Streams • Threading • Windows |
A list control presents lists in a number of formats: list view, report view, icon view and small icon view.
In any case, elements are numbered from zero. For all these modes, the items are stored in the control and must be added to it using wxListCtrl::InsertItem method.
A special case of report view quite different from the other modes of the list control is a virtual control in which the items data (including text, images and attributes) is managed by the main program and is requested by the control itself only when needed which allows to have controls with millions of items without consuming much memory. To use virtual list control you must use wxListCtrl::SetItemCount first and override at least wxListCtrl::OnGetItemText (and optionally wxListCtrl::OnGetItemImage or wxListCtrl::OnGetItemColumnImage and wxListCtrl::OnGetItemAttr) to return the information about the items when the control requests it.
Virtual list control can be used as a normal one except that no operations which can take time proportional to the number of items in the control happen – this is required to allow having a practically infinite number of items. For example, in a multiple selection virtual list control, the selections won't be sent when many items are selected at once because this could mean iterating over all the items.
Using many of wxListCtrl features is shown in the corresponding sample.
To intercept events from a list control, use the event table macros described in wxListEvent.
Note: Starting with wxWidgets 2.8 (wxMac), wxListCtrl uses a native implementation for report mode, and uses a generic implementation for other modes. You can use the generic implementation for report mode as well by setting the mac.listctrl.always_use_generic system option (see wxSystemOptions) to 1.
If wxListCtrl has more features than you need, have a look at wxListView. For a simpler listing control, look at wxListBox. For a more spreadsheet-like control, please look at wxGrid
Minimal example to get started
// Example uses fictive class "Item" and fictive getters to access them. Adapt for your needs
class MyFrame : public wxFrame
{
wxListCtrl* m_item_list;
public:
MyFrame() : wxFrame(NULL, wxID_ANY, wxT("Hello wxWidgets"), wxPoint(50,50), wxSize(800,600))
{
wxPanel* mainPane = new wxPanel(this);
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
m_item_list = new wxListCtrl(mainPane, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT);
// Add first column
wxListItem col0;
col0.SetId(0);
col0.SetText( _("Foo") );
col0.SetWidth(50);
m_item_list->InsertColumn(0, col0);
// Add second column
wxListItem col1;
col1.SetId(1);
col1.SetText( _("Name") );
m_item_list->InsertColumn(1, col1);
// Add third column
wxListItem col2;
col2.SetId(2);
col2.SetText( _("Comments") );
m_item_list->InsertColumn(2, col2);
const int item_amount = getItemAmount();
for (int n=0; n<item_amount; n++)
{
Item* curritem = getItem(n);
wxListItem item;
item.SetId(n);
item.SetText( curritem->getName() );
m_item_list->InsertItem( item );
// set value in first column
if (!curritem->isFoo())
{
m_item_list->SetItem(n, 0, wxT("Foo"));
}
else
{
m_item_list->SetItem(n, 0, wxT("Bar"));
}
// set value in second column
m_item_list->SetItem(n, 1, curritem->getName());
// set value in third column
m_item_list->SetItem(n, 2, curritem->getDescription());
}
sizer->Add(m_item_list,1, wxEXPAND | wxALL, 10);
mainPane->SetSizer(sizer);
}
};
class MyApp: public wxApp
{
wxFrame* m_frame;
public:
bool OnInit()
{
m_frame = new MyFrame();
m_frame->Show();
return true;
}
};
IMPLEMENT_APP(MyApp)
Minimal virtual list example to get started
class SearchResultsList: public wxListCtrl{
public:
SearchResultsList(wxWindow* parent);
wxString OnGetItemText(long item, long column) const;
};
//Constructor, sets up virtual report list with 3 columns
SearchResultsList::SearchResultsList(wxWindow* parent):
wxListCtrl(parent,wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxLC_VIRTUAL){
// Add first column
wxListItem col0;
col0.SetId(0);
col0.SetText( _("Track") );
col0.SetWidth(300);
InsertColumn(0, col0);
// Add second column
wxListItem col1;
col1.SetId(1);
col1.SetText( _("Score") );
col1.SetWidth(50);
InsertColumn(1, col1);
// Add third column
wxListItem col2;
col2.SetId(2);
col2.SetText( _("ID") );
col2.SetWidth(100);
InsertColumn(2, col2);
//This should reflect your data
SetItemCount(30);
}
//Overload virtual method of wxListCtrl to provide text data for virtual list
wxString SearchResultsList::OnGetItemText(long item, long column) const{
//Use item and column to return the correct data for that particular cell. This example just returns "bawls" no matter what
return _("bawls");
}
Adding items in a multiple column list
You need to do an InsertItem first, and store the item index. And then SetItem()s for the columns after the first.
long itemIndex = WxListCtrl1->InsertItem(0, "1"); //want this for col. 1
WxListCtrl1->SetItem(itemIndex, 1, "18:00"); //want this for col. 2
WxListCtrl1->SetItem(itemIndex, 2, "17 18 56 48 29 13"); //col. 3
Getting the selected item indexes
There is no simple function to return the currently selected index(es). Instead, you must iterate over all the items and check if they are selected or not. It can be done using GetNextItem with the appropriate flags.
long itemIndex = -1;
while ((itemIndex = listControl->GetNextItem(itemIndex,
wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != wxNOT_FOUND) {
// Got the selected item index
wxLogDebug(listControl->GetItemText(itemIndex));
}
Adding Images
m_pImageList = new wxImageList(16,16);
wxIcon icon;
icon.LoadFile(wxT("res/user_offline.ico"), wxBITMAP_TYPE_ICO);
m_pImageList->Add(icon);
SetImageList(m_pImageList, wxIMAGE_LIST_SMALL);
And when you add an item, after that call
SetItemImage(item, 0);
Important: The image list must be created and assigned to the wxListCtrl before adding any items it.
Otherwise, you will not get any error message but the list will not work as expected.
Deleting Selected Rows
Rows (or items) in a listctrl are indexed starting from zero. You often want to delete some items that the user has selected. Get the index numbers of the selected items, and then looping through, delete the selected items. Warning If you delete items starting at the lowest index, the higher index numbers will now point to different items than they did before the deletion. Subsequent deletions will erase the wrong items. Rather loop through starting with the highest index first. For example:
// Make a list of selected row numbers that you want to delete by
// whatever means your application decides when ones should be deleted.
wxArrayInt row_numbers_to_delete = ......
// Delete in reverse order.
for ( long n = ( row_numbers_to_delete.GetCount() -1 ); 0 <= n; n-- )
{
// Remove it from the listctrl.
DeleteItem( row_numbers_to_delete[ n ] );
}
// Usual clearing of arrays to free memory.
row_numbers_to_delete.Clear();
Get the String Contents of a "cell" in a LC_REPORT wxListCtrl
The functionality to extract a string from a wxListCtrl "cell" in report view is somewhat rough for a newcomer to wxWidgets who is looking through the API. By "cell", we mean the value located at a certain "row" under a certain "column". This code snippet will return the string at that numbered row and column. Each row and column starts at index number zero. Place this custom function in your derived wxListCtrl.
wxString MyListCtrl::GetCellContentsString( long row_number, int column )
{
wxListItem row_info;
wxString cell_contents_string;
// Set what row it is (m_itemId is a member of the regular wxListCtrl class)
row_info.m_itemId = row_number;
// Set what column of that row we want to query for information.
row_info.m_col = column;
// Set text mask
row_info.m_mask = wxLIST_MASK_TEXT;
// Get the info and store it in row_info variable.
GetItem( row_info );
// Extract the text out that cell
cell_contents_string = row_info.m_text;
return cell_contents_string;
}
An alternative definition:
bool MyListCtrl::GetCellContentsString( long row_number, int column, wxString& cell_contents_string ) const
{
wxListItem row_info;
// Set what row it is (m_itemId is a member of the regular wxListCtrl class)
row_info.m_itemId = row_number;
// Set what column of that row we want to query for information.
row_info.m_col = column;
// Set text mask
row_info.m_mask = wxLIST_MASK_TEXT;
// Get the info and store it in row_info variable.
if (GetItem( row_info ))
{
// Extract the text out of that cell
cell_contents_string = row_info.m_text;
return true;
}
return false;
}
Select or Deselect an Item
More constraints - see help: wxListCtrl::SetItem
// Deselect item (wxLIST_STATE_FOCUSED - dotted border)
wxListCtrl->SetItemState(item, 0, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
// Select item
wxListCtrl->SetItemState(item, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
Note that the second parameter, stateMask
in wxListCtrl::SetItemState
is not the same as the mask used in wxListItem
(although the actual flags are the same). The stateMask simply specifies which flags in state are valid. As the above example shows, to select an item, both the state and stateMask must be set to wxLIST_STATE_SELECTED
.
Implement wxListCtrl with Checkboxes
This is quite easy using checked and unchecked images. If you want a more complete version of wxCheckedListCtrl with events for checking and unchecking and completely transparent wxWidgets behaviour, see WebUpdate at wxCode. It contains a wxCheckedListCtrl implementation which can be extracted and used separately from WebUpdate. Just move the header and source file, checkedlistctrl.cpp/.h into your project. [seems no longer true: the header also has dependencies on webupdatedef.h and the cpp references checked/unchecked images, so a bit of editing is required.]
#include <bitmaps/checked.xpm>
#include <bitmaps/unchecked.xpm>
IMPLEMENT_CLASS(wxCheckedListCtrl, wxListCtrl)
BEGIN_EVENT_TABLE(wxCheckedListCtrl, wxListCtrl)
EVT_LEFT_DOWN(wxCheckedListCtrl::OnMouseEvent)
END_EVENT_TABLE()
wxCheckedListCtrl::wxCheckedListCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pt,
const wxSize& sz, long style):
wxListCtrl(parent, id, pt, sz, style), m_imageList(16, 16, TRUE)
{
SetImageList(&m_imageList, wxIMAGE_LIST_SMALL);
m_imageList.Add(wxICON(unchecked));
m_imageList.Add(wxICON(checked));
InsertColumn(0, _("Item"), wxLIST_FORMAT_LEFT, 200);
InsertColumn(1, _("Value"), wxLIST_FORMAT_LEFT, 80);
}
void wxCheckedListCtrl::OnMouseEvent(wxMouseEvent& event)
{
if (event.LeftDown())
{
int flags;
long item = HitTest(event.GetPosition(), flags);
if (item > -1 && (flags & wxLIST_HITTEST_ONITEMICON))
{
SetChecked(item, !IsChecked(item));
}
else
event.Skip();
}
else
{
event.Skip();
}
}
bool wxCheckedListCtrl::IsChecked(long item) const
{
wxListItem info;
info.m_mask = wxLIST_MASK_IMAGE ;
info.m_itemId = item;
if (GetItem(info))
{
return (info.m_image == 1);
}
else
return FALSE;
}
void wxCheckedListCtrl::SetChecked(long item, bool checked)
{
SetItemImage(item, (checked ? 1 : 0), -1);
}
If you'd like to use native checkboxes replace the bitmap XPMs with this bit of code in wxCheckedListCtrl::Create(...).
bool wxCheckedListCtrl::Create(wxWindow* parent, wxWindowID id, const wxPoint& pt,
const wxSize& sz, long style, const wxValidator& validator, const wxString& name)
{
if (!wxListCtrl::Create(parent, id, pt, sz, style, validator, name))
return FALSE;
// Get the native size of the checkbox
int width = wxRendererNative::Get().GetCheckBoxSize(this).GetWidth();
int height = wxRendererNative::Get().GetCheckBoxSize(this).GetHeight();
m_imagelist = new wxImageList(width, height, TRUE);
SetImageList(m_imagelist, wxIMAGE_LIST_SMALL);
wxBitmap unchecked_bmp(width, height),
checked_bmp(width, height),
unchecked_disabled_bmp(width, height),
checked_disabled_bmp(width, height);
wxMemoryDC renderer_dc;
// Unchecked
renderer_dc.SelectObject(unchecked_bmp);
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
renderer_dc.Clear();
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), 0);
// Checked
renderer_dc.SelectObject(checked_bmp);
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
renderer_dc.Clear();
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), wxCONTROL_CHECKED);
// Unchecked and Disabled
renderer_dc.SelectObject(unchecked_disabled_bmp);
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
renderer_dc.Clear();
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), 0 | wxCONTROL_DISABLED);
// Checked and Disabled
renderer_dc.SelectObject(checked_disabled_bmp);
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
renderer_dc.Clear();
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), wxCONTROL_CHECKED | wxCONTROL_DISABLED);
// Deselect the renderers Object
renderer_dc.SelectObject(wxNullBitmap);
// the add order must respect the wxCLC_XXX_IMGIDX defines in the headers !
m_imageList.Add(unchecked_bmp);
m_imageList.Add(checked_bmp);
m_imageList.Add(unchecked_disabled_bmp);
m_imageList.Add(checked_disabled_bmp);
return TRUE;
}
wxCheckedListCtrl list = new wxCheckedListCtrl(this,
wxID_ANY,wxDefaultPosition,wxDefaultSize,wxLC_REPORT|wxLC_SINGLE_SEL);
list->InsertColumn(0, wxT("Item"));
list->InsertItem(0, "ABC");
list->Check(0, FALSE);
list->InsertItem(1, "DEF");
list->Check(1, TRUE);
Using Specific Colours for Each Item
Trying to set specific item colours for a wxListCtrl doesn't have any effect with wxMSW 2.4.0 standard release. You have to enable it. Indeed, it is not set as default in the downloadable releases so you will have to recompile the library with _WIN32_IE at least defined as 0x300. To do so, just insert the -D_WIN32_IE=0x300 compile flag when making. See the Guides & Tutorials for further details about building wx. Then, just use wxListItem's methods to set the text colour, background colour and font of each item. For example:
MyListCtrl::AppendColouredItem(const wxString& Label) {
wxListItem NewItem;
NewItem.SetMask(wxLIST_MASK_TEXT);
NewItem.SetId(GetItemCount());
NewItem.SetText(sLabel);
NewItem.SetFont(*wxITALIC_FONT);
NewItem.SetTextColour(wxColour(*wxRED));
NewItem.SetBackgroundColour(wxColour(235, 235, 235));
InsertItem(NewItem);
}
This piece of code appends a new line (item) to MyListCtrl with the label of sLabel's value, an italic font, a red text colour and a light grey background.
NOTE: It seems that these methods still don't work with columns so the tip is to set the global text/background colours and font of MyListCtrl and then, when you append an item, set its colours/font to the proper values.
wxListCtrl::SetSingleStyle
The wxListCtrl::SetSingleStyle(long style, const bool add = true) function calls SetWindowStyleFlag(long style) which will delete all items. Use it before filling the control with items.
See Also