一个显示进度条的WTL状态栏类

引言

  好久没用WTL写代码了,WTL已经更新到8.1版本,但依旧没有提供对VS2013的支持,网上有相关更改想到模板的方法,但向导界面和VS2013的风格严重不搭,丑的一逼……好在WTL代码结构很简单,用不用向导都无所谓,不用也罢。

  自从C++0x/11发表以来,ATL/WTL的威力逐渐展现出来,ATL/WTL和STL的配搭是那么的自然和顺当,不像MFC,带齐了所有的东西,但总显得和C++的新特性格格不入。

  这次需要用一个带进度条状态栏,以前也用MFC做过一个,这次用WTL重新做一个,要干的活比用MFC多一些,但显然代码的结构要比MFC清晰很多,至少每一步在做什么都很清楚,不像MFC,很多时候的调用都糊里糊涂,不明所以然的。

  通过继承WTL的CMultiPaneStatusBarCtrlImpl类(多面板状态栏类),可以做到在状态栏的指定面板中加入进度条(其它类似控件也可以采用完全类似的方法),整个代码如下:


代码

ProgressStatusBar.h

#pragma once

#include 
#include 

/// 
/// 带进度条的状态栏
/// 
class CProgressStatusBar : public CMultiPaneStatusBarCtrlImpl
{
public:

	/// 
	/// 定义基类类型
	/// 
	typedef CMultiPaneStatusBarCtrlImpl Base;

	/// 
	/// 定义保存进度条组件的结构体
	/// 
	struct CProgressBarCtrlPair
	{
		UINT nPaneId;		// 进度条组件的Id
		CProgressBarCtrl* pProgressBarCtrl;	// 进度条组件的指针
	};

	/// 
	/// 定义保存进度条组件结构体的链表类型
	/// 
	typedef std::list CProgressBarCtrlList;

	/// 
	/// 定义当前组件的窗体类名称,并继承已知的窗体类
	/// 
	DECLARE_WND_SUPERCLASS(_T("softparty.controlex.progressstatusbar"), Base::GetWndClassName())

	/// 
	/// 消息映射表
	/// 
	BEGIN_MSG_MAP_EX(CProgressStatusBar)
		MESSAGE_HANDLER(WM_SIZE, OnSize)	// 处理WM_SIZE消息
		CHAIN_MSG_MAP(Base)		// 将其余消息链接到基类
	END_MSG_MAP()

	/// 
	/// 析构函数
	/// 
	~CProgressStatusBar()
	{
		std::for_each(m_progressBarList.begin(), m_progressBarList.end(),
						[](CProgressBarCtrlPair& pair) {
							delete pair.pProgressBarCtrl;
						});
	}

	/// 
	/// 在状态栏中设置进度条
	/// 
	/// 要放置进度条的状态栏面板Id
	/// 进度条最小值
	/// 进度条最大值
	/// 要放置进度条的状态栏面板Id
	BOOL SetProgressBar(UINT nId, UINT nMin, UINT nMax)
	{
		if (std::find_if(m_progressBarList.begin(), m_progressBarList.end(), 
							[nId](const CProgressBarCtrlPair& pair) {
								return pair.nPaneId == nId;
							}) != m_progressBarList.end())		// 查找指定Id的状态栏面板是否句柄进度条
				return FALSE;

		CRect rect;
		if (!GetPaneRect(nId, &rect))	// 获取指定面板的尺寸
			return FALSE;

		CProgressBarCtrl* pCtrl = new CProgressBarCtrl();	// 实例化进度条对象
		pCtrl->Create(*this, &rect, NULL, WS_CHILD | WS_VISIBLE);	// 创建进度条组件
		pCtrl->SetRange(nMin, nMax);	// 设置进度条范围
		CProgressBarCtrlPair pair = { nId, pCtrl };
		m_progressBarList.push_back(pair);	// 存储进度条组件
		return TRUE;
	}

	/// 
	/// 获取进度条组件对象
	/// 
	/// 状态栏面板Id
	/// 进度条组件对象指针
	CProgressBarCtrl* GetProcgressBar(UINT nId)
	{
		CProgressBarCtrlList::iterator iter = std::find_if(m_progressBarList.begin(), m_progressBarList.end(),
						[nId](const CProgressBarCtrlPair& pair) {
							return pair.nPaneId == nId;
						});		// 查找指定状态栏面板Id对应的进度条对象
		return iter == m_progressBarList.end() ? NULL : iter->pProgressBarCtrl;
	}

	/// 
	/// 处理WM_SIZE消息
	/// 
	/// 消息Id
	/// 消息参数
	/// 消息参数
	/// 消息执行结果
	LRESULT OnSize(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		LRESULT lRet = Base::OnSize(nMsg, wParam, lParam, bHandled);	// 调用超类的OnSize函数(必须提前调用,完成状态栏布局)
		std::for_each(m_progressBarList.begin(), m_progressBarList.end(),
						[&](CProgressBarCtrlPair& pair) {
							CRect rect;
							GetPaneRect(pair.nPaneId, &rect);
							pair.pProgressBarCtrl->MoveWindow(&rect);
						});	// 重新设置所有进度条的尺寸和位置
		return lRet;
	}

protected:
	
	CProgressBarCtrlList m_progressBarList;		// 保存进度条控件结构体的链表
};


调用

  用起来很简单,先给状态栏设置足够多的面板,并给面板设置足够大的尺寸,设置进度条就可以了。


资源中添加状态栏面板Id

Resource.h

………… // 其它Id
#define IDPANE_STATUS			50000
#define IDPANE_CAPS_INDICATOR		50001
#define IDPANE_PROGRESS_BAR1		50002
#define IDPANE_PROGRESS_BAR2		50003

