📄 grid_list_control.shtml.htm
字号:
ASSERT( nCol < colcount );
int *orderarray = new int[ colcount ];
Header_GetOrderArray( pHeader->m_hWnd, colcount, orderarray );
// Get the column offset
int offset = 0;
for( int i = 0; orderarray[i] != nCol; i++ )
offset += GetColumnWidth( orderarray[i] );
int colwidth = GetColumnWidth( nCol );
delete[] orderarray;
CRect rect;
GetItemRect( 0, &rect, LVIR_BOUNDS );
// Now scroll if we need to expose the column
CRect rcClient;
GetClientRect( &rcClient );
if( offset + rect.left < 0 || offset + colwidth + rect.left > rcClient.right )
{
CSize size;
size.cx = offset + rect.left;
size.cy = 0;
Scroll( size );
rect.left -= size.cx;
}
}
</FONT></TT></PRE>
<H4>
Step 3: Implement custom draw</H4>
Since this new message is not in VC++'s Class Wizard, you will need to add the message map
and definition yourself. Be sure to add them outside of the Class Wizard fences. If you do not, when
Class Wizard updates your source file, it will be lost. The other message maps are included
to clarify where you would add it yourself.
<PRE><TT><FONT COLOR="#990000">BEGIN_MESSAGE_MAP(CGridListCtrl, CListCtrl)
//{{AFX_MSG_MAP(CGridListCtrl)
ON_WM_LBUTTONDOWN()
ON_WM_HSCROLL()
ON_WM_VSCROLL()
//}}AFX_MSG_MAP
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
END_MESSAGE_MAP()
</PRE></TT></FONT>
Further you will also need to add the member function declaration as follows also outside
the Class Wizard fences:
<PRE><TT><FONT COLOR="#990000">protected:
//{{AFX_MSG(CGridListCtrl)
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
//}}AFX_MSG
afx_msg void OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult);
</PRE></TT></FONT>
Now the fun begins. We all like to draw, right?
<P>
The new custom control custom draw is a staged process. The same message
is sent at different drawing stages of the control. If you tell it that
the control can handle the rest of the stages, it will not send any more
custom drawing messages for that drawing session. The control
will always send the NM_CUSTOMDRAW message just before commencing a drawing session.
If you want to handle the drawing of the control (any of it), the return value
from this message must indicate it. In this implementation, we only want to
override the drawing of the subitem which has the focus. The rest of the drawing
can be done by the control.
<P>
There are four stages outlined as follows:
<OL>
<LI>The control asks if we want to handle drawing of the control. Respond that we want
to handle the drawing at the item level. (The response is achieved by the return value.)
<LI>The control responds when it is about to paint a given item. If the item has focus,
respond that we want to handle drawing at the subitem level. Otherwise, respond that
the control can draw the whole item.
<LI>The control responds before painting a given subitem which was fowarded from
stage two. Respond that we want to be notified after painting the subitem.
<LI>The control responds after painting the subitem from stage three. If the subitem
is the focused subitem, do the special drawing, and respond that we handled the
drawing so the control will not do any more drawing on the subitem. Otherwise, respond
that the list control should finish the subitem drawing. (Actually it is probably not
going to do anymore drawing anyway, but it is good form.)
</OL>
You might think that I could have performed the drawing from stage four in stage three.
If I had I would have also had to worry about indentation, state icon drawing, and item/subitem
icon drawing. This way, I let the list control draw those and I just draw on top of the label
after it is done.
<PRE><TT><FONT COLOR="#990000">void CGridListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
// This function is called by the control in different
// stages during the control drawing process.
NMLVCUSTOMDRAW *pCD = (NMLVCUSTOMDRAW*)pNMHDR;
// By default set the return value to do the default behavior.
*pResult = 0;
switch( pCD->nmcd.dwDrawStage )
{
case CDDS_PREPAINT: // First stage (for the whole control)
// Tell the control we want to receive drawing messages
// for drawing items.
*pResult = CDRF_NOTIFYITEMDRAW;
// The next stage is handled in the default:
break;
default: // Stage two handled here. (called for each item)
if( !(pCD->nmcd.uItemState & CDIS_FOCUS) )
{
// If this item does not have focus, let the
// control draw the whole item.
*pResult = CDRF_DODEFAULT;
}
else
{
// If this item has focus, tell the control we want
// to handle subitem drawing.
*pResult = CDRF_NOTIFYSUBITEMDRAW;
}
break;
case CDDS_ITEMPREPAINT | CDDS_SUBITEM: // Stage three (called for each subitem of the focused item)
{
// We don't want to draw anything here, but we need to respond
// of DODEFAULT will be the next stage.
// Tell the control we want to handle drawing after the subitem
// is drawn.
*pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NOTIFYPOSTPAINT;
}
break;
case CDDS_ITEMPOSTPAINT | CDDS_SUBITEM: // Stage four (called for each subitem of the focused item)
{
// We do the drawing here (well maybe).
// This is actually after the control has done its drawing
// on the subitem. Since drawing a cell is near instantaneous
// the user won't notice.
int subitem = pCD->iSubItem;
// Only do our own drawing if this subitem has focus at the item level.
if( (pCD->nmcd.uItemState & CDIS_FOCUS) )
{
// If this subitem is the subitem with the current focus,
// draw it. Otherwise let the control draw it.
CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
// We have to take into account the possibility that the
// columns may be reordered.
if( subitem == Header_OrderToIndex( pHeader->m_hWnd, m_CurSubItem ) )
{
// POSTERASE
CDC* pDC = CDC::FromHandle(pCD->nmcd.hdc);
// Calculate the offset of the text from the right and left of the cell.
int offset = pDC->GetTextExtent(_T(" "), 1 ).cx*2;
// The rect for the cell gives correct left and right values.
CRect rect = pCD->nmcd.rc;
CRect bounds;
GetItemRect( pCD->nmcd.dwItemSpec, &bounds, LVIR_BOUNDS );
// Get the top and bottom from the item itself.
rect.top = bounds.top;
rect.bottom = bounds.bottom;
// Adjust rectangle for horizontal scroll and first column label
{
if( subitem == 0 )
{
CRect lrect;
GetItemRect( pCD->nmcd.dwItemSpec, &lrect, LVIR_LABEL );
rect.left = lrect.left;
rect.right = lrect.right;
}
else
{
rect.right += bounds.left;
rect.left += bounds.left;
}
}
// Clear the background with button face color
pDC->FillRect(rect, &CBrush(::GetSysColor(COLOR_3DFACE)));
// PREPAINT
CString str;
str = GetItemText( pCD->nmcd.dwItemSpec, pCD->iSubItem );
// Deflate the rect by the horizontal offset.
rect.DeflateRect( offset, 0 );
// You could also make this column alignment sensitive here.
pDC->DrawText( str, rect,
DT_SINGLELINE|DT_NOPREFIX|DT_LEFT|DT_VCENTER|DT_END_ELLIPSIS);
// POSTPAINT
// Draw rounded edge
rect.InflateRect( offset, 0 );
pDC->Draw3dRect( &rect, ::GetSysColor(COLOR_3DSHADOW), ::GetSysColor(COLOR_3DFACE) );
rect.DeflateRect( 1, 1 );
pDC->Draw3dRect( &rect, ::GetSysColor(COLOR_3DDKSHADOW), ::GetSysColor(COLOR_3DHILIGHT) );
// Tell the control that we handled the drawing for this subitem.
*pResult = CDRF_SKIPDEFAULT;
}
}
}
break;
}
}
</FONT></TT></PRE>
<P>
<A HREF="GridList.zip" tppabs="http://www.codeguru.com/listview/GridList.zip">Download Sample/Code,<A>(95k)
<P>
<HR>
<TABLE BORDER=0 WIDTH="100%" >
<TR>
<TD WIDTH="33%"><FONT SIZE=-1><A HREF="../index.htm" tppabs="http://www.codeguru.com/">Goto HomePage</A></FONT></TD>
<TD WIDTH="33%">
<CENTER><FONT SIZE=-2>© 1997 Zafir Anjum</FONT> </CENTER>
</TD>
<TD WIDTH="34%">
<DIV ALIGN=right><FONT SIZE=-1>Contact me: <A HREF="mailto:zafir@home.com">zafir@home.com</A> </FONT></DIV>
</TD>
</TR>
</TABLE>
<CENTER> <IMG SRC="../cgi/Count.cgi-ft=2&dd=E-df=lv_add_checkbox.cnt" tppabs="http://www.codeguru.com/cgi/Count.cgi?ft=2&dd=E%7cdf=lv_add_checkbox.cnt" ALIGN="BOTTOM" BORDER="0"></CENTER>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -