在MFC程序中使用XML文件配置工具欄
睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接
<AppToolbar valid="1" caption="基礎工具">
<ToolButton file="Add.bmp" />
<ToolButton separator="true" />
<ToolButton file="Benchmark.bmp" />
<ToolButton file="Comment.bmp" />
<ToolButton file="Convert.bmp" />
<ToolButton file="Delete.bmp" />
<ToolButton file="Exit.bmp" />
</AppToolbar>
/**
* \brief 工具欄對應的圖像列表。
*/
CImageList m_imgToobar;
/**
* \brief 系統配置文件解析器,具體看我上傳的代碼。
*/
CXmlParse m_SysSetting;
/*!
* \brief 獲取exe所在的文件夾。
*
* \param [in][out]strBinPath exe程序所在的文件夾。
* \return 無。
*/
void CMainFrame::GetOutputPath(string &strBinPath)
{
TCHAR szModulePath[_MAX_PATH];
::GetModuleFileName(NULL,szModulePath,_MAX_PATH);
strBinPath = szModulePath;
strBinPath = strBinPath.substr(0,strBinPath.rfind('\\')+1);
}
/*!
* \brief 解析系統配置文件,獲取工具欄信息。
*
* \param [in][out]MyToolbar 工具欄信息。
* \return 無。
*/
void CMainFrame::ParseXml(ToolBar &MyToolbar)
{
string strBinPath;
GetOutputPath(strBinPath);
string strXmlPath = strBinPath + string(_T("SysConfig.xml"));
m_SysSetting.OpenXml(strXmlPath);
m_SysSetting.GetToolbarInfo(MyToolbar);
}
/*!
* \brief 根據工具欄圖片信息加載工具欄圖像列表。
*
* \param [in]MyToolbar 工具欄信息。
* \return 是否成功。true為成功,false表示失敗。
*/
BOOL CMainFrame::LoadImageList(ToolBar &MyToolbar)
{
// 獲取按鈕圖片的個數
int nBmpNum = MyToolbar.m_MenuItemVec.size();
HBITMAP hBitmap = NULL;
// 打開所有位圖,將其加進圖像列表
for(int i=0; i<nBmpNum; ++i)
{
if (MyToolbar.m_MenuItemVec[i].m_bIsSeparator)
{
continue;
}
string strBinPath;
GetOutputPath(strBinPath);
string strBmpPath = strBinPath + string(_T("Toolbar\\"));
strBmpPath = strBmpPath + MyToolbar.m_MenuItemVec[i].m_strBmpName;
hBitmap = (HBITMAP)LoadImage(AfxGetResourceHandle(),strBmpPath.c_str(), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR|LR_LOADFROMFILE);
if (NULL==hBitmap)
{
return FALSE;
}
CBitmap bmp;
bmp.Attach(hBitmap);
m_imgToobar.Add(&bmp, RGB(0, 0, 0));
bmp.DeleteObject();
}
return TRUE;
}
/*!
* \brief 設置工具欄按鈕風格。
*
* \param [in]MyToolbar 工具欄信息。
* \return 無。
*/
BOOL CMainFrame::SetStyleToolbar(ToolBar &MyToolbar)
{
CToolBarCtrl& tbc = m_wndToolBar.GetToolBarCtrl();
// 刪除之前的按鈕
while(tbc.DeleteButton(0));
// 設置當前圖像列表
tbc.SetImageList(&m_imgToobar);
int i = 0;
int nBtnNum = MyToolbar.m_MenuItemVec.size();
UINT nBtnID = SYS_COMMAND_BEGIN;
int nImgIndex = 0;
// 根據按鈕屬性逐個添加按鈕
for(i=0; i<nBtnNum; ++i)
{
if (MyToolbar.m_MenuItemVec[i].m_bIsSeparator)
{
TBBUTTON tb = {-1,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0};
tbc.AddButtons(1, &tb);
}
else
{
TBBUTTON tb = {nImgIndex,nBtnID,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0};
tbc.AddButtons(1, &tb);
nImgIndex++;
nBtnID++;
}
}
return TRUE;
}
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
/*
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))*/
// 解析系統配置文件,獲取位圖信息
ToolBar AppToolbar;
ParseXml(AppToolbar);
if(!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD|WS_VISIBLE|CBRS_TOP
|CBRS_GRIPPER|CBRS_TOOLTIPS|CBRS_FLYBY|CBRS_SIZE_DYNAMIC))
{
TRACE0("未能創建工具欄\n");
return -1; // 未能創建
}
m_wndToolBar.SetWindowText(AppToolbar.m_strCaption.c_str());
// 創建圖像列表
m_imgToobar.Create(32, 32, ILC_COLOR32|ILC_MASK, 0, 0);
if(LoadImageList(AppToolbar))
{
// 添加工具欄按鈕
SetStyleToolbar(AppToolbar);
}
// 設置工具欄按鈕大小
m_wndToolBar.SetSizes(CSize(32+7, 32+6), CSize(32, 32));
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
TRACE0("未能創建狀態欄\n");
return -1; // 未能創建
}
// TODO: 如果不需要工具欄可停靠,則刪除這三行
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
return 0;
}
/*!
* \brief 工具欄按鈕響應事件。
*
* \param [in]nID 工具欄按鈕ID。
* \return 無。
*/
void CMainFrame::OnButton(UINT nID)
{
switch (nID)
{
// SYS_COMMAND_BEGIN為工具欄按鈕的起始ID值
case SYS_COMMAND_BEGIN:
AfxMessageBox(_T("你單擊的是第一個按鈕"));
break;
case SYS_COMMAND_BEGIN+1:
AfxMessageBox(_T("你單擊的是第二個按鈕"));
break;
case SYS_COMMAND_BEGIN+2:
AfxMessageBox(_T("你單擊的是第三個按鈕"));
break;
case SYS_COMMAND_BEGIN+3:
AfxMessageBox(_T("你單擊的是第四個按鈕"));
break;
case SYS_COMMAND_BEGIN+4:
AfxMessageBox(_T("你單擊的是第五個按鈕"));
break;
case SYS_COMMAND_BEGIN+5:
AfxMessageBox(_T("你單擊的是第六個按鈕"));
break;
default:
break;
}
}
現在我發現使用Visual Studio的資源編輯器進行編輯資源有著諸多的不便:首先是任何資源的變動一般變動代碼,不利于系統維護,其次Visual Studio的資源編輯器的本身的功能有限,也不利于界面美化,三是不利于人員分工,開發人員既要忙實現功能,又要忙準備好的界面素材。對界面實現文件配置化正是解決上面問題的好方法。這次我實現了使用XML文件配置工具欄。這里所謂配置就是工具欄的界面信息如工具欄標題、按鈕圖片、是否為分隔符都在XML文件保存,程序通過解析XML文件來獲取工具欄信息來創建工具欄。這樣一旦發現界面不合適可以隨時修改配置文件,同時利于人員分工。
具體的做法如下:
1. 在工程的輸出目錄下有一個SysConfig.xml,作為系統配置文件。其中關于工具欄的配置部分如下:

