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

  • Wednesday, September 24, 2014 8:00 AM - 9:00 AM PDT According to a recent Forrester Research report, many companies are choosing low-code platforms over traditional programming platforms, due to the speed with which low-code apps can be assembled and tested. With customer-facing applications on the rise, traditional programming platforms simply can't keep up with the "short schedules and rapid change cycles" required to develop these applications. Check out this upcoming webinar and join Clay Richardson from …

  • Packaged application development teams frequently operate with limited testing environments due to time and labor constraints. By virtualizing the entire application stack, packaged application development teams can deliver business results faster, at higher quality, and with lower risk.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds