CGridListCtrlEx - Grid Control Based on CListCtrl
Introduction
Microsoft's CListCtrl
has support for displaying data in a grid using the report style, but we have to make several changes to implement features like:
- Sorting
- Cell navigation and keyboard search
- Tooltips
- Hiding and showing columns
- Cell editing
- Custom row and cell coloring
- Grouping
- Clipboard (copy only)
- Persist column width, position and visibility
- OLE Drag and Drop (includes reordering of items)
This article demonstrates how to use CGridListCtrlEx
, which implements all the above features while maintaining the Windows XP/Vista look.
The Google Code - CGridListCtrlEx can be used if wanting SubVersion access, and there is also Doxygen Documentation.
Background
There are lots of advanced grid controls that extend the CListCtrl
, and one of those is the Enhanced List Control (CGfxListCtrl). This wonderful control provides all the above features, but fails to handle Windows XP and Vista. Finding a good replacement for this control is not very easy:
- MFC Grid Control - Doesn't inherit from
CListCtrl
so it is not restricted by it, but it will not benefit from any improvements that Microsoft adds to theCListCtrl
. - Ultimate Grid - Like MFC Grid Control, it doesn't inherit from
CListCtrl
. Originally one had to buy it, but now it is free to use. - CQuickList - Very close to being a perfect replacement, but hard to add new ways to display data, and it requires
LVS_OWNERDATA
that makes sorting a little harder. - XListCtrl - Also a very complete
CListCtrl
, but hard to add new ways to display data, and it fails to supportLVS_OWNERDATA
. One now has to buy a license, to get the latest version. - Another Report List Control - Simple and easy to use, but lacks other means to edit data besides using
CEdit
, and also misses subitem navigation. - CListCtrlEx - Implements lots of feature and is well documented. Originally required
LVS_OWNERDRAWFIXED
, and now evolved into using custom draw. The combination of both custom draw and owner draw causes the code to be a little complicated, and it also doesn't supportLVS_OWNERDATA
.
The CGridListCtrlEx
inserts an abstract layer called column traits that handles the cell drawing and editing. In case Microsoft extends their CListCtrl
again, then hopefully, the core of CGridListCtrlEx
will continue to function.
How to Use the CGridListCtrlEx
The CGridListCtrlEx
tries to stay true to the CListCtrl
, and doesn't try to replace anything the CListCtrl
already provides. This means we can replace a CListCtrl
with CGridListCtrlEx
without needing to do anything more.
It is recommended that we don't use the CGridListCtrlEx
directly, but create a new class that inherits/derives from CGridListCtrlEx
. This will make it easier to migrate any updates there will be to the CGridListCtrlEx
class later on.
Editing Cells/Subitems
By default, when inserting columns in the CGridListCtrlEx
, they will be configured as read-only, without the ability to be edited. By using CGridListCtrlEx::InsertColumnTrait()
, we can provide a CGridColumnTrait
class which specifies what type of editor it should use.
CGridColumnTrait* pTrait = new CGridColumnTraitEdit;
m_ListCtrl.InsertColumnTrait(nCol, title.c_str(), LVCFMT_LEFT, 100, nCol, pTrait);
When having edited an item, a standard LVN_ENDLABELEDIT
message will be sent to the CListCtrl
. When the CGridListCtrlEx
receives this message, it will automatically call the virtual method CGridListCtrlEx::OnEditComplete()
, allowing a derived class to validate the input and maybe update an underlying data model.
Editing Cells/Subitems with a Combo-box
By using CGridListCtrlEx::InsertColumnTrait()
, we can also provide a CGridColumnTrait
class which works as a CComboBox
.
CGridColumnTraitCombo* pTrait = new CGridColumnTraitCombo;
pTrait->AddItem(0, "Hello");
pTrait->AddItem(1, "Goodbye");
m_ListCtrl.InsertColumnTrait(nCol, title.c_str(), LVCFMT_LEFT, 100, nCol, pTrait);
We can specify the items of the CComboBox
when inserting the columns (as shown above). If we want to provide the CComboBox
items dynamically, then we can override the CGridListCtrlEx::OnEditBegin()
. Use dynamic_cast<>
to either call the column trait method CGridColumnTraitCombo::LoadList()
, or to work on the returned CComboBox
-editor directly.
Sorting Rows
By default, the GridListCtrlEx
will have sorting enabled for all columns, where it will perform a simple text-comparison. It is possible through the column traits to implement custom sorting by overriding CGridColumnTrait::OnSortRows()
.
To configure a column trait to sort using number comparison:
CGridColumnTraitEdit* pTrait = new CGridColumnTraitEdit;
pTrait->SetSortFormatNumber(true); // Numeric column
m_ListCtrl.InsertColumnTrait(nCol, title.c_str(), LVCFMT_LEFT, 100, nCol, pTrait);
The column trait CGridColumnTraitDateTime
will automatically attempt to sort using date comparison.
We also have the option to override the CGridListCtrlEx::SortColumn()
method. Then, it is just a matter of choosing the right way to perform the sorting. See also CListCtrl and Sorting Rows.
Showing Tooltip
By default, the CGridListCtrlEx
will just display the cell contents as tooltip. If we want to display something different in the tooltip, then we can override the CGridListCtrlEx::OnDisplayCellTooltip()
method.
Formatting Cells/Subitems
If we want to change the foreground/background color or the font style (bold, italic, underline), then we can override the methods CGridListCtrlEx::OnDisplayCellColor()
and CGridListCtrlEx::OnDisplayCellFont()
.
Displaying Cell/Subitem Images
The CGridListCtrlEx
enables the extended style LVS_EX_SUBITEMIMAGES
by default, but one is still required to attach a CImageList
using CListCtrl::SetImageList()
.
After having attached the images, one can bind a cell/subitem with an index in the CImageList
. This can be done with CGridListCtrlEx::SetCellImage()
, or if using I_IMAGECALLBACK
then return the image index by overriding CGridListCtrlEx::OnDisplayCellImage()
.
The CGridListCtrlEx
also enables the extended style LVS_EX_GRIDLINES
by default, which can cause subitem images to overlap the grid border. This can be solved by making sure that the image only uses 15 of the 16 pixels (first pixel transparent).
When using subitem images and running the application on Windows XP or using classic style, it will show a white background when a row is selected. This can be fixed by using CGridRowTraitXP
:
m_ListCtrl.SetDefaultRowTrait(new CGridRowTraitXP);
Changing Row Height
The CGridListCtrlEx
uses customdraw, so there are only these available solutions:
- Assign a
CImageList
where the image has the height wanted for the row. - Change the font of the grid control, and the row height will follow.
CGridListCtrlEx::SetCellMargin()
uses this trick to increase the font of the grid control, while keeping the row font intact.
Changing the Empty Markup Text
When the CGridListCtrlEx
doesn't contain any items, it will display markup text to indicate the list is empty.
Use CGridListCtrlEx::SetEmptyMarkupText()
to change this markup text. If providing empty text, then it will behave like a normal CListCtrl
.
If using CGridListCtrlGroups
, it will instead react to LVN_GETEMPTYMARKUP
if running on Windows Vista.
Load and Save Column Width and Position
CViewConfigSectionWinApp
provides the ability to store the width, position and whether columns are shown. After having added all available columns to the CGridListCtrlEx
, assign an instance of CViewConfigSectionWinApp
using CGridListCtrlEx::SetupColumnConfig()
and it will restore the last saved column configuration through CWinApp
.
m_ListCtrl.SetupColumnConfig(new CViewConfigSectionWinApp("MyList"));
If using CGridListCtrlEx
several places in your application, then one should ensure creation of a unique CViewConfigSectionWinApp
for each place.
OLE Drag and Drop
CGridListCtrlEx
can both work as an OLE drag source and an OLE drop target. This allows drag operations between the CGridListCtrlEx
and other windows and applications.
CGridListCtrlEx
supports reordering of rows when doing internal drag and drop. This is implemented using a special sort operation, so items are not deleted/inserted.
To implement your own special behavior for a drop operation, either override OnDropSelf()
or OnDropExternal()
depending on what situation you want to handle.
To control what is placed in the drag-source when a drag is initiated, override OnDisplayToDragDrop()
.
Checkbox Support
CListCtrl
supports checkboxes for the label column out of the box. Just apply the extended style LVS_EX_CHECKBOXES
:
m_ListCtrl.SetExtendedStyle(m_ListCtrl.GetExtendedStyle() | LVS_EX_CHECKBOXES);
Remember not to use InsertHiddenLabelColumn()
as it will hide the label column and its checkbox. One can use GetCheck()
/ SetCheck()
to retrieve/modify the checkbox value.
If wanting to have checkboxes for multiple columns, then one can use CGridColumnTraitImage
(and its specializations):
// Appends the unchecked/checked state images to the list control image list
int nStateImageIdx = CGridColumnTraitImage::AppendStateImages(m_ListCtrl, m_ImageList);
m_ListCtrl.SetImageList(&m_ImageList, LVSIL_SMALL);
// Creates an image column, that can switch between the 2 images
CGridColumnTrait* pTrait = new CGridColumnTraitImage(nStateIdx, 2);
m_ListCtrl.InsertColumnTrait(nCol, title.c_str(), LVCFMT_LEFT, 20, nCol, pTrait);
for(int i=0; i < m_ListCtrl.GetItemCount(); ++i)
m_ListCtrl.SetCellImage(i, nCol, nStateImageIdx); // Uncheck item
The label column will automatically display the image column when assigning an CImageList
using SetImageList()
. It is not possible to disable this behavior, but one can hide the label column using InsertHiddenLabelColumn()
.
CGridColumnTraitImage
uses the cell image to draw the checkbox, so it is not possible to have both cell image and checkbox in the same column. To get and set the checked / unchecked state, one can use GetCellImage()
/ SetCellImage()
.
CGridColumnTraitImage
supports sorting according to whether the checkbox is enabled. Use CGridColumnTraitImage::SetSortImageIndex()
to enable this.
CGridColumnTraitImage
also supports toggling of checkboxes for all selected rows. Use CGridColumnTraitImage::SetToggleSelection()
to enable this.
How Does the CGridColumnTrait Work
CGridListCtrlEx
tries to keep away from all the nasty details about how to display and edit data. These things are instead handled by the CGridColumnTrait
class, and if we want to modify how data is displayed, then it is "just" a matter of creating a new CGridColumnTrait
class.
When inserting a column, we can assign a CGridColumnTrait
to the column. The CGridListCtrlEx
will activate the appropriate CGridColumnTrait
when we need to draw a cell in that column, or edit a cell in the column.
The CGridColumnTrait
includes some special members known as meta-data. These members can be used by your own class when it derives from CGridListCtrlEx
, so we can easily add extra properties to a column.
When inheriting from CGridColumnTrait
, we must consider the following:
- If performing custom drawing, we must also handle the selection and focus coloring.
- If performing editing, we must ensure that the editor closes when it loses focus, and also sends a
LVN_ENDLABELEDIT
message when the edit is complete.
How Does the CGridRowTrait Work
It is based on the same idea as CGridColumnTrait
but operates at row level instead of column level. This is useful for situations where one has to modify the display behavior of all columns.
Using the Code
The source code includes the following classes:
CGridListCtrlEx
- The specializedCListCtrl
CGridListCtrlGroups
-CGridListCtrlEx
extended with support for groupingCGridColumnTrait
- Specifies the interface of a column-traitCGridColumnTraitText
- Implements cell formattingCGridColumnTraitImage
- Implements cell editing by switching between images (can mimic a checkbox)CGridColumnTraitEdit
- Implements cell editing withCEdit
CGridColumnTraitCombo
- Implements cell editing withCComboBox
CGridColumnTraitDateTime
- Implements cell editing withCDateTimeCtrl
CGridRowTrait
- Specifies the interface of a row traitCGridRowTraitText
- Implements row formattingCGridRowTraitXP
- Implements drawing of subitem image background when using classic- or XP-style
CViewConfigSection
- Abstract interface for persisting column setupCViewConfigSectionWinApp
- Implements the interface and can switch between multiple column setups.
Things To Do
The CGridListCtrlEx
tries to stay away from performing any drawing itself. This means that the following features/bugs will not get that much attention:
- Support for progress bar - Requires a
CGridColumnTrait
class that draws the entire cell.
Implementing a CGridColumnTrait
class that draws the entire cell could probably be done by stealing/borrowing some code from ListCtrl - A WTL List Control with Windows Vista Style Item Selection.
Contributions to this project are very welcome.
History
- Version 1.0 (2008-09-04) First release
- Version 1.1 (2008-09-18)
- Added support for grouping with
CGridListCtrlGroups
- Added
CDateTimeCtrl
editor - Fixed drawing bugs when using Classic- and XP-style
- Fixed image background color for selected subitems (no longer white)
- Fixed grid border disappearing when scrolling right and left
- Indicate the list is empty when it contains no items
- Extended
CComboBox
editor, so it automatically resizes dropdown width to its contents
- Added support for grouping with
- Version 1.2 (2008-09-24)
- Replaced the
CGridListCtrlXP
withCGridRowTraitXP
- Fixed some reported bugs
- Replaced the
- Version 1.3 (2008-10-09)
- Fixed extended style when used in
CView
- Fixed positioning of context-menu when using keyboard shortcut (SHIFT+F10)
- Fixed compiler errors that appeared when using Visual Studio 6 (VC6)
- Fixed extended style when used in
- Version 1.4 (2008-11-07)
- Added clipboard support for copying the contents of the selected cell / rows
- Renamed the "
Callback
"-functions to "OnDisplay
", as it resembles the MFC naming convention - Fixed some reported bugs
- Version 1.5 (2009-03-29)
- Added column manager
CGridColumnManager
- Added support for groups on VC6 with platform SDK
- Added sample projects for the different versions of Visual Studio
- Improved documentation through Doxygen comments
- Added column manager
- Version 1.6 (2009-09-13)
- Added OLE drag and drop support
- Added support for
checkbox
styleLVS_EX_CHECKBOX
when usingLVS_OWNERDATA
- Added better support for keyboard search with
LVS_OWNERDATA
- Fixed several bugs
- Version 1.7 (2009-12-12)
- Added
CGridColumnTraitImage
, that can mimic checkbox editing for any column - Renamed
OnTraitEditBegin()
toOnEditBegin()
- Renamed
OnTraitEditComplete()
toOnEditComplete()
- Renamed
OnTraitCustomDraw()
toOnCustomDrawCell()
- Fixed several bugs (Mostly row and cell coloring)
- Added
- Version 1.8 (2010-10-01)
- Made
CGridColumnTraitImage
a base class for all editor column traits, so they all can mimic checkbox support - Implemented multiple selection checkbox support, so checkboxes are flipped for all selected rows
- Implemented min and max column width through the base column trait
- Fixed several bugs
- Made
- Version 1.9 (2011-05-30)
- Changed
CGridColumnTrait::OnSortRows
to take anLVITEM
as argument instead of character strings - Renamed
CGridColumnConfig
toCViewConfigSection
- Removed
CGridColumnManager
and movedLoadState
/SaveState
intoCGridListCtrlEx
(breaking change)
- Changed
发表评论
What i do not realize is actually how you are
now not actually a lot more neatly-appreciated than you may
be now. You are so intelligent. You realize therefore considerably when it
comes to this subject, produced me for my part consider it from
so many various angles. Its like women and men are not interested unless it is something to accomplish with Lady gaga!
Your individual stuffs great. At all times handle it up!
aid for ukraineSpot on with this write-up, I seriously feel this website needs much more attention. I'll
probably be returning to read through more, thanks for the information! help refugesqGPJZQ Im obliged for the article post.Really looking forward to read more. Great.
IyRiSz The facts mentioned within the article are a few of the most beneficial readily available
g0n1iF Looking forward to reading more. Great blog article.Really thank you! Fantastic.
VdtGD3 I really liked your blog.Really looking forward to read more. Really Cool.
Wow, awesome weblog format! How lengthy have you ever been running a blog for? you make running a blog glance easy. The full look of your website is excellent, let alone the content material!
ZYeHCi Thanks so much for the blog. Much obliged.
KDd5Q5 This is how to get your foot in the door.
avouTl Very very good publish, thank that you simply lot regarding sharing. Do you happen a great RSS feed I can subscribe to be able to?
Q30bxO You made some good points there. I looked on the net for additional information about the issue and found most individuals will go along with your views on this web site.
IjsqiL You completed a few fine points there. I did a search on the matter and found mainly people will have the same opinion with your blog.
Xbe3C3 The move by the sports shoe business that clearly has ravens nfl nike jerseys online concerned, said he thought he was one of the hottest teams in football.
2Zaejw It as not that I want to copy your web site, but I really like the design and style. Could you tell me which style are you using? Or was it especially designed?
glbBOV Some really fantastic content on this website , thanks for contribution.
nJQ2zw I'аve read various exceptional stuff right here. Surely worth bookmarking for revisiting. I surprise how lots try you set to produce this sort of great informative internet site.
gR1vmQ Really enjoyed this blog.Much thanks again. Cool.
CPjdD7 Your style is so unique in comparison to other people I have read stuff from. Thank you for posting when you have the opportunity, Guess I all just bookmark this page.
Glz33B Pretty! This has been an extremely wonderful article. Thank you for supplying this information.
5mrhca My brother suggested I might like this blog. He was totally right. This post truly made my day. You can not imagine just how much time I had spent for this information! Thanks!
1F5PZs Just wanted to say thanks for posting this!
V4bdYV This site was how do you say it? Relevant!! Finally I have found something that helped me. Appreciate it!
G40Vhg MANIC STREET PREACHERS I Think Ive Found It Shalala.ru
VuCDP9 Website We Recommend You made several fine points there. I did a search on the issue and found nearly all persons will have the same opinion with your blog.
z0WhtB wow, awesome blog article.Much thanks again. Fantastic.
a3uhWg Very fantastic info can be found on website.
ZxWOMy Thanks again for the blog post.Much thanks again. Much obliged.
JtVA9O you ave got a fantastic blog right here! would you wish to make some invite posts on my weblog?
CWP4sN Way cool! Some very valid points! I appreciate you writing this write-up and also the rest of the website is also very good.
d28hqQ Really appreciate you sharing this article.Really looking forward to read more. Awesome.
X8J6Ap Wow! I cant believe I have found your weblog. Extremely useful info.
nkujiA Very good blog post.Really looking forward to read more.
K63R9L This is a great tip particularly to those fresh to the blogosphere. Short but very precise information Thanks for sharing this one. A must read article!
Brq5cf It was hard It was hard to get a grip on everything, since it was impossible to take in the entire surroundings of scenes.
OJabQV This is very interesting, You are a very skilled blogger. I ave joined your feed and look forward to seeking more of your excellent post. Also, I ave shared your website in my social networks!
Thank you ever so for you article post.Much thanks again. Great.
Muchos Gracias for your blog.Thanks Again. Fantastic.
Oqsf3T will leave out your wonderful writing because of this problem.
2n8BSG using for this site? I am getting sick and tired of WordPress because I ave had
pHxDg1 Nice blog right here! Additionally your web site so much up fast! What host are you the usage of? Can I get your affiliate link in your host? I desire my site loaded up as fast as yours lol
DHADTx Wow! This could be one particular of the most helpful blogs We have ever arrive across on this subject. Actually Excellent. I am also a specialist in this topic so I can understand your effort.
AZtj5f Thanks for sharing your thoughts. I really appreciate your efforts and I am waiting for your further post thanks once again.
uKfPOD yeah bookmaking this wasn at a risky determination outstanding post!.
OhmZH2 Major thanks for the blog article.Much thanks again. Great.
cYKQ0X
3O3Tsp
RBaf9e Usually I don at read article on blogs, but I would like to say that this write-up very compelled me to try and do so! Your writing style has been surprised me. Thanks, very nice article.
wQgK5L Volutpat lacinia dui quis Curabitur Pellentesque ipsum eu
j1xkLj Looking forward to reading more. Great blog article. Great.
O7hEFC Say, you got a nice blog post. Cool.