现在我发现使用Visual Studio的资源编辑器进行编辑资源有着诸多的不便:首先是任何资源的变动一般变动代码,不利于系统维护,其次Visual Studio的资源编辑器的本身的功能有限,也不利于界面美化,三是不利于人员分工,开发人员既要忙实现功能,又要忙准备好的界面素材。对界面实现文件配置化正是解决上面问题的好方法。这次我实现了使用XML文件配置工具栏。这里所谓配置就是工具栏的界面信息如工具栏标题、按钮图片、是否为分隔符都在XML文件保存,程序通过解析XML文件来获取工具栏信息来创建工具栏。这样一旦发现界面不合适可以随时修改配置文件,同时利于人员分工。
具体的做法如下:
1. 在工程的输出目录下有一个SysConfig.xml,作为系统配置文件。其中关于工具栏的配置部分如下:
<
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
>
简单解释一下上面的节点意义: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实现 多风格真彩色大图标工具栏按钮 (感谢万连文大侠提供)