<ToolButton file="Add.bmp" />
<ToolButton separator="true" />
<ToolButton file="Benchmark.bmp" />
<ToolButton file="Comment.bmp" />
<ToolButton file="Convert.bmp" />
<ToolButton file="Delete.bmp" />
<ToolButton file="Exit.bmp" />
</AppToolbar>

簡單解釋一下上面的節點意義:valid表示工具欄是否有效,caption表示工具欄標題,file節點為工具欄按鈕所貼圖片,separator表示按鈕是分隔符。
2. 通過解析XML文件獲取工具欄信息來創建工具欄。首先在CMainFrame類添加兩個數據成員:

* \brief 工具欄對應的圖像列表。
*/
CImageList m_imgToobar;
/**
* \brief 系統配置文件解析器,具體看我上傳的代碼。
*/
CXmlParse m_SysSetting;

然后實現如下函數:

* \brief 獲取exe所在的文件夾。
*
* \param [in][out]strBinPath exe程序所在的文件夾。
* \return 無。
*/
void CMainFrame::GetOutputPath(string &strBinPath)
{
TCHAR szModulePath[_MAX_PATH];
::GetModuleFileName(NULL,szModulePath,_MAX_PATH);
strBinPath = szModulePath;
strBinPath = strBinPath.substr(0,strBinPath.rfind('\\')+1);
}
/*!
* \brief 解析系統配置文件,獲取工具欄信息。
*
* \param [in][out]MyToolbar 工具欄信息。
* \return 無。
*/
void CMainFrame::ParseXml(ToolBar &MyToolbar)
{
string strBinPath;
GetOutputPath(strBinPath);
string strXmlPath = strBinPath + string(_T("SysConfig.xml"));
m_SysSetting.OpenXml(strXmlPath);
m_SysSetting.GetToolbarInfo(MyToolbar);
}
/*!
* \brief 根據工具欄圖片信息加載工具欄圖像列表。
*
* \param [in]MyToolbar 工具欄信息。
* \return 是否成功。true為成功,false表示失敗。
*/
BOOL CMainFrame::LoadImageList(ToolBar &MyToolbar)
{
// 獲取按鈕圖片的個數
int nBmpNum = MyToolbar.m_MenuItemVec.size();
HBITMAP hBitmap = NULL;
// 打開所有位圖,將其加進圖像列表
for(int i=0; i<nBmpNum; ++i)
{
if (MyToolbar.m_MenuItemVec[i].m_bIsSeparator)
{
continue;
}
string strBinPath;
GetOutputPath(strBinPath);
string strBmpPath = strBinPath + string(_T("Toolbar\\"));
strBmpPath = strBmpPath + MyToolbar.m_MenuItemVec[i].m_strBmpName;
hBitmap = (HBITMAP)LoadImage(AfxGetResourceHandle(),strBmpPath.c_str(), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR|LR_LOADFROMFILE);
if (NULL==hBitmap)
{
return FALSE;
}
CBitmap bmp;
bmp.Attach(hBitmap);
m_imgToobar.Add(&bmp, RGB(0, 0, 0));
bmp.DeleteObject();
}
return TRUE;
}
/*!
* \brief 設置工具欄按鈕風格。
*
* \param [in]MyToolbar 工具欄信息。
* \return 無。
*/
BOOL CMainFrame::SetStyleToolbar(ToolBar &MyToolbar)
{
CToolBarCtrl& tbc = m_wndToolBar.GetToolBarCtrl();
// 刪除之前的按鈕
while(tbc.DeleteButton(0));
// 設置當前圖像列表
tbc.SetImageList(&m_imgToobar);
int i = 0;
int nBtnNum = MyToolbar.m_MenuItemVec.size();
UINT nBtnID = SYS_COMMAND_BEGIN;
int nImgIndex = 0;
// 根據按鈕屬性逐個添加按鈕
for(i=0; i<nBtnNum; ++i)
{
if (MyToolbar.m_MenuItemVec[i].m_bIsSeparator)
{
TBBUTTON tb = {-1,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0};
tbc.AddButtons(1, &tb);
}
else
{
TBBUTTON tb = {nImgIndex,nBtnID,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0};
tbc.AddButtons(1, &tb);
nImgIndex++;
nBtnID++;
}
}
return TRUE;
}
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
/*
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))*/
// 解析系統配置文件,獲取位圖信息
ToolBar AppToolbar;
ParseXml(AppToolbar);
if(!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD|WS_VISIBLE|CBRS_TOP
|CBRS_GRIPPER|CBRS_TOOLTIPS|CBRS_FLYBY|CBRS_SIZE_DYNAMIC))
{
TRACE0("未能創建工具欄\n");
return -1; // 未能創建
}
m_wndToolBar.SetWindowText(AppToolbar.m_strCaption.c_str());
// 創建圖像列表
m_imgToobar.Create(32, 32, ILC_COLOR32|ILC_MASK, 0, 0);
if(LoadImageList(AppToolbar))
{
// 添加工具欄按鈕
SetStyleToolbar(AppToolbar);
}
// 設置工具欄按鈕大小
m_wndToolBar.SetSizes(CSize(32+7, 32+6), CSize(32, 32));
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
TRACE0("未能創建狀態欄\n");
return -1; // 未能創建
}
// TODO: 如果不需要工具欄可停靠,則刪除這三行
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
return 0;
}

