How to Add a Checkbox to a List View Column Header
Introduction
There doesn't appear to be much, if any, example code of how to place a checkbox in the column header of a list view control. This can be used to add "check/uncheck all" functionality if you're using LVS_EX_CHECKBOXES in your list view window style to display a check box next to each item.
Unfortunately, the method used here only works with Windows Vista/Server 2008 and later. It appears to fail gracefully (no checkbox is displayed) when run on XP, but extensive testing has not been done.
Background
The list view control does not expose a way to add a checkbox to a column heading directly. It creates a Header control to display the column headings. A handle to this control can be obtained with the ListView_GetHeader() macro (or equivilent message) with which you can then manipulate.
Using the code
The sample project included was generated with VS2010 using the WTL AppWizard. Neither ATL nor WTL are needed for this technique to work. The code relevant to this method has actually been written using the SDK macros. You can of course use ATL/WTL helpers to make life easier.
The sample app is simply a modal dialog application with a list view control.
We'll throw some initialization code in our OnInitDialog method. First, we'll save the HWND of the list view control to a member variable for easy access later. Then, we add some styles to the control so it will display checkboxes and select the full row when clicked:
// Grab the window handle for our list view m_list = GetDlgItem(IDC_LIST); // Set some styles for the list view control. We want checkboxes and full row select. ListView_SetExtendedListViewStyle(m_list, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT);
Next, we'll actually create the columns. The first column will hold only the checkbox:
// Add some columns to the list view control LVCOLUMN lvc = {0}; ListView_InsertColumn(m_list, 0, &lvc); lvc.mask = LVCF_TEXT; lvc.iSubItem++; lvc.pszText = _T("First Name"); ListView_InsertColumn(m_list, 1, &lvc); lvc.iSubItem++; lvc.pszText = _T("Last Name"); ListView_InsertColumn(m_list, 2, &lvc); lvc.iSubItem++; lvc.pszText = _T("Company"); ListView_InsertColumn(m_list, 3, &lvc); // Set column widths ListView_SetColumnWidth(m_list, 0, LVSCW_AUTOSIZE_USEHEADER); ListView_SetColumnWidth(m_list, 1, LVSCW_AUTOSIZE_USEHEADER); ListView_SetColumnWidth(m_list, 2, LVSCW_AUTOSIZE_USEHEADER); ListView_SetColumnWidth(m_list, 3, LVSCW_AUTOSIZE_USEHEADER);
And here's where the magic starts. First, we obtain the HWND to the header control used by the list view. Then, we can modify it's window style to add the HDS_CHECKBOXES style which will allow us to display a checkbox in the header. If we don't do this, the control will not render a checkbox. We also store the control ID for later use by our message handler so we can detect when someone clicks the checkbox:
// Here's where we can add the checkbox to the column header // First, we need to snag the header control and give it the // HDS_CHECKBOXES style since the list view doesn't do this for us HWND header = ListView_GetHeader(m_list); DWORD dwHeaderStyle = ::GetWindowLong(header, GWL_STYLE); dwHeaderStyle |= HDS_CHECKBOXES; ::SetWindowLong(header, GWL_STYLE, dwHeaderStyle); // Store the ID of the header control so we can handle its notification by ID m_HeaderId = ::GetDlgCtrlID(header);
Finally, we ask the header control to populate an HDITEM struct with the format for the first column in our list view. We then apply the HDF_CHECKBOX format flag. We also apply the HDF_FIXEDWIDTH flag which prevents users from resizing the column. This is also a Vista and later flag:
// Now, we can update the format for the first header item, // which corresponds to the first column HDITEM hdi = { 0 }; hdi.mask = HDI_FORMAT; Header_GetItem(header, 0, &hdi); hdi.fmt |= HDF_CHECKBOX | HDF_FIXEDWIDTH; Header_SetItem(header, 0, &hdi);
Okay, that will get the checkbox to display in the header. If we don't handle any of the notifications, the default behavior is to select and unselect the items in the list view when clicking the checkbox. This probably isn't what you want, so we'll make it actually chck and uncheck the items. First, let's set up a couple notification mappings:
BEGIN_MSG_MAP(CMainDlg) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout) COMMAND_ID_HANDLER(IDOK, OnOK) COMMAND_ID_HANDLER(IDCANCEL, OnCancel) NOTIFY_HANDLER(m_HeaderId, HDN_ITEMSTATEICONCLICK, OnHeaderItemStateIconClick) NOTIFY_HANDLER(IDC_LIST, LVN_ITEMCHANGED, OnListItemChanged) END_MSG_MAP()
The header control will send a HDN_ITEMSTATEICONCLICK notification when the user clicks the checkbox. We handle this in our OnHeaderItemStateIconClick method. Basically, we check to see if the provided HDITEM contains information about the state of our checkbox. If it does, we call our CheckAllItems() function to check the checkboxes of all of the items in the list view. Then, we call SetHeaderCheckbox() which sets the state of the checkbox in the header:
LRESULT OnHeaderItemStateIconClick(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) { LPNMHEADER pnmHeader = (LPNMHEADER)pnmh; if (pnmHeader->pitem->mask & HDI_FORMAT && pnmHeader->pitem->fmt & HDF_CHECKBOX) { CheckAllItems(!(pnmHeader->pitem->fmt & HDF_CHECKED)); SetHeaderCheckbox(); return 1; } return 0; } void CheckAllItems(BOOL fChecked) { for (int nItem = 0; nItem < ListView_GetItemCount(m_list); nItem++) { ListView_SetCheckState(m_list, nItem, fChecked); } } void SetHeaderCheckbox(void) { // Loop through all of our items. If any of them are // unchecked, we'll want to uncheck the header checkbox. BOOL fChecked = TRUE; for (int nItem = 0; nItem < ListView_GetItemCount(m_list); nItem++) { if (!ListView_GetCheckState(m_list, nItem)) { fChecked = FALSE; break; } } // We need to get the current format of the header // and set or remove the HDF_CHECKED flag HWND header = ListView_GetHeader(m_list); HDITEM hdi = { 0 }; hdi.mask = HDI_FORMAT; Header_GetItem(header, 0, &hdi); if (fChecked) { hdi.fmt |= HDF_CHECKED; } else { hdi.fmt &= ~HDF_CHECKED; } Header_SetItem(header, 0, &hdi); }
Now, we handle when the user checks one of the items in the list. We want the header checkbox to check itself if the user manually checks all of the items. We do this by just calling the SetHeaderCheckbox() method:
LRESULT OnListItemChanged(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) { LPNMLISTVIEW pnmlv = (LPNMLISTVIEW)pnmh; if (pnmlv->uChanged & LVIF_STATE) { SetHeaderCheckbox(); } return 0; }
History
- 2011-02-12 Initial release.
发表评论
Thanks-a-mundo for the post.Really thank you! Awesome.
8wCFFa Ridiculous story there. What occurred after? Thanks!
XdPFVm Major thanks for the blog post.Really looking forward to read more. Cool.
IoamfG Im grateful for the blog post.Much thanks again. Much obliged.
lWsEN4 Than?s for your maаА аБТvаА аЂаlаА аБТus posting!
x7E0G8 Hello there, You have done a great job. I'll definitely digg it and personally suggest to my friends. I'm confident they'll be benefited from this site.
MeMXZZ I've recently started a blog, the info you offer on this website has helped me greatly. Thanks for all of your time & work.
RGhmiK A round of applause for your blog article.Thanks Again. Really Cool.
FjLbz9 Hey, thanks for the blog article.Much thanks again. Really Great.
n1IwuS Major thankies for the blog post.Thanks Again. Awesome.
6zBc9W I think this is a real great blog post. Great.
B2vRu1 Im grateful for the post.Much thanks again. Much obliged.
Свежая основание ради хрумера с параметрами - это проверенная воеже ТИЦ и чтобы наличие в яндекс каталоге ресурсов, предназначенная для регистрации и постинга с через хрумера последней версии.
Начиная с этой базы, всетаки базы чтобы хрумера будут прятаться около ХАЙД ( посредством 5 прежде 100 комментариев /сообщений ). Отдельная ходатайство к форумчанину с ником smocki на форуме ботмастера около выкладывании баз чтобы форуме давать активную ссылку чтобы распространенный ресурс.Жидкая теплоизоляция Re-Therm - Re-Therm.Ru. Теплоизоляция Re-Therm является лучшим способом теплоизоляции стен. Краска-термос Re-Therm – это средство, которым можно устранить промерзание откосов. Лучшее средство по борьбе с плесенью. Теплоизолятор действует как тепловой экран. Re-Therm - самый надежный и недорогой способ утепления стен, подвалов. Re-Therm обладает высокой прочностью к механическим воздействиям. Обращайтесь!
sex
Hi
We are looking on not-expensive GUI designers. Please advice.
We develop apps for Android operation system.
--
Ann ChapmakHi
We are looking on not-expensive GUI designers. Please advice.
We develop apps for Android.
--
Irvin ChapmakHi
We are looking for not-expensive GUI designers. Please advice.
We develop apps for Android.
--
Irvin Chapmak