Introduction
This is a gradient based, embossed progress control that uses an image dynamically generated
from a string parameter.
How it was created
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.
Code Details
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.
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 );
pFont->SetHeight( nFontHeight );
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()
.
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,
TempRect.left,
TempRect.right,
(HBITMAP)(*m_pBitmap),
IDC_PROGRESS
);
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.
Quick Sample Code
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.
Conclusion
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.