好久没用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
项目名称.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; // 多面板状态栏
};
………… // 其它处理函数
///
/// 处理窗口创建消息
///
/// 指向窗口属性结构体的指针
/// 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均可)。执行结果如下: