Windows Interface - Redrawing ListCtrl Using Custom Draw

xiaoxiao2021-04-04  235

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 (PNMHDR);

// 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

(pnmhdr);

// 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 (pNMHDR); * pResult = 0; // If this is the beginning of the control's paint cycle, request // notifications for each item if (CDDS_PREPAINT == pLVCD-> nmcd.dwDrawStage) {* pResult = CDRF_NOTIFYITEMDRAW;}. else if (CDDS_ITEMPREPAINT == pLVCD-> nmcd.dwDrawStage) {// This is the pre-paint stage for an item. We need to make another // request to be notified during the post-paint stage * pResult = CDRF_NOTIFYPOSTPAINT;.} else if (CDDS_ITEMPOSTPAINT == pLVCD-> nmcd.dwDrawStage) {// If this item is selected, re-draw the Icon in its normal // color (not bededed with the highlight color). lvitem RITEM; int NITEM = static_cast (PLVCD-> nmcd.dwItemspec); // Get The Image Index and State of this Item. Note That WE Need to // check the selected state manually. t He DOCS _SAY_ THAT THE // Item's State Is in PLVCD-> NMCD.UITEMSTATATE, But During My Testing // IT WAS ALWAYS Equal To 0x0201, Which Doesn't make Sense, Since // The max cdis_ constant in command.h is 0x0100 ZeroMemory (& rItem, sizeof (LVITEM));. rItem.mask = LVIF_IMAGE | LVIF_STATE; rItem.iItem = nItem; rItem.stateMask = LVIS_SELECTED; m_list.GetItem (& rItem); // If this item is selected, redraw the icon ITS Normal Colors. if (RItem.State & lvis_selected) {CDC * PDC = CDC :: fromHandle (PLVCD-> nmcd.hdc); CRECT RCICON;

// 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

转载请注明原文地址:https://www.9cbs.com/read-131766.html

New Post(0)