65.9K
CodeProject 正在变化。 阅读更多。
Home

动态浮雕文字进度控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.63/5 (8投票s)

2002年10月6日

4分钟阅读

viewsIcon

75153

downloadIcon

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 上找到的几个类,但结果相当不错。而且,在撰写这篇文章的过程中,我更加体会到其他人为发布他们的文章所付出的努力。

© . All rights reserved.