其中某些ID可以对应指定的字符串资源

项目名称.rc

STRINGTABLE
BEGIN
    IDPANE_STATUS           "状态"
    IDPANE_CAPS_INDICATOR   "大写"
END


框架窗口类代码如下:

FrameWnd.h

class CFrameWnd :
	public CFrameWindowImpl,
	public CUpdateUI,		
	public CMessageFilter,				
	public CIdleHandler					
{
public:
	/// 
	/// 界面元素状态更新映射表
	/// 
	BEGIN_UPDATE_UI_MAP(CFrameWnd)
		UPDATE_ELEMENT(0, UPDUI_STATUSBAR)
		UPDATE_ELEMENT(1, UPDUI_STATUSBAR)
		UPDATE_ELEMENT(3, UPDUI_STATUSBAR)
	END_UPDATE_UI_MAP()
	
	/// 
	/// 消息映射表
	/// 
	BEGIN_MSG_MAP_EX(CFrameWnd)
		…………	// 其它消息处理宏
		MSG_WM_CREATE(OnCreate)	// 窗体创建消息
		MSG_WM_TIMER(OnTimer)	// 定时器消息
		CHAIN_MSG_MAP(CFrameWindowImpl)	// 将消息处理链接到CFrameWindowImpl基类
		CHAIN_MSG_MAP(CUpdateUI)			// 将消息处理链接到CUpdateUI基类
	END_MSG_MAP()
	
	………… // 其它处理函数
	
	/// 
	/// 处理空闲消息
	/// 
	/// 是否继续传递
	BOOL OnIdle();
	
	/// 
	/// 处理窗口创建消息
	/// 
	/// 指向窗口属性结构体的指针
	/// 0表示窗口创建成功,-1表示失败
	int OnCreate(LPCREATESTRUCT/* lpcs*/);
	
	/// 
	/// 处理定时器消息
	/// 
	/// 定时器Id
	void OnTimer(UINT_PTR nTimeId);
	
private:
	………… // 其它成员
	CProgressStatusBar m_statusBar;	// 多面板状态栏
};

FrameWnd.cpp

…………  // 其它处理函数

/// 
/// 处理窗口创建消息
/// 
/// 指向窗口属性结构体的指针
/// 0表示窗口创建成功,-1表示失败
int CFrameWnd::OnCreate(LPCREATESTRUCT/* lpcs*/)
{
	…………// 其它窗口创建代码
	
	m_hWndStatusBar = m_statusBar.Create(m_hWnd);		// 创建多面板状态栏
	int nPanes[] = { ID_DEFAULT_PANE, IDPANE_STATUS, IDPANE_PROGRESS_BAR1, IDPANE_CAPS_INDICATOR, IDPANE_PROGRESS_BAR2 };	// 定义状态栏面板数组
	m_statusBar.SetPanes(nPanes, sizeof(nPanes) / sizeof(*nPanes));	// 设置状态栏面板
	m_statusBar.SetPaneIcon(ID_DEFAULT_PANE, AtlLoadIconImage(IDR_MAINFRAME, LR_DEFAULTCOLOR, 16, 16));	// 为指定面板加入图标
	m_statusBar.SetPaneWidth(IDPANE_PROGRESS_BAR1, 100);
	m_statusBar.SetPaneWidth(IDPANE_PROGRESS_BAR2, 150);
	m_statusBar.SetProgressBar(IDPANE_PROGRESS_BAR1, 0, 100);
	m_statusBar.SetProgressBar(IDPANE_PROGRESS_BAR2, 0, 150);
	UIAddStatusBar(m_hWndStatusBar);	// 将状态栏纳入界面更新管理
	UpdateLayout();

	SetTimer(0, 100);	// 设置定时器

	return 0;
}

/// 
/// 处理空闲消息
/// 
/// 是否进行了空闲处理
BOOL CFrameWnd::OnIdle()
{
	UISetText(1, CString(MAKEINTRESOURCE(IDPANE_STATUS))));
	if (GetKeyState(VK_CAPITAL) & 1)
		UISetText(3, CString(MAKEINTRESOURCE(IDPANE_CAPS_INDICATOR)));
	else
		UISetText(3, _T(""));

	UIUpdateMenuBar();
	UIUpdateToolBar();
	UIUpdateStatusBar();

	return FALSE;
}

/// 
/// 处理定时器消息
/// 
/// 定时器Id
void CFrameWnd::OnTimer(UINT_PTR nTimeId)
{
	int nMin, nMax;
	for (UINT nId = IDPANE_PROGRESS_BAR1; nId <= IDPANE_PROGRESS_BAR2; nId++)	// 遍历所有关联进度条的状态栏面板
	{
		CProgressBarCtrl* pCtrl = m_statusBar.GetProcgressBar(nId);	// 获取进度条对象
		if (pCtrl)
		{
			pCtrl->GetRange(nMin, nMax);	// 获取进度条范围
			// 设置进度条当前值
			if (pCtrl->GetPos() == nMax)
				pCtrl->SetPos(nMin);
			else
				pCtrl->SetStep(1);
			// 滚动进度条
			pCtrl->StepIt();
		}
	}
}


说明

  在OnCreate函数中演示了如何在状态栏中加入进度条,在OnIdle函数中演示了如何使用状态栏,在OnTimer函数中演示了如何改变状态栏中的进度条值。这段代码使用了C++新特性,必须在C++11中才能执行(VS2012和VS2013均可)。执行结果如下:

一个显示进度条的WTL状态栏类_第1张图片

你可能感兴趣的:(WTL)