自绘透明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.}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值