自绘透明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 item 004. } 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. else 034. { 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. else 049. { 050. Dc.SetTextColor(m_Color); 051. } 052. } 053. else 054. { 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 painting 065. 066. CRect
Rect; 067. GetClientRect(&Rect); 068. 069. int Width
= Rect.Width(); 070. int Height
= Rect.Height(); 071. 072. //create
memory DC's 073. 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 dc 081. 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 DC 093. for ( int i
= GetTopIndex(); i < size 094. &&
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 dc 101. 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 drawing 05. CListBox::OnVScroll(nSBCode,nPos,pScrollBar); 06. SetRedraw(TRUE); //restore
drawing 07. 08. //draw
the frame and window content in one shot 09. 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 CDialog 02. { 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. } |