自绘透明ListBox
- 文章概要:
- 任何一个有经验的windows工程师都觉得在windows中,透明度不是一个很细小的任务。一个透明的listbox控件也不例外。
|
一. 前言 任何一个有经验的windows工程师都觉得在windows中,透明度不是一个很细小的任务。一个透明的listbox控件也不例外。事实上ListBox会比其他控件难一点。原因是ListBox自带滚动条。但是总体来说,实现起来是一个非常简单的概念。 例如,实现一个透明的static控件,要处理WM_ERASEBKGND并且当用户调用SetWindowText时重绘控件。 一个ListBox,我们都要接管WM_ERASEBKGND消息并且让它的返回值为TRUE(基本上,这是透明最容易的方法)。当用户按ListBox中滚动条的下面按钮时,windows会按顺序把顶端index+1项贴到上一个显示项,然后再绘制新项。此时项目的背景被复制。 二.透明ListBox的实现 如何实现透明的ListBox?让我们从自绘ListBox开始。 首先在第一次绘制ListBox前,我们必须要复制其父窗口的背景图形,这样我们才能为listbox准备背景图。这个,我们可以在WM_ERASEBKGND消息中处理。当这个消息第一次到达时,还没绘制ListBox,因此,在这里截取父窗口的背景是安全的。 01.BOOL CTransListBox::OnEraseBkgnd(CDC*
pDC)02.{03.if (!m_HasBackGround)04.{05.CWnd
*pParent = GetParent();06.if (pParent)07.{08.CRect
Rect;09.GetClientRect(&Rect);10.ClientToScreen(&Rect);11.pParent->ScreenToClient(&Rect);12.CDC
*pDC = pParent->GetDC();13.m_Width
= Rect.Width();14.m_Height
= Rect.Height();15.CDC
memdc;16.memdc.CreateCompatibleDC(pDC);17.CBitmap
*oldbmp = memdc.SelectObject(&m_Bmp);18.memdc.BitBlt(0,0,Rect.Width(),Rect.Height(),19.pDC,Rect.left,Rect.top,SRCCOPY);20.memdc.SelectObject(oldbmp);21.m_HasBackGround
= TRUE;22.pParent->ReleaseDC(pDC);23.}24.}25.return TRUE;26.}其次我们得在屏幕上绘制listbox的每一个item。因为有滚动条,我们不能让ListBox为我们做任何绘图。因此,重载DrawItem方法,但什么也不干。往下有一个自己定义的函数DrawItem,当在OnPaint函数中绘制ListBox时,我们可以调用它。在OnPaint函数中做了:把背景图绘制到一个内存DC中,然后把可见的items绘制到同样的内存DC中,最后把整个东西都贴到ListBox的DC中去。代码如下: 001.void CTransListBox::DrawItem(
LPDRAWITEMSTRUCT lpDrawItemStruct )002.{003.//do
nothing when the listbox asks you to draw an item004.}005.void CTransListBox::DrawItem(CDC
&Dc, int Index,CRect
&Rect,BOOL Selected)006.{007.if (Index
== LB_ERR || Index >= GetCount())008.return;009.if (Rect.top
< 0 || Rect.bottom > m_Height)010.{011.return;012.}013.CRect
TheRect = Rect;014.Dc.SetBkMode(TRANSPARENT);015. 016.CDC
memdc;017.memdc.CreateCompatibleDC(&Dc);018. 019.CFont
*pFont = GetFont();020.CFont
*oldFont = Dc.SelectObject(pFont);021.CBitmap
*oldbmp = memdc.SelectObject(&m_Bmp);022.Dc.BitBlt(TheRect.left,TheRect.top,TheRect.Width(),023.TheRect.Height(),&memdc,TheRect.left,024.TheRect.top,SRCCOPY);025.CString
Text;026.GetText(Index,Text);027.if (m_Shadow)028.{029.if (IsWindowEnabled())030.{031.Dc.SetTextColor(m_ShadowColor);032.}033.else034.{035.Dc.SetTextColor(RGB(255,255,255));036.}037.TheRect.OffsetRect(m_ShadowOffset,m_ShadowOffset);038.Dc.DrawText(Text,TheRect,DT_LEFT|DT_EXPANDTABS|DT_NOPREFIX);039.TheRect.OffsetRect(-m_ShadowOffset,-m_ShadowOffset);040.}041. 042.if (IsWindowEnabled())043.{044.if (Selected)045.{046.Dc.SetTextColor(m_SelColor);047.}048.else049.{050.Dc.SetTextColor(m_Color);051.}052.}053.else054.{055.Dc.SetTextColor(RGB(140,140,140));056.}057.Dc.DrawText(Text,TheRect,DT_LEFT|DT_EXPANDTABS|DT_NOPREFIX);058.Dc.SelectObject(oldFont);059.memdc.SelectObject(oldbmp);060.}061. 062.void CTransListBox::OnPaint()063.{064.CPaintDC
dc(this); //
device context for painting065. 066.CRect
Rect;067.GetClientRect(&Rect);068. 069.int Width
= Rect.Width();070.int Height
= Rect.Height();071. 072.//create
memory DC's073.CDC
MemDC;074.MemDC.CreateCompatibleDC(&dc);075.CBitmap
MemBmp;076.MemBmp.CreateCompatibleBitmap(&dc,Width,Height);077. 078.CBitmap
*pOldMemBmp = MemDC.SelectObject(&MemBmp);079. 080.//paint
the background bitmap on the memory dc081.CBitmap
*pOldbmp = dc.SelectObject(&m_Bmp);082.MemDC.BitBlt(0,0,Width,Height,&dc,0,0,SRCCOPY);083.dc.SelectObject(pOldbmp);084. 085. 086.Rect.top
= 0;087.Rect.left
= 0;088.Rect.bottom
= Rect.top + GetItemHeight(0);089.Rect.right
= Width;090. 091.int size
= GetCount();092.//draw
each item on memory DC093.for (int i
= GetTopIndex(); i < size094.&&
Rect.top <= Height;++i)095.{096.DrawItem(MemDC,i,Rect,GetSel(i));097.Rect.OffsetRect(0,GetItemHeight(i));098.}099. 100.//draw
the results onto the listbox dc101.dc.BitBlt(0,0,Width,Height,&MemDC,0,0,SRCCOPY);102. 103.MemDC.SelectObject(pOldMemBmp);104.}最后也是最棘手的滚动消息部分。为了处理滚动问题,我们可以拦截WM_VSCROLL消息,并且把CListBox::OnVScroll 放SetRedraw(FALSE)和SetRedraw(TRUE)之间;即在调用ListBox的滚动函数前,禁止绘制,之后才重启绘制。接下来,调用RedrawWindow更新listbox中的内容。代码如下: 01.void CTransListBox::OnVScroll(UINT nSBCode,02.UINT nPos,
CScrollBar* pScrollBar)03.{04.SetRedraw(FALSE); //prevent
any drawing05.CListBox::OnVScroll(nSBCode,nPos,pScrollBar);06.SetRedraw(TRUE); //restore
drawing07. 08.//draw
the frame and window content in one shot09.RedrawWindow(0,0,RDW_FRAME|RDW_INVALIDATE|RDW_UPDATENOW);10.}当一个items被选中时,将产生同类型的方法。因此要拦截LBN_SELCHANGE消息,并且使其重绘,因为我们自己的DrawItem什么也没干。 1.BOOL CTransListBox::OnLbnSelchange()2.{3.Invalidate();4.UpdateWindow();5.return TRUE;6.}三.透明ListBox类CTransListBox的使用 使用这个类的话,只需在你的对话框中添加一个ListBox控件,然后在Listbox控件属性中设置Owner-draw和 Has Strings两项,给listbox控件关联一个CTransListBox类型的变量m_TransList。自绘类CTransListBox还有一些别的功能:指定不同的字体、颜色、阴影。代码如下: 01.class CTransparentListboxDemoDlg
: public CDialog02.{03.....04.CTransListBox
m_TransList;05.};06.void CTransparentListboxDemoDlg::DoDataExchange(CDataExchange*
pDX)07.{08.CDialog::DoDataExchange(pDX);09.DDX_Control(pDX,
IDC_LIST1, m_ListBox);10.}11. 12.BOOL CTransparentListboxDemoDlg::OnInitDialog()13.{14.CDialog::OnInitDialog();15. 16.m_TransList.SetFont(12,"Aria",17.RGB(255,255,255),RGB(255,0,0)); //Optional 18. 19.m_TransList.AddString(“Test”);20.} |
658

被折叠的 条评论
为什么被折叠?



