Prof-UIS 2.6
第1章框架生成的基本结构
1.1向导生成的代码
在使用Prof-UIS的向导时不选择任何Prof-UIS的属性,来生成一个SDI工程。生成的SDI工程经编译运行后的执行结果如图:
图1.1
Prof-UIS向导默认在程序中使用Prof-UIS扩展的工具栏和状态栏。下面是向导生成的代码:
在stdafx.h中包含了Prof-UIS扩展库的头文件
#include <Prof-UIS.h>
在YourApp.cpp中加为CYourApp类添加了一个新的成员函数
void SetupUiAdvancedOptions();
定义如下:
void CUIS0App::SetupUiAdvancedOptions()
{
VERIFY(
g_CmdManager->ProfileSetup(__PROF_UIS_PROJECT_CMD_PROFILE_NAME)
);
g_PaintManager.InstallPaintManager(
//RUNTIME_CLASS( CExtPaintManager )
RUNTIME_CLASS( CExtPaintManagerXP )
//RUNTIME_CLASS( CExtPaintManagerOffice2003 )
//RUNTIME_CLASS( CExtPaintManagerOffice2003NoThemes )
//RUNTIME_CLASS( CExtPaintManagerStudio2005 )
//RUNTIME_CLASS( CExtPaintManagerNativeXP )
//RUNTIME_CLASS( CExtPaintManagerOffice2007_R1 )
//RUNTIME_CLASS( CExtPaintManagerOffice2007_R2_LunaBlue )
//RUNTIME_CLASS( CExtPaintManagerOffice2007_R2_Obsidian )
);
CExtPopupMenuWnd::g_bMenuWithShadows = false;
CExtPopupMenuWnd::g_bMenuShowCoolTips = false;
CExtPopupMenuWnd::g_bMenuExpanding = false;
CExtPopupMenuWnd::g_bMenuHighlightRarely = false;
CExtPopupMenuWnd::g_bMenuExpandAnimation = false;
CExtPopupMenuWnd::g_bUseDesktopWorkArea = false;
CExtPopupMenuWnd::g_DefAnimationType=CExtPopupMenuWnd::__AT_FADE;
//$$__PROFUISAPPWIZ_KEY_MENU_ANIM_DISPMS$$
}
此函数在InitInstance函数中调用如下:
BOOL CUIS0App::InitInstance()
{
CWinApp::InitInstance();
SetupUiAdvancedOptions();
//...
}
它的作用是初始化Prof-UIS库并设置相应的控件风格
g_CmdManager->ProfileSetup(__PROF_UIS_PROJECT_CMD_PROFILE_NAME)中
g_CmdManager是Prof-UIS库中的一个全局变量定义在ExtCmdManager.h和ExtCmdManager.cpp中如下:
extern __PROF_UIS_API CExtCmdManager::CExtCmdManagerAutoPtr g_CmdManager;和
CExtCmdManager::CExtCmdManagerAutoPtr g_CmdManager;
CExtCmdManager类是一个辅助类,它包含着所用的Prof-UIS的用户接口的控件信息,包括,我们可以通过全局变量g_CmdManager来得到指向CExtCmdManager的指针。
CExtCmdManagerAutoPtr类有一个重载运算符
// command manager instance access
CExtCmdManager * operator -> ();
通过g_CmdManager->可以得到CExtCmdManager的指针。
1.2 Command Manager,Command Profile, Command description
Command Manager在Prof-UIS中是一个重要的概念,Command Manager是类CExtCmdManager的对象。Command Manager是一个指定的Command Profile的集合,每一个Command Profile是类CExtCmdProfile的对象,每一个Command Profile都有它自己唯一的名字并保存有一系列的HWND句柄,这就使得一个窗口(控件也是窗口)知道它属于哪一个Command Profile。Command Profile更重要的任务是保存Command description,Command description是类CExtCmdItem的对象,它保存这单一窗口的信息,包括:
标识符
菜单项,工具栏按钮,提示,状态栏的文本
图标
支持可扩展的弹出菜单的命令
大部分应用程序都是基于Command Manager中的单一的Command Profile,这个Command Profile保存这主框架窗口或主对话框的HWND句柄,这允许主窗口和它的所有的子窗口都可以自动的得到需要的Command description。
当开始使用Prof-UIS库时必须初始化一个Command Manager,将一个Command Profile添加到Command Manager中,然后把要使用的所有的Command description放到这个Command Profile中。你要在程序初始化或窗口创建时添加下列代码:
VERIFY(
g_CmdManager->ProfileSetup(
"name of the command profile",
GetSafeHwnd() //框架窗口的句柄
)
);
接下来就是注册你的控件的Command description
VERIFY(
g_CmdManager->UpdateFromMenu(
"name of the command profile",
IDR_MAINFRAME
)
);
VERIFY(
g_CmdManager->UpdateFromMenu(
"name of the command profile",
IDR_YourMdiProjectsDOCTYPE
)
);
VERIFY(
g_CmdManager->UpdateFromToolBar(
"name of the command profile",
IDR_TOOLBAR1
)
);
注意上面的"name of the command profile"这是Command Profile的标识符,必须使用你想要添加到并且是以及安装到Command Manager的Command Profile的名字。
注意,最后Command Manager必须被销毁,在CMainFrame::DestroyWindow()中(基于对话框的程序在OnOK()和OnCancel()中),以使得窗口句柄得到释放:
g_CmdManager->ProfileWndRemove(GetSafeHwnd());
1.3初始化过程
了解了Command Manager的概念和Prof-UIS库的初始化后再来看看用Prof-UIS向导生成的代码的初始化过程:
首先初始化
VERIFY(
g_CmdManager->ProfileSetup(__PROF_UIS_PROJECT_CMD_PROFILE_NAME)
);
首先为Command Manager安装一个不与任何窗口关联的Command Profile,在YourApp.h中有如下的定义
#define __PROF_UIS_PROJECT_CMD_PROFILE_NAME /
_T("CYourApp-command-manager-profile")
在int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)中向已安装的Command Profile中添加关联窗口,
VERIFY(
g_CmdManager->ProfileWndAdd(
__PROF_UIS_PROJECT_CMD_PROFILE_NAME,
GetSafeHwnd()
)
);
在BOOL CMainFrame::DestroyWindow()中销毁Command Manager
g_CmdManager->ProfileWndRemove(GetSafeHwnd());
1.4 显示风格
在void CUIS0App::SetupUiAdvancedOptions()中有下面一段代码:
g_PaintManager.InstallPaintManager(
//RUNTIME_CLASS( CExtPaintManager )
RUNTIME_CLASS( CExtPaintManagerXP )
//RUNTIME_CLASS( CExtPaintManagerOffice2003 )
//RUNTIME_CLASS( CExtPaintManagerOffice2003NoThemes )
//RUNTIME_CLASS( CExtPaintManagerStudio2005 )
//RUNTIME_CLASS( CExtPaintManagerNativeXP )
//RUNTIME_CLASS( CExtPaintManagerOffice2007_R1 )
//RUNTIME_CLASS( CExtPaintManagerOffice2007_R2_LunaBlue )
//RUNTIME_CLASS( CExtPaintManagerOffice2007_R2_Obsidian )
);
CExtPopupMenuWnd::g_bMenuWithShadows = false;
CExtPopupMenuWnd::g_bMenuShowCoolTips = false;
CExtPopupMenuWnd::g_bMenuExpanding = false;
CExtPopupMenuWnd::g_bMenuHighlightRarely = false;
CExtPopupMenuWnd::g_bMenuExpandAnimation = false;
CExtPopupMenuWnd::g_bUseDesktopWorkArea = false;
CExtPopupMenuWnd::g_DefAnimationType = CExtPopupMenuWnd::__AT_FADE;
这段代码是设置显示风格和设在菜单风格的代码。
g_PaintManager是全局变量在ExtPaintManager.h中ExtPaintManager.cpp分别有定义:
extern __PROF_UIS_API CExtPaintManager::CExtPaintManagerAutoPtr g_PaintManager;
和
CExtPaintManager::CExtPaintManagerAutoPtr g_PaintManager;
bool CExtPaintManager::CExtPaintManagerAutoPtr::
InstallPaintManager(CRuntimeClass * pRtcPaintManager)
和
bool CExtPaintManager::CExtPaintManagerAutoPtr::
InstallPaintManager(CExtPaintManager * pPaintManager)
是用来安装显示风格的,以上的
CExtPaintManager是传统的显示风格9x/2000的风格
CExtPaintManagerXP
CExtPaintManagerOffice2003等都是从CExtPaintManager派生的
CExtPopupMenuWnd::g_bMenuWithShadows = false;
CExtPopupMenuWnd::g_bMenuShowCoolTips = false;
CExtPopupMenuWnd::g_bMenuExpanding = false;
CExtPopupMenuWnd::g_bMenuHighlightRarely = false;
CExtPopupMenuWnd::g_bMenuExpandAnimation = false;
CExtPopupMenuWnd::g_bUseDesktopWorkArea = false;
CExtPopupMenuWnd::g_DefAnimationType = CExtPopupMenuWnd::__AT_FADE;
菜单的显示样式,具体在菜单的章节中介绍。
1.5 CExtNCW
Prof-UIS向导生成的代码中,对话框和主框架窗口都是从CExtNCW模板类继承的如:
class CAboutDlg : public CExtNCW < CExtResizableDialog >
class CMainFrame : public CExtNCW < CFrameWnd >
是对窗口的模板化,其中对窗口进行了一些处理以便适应不同显示样式对窗口的要求,主要是Office2007模式。
其中class CMainFrame : public CExtNCW < CFrameWnd >
使用了基本的模板类,但是
class CAboutDlg : public CExtNCW < CExtResizableDialog >
使用的是CExtNCW模板类的一个实例化。
1.6 系统默认工具栏和状态栏
Prof-UIS向导生成的代码中默认使用Prof-UIS扩展的工具栏和状态栏风格,因为Prof-UID扩展风格的菜单是使用Menu bar实现的,所以向导会要求用户选择是否使用menu bar。如果用户使用传统的菜单时,向导就不会对菜单进行扩展。
向导生成的代码中使用了如下的工具栏和状态栏
CExtStatusControlBar m_wndStatusBar;
CExtToolControlBar m_wndToolBar;
1.6.1 工具栏的创建过程:
使用CExtControlBar::Create函数创建工具栏如下:
if( !m_wndToolBar.Create(
NULL, // _T("Main Toolbar"),当Toolbar浮动是标题中显示的文本
this,
AFX_IDW_TOOLBAR,
WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_FLYBY | CBRS_SIZE_DYNAMIC | CBRS_TOOLTIPS) ||
!m_wndToolBar.LoadToolBar( IDR_MAINFRAME )
)
{
TRACE0("Failed to create toolbar/n");
return -1; // fail to create
}
使用
if( !CExtControlBar::FrameEnableDocking(this) )
{
ASSERT( FALSE );
return -1;
}
代替MFC的EnableDocking()
1.6.2 状态栏的创建过程
CExtStatusControlBar是直接从MFC的CStatusBar继承来的,所以它的创建方法与MFC的相同:
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failed to create status bar/n");
return -1; // fail to create
}
1.6.3 预留接口
在Prof-UIS向导生成的代码中预留了很多的扩展接口。
CWinApp * pApp = ::AfxGetApp();
ASSERT( pApp != NULL );
ASSERT( pApp->m_pszRegistryKey != NULL );
ASSERT( pApp->m_pszRegistryKey[0] != _T('/0') );
ASSERT( pApp->m_pszProfileName != NULL );
ASSERT( pApp->m_pszProfileName[0] != _T('/0') );
pApp;
/////////////////////////////////////////////////////////////
VERIFY(
g_CmdManager->UpdateFromMenu(
__PROF_UIS_PROJECT_CMD_PROFILE_NAME,
IDR_MAINFRAME
)
);
/////////////////////////////////////////////////////////////
void CMainFrame::OnUpdateControlBarMenu(CCmdUI* pCmdUI)
{
CExtControlBar::DoFrameBarCheckUpdate(
this,
pCmdUI,
false
);
}
BOOL CMainFrame::OnBarCheck(UINT nID)
{
return
CExtControlBar::DoFrameBarCheckCmd(
this,
nID,
false
);
}
这些都是向导生成的预留的代码,当程序扩展的时候使用。
第2章工具栏
2.1 响应工具栏命令
Prof-UIS扩展风格的工具栏命令响应的方式与普通的MFC工具栏命令响应方式相同,我们可以使用ON_COMMAND或ON_COMMAND_EX来响应工具栏按钮命令,使用ON_UPDATE_COMMAND_UI来更新工具栏按钮状态。
在Prof-UIS向导生成的代码里,对工具栏命令的响应和更新函数中分别调用了
CExtControlBar::DoFrameBarCheckCmd
CExtControlBar::DoFrameBarCheckUpdate
它们的作用是针对控件的隐藏和显示以及是控件的到焦点的函数。
2.2 添加一个新工具栏
2.2.1 添加工具栏
下面我们来添加一个自己的工具栏,添加Prof-UIS扩展风格的工具栏与添加传统的MFC风格的工具栏方法相同。
首先在CMainFrame中添加成员m_wndMyToolBar如下:
CExtToolControlBar m_wndMyToolBar;
要使用添加Prof-UIS扩展风格的工具栏,就要构建一个CExtToolControlBar的对象来代替传统的MFC风格的CToolBar的对象。然后在资源编辑器中创建一个新的工具栏,资源ID为IDR_MY_TOOLBAR。接下来要编写创建工具栏和加载工具栏资源的代码:
在CMainFrame::OnCreate中加入如下代码
if(!m_wndMyToolBar.Create(_T("我的工具栏"),this,ID_TOOLBAR_MY,
WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_FLYBY | CBRS_SIZE_DYNAMIC| CBRS_TOOLTIPS) ||
!m_wndMyToolBar.LoadToolBar(IDR_MY_TOOLBAR))
{
TRACE0("Failed to create my toolbar/n");
return -1;
}
注意:在m_wndMyToolBar.Create中有一个ID_TOOLBAR_MY的参数,它是生成的工具栏的ID,也是对应该工具栏的工具栏更新菜单项的ID(这个ID要在Resourec.h中手工定义,下面会讲到工具栏更新菜单),要把它与IDR_MY_TOOLBAR区别开来,IDR_MY_TOOLBAR是工具栏资源ID。
最后在OnCreate的后部加入下面一段代码:
m_wndMyToolBar.EnableDocking(CBRS_ALIGN_ANY);
//这两条语句分别在if( !CExtControlBar::FrameEnableDocking(this) )前后
DockControlBar(&m_wndMyToolBar);
然后编译执行就可以创建一个新的工具栏了
如图:
图 2.1
2.2.2 响应工具栏更新命令
按照上面的方法我们已经创建了一个新的工具栏,并且Prof-UIS库为我们创建了一个工具栏更新的弹出菜单,解决当用户关闭工具栏后不能再次显示的问题。当用户在工具栏空白处单击右键是会显示工具栏更新弹出菜单,但是这里有一个问题,我们刚刚创建的工具栏更新菜单项是不可用的如图2.1,这是因为程序没有响应工具栏更新菜单命令。
要响应工具栏更新菜单命令就要用到向导预留的代码和我们定义的工具栏ID。还记得上面我们创建工具栏的代码吗?
m_wndMyToolBar.Create(_T("我的工具栏"),this,ID_TOOLBAR_MY,
WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_FLYBY | CBRS_SIZE_DYNAMIC| CBRS_TOOLTIPS)
Prof-UIS自动为工具栏和相应的更新菜单项关联起来,参数_T("我的工具栏")即使工具栏的标题(当工具栏浮动时显示)也是更新菜单项的文本,ID_TOOLBAR_MY即使工具栏ID也是更新菜单项的ID,下面响应菜单命令时会使用这个ID。
在CMainFrame中加入菜单响应函数和更新函数:
afx_msg BOOL OnBarCheck(UINT nID);
afx_msg void OnUpdateControlBarMenu(CCmdUI* pCmdUI);
在MainFrame.cpp中实现
ON_COMMAND_EX(ID_TOOLBAR_MY,OnBarCheck)
ON_UPDATE_COMMAND_UI(ID_TOOLBAR_MY,OnUpdateControlBarMenu)
BOOL CMainFrame::OnBarCheck(UINT nID)
{
return CExtControlBar::DoFrameBarCheckCmd(this,nID,false);
}
void CMainFrame::OnUpdateControlBarMenu(CCmdUI* pCmdUI)
{
CExtControlBar::DoFrameBarCheckUpdate(this,pCmdUI,false);
}
注意:上面使用了ON_COMMAND_EX宏它与ON_COMMAND宏的作用相同但是它要求命令响应函数的形式必须是BOOL FunName(UINT)这样的形式。
在上面的命令响应函数和更新函数中分别了
CExtControlBar::DoFrameBarCheckCmd(this,nID,false);
CExtControlBar::DoFrameBarCheckUpdate(this,pCmdUI,false);
这两个函数是Prof-UIS库为我们提供的更新代码,用它们来实行工具栏的显示和隐藏,具体的内容请参看Prof-UIS help。
再次编译并执行程序,就可以使用工具栏更新菜单项了。
2.2.3 使用真彩位图
在Prof-UIS库中使用真彩位图工具栏的方法与以前使用MFC时使用真彩位图工具栏的方法一致。
2.3 工具栏按钮显示文本
在使用CToolBar时使用SetButtonText和SetSizes来实现在工具栏按钮上显示文本,但是使用CExtToolControlBar时就不能在使用SetButtonText和SetSizes了,因为这两个函数时CToolBar的成员函数,但是CExtToolControlBar是间接继承于MFC的CControlBar类,所以SetButtonText和SetSizes不再有效了。
这里就要用到Command Profile和Command description了。每一个Command description都保存有一个控件的信息,可以通过修改控件的基本信息来改变它显示效果。
首先必须在已经安装的Command Profile中注册你的控件,用下面的代码来注册新添加的工具栏:
VERIFY(
g_CmdManager->UpdateFromToolBar(
__PROF_UIS_PROJECT_CMD_PROFILE_NAME,
IDR_TOOLBAR_MY)
);
注意:注册是使用的ID必须是资源ID
通过CExtCmdManager::CmdGetPtr获得指定的CExtCmdItem的指针,这个指针是由指定的Command Profile名字和控件的ID指定的。
CExtCmdItem* pCmdItem = g_CmdManager->CmdGetPtr(
__PROF_UIS_PROJECT_CMD_PROFILE_NAME,ID_BUTTON_ONE);
ASSERT(pCmdItem != NULL);
获得了Command Description的指针就可以对起进行修改了,
CExtCmdItem::m_sToolBarText是显示在工具栏按钮上的文本,默认情况下是空,我们只要修改它就可以了。
pCmdItem->m_sToolBarText = CString(“Button One”);
注意:以上这些代码必须在Command Profile安装之后,m_wndMyToolBar创建以前加入。
编译并运行如图:
图2.2
如果要动态的修改工具栏按钮的文本,也可以使用上面的代码,但是不要忘了在后面调用CWnd::RecalcLayout();
2.4 工具栏初始化
2.4.1 位置
默认情况下当应用程序启动是,工具栏的分布情况如图2.2每一个工具栏都会占用一行,这样太浪费空间。要是程序启动是工具栏排列在同一行上,这与MFC中使用同样的方法:
DockControlBar( &m_wndToolBar );
RecalcLayout();
CRect rectDockMyBar;
m_wndToolBar.GetWindowRect(&rectDockMyBar);
rectDockMyBar.OffsetRect(1,0);
DockControlBar(&m_wndMyToolBar,AFX_IDW_DOCKBAR_TOP,
&rectDockMyBar);
RecalcLayout();
DockControlBar(&m_wndYourToolBar);
我在程序中又加入了一个m_wndYourToolBar用以作为对比,编译并执行程序得到的结果如图:
图 2.3
2.4.2 浮动
要是工具栏浮动使用Prof-UIS的CExtControlBar::FloatControlBar来代替MFC的CFrameWnd::DockControlBar。
2.4.3 隐藏
如果工具栏停靠在框架窗口中,使用CFrameWnd::ShowControlBar来隐藏
ShowControlBar(&m_wndMyToolBar,false,false);。
2.4.4 大图标
动态显示改变图标大小就是要改变。
static bool CExtToolControlBar::g_bToolbarLargeIcons的值
true 为大图标 false 为正常图标
语句后面要调用RecalcLayout()来改变工具栏的布局。
2.5 drop-down button
很多应用程序的工具栏都含有drop-down button,在Prof-UIS库中我们很容易实现drop-down button。
首先使用菜单编辑器编辑一个弹出菜单,并将其注册到已安装的Command Profile中。
然后在工具栏创建后得到想要加载弹出菜单的按钮的索引
int nButtonIndex = m_wndMyToolBar.CommandToIndex(ID_BUTTON_TWO);
ASSERT(nButtonIndex >= 0);
CMenu menuDropDown;
VERIFY(menuDropDown.LoadMenu(IDR_MENU_FOR_TOOLBAR));
VERIFY(m_wndMyToolBar.SetButtonMenu(nButtonIndex,
menuDropDown.Detach()));
这样就可以为指定的工具栏按钮加载弹出菜单了。
drop-down button有两种形式,上面实现的是单一的弹出菜单按钮,只要单击工具栏中相应的按钮就会弹出菜单,还有一种是drop-down arrow和button分离的,当单击按钮是不会弹出菜单,单击箭头是会弹出菜单。
在上面代码的基层上在加上下面的代码就会实现第二种drop-down button
CExtBarButton* pTBB = m_wndMyToolBar.GetButton(nButtonIndex);
ASSERT(pTBB);
pTBB->SetSeparatedDropDown();//分离button和arrow的关键
pTBB->SetNoRotateVerticalLayout();//是工具栏停靠在侧面时按钮的显示//效果与停靠在顶部时相同
编译并运行程序,可以看到指定的工具栏按钮旁边出现了一个下拉的箭头。但如果这个工具栏按钮不是活动的,也就是程序中并没有响应该按钮的消息的话,第一种按钮不会弹出菜单,第二种按钮的按钮无效但是下拉箭头有效。
所以你必须为该工具栏按钮提供消息的响应代码使按钮处于活动状态。需要注意的是对于第一种按钮这个函数只是起到是按钮获得的作用,当用户单击工具栏按钮时只弹出相应的弹出菜单,函数中的代码并不执行。对于第二种按钮来说这个函数是响应按钮消息的,当用户单击工具栏按钮时执行函数中的代码,不会弹出菜单。作为试验可以在消息函数中加入一个弹出对话框:
AfxMessageBox(“Button Clicked”);
来对两种按钮进行测试。
最后的效果如图:
图 2.4
2.6 在工具栏中添加控件
首先在要添加控件的工具栏的适当位置添加一个工具栏按钮,按钮的图标不用管,定义一个ID,例如:ID_COMBOBOX_IN_TOOLBAR。
然后为CMainFrame添加一个成员
CExtComboBox m_Combo;//如果使用CComboBox也可以,但是显示的是MFC传统风//格的ComboBox
要在m_wndYourToolBar上的ID为ID_COMBOBOX_IN_TOOLBAR处添加ComboBox,必须在m_wndYourToolBar创建以后才能添加。添加ComboBox的代码如下:
if(!m_Combo.Create(WS_CHILD | WS_VISIBLE | CBS_DROPDOWN,
CRect(0,0,200,480),
&m_wndYourToolBar,ID_COMBOBOX_IN_TOOLBAR) ||
!m_wndYourToolBar.SetButtonCtrl(
m_wndYourToolBar.CommandToIndex(ID_COMBOBOX_IN_TOOLBAR),
&m_Combo))
{
TRACE0("Failed to create combobox/n");
return -1;
}
m_Combo.SetItemHeight( -1, 16 );
m_Combo.AddString( _T("ComboBox Item 0") );
m_Combo.AddString(_T("ComboBox Item 1"));
m_Combo.AddString(_T("ComboBox Item 2"));
m_Combo.SetCurSel(0);
编译并运行ComboBox虽然是创建了但是不可用,那是因为ID_COMBOBOX_IN_TOOLBAR处于不可用的状态,要是它可用仍然要响应该按钮的消息,这样就可以使用了如图:
图 2.5
2.7 CExtThemeSwitcherToolControlBar
Prof-UIS库为程序员内置了一个主题选择工具栏
CExtThemeSwitcherToolControlBar,使用这个工具栏就可以在程序中切换各个Prof-UIS内置的界面风格。
在CMainFrame中声明一个CExtThemeSwitcherToolControlBar的对象
CExtThemeSwitcherToolControlBar m_wndTSTCB;
创建并初始化该工具栏
if(!m_wndTSTCB.Create(_T("Theme Switcher"),
this,
ID_THEME_SWITCHER_TOOL_CONTROL_BAR) ||
!m_wndTSTCB.ThemeSwitcherInit())
{
TRACE0("Failed to create Theme switcher toolbar/n");
return -1;
}
停靠CExtThemeSwitcherToolControlBar与停靠其他的工具栏相同
m_wndTSTCB.EnableDocking(CBRS_ALIGN_ANY);
//...
DockControlBar(&m_wndTSTCB,AFX_IDW_DOCKBAR_RIGHT);//停靠在窗口右侧
效果如图:
图 2.6
第3章状态栏
Prof-UIS中的状态栏是比较简单的一个控件,Prof-UIS向导生成的代码就默认的包含扩增风格的状态栏。
3.1 创建和初始化状态栏
Prof-UIS的状态栏类是CExtStatusControlBar它直接从MFC的CStatusBar类继承下来。在程序中使用状态栏时要在CMainFrame中声明一个CExtStatusControlBar的对象,然后创建并初始化该对象:
CExtStatusControlBar m_wndStatusBar;
在OnCreate中创建并初始化状态栏对象
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failed to create status bar/n");
return -1; // fail to create
}
这里的Create和SetIndicators都是CStatusBar的成员函数,所以Prof-UIS的状态栏的使用方法与MFC状态栏的使用方法没什么不同。
初始化时调用的indicators是在OnCreate前部定义的数组:
static UINT indicators[] =
{
ID_SEPARATOR, // status line indicator
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
};
我们也可以加入自己的指示器,先在资源编辑器中定义一个字符串资源:
// In the RC file
STRINGTABLE
BEGIN
ID_INDICATOR_MYSTRING "String"
END
// In CMainFrame's message map
ON_UPDATE_COMMAND_UI (ID_INDICATOR_MYSTRING, OnUpdateIndicator)
// In CMainFrame::OnCreate
static UINT nIndicators[] = {
ID_SEPARATOR,
ID_INDICATOR_MYSTRING
};
m_wndStatusBar.Create (this);
m_wndStatusBar.SetIndicators (nIndicators, 2);
// Elsewhere in CMainFrame
void CMainFrame::OnUpdateIndicator (CCmdUI* pCmdUI)
{
pCmdUI->Enable (m_bMyString);
}
m_bMyString是字符串显示的控制标志
这样当你触发某项设置时使m_bMyString为true,字符串”String”就会显示在状态栏中,其实NUM CAP SCRL都是这样实现的只不过它们是在MFC内部实现的。
Prof-UIS改写了CStatusBar的SetPaneInfo但是参数与使用方法与MFC的一致,要改变状态栏Indicator的属性请使用SetPaneInfo。
3.2 在状态栏中添加控件
经常要在状态栏中添加一些控件,Prof-UIS为程序员提供了便利的方法。现在CMainFrame中定义一个CExtStatusControlBar的对象,然后在OnCreate中创建一个状态栏(其实这些步骤Wizard已经为程序员写好了,在第一章中有介绍)。然后加入控件,下面是加入控件的过程(以加入一个普通按钮为例):
1. 在CMainFrame中定义一个CButton对象,并在添加到状态栏之前创建它:
CButton m_buttonOnPane;
////////////////
m_buttonOnPane.Create(_T(“My Button”),WS_CHILD|WS_VISIBLE|
WS_TABSTOP,CRect(0,0,0,0),&m_wndStatusBar,ID_PANE_BUTTON)
m_buttonOnPane.SetFont(
CFont::FromHandle((HFONT)::GetStockObject(DEFAULT_GUI_FONT))
);
2. 为现有的状态栏添加pane:
m_wndStatusBar.AddPane(ID_PANE_ADD_BUTTON,1);
3. 设置状态栏的属性:
UINT nIndex =
m_wndStatusBar.CommandToIndex(ID_PANE_ADD_BUTTON));
if(nIndex ==-1)
return -1;
m_wndStatusBar.SetPaneWidth(nIndex,70);
4. 向新添加的Pane中加入控件:
m_wndStatusBar.SetPaneControl(&m_buttonOnPane,
ID_PANE_ADD_BUTTON,true));
5. 响应控件消息:
afx_msg void OnPaneButtonClicked(void);
/////////////////////////////////////////////
ON_BN_CLICKED(ID_PANE_BUTTON,OnPaneButtonClicked)
/////////////////////////////////////////////////
void CMainFrame::OnPaneButtonClicked()
{ AfxMessageBox(“Button Clicked”); }
编译运行的结果如图:
图 3.1
第4章菜单栏
Prof-UIS的向导在默认的情况下是不生存菜单栏的,当程序员在Prof-UIS Application Wizard的Prof-UIS Features Step 3中选择Use Menu Bar的选项,系统就会在为你生成的应用程序中使用菜单栏。
4.1 Wizard生成的Menu Bar
看看向导生成的代码的结构,在CMainFrame中多出了一个m_wndMenuBar的变量,它是一个CExtMenuControlBar的对象,这就是菜单栏了。
菜单栏的创建与工具栏的创建是一致的这是因为CExtMenuControlBar是直接派生自CExtToolControlBar的,而且CExtMenuControlBar并没有改写Create函数,所以菜单栏的创建和工具栏的创建都是调用的CExtControlBar的Create函数。
在CMainFrame的OnCreate函数中:
if( !m_wndMenuBar.Create(
NULL, // _T("Menu Bar"),
this,
ID_VIEW_MENUBAR,
WS_CHILD|WS_VISIBLE
|CBRS_TOP|CBRS_TOOLTIPS|CBRS_GRIPPER
|CBRS_TOOLTIPS
|CBRS_FLYBY
|CBRS_SIZE_DYNAMIC
)
)
{
TRACE0("Failed to create menubar/n");
return -1; // failed to create
}
最后还要停靠菜单栏,这与停靠工具栏是相同的:
m_wndMenuBar.EnableDocking( CBRS_ALIGN_ANY );
m_wndToolBar.EnableDocking( CBRS_ALIGN_ANY );
//VERBOSE
if( !CExtControlBar::FrameEnableDocking(this) )
{
ASSERT( FALSE );
return -1;
}
DockControlBar( &m_wndMenuBar );
DockControlBar( &m_wndToolBar );
4.2 添加一个新菜单
上面的代码有令人费解的地方,m_wndMenuBar从定义到创建再到停靠,没有和主菜单资源有过任何的联系,那为什么程序执行后菜单栏中的菜单项和菜单资源一样呢?这里Prof-UIS默认将创建后没有加载菜单资源的CExtMenuControlBar的对象加载IDR_MAINFRAME菜单资源。所以要添加一个非默认的菜单栏,就要使用
CExtMenuControlBar::LoadMenuBar函数,并在创建后加载菜单资源。
下面要给程序添加一个新的菜单栏,并实现在两个菜单栏之间切换。
首先要在资源编辑器中添加一个新的菜单,同时使两个菜单拥有一个共同的项
ID_MENU_CHANGE(为了切换菜单栏)。然后将菜单注册到已经安装的Profile中:
VERIFY(
g_CmdManager->UpdateFromMenu(
__PROF_UIS_PROJECT_CMD_PROFILE_NAME,
IDR_MENU_NEW)
);
在CMainFrame中添加一个菜单表标识成员
bool m_bMenuChange;在构造函数中初始化(初始化成什么随兴趣而定)。
添加菜单项ID_MENU_CHANGE命令的处理函数OnMenuChange:
void CMainFrame::OnMenuChange()
{
if(m_bMenuChange)
m_wndMenuBar.LoadMenuBar(IDR_MAINFRAME);
else
m_wndMenuBar.LoadMenuBar(IDR_MENU_NEW);
m_wndMenuBar.UpdateMenuBar(true);
}
最后要调用CExtMenuControlBar::UpdateMenubar来更新菜单栏。
4.3 动态更新菜单项
动态修改菜单项很简单,因为CExtMenuControlBar提供了一个成员函数:
CMenu* CExtMenuControlBar::GetMenu() const;可以放回菜单的指针,有了这个指针我们就可以作很多事情了。例如可以用在MFC下添加菜单项的方法来添加菜单项:
m_wndMenuBar.GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,
ID_APPEND_MENU,”AppendMenu”);
等方法来更改菜单。
4.4 菜单特点
我们的应用程序执行后菜单看起来不是很美观,因为它缺少阴影,提示等细节。下面来看看什么因素决定了这些细节。
在向导产生的代码中主程序类中多出了一个这样的函数SetupUiAdvancedOptions
在这个函数的最后部分有一大段代码:
CExtPopupMenuWnd::g_bMenuWithShadows = false;
CExtPopupMenuWnd::g_bMenuShowCoolTips = false;
CExtPopupMenuWnd::g_bMenuExpanding = false;
CExtPopupMenuWnd::g_bMenuHighlightRarely = false;
CExtPopupMenuWnd::g_bMenuExpandAnimation = false;
CExtPopupMenuWnd::g_bUseDesktopWorkArea = false;
CExtPopupMenuWnd::g_DefAnimationType = CExtPopupMenuWnd::__AT_FADE;
//$$__PROFUISAPPWIZ_KEY_MENU_ANIM_DISPMS$$
这些代码就是决定显示细节的。着只是其中的一部分,CExtPopupMenuWnd一共有十个全局变量决定菜单的显示风格:
static bool CExtPopupMenuWnd::g_bFullScreenMode;
指出应用程序是否在全屏模式下。
static bool CExtPopupMenuWnd::g_bMenuWithShadwos;
显示阴影的标志
static bool CExtPopupMenuWnd::g_bMenuShowCoolTips;
显示漂亮的提示
static bool CExtPopupMenuWnd::g_bMenuLargeIcons;
显示大图标
static bool CExtPopupMenuWnd::g_bMenuExpanding;
隐藏很少使用的菜单项
static bool CExtPopupMenuWnd::g_bMenuExpandAnimation;
自动隐藏很少使用的菜单项
static bool CExtPopupMenuWnd::g_bMenuDelayExpanding;
延缓显示全部菜单项
static bool CExtPoputMenuWnd::g_bMenuHighLightRarely;
高亮显示常使用的菜单项
static bool CExtPopupMenuWnd::g_bUseDesktopWorkArea;
是否与桌面相匹配
static e_animation_type_t CExtPopupMenuWnd::g_DefAnimationType;
显示弹出菜单的动态方式
e_animation_type_t使CExtPopupMenuWnd内的一个枚举类型:
__AT_NONE 没有简便效果
__AT_RANDOM 随机模式
__AT_ROLL 菜单从左上角滑出
__AT_SLIDE 菜单从顶部滑出
__AT_FADE 逐渐出现
__AT_ROLL_AND_STRETCH 与__AT_ROLL一样但是使用缩放比例
__AT_SLIDE_AND_STRETCH 与__AT_SLIDE一样但是使用缩放比例
__AT_NOISE 菜单由象素点组成
__AT_BOXES 菜单由小方形组成
__AT_CIRCLES 菜单由小圆形组成
__AT_HOLES 菜单由小孔组成,是__AT_CIRCLES的反向显示
4.5 上下文菜单
可以使用传统的MFC的上下文菜单,但是只是显示出MFC风格的菜单。Prof-UIS中的上下文菜单是通过CExtPopupMenuWnd实现的。首先在资源编辑器中编辑一个菜单资源,然后再添加WM_CONTEXTMENU消息的处理函数。
在函数OnContextMenu中添加如下代码:
CExtPopupMenuWnd* pPopMenu = new CExtPopupMenuWnd;
pPopMenu->LoadMenu(GetSafeHwnd(),IDR_MENU_POPUP);
pPopMenu->TrackPopupMenu(TPMX_LEFTALIGN,point.x,point.y);
就可以实现右键菜单了,但是一定要将菜单资源注册到以安装的Profile中。TPMX_LEFTALIGN是菜单的显示风格,具体请参阅Prof-UIS help的CExtPopupMenuWnd类和ExtPopupMenuWnd.h中从732行到790行的部分代码。
4.6 内建弹出菜单
Prof-UIS库内建了许多的弹出菜单,有...
4.6.1 Theme Switcher Menu
Theme Switcher Menu的使用很方便,当某一菜单项的ID被设置为 29999 或者
ID_EXT_PM_MENU_MARKER_THEME_CHANGER 这个菜单项就会被替换成Theme Switcher Menu。因为在ResPM.H中有这样的定义:
#define ID_EXT_PM_MENU_MARKER_THEME_CHANGER 29999
4.6.2 Color Selection Menu
Color Selection Menu没有内置的ID,所以要在编程是手工加入。每一个
CExtPopupColorMenuWnd对象代表一个Color Selection Menu,
CExtPopupColorMenuWnd是从CExtPopupMenuWnd继承下来的。使用时首先在菜单中添加一个普通的菜单项用来当作Color Selection Menu的上级菜单项,例如ID为ID_POPUP_8X5(默认规格为8X5)。下面的代码是将Color Selection Menu加载到指定菜单项中(实质是替换):
int nReplacePos = pPopMenu->ItemFindPosForCmdID(ID_POPUP_8X5);
if(nReplacePos >= 0)
{
CExtPopupColorMenuWnd* pColor8x5 = new CExtPopupColorMenuWnd;
pColor8x5->m_lParamCookie = LPARAM(ID_POPUP_8X5);
pColor8x5->m_hWndNotifyColorChanged = GetSafeHwnd();
pColor8x5->m_clrDefault = COLORREF(-1);
VERIFY(
pPopMenu->ItemInsertSpecPopup(
pColor8x5,
nReplacePos+1,
pPopMenu->ItemGetText(nReplacePos),
pPopMenu->ItemGetIcon(nReplacePos))
);
pPopMenu->ItemSetDisplayed(nReplacePos+1,true);
VERIFY(
pPopMenu->ItemRemove(nReplacePos)
);
}
上面的代码将ID_POPUP_8X5替换成一个弹出的Color Selection Menu,这个弹出菜单显示了5行8列的颜色块,CExtPopupColorMenuWnd提供了两个成员函数:
SetColors8x5和SetColors8x2分别用来设置Color Selection Menu的显示样式。在调用CExtPopupMenuWnd::ItemInsertSpecPopup之前通过调用
CExtPopupColorMenuWnd::SetColors8x2就可以将Color Selection Menu的显示风格改变,如图:
图4.1
除了使用CExtPopupColorMenuWnd给定的两个显示形式以为,还可以自定义显示形式。
安装上面的方法对Color Selection Menu进行初始化,之后将所有的颜色位图全部删除
pColorPopup->RemoveAllColors();
删除后弹出菜单只剩下“默认颜色”和“自定义颜色...”两项了。
然后定义Color Selection Menu每一行显示的Color Cell的数目和每一个Color Cell的尺寸。
int nGrayColors = 16;
pColorPopup->SetColorsInRow( nGrayColors );
pColorPopup->m_sizeColorItemCell.cy *= 2;
这里我们定义每一行显示16个Color Cell并且每一个Color Cell的高度是原来的2被。
下面的任务是项Color Selection Menu中添加自定义的Color Cell。
for( int nColorIndex = 0; nColorIndex < nGrayColors;
nColorIndex++ )
{
double f = double(nColorIndex)/double(nGrayColors-1);
//由色调,亮度和饱和度来生成RGB值
COLORREF clr = CExtBitmap::stat_HLStoRGB( 0.0, f, 0.0 );
//GetRValue宏返回RGB值中的R的值
int n = GetRValue(clr);
CExtSafeString str;
str.Format(
_T("#%02X%02X%02X"),
n,
n,
n
);
//向Color Selection Menu中添加Color Cell
pColorPopup->AddColor(
//COLORREF_TABLE_ENTRY是CExtPopupColorMenuWnd中定义的
//一个结构,用来表示Color Cell
new CExtPopupColorMenuWnd::COLORREF_TABLE_ENTRY(
0,
clr,
str
)
);
}
下面就是将Color Selection Cell加载到菜单项中,与上面的代码相同。如图:
图 4.2
除了可以改变Color Selection Menu的显示样式以为,CExtPopupColorMenuWnd还提供了几个共有的数据成员,这些数据成员决定了Color Selection Menu的显示细节。
“默认颜色”是否显示
bool m_bEnableBtnColorDefault
“自定义颜色...”是否显示
bool m_bEnableBtnColorCustom
“默认颜色”的文本
CExtSafeString m_sBtnTextColorDefault
“自定义颜色...”的文本
CExtSafeString m_sBtnTextColorCustom
选中的颜色//如果给定的颜色菜单中没有则不显示
COLORREF m_clrInitial
上面的代码生成的只是一个Color Selection Menu但它不是有效的,以为并没有对它进行消息的映射。下面来添加消息函数,使Color Selection Menu有效。
CExtPopupColorMenuWnd已经注册了三个消息:
g_nMsgNotifyColorChanged消息,当鼠标在Color Cell上面时发送给它的处理窗口,
g_nMsgNotifyColorChangedFinally消息,当鼠标选取某一Color Cell时发送,
g_nMsgNotifyCustColor消息,当选取自定义颜色的按钮时发送。
按照如下的方法映射并处理这三个消息:
afx_msg LRESULT OnColorSelectCustom(WPARAM wParam,
LPARAM lParam);
afx_msg LRESULT OnColorChangedFinally(WPARAM wParam,
LPARAM lParam);
afx_msg LRESULT OnColorChanged(WPARAM wParam,
LPARAM lParam);
ON_REGISTERED_MESSAGE(
CExtPopupColorMenuWnd::g_nMsgNotifyCustColor,
OnColorSelectCustom)
ON_REGISTERED_MESSAGE(
CExtPopupColorMenuWnd::g_nMsgNotifyColorChangedFinally,
OnColorChangedFinally)
ON_REGISTERED_MESSAGE(
CExtPopupColorMenuWnd::g_nMsgNotifyColorChanged,
OnColorChanged)
LRESULT CMainFrame::OnColorChanged(WPARAM wParam,LPARAM lParam)
{
//do something
return 0;
}
LRESULT CMainFrame::OnColorChangedFinally(WPARAM wParam,
LPARAM lParam)
{
COLORREF color = COLORREF(wParam);
if(color == COLORREF(-1))
AfxMessageBox(_T("Default Color"));
else
{
CString strColor;
strColor.Format(_T("RGB:%02X%02X%02X"),
(int)GetRValue(color),
(int)GetGValue(color),
(int)GetBValue(color));
AfxMessageBox(strColor);
}
return 0;
}
LRESULT CMainFrame::OnColorSelectCustom(WPARAM wParam,
LPARAM lParam)
{
switch((UINT)lParam)
{
case ID_POPUP_8X5:
AfxMessageBox(_T("ID_POPUP_8X5 Custom"));
break;
case ID_POPUP_8X2:
AfxMessageBox(_T("ID_POPUP_8X2 Custom"));
break;
case ID_POPUP_CUSTOM:
AfxMessageBox(_T("ID_POPUP_CUSTOM Custom"));
break;
default:
AfxMessageBox(_T("No ID"));
}
return 0;
}
为什么这里的lParam会携带ID这样的信息哪?这些消息的处理函数被映射到哪里呢?
这些问题就是Color Selection Menu初始化时的事情了:
pColor8x5->m_lParamCookie = LPARAM(ID_POPUP_8X5);
pColor8x5->m_hWndNotifyColorChanged = GetSafeHwnd();
这两个成员表示了消息的ID以及处理消息的窗口的句柄,另外消息的wParam项携带的是触发消息的颜色的值。
4.6.3 Date Picker Menu
Date Picker Menu使用来选择日期的菜单,它是CExtPopupDatePickerMenuWnd的一个实例,添加Date Picker Menu的方法与添加Color Selection Menu的方法相同。下面是添加Date Picker Menu的具体代码:
nReplacePos = pPopMenu->ItemFindPosForCmdID(ID_POPUP_DATE);
if(nReplacePos>=0)
{
CExtPopupControlMenuWnd::g_bControlMenuWithShadows = true;
CExtPopupDatePickerMenuWnd* pDatePicker =
new CExtPopupDatePickerMenuWnd;
VERIFY(
pPopMenu->ItemInsertSpecPopup(
pDatePicker,
nReplacePos+1,
pPopMenu->ItemGetText(nReplacePos),
pPopMenu->ItemGetIcon(nReplacePos)
)
);
pPopMenu->ItemSetDisplayed(nReplacePos+1,true);
VERIFY(pPopMenu->ItemRemove(nReplacePos));
}
这样就可以添加一个Date Picker Menu菜单了如图:
图4.3
响应Date Picker Menu消息时使用CExtDatePickerWnd注册的消息
CExtDatePickerWnd::g_nMsgSelectionNotification如下:
afx_msg LRESULT OnMsgSelectionNotification(WPARAM wParam,
LPARAM lParam);
ON_REGISTERED_MESSAGE(
CExtDatePickerWnd::g_nMsgSelectionNotification,
OnMsgSelectionNotification)
LRESULT CMainFrame::OnMsgSelectionNotification(WPARAM wParam,
LPARAM lParam)
{
const CExtDatePickerWnd::SELECTION_NOTIFICATION * pSN =
CExtDatePickerWnd::SELECTION_NOTIFICATION::FromWPARAM( wParam );
if( !pSN->m_bFinalSelectionChanging )
return 0L;
CString strMsg, strMsgAll,
strDtBegin( pSN->m_dtBegin.Format() ),
strDtEnd( pSN->m_dtEnd.Format() );
strMsg.Format(
_T("The date selected: %s"),
strDtBegin
);
if( pSN->m_dtBegin != pSN->m_dtEnd ){
strMsg += _T(" - ");
strMsg += strDtEnd;
}
strMsg += _T("/r/n");
TRACE( strMsg );
AfxMessageBox(strMsg);
return 0L;
}
注意:Prof-UIS 2.6 Freeware 不支持Date Picker Menu,Prof-UIS 2.6 Trial支持Date Picker Menu但是这个版本只提供MSCS Debug版本。
4.7菜单内置消息
Prof-UIS注册了大量的菜单消息,但是这些消息都是CExtPopupBaseWnd注册的,源代码在ExtPopupMenuWnd.h第303行~317行和ExtPopupMenuWnd.cpp第178行~238行。其中有些消息事是很常用并且很重要的。
CExtPopupMenuWnd::g_nMsgPrepareMenu消息:
当弹出菜单显示前被发送一次,其中wParam参数携带消息信息。一般情况将该消息映射到函数LRESULT OnExtMenuPrepare(WPARAM wParam,LPARAM lParam);
函数的通常形式如下:
LRESULT CPagePopupMenus::OnExtMenuPrepare(WPARAM wParam,
LPARAM lParam)
{
lParam;
CExtPopupMenuWnd::MsgPrepareMenuData_t * pData =
reinterpret_cast
< CExtPopupMenuWnd::MsgPrepareMenuData_t * >
( wParam );
ASSERT( pData != NULL );
CExtPopupMenuWnd * pPopup = pData->m_pPopup;
ASSERT( pPopup != NULL );
pData->m_bMenuChanged = true;
//修改弹出菜单
return TRUE;
}
如果菜单被修改pData->m_bMenuChanged一定被设置为true;
CExtPopupMenuWnd::g_nMsgPrepareOneMenuLevel消息
这个消息和CExtPopupMenuWnd::g_nMsgPrepareMenu消息一样,不同是这个消息是当每一级的弹出菜单显示前都会被发送。这个消息的处理过程也与CExtPopupMenuWnd::
g_nMsgPrepareMenu消息一样。
CExtPopupMenuWnd::g_nMsgPopupDrawItem消息:
当每一项被绘制的时候发送,必要条件是,调用TrackPopupMenu时参数dwTrackFlags必须包含TPMX_OWNERDRAW_FIXED标志。这个消息一般映射到函数
afx_msg LRESULT OnDrawPopupMenuItem(WPARAM wParam,LPARAM lParam);
函数的形式如下:
LRESULT CMainFrame::OnDrawPopupMenuItem( WPARAM wParam,
LPARAM lParam )
{
wParam;
CExtPopupMenuWnd::DRAWITEMDATA * pDrawItemData =
reinterpret_cast
< CExtPopupMenuWnd::DRAWITEMDATA * >
( lParam );
ASSERT( pDrawItemData != NULL );
UINT nCmdID = pDrawItemData->GetCmdID();
if( nCmdID != ID_OWNERDRAW_ITEM )
return 0; // peform default painting
// paint default menu item's background
pDrawItemData->PaintDefaultBk();
CDC & dc = *( (CDC *) *pDrawItemData );
CRect rcItem = LPCRECT(*pDrawItemData);
// painting code goes here
return 1L;
}
CExtPopupMenuWnd::g_nMsgPopupDrawLeftArea消息:
当弹出菜单显示的时候发送,这个消息被映射到
afx_msg LRESULT OnDrawPopupLeftArea( WPARAM wParam, LPARAM lParam );
函数的形式如下:
LRESULT CMainFrame::OnDrawPopupLeftArea( WPARAM wParam,
LPARAM lParam )
{
wParam;
CExtPopupMenuWnd::DRAWLEFTAREADATA * pDrawLeftAreaData =
reinterpret_cast
< CExtPopupMenuWnd::DRAWLEFTAREADATA * >
( lParam );
ASSERT( pDrawLeftAreaData != NULL );
CDC & dc = *( (CDC *) *pDrawLeftAreaData );
CRect rcItem = LPCRECT(*pDrawLeftAreaData);
// painting code goes here
return 1L;
}
下面的菜单都是应用上面的消息实现的:
4.7.1 Owner Draw Menu Item
利用CExtPopupMenuWnd::g_nMsgPopupDrawItem消息可以实现菜单项的自绘,添加这个消息的处理函数
LRESULT CMainFrame::OnDrawPopupMenuItem(WPARAM wParam,
LPARAM lParam)
{
CExtPopupMenuWnd::DRAWITEMDATA* pDrawItem =
reinterpret_cast <CExtPopupMenuWnd::DRAWITEMDATA*> (lParam);
ASSERT(pDrawItem != NULL);
UINT nCmdID = pDrawItem->GetCmdID();
if(nCmdID != ID_OWNER_DRAW)
return 0;
pDrawItem->PaintDefaultBk();
CRect rectItem = LPCRECT(*pDrawItem);
rectItem.DeflateRect(10,1,10,4);
LPCTSTR sItemText = __EXT_MFC_SAFE_LPCTSTR( *pDrawItem );
INT nItemTextLen = INT( _tcslen( sItemText ) );
// get draw DC
CDC & dc = *( (CDC *) *pDrawItem );
CExtWndShadow _shadow;
_shadow.Paint(
NULL,
dc, rectItem, CRect(0,0,0,0), CRect(0,0,0,0),
3,
CExtWndShadow::DEF_BRIGHTNESS_MIN,
CExtWndShadow::DEF_BRIGHTNESS_MAX,
false
);
CExtPaintManager::stat_PaintGradientRect(
dc,
&rectItem,
dc.GetNearestColor( RGB( 255, 0, 0 ) ),
dc.GetNearestColor( RGB( 0, 0, 255 ) ),
false
);
INT nOldBkMode = dc.SetBkMode( TRANSPARENT );
COLORREF clrOldText = dc.SetTextColor( RGB( 0, 0, 0 ) );
CFont * pOldFont = dc.SelectObject( &g_PaintManager->m_FontBold );
rectItem.OffsetRect( 2, 1 );
dc.DrawText(
sItemText, nItemTextLen, &rectItem,
DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_CENTER
);
clrOldText = dc.SetTextColor( RGB( 255, 255, 255 ) );
rectItem.OffsetRect( -2, -1 );
dc.DrawText(
sItemText, nItemTextLen, &rectItem,
DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS
);
dc.SelectObject( pOldFont );
dc.SetBkMode( nOldBkMode );
dc.SetTextColor( clrOldText );
return 1L;
}
编译运行的结果如图:
图4.4
4.7.2 Popup Menu With Left Area
实现Left Area除了处理CExtPopupMenuWnd::g_nMsgPopupDrawLeftArea消息外,还要在加载弹出菜单资源前调用函数SetLeftAreaWidth来设置宽度和触发
CExtPopupMenuWnd::g_nMsgPopupDrawLeftArea。下面是消息的处理过程:
LRESULT CMainFrame::OnDrawPopupLeftArea(WPARAM wParam,
LPARAM lParam)
{
wParam;
CExtPopupMenuWnd::DRAWLEFTAREADATA * pDrawLeftAreaData =
reinterpret_cast < CExtPopupMenuWnd::DRAWLEFTAREADATA * >
( lParam );
ASSERT( pDrawLeftAreaData != NULL );
ASSERT( pDrawLeftAreaData->m_pPopup != NULL );
if( (pDrawLeftAreaData->m_pPopup->TrackFlagsGet()&TPMX_PALETTE)
!= 0 )
return 0;
// get draw DC
CDC & dc = *( (CDC *) *pDrawLeftAreaData );
CRect rcItem = LPCRECT(*pDrawLeftAreaData);
CExtPaintManager::stat_PaintGradientRect(
dc,
&rcItem,
dc.GetNearestColor( RGB( 0, 0, 0 ) ),
dc.GetNearestColor( RGB( 100, 100, 255 ) ),
true
);
LOGFONT lf;
::memset(&lf,0,sizeof(LOGFONT));
g_PaintManager->m_FontNormalVertX.GetLogFont( &lf );
lf.lfHeight = -18;
lf.lfWidth = 0;
lf.lfWeight = 900;
__EXT_MFC_STRCPY( lf.lfFaceName, LF_FACESIZE,
_T("Courier New") );
CFont font;
VERIFY(
font.CreateFontIndirect(&lf)
);
CFont * pOldFont = dc.SelectObject( &font );
INT nOldBkMode = dc.SetBkMode( TRANSPARENT );
COLORREF clrOldText = dc.SetTextColor( RGB( 0, 0, 0 ) );
static CString sTitle( _T("Left Area") );
CPoint ptText( rcItem.left+4, rcItem.bottom-5 );
dc.DrawState(
ptText, rcItem.Size(), (LPCTSTR)sTitle,
DSS_NORMAL, TRUE, 0, (CBrush*)NULL
);
dc.SetTextColor( RGB( 255, 255, 255 ) );
ptText.Offset( -1, -2 );
dc.DrawState(
ptText, rcItem.Size(), (LPCTSTR)sTitle,
DSS_NORMAL, TRUE, 0, (CBrush*)NULL
);
/*这段是用来加载图标的
const int nIconMetric = 24;
HICON hIcon = (HICON)
::LoadImage(
::AfxGetInstanceHandle(),
MAKEINTRESOURCE(IDI_ICON_FOR_POPUP_MENU),
IMAGE_ICON,
nIconMetric,
nIconMetric,
0
);
if( hIcon != NULL )
{
int nOffset = (rcItem.Width() - nIconMetric) / 2;
VERIFY(
::DrawIconEx(
dc.GetSafeHdc(),
rcItem.left + nOffset,
rcItem.top + nOffset,
hIcon,
nIconMetric,
nIconMetric,
0,
(HBRUSH)NULL,
DI_NORMAL
)
);
VERIFY( DestroyIcon( hIcon ) );
}*/
dc.SetBkMode( nOldBkMode );
dc.SetTextColor( clrOldText );
dc.SelectObject( pOldFont );
return 1L;
}
编译运行结果如图:
图4.5
4.7.3 Palette Menu
通过CExtPopupMenuWnd::g_nMsgPrepareMenu消息可以实现Palette Menu,首先要准备一个位图资源,然后按照你的Palette Menu有多少项,在Resource.h中申请多少个连续ID(ID连续很重要)。然后实现消息的处理函数如下:
LRESULT CMainFrame::OnExtMenuPrepare(WPARAM wParam,LPARAM lParam)
{
lParam;
CExtPopupMenuWnd::MsgPrepareMenuData_t * pData =
reinterpret_cast
< CExtPopupMenuWnd::MsgPrepareMenuData_t * >
( wParam );
ASSERT( pData != NULL );
CExtPopupMenuWnd * pPopup = pData->m_pPopup;
ASSERT( pPopup != NULL );
pData->m_bMenuChanged = true;
INT nReplacePos =
pPopup->ItemFindPosForCmdID( ID_POPUP_TOOLS );
if( nReplacePos >= 0 )
{
pPopup->ItemRemove( nReplacePos );
CExtPopupMenuWnd * pPalettePopup = new CExtPopupMenuWnd;
pPopup->ItemInsertSpecPopup(
pPalettePopup,
nReplacePos,
g_CmdManager->CmdGetPtr(
g_CmdManager->ProfileNameFromWnd( m_hWnd ),
ID_POPUP_TOOLS
)->m_sMenuText
);
pPalettePopup->TrackFlagsSet(
pPalettePopup->TrackFlagsGet()
| TPMX_PALETTE
);
//pPalettePopup->SetLeftAreaWidth( 10 );
/*
pPalettePopup->ItemInsertCommand(
1,
-1,
_T("Palette 6 x 2:")
);*/
INT nLastPos = pPalettePopup->ItemGetCount() - 1;
pPalettePopup->ItemEnabledSet( nLastPos, false );
pPalettePopup->ItemEnabledAppearanceSet( nLastPos );
pPalettePopup->ItemBoldSet( nLastPos );
pPalettePopup->ItemPaletteRowWrapSet( nLastPos );
CExtBitmap _bmp;
VERIFY(
_bmp.LoadBMP_Resource(
MAKEINTRESOURCE(IDB_BITMAP_TOOLS)
)
);
INT nRow, nColumn;
static const INT nRowCountPaint = 6, nColumnCountPaint = 3;
static const COLORREF clrTransparentPaint = RGB(212,208,200);
static const CSize _sizeBitmapPartPaint(32,32);
int nPaletteID = 0;
for( nRow = 0; nRow < nRowCountPaint; nRow ++ )
{
for( nColumn = 0; nColumn < nColumnCountPaint; nColumn ++ )
{
nPaletteID++;
CPoint ptBitmapOffset(
nRow * nColumnCountPaint * _sizeBitmapPartPaint.cx
+ nColumn * _sizeBitmapPartPaint.cx,
0
);
CRect rcBitmapPart(
ptBitmapOffset,
_sizeBitmapPartPaint
);
CExtCmdIcon _icon;
_icon.m_bmpNormal.FromBitmap(
_bmp,
&rcBitmapPart
);
ASSERT( ! _icon.IsEmpty() );
_icon.m_bmpNormal.AlphaColor( clrTransparentPaint, RGB(0,0,0), 0 );
pPalettePopup->ItemInsertCommand(
//3 + nRow*nColumnCountPaint + nColumn,
40000+nPaletteID,
-1,
NULL,
NULL,
_icon
);
if( nColumn == (nColumnCountPaint-1) )
pPalettePopup->ItemPaletteRowWrapSet(
pPalettePopup->ItemGetCount() - 1
);
} // for( nColumn = 0; nColumn < nColumnCountPaint; nColumn ++ )
} // for( nRow = 0; nRow < nRowCountPaint; nRow ++ )
}
return 1L;
}
例如申请的ID是从40001开始的
#define ID_PALETTE_ONE 40001
#define ID_PALETTE_TWO 40002
//...
那么你插入到Palette Menu中的各项的ID就依次是ID_PALETTE_ONE,
ID_PALETTE_TWO,所以就可以对各项的命令进行处理了。编译并运行如图:
图4.6
4.8 留下的问题
还有几种菜单没有介绍如:Tear-Off Menu和Menu With Combo Edit Date Field这些菜单都需要CExtCustomSite,这个类后面会学习