动态浮雕文字进度控件






4.63/5 (8投票s)
2002年10月6日
4分钟阅读

75153

2175
一个动态浮雕文字进度控件,
引言
这是一个基于渐变、压花的进度条,它使用从字符串参数动态生成的图像。
如何创建的
大约一年前,我为工作创建了一个实用程序。由于在解析一个或多个文件时涉及耗时的操作,所以我需要一个进度条。在 Code Project 上闲逛时,我偶然发现了 Jean-Edouard Lachand-Robert 创建的CSuperProgressCtrl
。我喜欢那个引人注目的压花效果(直接来自 Zafir Anjum 的一篇题为“在位图上压花文本和其他形状”的文章),这让进度条效果很棒。
在为进度条准备对话框时,我遇到了 Chris Maunder 在 Code Project 上的一篇文章。他关于CProgressWnd
的文章描述了如何拥有一个进度对话框而无需资源。用压花版本替换普通进度条并修改CProgressWnd::SetWindowSize()
函数得到了不错的结果,它允许在进度条下方有几行文本。
尽管我很喜欢这个控件,但它有一个限制,就是需要黑白位图资源,所以不能根据文本动态更改。当我思考这个问题的时候,我的一个同事建议我使用文本字符串来生成位图,而不是生成位图资源文件来编译到项目中。这个解决方案允许我更改压花文本为任何我想要的或需要的。想到这一点,我从原来的CProgressWnd
类中移除了文本显示,并开始寻找一个地方来挂入位图创建。唯一需要修改的是 CProgressWnd::Create()
和CProgressWnd::SetWindowSize()
函数。我还修改了CSuperProgressCtrl
以添加GetHeight()
和GetWidth()
函数,以便对CProgressWnd::SetWindowSize()
进行修改。
我想能够控制位图文本的字体和大小。为了实现这一点,Create()
被更改为除了父窗口和标题之外,它还接受一个要显示的字符串、字体名称和字体高度。
代码详情
所有实际的代码更改都在CProgressWnd::Create()
和CProgressWnd::SetWindowSize()
中。我将跳过原始代码,只关注我对该类所做的代码更改。在Create()
内部,我使用一个实用程序类来创建所需类型和大小的字体。Jamie Nordmeyer 的CAutoFont
类极大地简化了任务。我选择使用粗体字体,因为我更喜欢由此产生的显示效果。然后,我将新创建的字体选入我创建的用于绘图的设备上下文。
// If no font was passed in, use the default. csFontName = lpcFontName; if( lpcFontName && _tcslen(lpcFontName) ) pFont = new CAutoFont( lpcFontName ); else pFont = new CAutoFont(); hMemDC = CreateCompatibleDC( NULL ); if( hMemDC ) { CDC* pDC = CDC::FromHandle( hMemDC ); if( pDC ) { pFont->SetBold( TRUE ); // The font size. pFont->SetHeight( nFontHeight ); // Select the font so that a size can be determined. pOldFont = pDC->SelectObject( pFont ); string_size = pDC->GetTextExtent( (LPCTSTR)csBitmapString, csBitmapString.GetLength() ); pOldFont = pDC->SelectObject( pOldFont ); //...
现在我已经有了字体和设备上下文,是时候将文本放在位图中供进度条使用了。使用 Anneke Sicherer-Roetman 创建的CBitmapDC
类,我在内存中创建了一个位图,其大小通过调用GetTextExtent()
计算得出。
// Create a bitmap in memory using the string passed in. CBitmapDC bitmapDC( string_size.cx, string_size.cy, pDC ); pOldFont = bitmapDC.SelectObject( pFont ); string_size = bitmapDC.GetTextExtent( (LPCTSTR)csBitmapString, csBitmapString.GetLength() ); bitmapDC.TextOut( 0, 0, (LPCTSTR)csBitmapString, csBitmapString.GetLength() ); m_pBitmap = bitmapDC.Close();
现在要做的就是为CSuperProgressCtrl::Create()
调用提供刚创建的位图的句柄。
//... bSuccess = m_wndProgress.Create( this, // Parent window. TempRect.left, // X Position. TempRect.right, // Y Position. (HBITMAP)(*m_pBitmap), IDC_PROGRESS // ID ); pOldFont = bitmapDC.SelectObject( pOldFont ); ReleaseDC( pDC ); } DeleteDC( hMemDC ); }
一旦创建了新的压花位图进度条,就必须对其进行调整大小和定位。这在CProgressWnd::SetWindowSize()
中完成。调用 m_wndProgress.GetWidth()
和m_wndProgress.GetHeight()
可以轻松进行计算。代码是带注释的,而且相对清晰,所以我不进行评论。
快速示例代码
我花了一段时间才初步发现的一件事是,在使用CSuperProgressCtrl
类之前必须先注册它。要实现这一点,请将以下函数调用放在程序初始化中的某个位置。在示例中,我将其放在OnInitialUpdate()
中。
CSuperProgressCtrl::RegisterClass();
在OnBtnRun()
函数中,我创建了进度条,传入文本字符串csTemp
作为位图,以及一个 36 点的字体。NULL
会导致使用默认字体。
pProgressWnd = new CProgressWnd( this, _T("Progress"), (LPCTSTR)csTemp, NULL, 36 );
调用SetRange()
来设置范围。在循环中,调用 SetPos()
来更新进度条。PeekAndPump()
调用使进度条有机会响应取消按钮上的任何点击。通过调用Cancelled()
,应用程序可以判断是否按下了取消按钮。
结论
就这样,结束了!这是一个快速而粗糙的东西,使用了我在 Code Project 上找到的几个类,但结果相当不错。而且,在撰写这篇文章的过程中,我更加体会到其他人为发布他们的文章所付出的努力。