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.
发表评论
hMGGLX Very good article post.Much thanks again. Really Cool.
8ieECF Thank you ever so for you blog.Really looking forward to read more. Great.
kQSbhU Nice blog right here! Also your web site so much up very fast! What host are you the usage of? Can I am getting your associate link on your host? I desire my website loaded up as fast as yours lol
Im obliged for the blog.Much thanks again.
CVCkPH This is one awesome article.Really thank you! Will read on
slK9Gf I really loved what you had to say, and more than that,
2KQ6GS lunette ray ban Is Totally Neat Within A Descendant Typical
uJpuV9 You have made some good points there. I checked on the net to learn more about the issue and found most people will go along with your views on this site.
2r0i8e
mH8tZV
iYiaWw You have already known that coconut oil is not low calorie food however.
yBpRwQ Your style is so unique in comparison to other folks I ave read stuff from. Many thanks for posting when you ave got the opportunity, Guess I all just bookmark this page.
YE2WdK Positive Momentum Encouraging the Role of the US Dollar
qng9Qe Im grateful for the blog article.Much thanks again.
q6NMxM A round of applause for your article post.Really looking forward to read more. Keep writing.
zocI3s Really appreciate you sharing this article post. Much obliged.
uqHI1O Awesome post.Much thanks again. Much obliged.
VArAMf Thanks for another excellent post. Where else could anyone get that type of information in such a perfect way of writing? I have a presentation next week, and I'm on the look for such info.
2HtmhT Really enjoyed this blog post, can I set it up so I get an email sent to me whenever you make a new article?
sXFSoa Of course, what a fantastic website and revealing posts, I will bookmark your blog.Have an awsome day!
UE028U I am curious to find out what blog platform you're utilizing? I'm experiencing some minor security problems with my latest blog and I'd like to find something more safe. Do you have any solutions?
TL12Sr Im obliged for the blog post.Really thank you! Great.
DjjO7y Good write-up, I am regular visitor of one's site, maintain up the nice operate, and It's going to be a regular visitor for a long time.
23wBIR Appreciate you sharing, great blog article.Really thank you! Great.
qSc6H6 Really appreciate you sharing this blog article. Cool.
3aS7nM Really enjoyed this article. Really Great.
4ynCIH Great blog.Thanks Again. Awesome.
6EH5Tm I really liked your article post.Really thank you! Really Great.
xHb5px I think this is a real great post.Really looking forward to read more. Keep writing.
SK7vmA Muchos Gracias for your blog.Much thanks again. Want more.
pNmZjv Muchos Gracias for your blog post.Really thank you! Will read on...
5MvN6t I really like and appreciate your blog article.Really looking forward to read more. Fantastic.
c3zY0L Im obliged for the post.Much thanks again. Great.
RlfAdn Thanks again for the post.Really looking forward to read more. Really Great.
u3SFCI Really informative article post.Thanks Again. Want more.
NkGNGo I really enjoy the blog post. Really Great.
OAoZfc Awesome post. Much obliged.
2zhxzM Enjoyed every bit of your blog.Thanks Again.
OvXfPL Really enjoyed this blog article.Thanks Again. Awesome.
Ecs2Gs I appreciate you sharing this article.Really looking forward to read more.
YHbyiY I value the article. Fantastic.
n8boNE Very neat blog post.Thanks Again. Really Great.
JKEg7i Enjoyed every bit of your article post.Really looking forward to read more. Will read on...
WioxSd Thank you for your blog post. Great.