分类: C/C++
2008-09-14 21:00:46
This is a gradient based, embossed progress control that uses an image dynamically generated from a string parameter.
About a year ago I created a utility for work. Because of the lengthy operations involved while parsing through one or more files, I needed a progress control. Spending time on Code Project, I came across CSuperProgressCtrl
created by Jean-Edouard Lachand-Robert. I liked the eye-catching embossing (taken directly from an article by Zafir Anjum entitled "Emboss text and other shape on your bitmap") of the progress control. Using a black and white bitmap resource in the project, it made for a great display.
While putting together a dialog for the progress control to reside in, I came across an article on Code Project by Chris Maunder. His article about CProgressWnd
described how to have a progress dialog without having to have a resource. Replacing the normal progress control with the embossed version and modifying the CProgressWnd::SetWindowSize()
function gave a nice result that allowed for a progress control with some text lines below it.
As much as I liked the control, it had the limitation of requiring a black and white bitmap resource so it could not be changed on the fly depending on the text. About the the time I was pondering this problem, one of my workmates suggested that I generate the bitmap using a text string rather than generating a bitmap resource file of the text to be compiled into the project. This solution would allow me to change the embossed text to whatever I wanted or needed. Working with that thought in mind, I removed the text display from the original CProgressWnd
class and began looking for a place to hook in the bitmap creation. The only two functions that needed to be modified were CProgressWnd::Create()
and CProgressWnd::SetWindowSize()
. I also modified CSuperProgressCtrl
to add the functions GetHeight()
and GetWidth()
to make the changes to CProgressWnd::SetWindowSize()
possible.
I wanted to be able to control the font and size of the bitmap text. To accomplish this Create()
was changed so that in addition to the parent window and the title, it takes a string to display, a font name, and a font height.
All of the real code changes are in CProgressWnd::Create()
and CProgressWnd::SetWindowSize()
. I'll skip over the original code and concentrate only on the code changes that I've made to the class. Inside of Create()
, I use a utility class to create a font of the desired type and size. The class CAutoFont
class by Jamie Nordmeyer greatly simplifies the task. I chose to use a bold version of the font because I like the resulting display better. I then select the newly created font into a device context that I create for drawing.
// 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 ); //...
Now that I have a font and a device context, it's time to place the text in a bitmap for the progress control to use. Using the CBitmapDC
class created by Anneke Sicherer-Roetman, I create a bitmap in memory of the size calculated earlier with the call to 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();
Now all that's left to do is supply the CSuperProgressCtrl::Create()
call with a handle to the bitmap that was just created.
//... 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 ); }
Once the new embossed bitmap progress control is created, it must be sized and positioned. This is done in CProgressWnd::SetWindowSize()
. Calling m_wndProgress.GetWidth()
and m_wndProgress.GetHeight()
makes the calculations easy. The code is commented and relatively clean so I won't comment on it.
One thing that took me awhile to initially discover is that the CSuperProgressCtrl
class must be registered before being used. To accomplish this, place the function call below somewhere in the programs initialization. In the sample, I place the call in OnInitialUpdate()
.
CSuperProgressCtrl::RegisterClass();
In the OnBtnRun()
function, I create the progress bar passing in the text string csTemp
for the bitmap and a 36 point font. The NULL
causes the default font to be used.
pProgressWnd = new CProgressWnd( this, _T("Progress"), (LPCTSTR)csTemp, NULL, 36 );
A call to SetRange()
sets the range. Inside of the loop a call to SetPos()
updates the progress control. The PeekAndPump()
call gives the progress control a chance to pick up any button clicks on the cancel button. By calling Cancelled()
, the application can tell if the cancel button was pressed.
And that's really it. Whew! It was a quick and dirty thing using several classes I found here on Code Project, but it turned out pretty well. And during the course of writing this little article, I've gained an even greater appreciation for the effort that others put into publishing their articles.