為使工具欄處于有效狀態,還得添加一個簡單的消息處理函數,簡單如下:

* \brief 工具欄按鈕響應事件。
*
* \param [in]nID 工具欄按鈕ID。
* \return 無。
*/
void CMainFrame::OnButton(UINT nID)
{
switch (nID)
{
// SYS_COMMAND_BEGIN為工具欄按鈕的起始ID值
case SYS_COMMAND_BEGIN:
AfxMessageBox(_T("你單擊的是第一個按鈕"));
break;
case SYS_COMMAND_BEGIN+1:
AfxMessageBox(_T("你單擊的是第二個按鈕"));
break;
case SYS_COMMAND_BEGIN+2:
AfxMessageBox(_T("你單擊的是第三個按鈕"));
break;
case SYS_COMMAND_BEGIN+3:
AfxMessageBox(_T("你單擊的是第四個按鈕"));
break;
case SYS_COMMAND_BEGIN+4:
AfxMessageBox(_T("你單擊的是第五個按鈕"));
break;
case SYS_COMMAND_BEGIN+5:
AfxMessageBox(_T("你單擊的是第六個按鈕"));
break;
default:
break;
}
}

開發環境為Visual C++ 2005 + sp1,Win XP + sp3。程序效果圖如下:
工程源碼已上傳到聯合程序開發網,鏈接為:
源碼下載。
參考文獻:
來源:http://www.cnblogs.com/clever101
1. MFC實現 多風格真彩色大圖標工具欄按鈕 (感謝萬連文大俠提供)
RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成