A UI class for long operation feedback

CLongOperation is a class designed to give visual feedback for long-lasting operations. It has support for:

  • Displaying a wait cursor
  • Showing a text in the status bar
  • Displaying a progress bar in a dynamically created status bar pane

In the simplest case, this class can be used as a replacement for MFC's CWaitCursor:

	CLongOperation wait;
	// some hard work going here...

To display some textual progress information in the status bar:

	CLongOperation wait;
	wait.SetText("Pass 1");
	// ...
	wait.SetText("Pass 2");
	// ...

To display a progress bar in the status bar:

	CLongOperation wait;
	for (int i = 0; i < count; i++)
	{
		wait.Step((100*i)/cnt);
		// ...
	}
	wait.Stop();

/////////////////////////////////////////////////////////////////
// LongOperation.h
// (c) 1997, Klaus G|tter
class CLongOperation : public CObject
{
public:
	// IDS_PLEASE_WAIT is a string resource ID for the default text,
	// e.g. "Please wait..."
	CLongOperation(UINT nIDText = IDS_PLEASE_WAIT, bool bStart = true);
	CLongOperation(LPCTSTR lpszText, bool bStart = true);
	~CLongOperation();

	void Start();
	void Stop();
	void Step(int nPercentage = -1);
	void SetText(LPCTSTR lpszText);

protected:
	CString m_strText;
	bool m_bStarted;
	HWND m_hwndProgress;
	void CreateProgressControl();
};


/////////////////////////////////////////////////////////////////
// LongOperation.cpp
// (c) 1997, Klaus G|tter

#include "stdafx.h"
#include <afxpriv.h> // defines WM_SETMESSAGESTRING
#include "LongOperation.h"

#ifdef _DEBUG
#undef THIS_FILE
#define new DEBUG_NEW
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

CLongOperation::CLongOperation(UINT nIDText, bool bStart)
:	m_bStarted(false)
,	m_hwndProgress(NULL)
{
	VERIFY(m_strText.LoadString(nIDText));
	if (bStart)
		Start();
}

CLongOperation::CLongOperation(LPCTSTR lpszText, bool bStart)
:	m_strText(lpszText)
,	m_bStarted(false)
,	m_hwndProgress(NULL)
{
	if (bStart)
		Start();
}

CLongOperation::~CLongOperation()
{
	if (m_bStarted)
		Stop();
}

void CLongOperation::Start()
{
	if (m_bStarted)
		Stop();

	// display text in the status bar
	CWnd* pMainWnd = ::AfxGetMainWnd();
	if (pMainWnd)
		pMainWnd->SendMessage(WM_SETMESSAGESTRING, 0, (LPARAM)(LPCTSTR)m_strText);

	// switch on wait cursor
	::AfxGetApp()->BeginWaitCursor();

	m_bStarted = true;
}

void CLongOperation::Stop()
{
	if (!m_bStarted)
		return;

	if (m_hwndProgress)
	{
		// clean up and destroy progress bar
		CStatusBar* pStatusBar = DYNAMIC_DOWNCAST(CStatusBar, CWnd::FromHandle(::GetParent(m_hwndProgress)));
		ASSERT_VALID(pStatusBar);

		::DestroyWindow(m_hwndProgress);
		m_hwndProgress = NULL;

		// remove progress bar pane
		int anPart[32];
		int nParts = pStatusBar->GetStatusBarCtrl().GetParts(31, anPart);
		nParts--;
		pStatusBar->GetStatusBarCtrl().SetParts(nParts, anPart+1);
	}

	// switch back to standard text in the status bar
	CWnd* pMainWnd = ::AfxGetMainWnd();
	if (pMainWnd)
		pMainWnd->SendMessage(WM_SETMESSAGESTRING, AFX_IDS_IDLEMESSAGE, 0);

	// switch off wait cursor
	::AfxGetApp()->EndWaitCursor();

	m_bStarted = false;
}

void CLongOperation::Step(int nPercentage)
{
	if (!m_bStarted)
		Start();

	::AfxGetApp()->RestoreWaitCursor();

	if (nPercentage >= 0)
	{
		ASSERT(nPercentage <= 100);
		// create or update a progress control in the status bar
		if (m_hwndProgress == NULL)
			CreateProgressControl();

		if (m_hwndProgress)
			::SendMessage(m_hwndProgress, PBM_SETPOS, (WPARAM)nPercentage, 0);
	}
}

void CLongOperation::SetText(LPCTSTR lpszText)
{
	m_strText = lpszText;
	CWnd* pMainWnd = ::AfxGetMainWnd();
	if (pMainWnd)
		pMainWnd->SendMessage(WM_SETMESSAGESTRING, 0, (LPARAM)(LPCTSTR)m_strText);
}

void CLongOperation::CreateProgressControl()
{
	ASSERT(m_hwndProgress == NULL);

	// find status bar
	CWnd* pMainWnd = ::AfxGetMainWnd();
	if (pMainWnd == NULL)
		return;
	CStatusBar* pStatusBar = DYNAMIC_DOWNCAST(CStatusBar, 
		pMainWnd->GetDescendantWindow(AFX_IDW_STATUS_BAR, TRUE));
	if (pStatusBar == NULL || pStatusBar->m_hWnd == NULL)
		return;

	CRect rc; // this will be the location for the progress bar pane
	pStatusBar->GetItemRect(0, rc);
	if (!m_strText.IsEmpty())
	{
		// adjust so that the text in the leftmost pane will not be covered
		CClientDC dc(pStatusBar);
		dc.SelectObject(pStatusBar->GetFont());
		CSize sz = dc.GetTextExtent(m_strText);
		TEXTMETRIC tm;
		dc.GetTextMetrics(&tm);
		rc.left += sz.cx + 2*tm.tmAveCharWidth;
	}
	int cx = rc.Width();
	if (cx < 20)
	{
		// no sense in displaying such a small progress bar
		TRACE0("ProgressDisplay would be too small\n");
		return;
	}
	else if (cx > 200)
	{
		// arbitrarily limiting progress bar width to 200 pixel
		cx = 200;
		rc.left = rc.right - cx;
	}

	// add a pane between the text and the currently leftmost pane
	int anPart[32];
	int nParts = pStatusBar->GetStatusBarCtrl().GetParts(31, anPart+1);
	anPart[0] = rc.left;
	nParts++;
	pStatusBar->GetStatusBarCtrl().SetParts(nParts, anPart);
	pStatusBar->GetStatusBarCtrl().GetRect(1, rc);

	// create progress bar control
	m_hwndProgress = ::CreateWindow(PROGRESS_CLASS, "",
		WS_CHILD | WS_VISIBLE, rc.left, rc.top, rc.Width(), rc.Height(),
		pStatusBar->m_hWnd, (HMENU)1, AfxGetInstanceHandle(), NULL);

		pStatusBar->UpdateWindow();
}



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

  • The explosion in mobile devices and applications has generated a great deal of interest in APIs. Today's businesses are under increased pressure to make it easy to build apps, supply tools to help developers work more quickly, and deploy operational analytics so they can track users, developers, application performance, and more. Apigee Edge provides comprehensive API delivery tools and both operational and business-level analytics in an integrated platform. It is available as on-premise software or through …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds