WxListCtrl
From WxWiki
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
[edit] 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 thirs 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)
[edit] 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"); }
[edit] 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
[edit] Getting the selected item indexes
There are 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; for (;;) { itemIndex = listControl->GetNextItem(itemIndex, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); if (itemIndex == -1) break; // Got the selected item index wxLogDebug(listControl->GetItemText(itemIndex)); }
[edit] 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.
[edit] 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();
[edit] 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; }
[edit] 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.
[edit] 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.
#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; SetImageList(&m_imageList, wxIMAGE_LIST_SMALL); // TODO: Using the native size would be better wxBitmap unchecked_bmp(16, 16), checked_bmp(16, 16), unchecked_disabled_bmp(16, 16), checked_disabled_bmp(16, 16); // Bitmaps must not be selected by a DC for addition to the image list but I don't see // a way of diselecting them in wxMemoryDC so let's just use a code block to end the scope { 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, 16, 16), 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, 16, 16), 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, 16, 16), 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, 16, 16), wxCONTROL_CHECKED | wxCONTROL_DISABLED); } // 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; }
[edit] 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.
[edit] 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.
