自绘透明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.
}
|