自绘透明ListBox

自绘透明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. }
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值