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

  • With 81% of employees using their phones at work, companies have stopped asking: "Is corporate data leaking from personal devices?" and started asking: "How do we effectively prevent corporate data from leaking from personal devices?" The answer has not been simple. ZixOne raises the bar on BYOD security by not allowing email data to reside on the device. In addition, Zix allows employees to maintain complete control of their personal device, therefore satisfying privacy demands of valued employees and the …

  • Agile development principles have gone from something used only by cutting-edge teams to a mainstream approach used by teams large and small. If you're not using agile methods already though, or if you've only been exposed to agile on small projects here and there, you may wonder if agile can ever work in your environment. Read this eBook to learn the fundamentals of agile and how to increase the productivity of your software teams while enabling them to produce higher-quality solutions that better fulfill …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds