use
Custom Draw
Elegant implementation
Listctrl
Redraw
CommON Control 4.7 introduces a new feature called Custom Draw, this name is blurred, and people don't touch the mind, and MSDN only gives some wind interpretation and examples, no one tells you you want I know, and what is the benefit of this feature.
Custom Draw can be imagined into a lightweight, easy to use redraw (there are several of the redrawing methods, such as Owner Draw, etc.). This is easy to come from us to only process a message (
NM_CUSTOMDRAW, you can get Windows to work for you, you don't have to be forced to handle all dirty lives in the "redraw process".
The focus of this article is how to use the Custom Draw message on a listCtrl control. The reason, part of it is because I have used Custom Draw in my job for a while, I am familiar with it. Another reason is that this mechanism is really very easy, you only need to write a lot of code to achieve a good effect. Use Custom Draw to program the appearance of the control and even replace a lot of ancient methods.
The following code is written in the environment of WIN98 and VC6 SP2, and the version of the Common Controls DLL is 5.0. I have tested it on WinNT 4. The system is to run these code, and its version of the CommON Controls DLL must be at least 4.71. But with the release of IE4, this is no problem. (IE will release this DLL)
Custom Draw foundation
I will do my best to clear the processing description of the Custom Draw, not a simple reference MSDN document. These examples require your program to have a listCtrl on the dialog, and this listCtrl is in Report and multiple column modes.
Message mapping in Custom Draw
Custom Draw is a process similar to the callback. Windows informs you the program at a point in time drawn List Ctrl, you can choose to ignore all notifications (so you will see the standard listctrl), or Handling a part of the drawing (simple effect), even the entire control is drawn by you (just like using Owner-Drawing). The true selling point of this mechanism is: You only need to implement some you need, the rest can make Windows for you.
Ok, now you can start adding a Custom Draw to your ListCtrl to do some personalized things. You first have the correct Comm Ctrl DLL version, and then Windows will send you.
NM_CUSTOMDRAW
Message, you only need to add a handler to start using Custom Draw. First add a message mapping, like the following:
ON_NOTIFY (NM_CUSTOMDRAW, IDC_MY_LIST, ONCUSTOMDRAWMYLIST)
The original shape of the handler is as follows:
AFX_MSG Void OncustomDrawmyList (NmHDR * PNMHDR, LRESULT * PRESULT);
This tells MFC you have to handle from your ListCtrl control.
WM_NOTIFY
Message, ID is
IDC_MY_LIST
And the notice code is
NM_CUSTOMDRAW
,
ONCUSTOMDRAWMYLIST
It is your handler.
If you have a class derived from the ClistCtr, you want to add a Custom Draw for it, you can use ON_NOTIFY_REFLECT
Instead. as follows:
ON_NOTIFY_REFLECT (nm_customdraw, oncustomed)
The original shape of OnCustomDraw is consistent with the above function, but it is declared in your derived class.
Custom DRAW divides the control of the control into two parts: erase and painting. Windows will send NM_CUSTOMDRAW messages every beginning and end. So there are 4 messages in total. But actually your program received a message may only be 1 or more, depending on how you want Windows. The time period each time the message is called as a "painting section". You must grasp this concept tight because it runs through the entire "redraw" process.
So, you will receive a notice at the following point in time:
l Before Items were painted - "Painting" section
l After Items are drawn - "Posthand" segment
l Before ITEM was erased - "Erase before" segment
l After Item is erased - "After erase" segment
Not all the news is the same, in fact, I don't need to handle all the news until this article is complete, I have not used the message after erase and erase. So don't be scared by these messages.
NM_CUSTOMDRAW MESSAGES provides you with information:
l nm_customdraw message will provide you with the following information:
l Listctrl handle
l Listctrl ID
l The current "painting section"
l Painting DC, let you use it to draw
l The control, item, and subsser RECT value being drawn.
l The Item of the Item is drawn.
l Subitem's index value being drawn
l The status value of the Item that is drawn (
SELECTED, GRAYED,
and many more)
l Item's LPARAM value is you use
CListCtrl :: setItemData set by the value
All of the above information may be important to you, depending on what effect you want, but most often use is "Painting Segment", "Painting DC", "Item Index", "Lparam". value.
A simple example:
Ok, after the above boring details, we are time to look at some simple code. The first example is very simple, it just changed the color of the text in the control.
The code for processing is as follows:
Void CPANEL1 :: OnCustomDrawlist (nmhdr * pnmhdr, lresult * presult) {nmlvcustomDraw * plvcd = reinterpret_cast
// Take The Default Processing Unless We set this to something else below. * PRESULT = 0;
// First thing - check the draw stage If it's the control's prepaint // stage, then tell Windows we want messages for every item if (CDDS_PREPAINT == pLVCD-> nmcd.dwDrawStage) {* pResult = CDRF_NOTIFYITEMDRAW;} else if.. (CDDS_ITEMPREPAINT == pLVCD-> nmcd.dwDrawStage) {// This is the prepaint stage for an item. Here's where we set the // item's text color. Our return value will tell Windows to draw the // item itself, but it Will Use The New Color We set here. // We'll Cycle The Colors Through Red, Green, And Light Blue. ColorRef Crtext; IF ((PLVCD-> NMCD.DwItemspec% 3) == 0) CRText = RGB (255 , 0, 0); ELSE IF ((PLVCD-> nmcd.dwitemspec% 3) == 1) CRText = RGB (0,255,0); Else Crtext = RGB (128, 128, 255);
// Store The Color Back In The NmlvcustomDraw structure. Plvcd-> clrtext = crtext;
// Tell Windows to Paint The Control Itself. * PRESULT = CDRF_DODEFAULT;}}
The result is as follows, you can see the interlaced display of the colors between rows and lines, but cool, and this only needs two IF judgments to do it.
One thing must be remembered, before doing any painting, you have to check the "painting section" that is in place, because your handle function will receive a lot of news, and "Painting Segment" will determine your code. behavior.
A smaller simple example:
The following example will demonstrate how to deal with the painting of Subitem (in fact, Subitem is also a column)
Processing the NM_CustomDraw message before the ListCtrl control painting. Tell Windows We want to process NM_CUSTOMDRAW messages for each ITEM. When one of these messages, telling Windows We want to process this message before each SubItem's drawing. When these messages arrive, we set the color of the text and background for each SubItem.
Void
CMYDLG :: ONCUSTOMDRAWMYLIST (NmHDR * PNMHDR, LRESULT * PRESULT)
{
NMLVCUSTOMDRAW * PLVCD =
Reinterpret_cast
// Take The Default Processing Unless We set this to something else bellow.
* PRESULT = CDRF_DODEFAULT; // First Thing - Check The Draw Stage. If IT'S THE Control's Prepaint
// Stage, The Tell Windows WE Want Messages for Every Item.
IF
(CDDS_PREPAINT == PLVCD-> NMCD.dwdrawStage)
{
* PRESULT = CDRF_NOTIFYITEMDRAW;
}
Else
IF
(CDDS_ItemprepAint == PLVCD-> NMCD.DwdrawStage)
{
// this is the notification message for an item. We'll Request
// Notifications Before Each SubItem's Prepaint Stage.
* PRESULT = CDRF_NOTIFYSUBITEMDRAW;
}
Else
IF
((CDDS_Itemprepaint | CDDS_SUBITEM) == PLVCD-> NMCD.dwdrawStage)
{
// this is the prepaint Stage for a Subitem. Here's Where We set the
// item's text and background colors. Our return value will tell
// Windows to Draw The Subitem Itself, But it will use the New Colors
// We set here.
// The text Color Will Cycle Through Red, Green, And Light Blue.
// The Background Color Will BE Light Blue for Column 0, Red for
// Column 1, And Black for Column 2.
ColorRef crText, crbkgn;
IF
(
0
== PLVCD-> ISUBITEM)
{
Crtext = RGB (
255
,
0
,
0
);
CRBKGND = RGB (
128
,
128
,
255
);
}
Else
IF
(
1
== PLVCD-> ISUBITEM)
{
Crtext = RGB (
0
,
255
,
0
);
CRBKGND = RGB (
255
,
0
,
0
);
}
Else
{
Crtext = RGB (
128
,
128
,
255
);
CRBKGND = RGB (
0
,
0
,
0
);
}
// Store The Colors Back in The NmlvcustomDraw structure.
PLVCD-> CLRText = CRText;
PLVCD-> CLRTextBk = crbkgnd;
// Tell Windows to Paint The Control Itself.
* PRESULT = CDRF_DODEFAULT;
}
}
The result of the execution is as follows:
I need to pay attention to two things here:
l
The color of the CLRTextBK is just for each column, and the area color on the right of the last column is also consistent with the background color of the ListCtrl control.
l When I re-read the document, I noticed that there is a topic "NM_CUSTOMDRAW"
(List view) "Article, it said that you can start at the beginning
Custom Draw
Return in the message
CDRF_NOTIFYSUBITEMDRAW can handle SubItem, without need
CDDS_ItemprepAint painting segment to specify
CDRF_NOTIFYSUBITEMDRAW. But I tried it, I found that this method doesn't work, you still need to deal with
CDDS_ItemprepAint segment.
Segment processing "after painting"
Examples of the limit are the segments of "pre-painting", when
Windows
draw
List item
Previously change its appearance. However, in "pre-drawing", your drawing behavior is limited, you can only change the color or appearance of the font. If you want to change the icon to draw, you can put the whole "before painting"
Item
Heavy painting or "painting" to do this. When you do "custom painting" after painting, your "painting process" will be
Windows
Finish
Item
or
Subitem
When you are called, you can doodle as you want! !
In this example, I will create one
Listctrl
Generally
Listctrl
of
Item
If it is selected, it
Icon
The selected state will also be presented. And I created this
Listctrl
of
Icon
Whether it is presented in a selected state. Proceed as follows:
Treat NM_CUSTOMDRAW messages on "Painting". Tell Windows We want to get NM_CUSTOMDRAW messages when each Item is drawn. When these news is coming, tell Windows We want to get NM_CUSTOMDRAW messages when you draw. When these messages come, we regenerate every Item icon.
void CPanel3 :: OnCustomdrawList (NMHDR * pNMHDR, LRESULT * pResult) {NMLVCUSTOMDRAW * pLVCD = reinterpret_cast
// Get the rect that holds the item's icon m_list.GetItemRect (nItem, & rcIcon, LVIR_ICON);. // Draw the icon m_imglist.Draw (pDC, rItem.iImage, rcIcon.TopLeft (), ILD_TRANSPARENT);. * PResult = CDRF_SKIPDEFAULT;}}} Repeat, Custom Draw allows us to do as few work, the above example is to let us finish all work, then we will re-press the Item icon of the selected state, that is The icon we see. The result is as follows:
The only shortcomings are that such a method will make you feel a little flicker. Because the icon is drawn twice (although very fast).
use
Custom Draw
instead
Owner Draw
Another elegant thing is that you can use Custom Draw instead of Owner Draw. The difference between them is in my opinion:
l Write the Custom Draw code easier than writing the code of Owner Draw.
If you only need to change the look of a row, you can do it without the painting of other rows, let Windows do it. But if you use
Owner Draw, you have to handle all the work. When you want to do all the controls, you can process
NM_CUSTOMDRAW
The last return of the message
CDRF_SKIPDEFAULT, which is a bit different from us so far.
CDRF_SKIPDEFAULT
Tell Windows to do all control paintings by us, you don't need anything.
I have not included here the code of this example because it is a bit long, but you can debug code in the debugger at step by step, you can see each
What happened. If you place the window, let you see the debugger and demo program, then in your step debugging, you can see
The control is drawn every step, and the listctrl here is very simple, only one column and there is no column, as follows:
If you need to see the original text and download example, please come to this website:
http://www.codeproject.com/listctrl/lvcustomdraw.asp