动态管理MDI应用程序的子菜单条 Visual C++ 多文档框架程序利用应用的框架窗口,通过更新用户对象管理，可动态变换窗口框架菜单条的子菜单选项。本文所示的多文档窗口应用程序，通过动态更新子窗口框架的Color子菜单，为子窗口中的显示文本设置不同的前景色，应用程序界面如图所示。 一、利用摸式对话框设置可供择的颜色种类 创建颜色选择的模式对话框，通过检查框设置子窗口允许选择的前景色，每种颜色对应一个控件，将控件设置为检查状态，此颜色将添加到子窗口框架的菜单条中。 1．创建颜色选择对话框
通过对话框编辑器创建颜色选择的模式对话框资源，在对话框中插入四个检查框控件，控件颜色依次为黑、红、紫、蓝四色，并创建与颜色选择对话框关联的CColorOptDlg派生类，此类派生于CDialog类。颜色选择对话框标识符为IDD_CHANGE_COLOR，检查框标识符如表1所示，颜色选择对话框如图所示。

2．模式对话框的数据交换
要实现对话框中检查框控件的数据交换，应通过CWnd类的DoDateExchange虚函数，重载CColorOptDlg类，以传递检查框控件的数据变化。颜色选择对话框中控件的属性设置如表1所示：
表1颜色选择对话框的控件属性设置

根据对话框控件的数据变化，动态保存对话框的颜色设置，DoDateExchange函数显示如下。
void CColorOptDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CColorOptDlg)
DDX_Check(pDX, ID_COLOR1, m_black);
DDX_Check(pDX, ID_COLOR3, m_purle);
DDX_Check(pDX, ID_COLOR2, m_red);
DDX_Check(pDX, ID_COLOR4, m_blue);
//}}AFX_DATA_MAP
}

二、菜单的动态管理

1．编辑菜单资源
创建基于MFC基本类库的多文档工程MyMenu，在子窗口缺省菜单（IDR_MYMENUTYPE）中添加Color菜单及Change options选项，选项标识符为ID_COLOR_OPTIONS，菜单资源如下图所示。Color子菜单应定义颜色选项的字符串及其标识，颜色菜单项的字符串资源定义如下：
2．菜单的动态管理
MDI应用程序使用ON_UPDATE_COMMAND_UI机制管理菜单条。执行应用程序，激活子窗口，选择Change options选项，弹出颜色选择的模式对话框，对话框控件设置为检查状态，所选颜色可自动加到Color的子菜单中，根据子菜单的颜色设置显示子窗口文本。
（1）文档类中的变量设置

在CMyMenuDoc类中定义以下常量：
const int NUM_TEXTCOLOR=4;//设置文本的颜色数目
const int MAX_COLORLEN=20;//颜色名的最大数目
文档类中定义数据成员：
int m_iColor; //当前文本颜色
BOOL m_abAllowColor[NUM_TEXTCOLOR]; //颜色是否正在使用
文档类定义以下结构：
struct MyMenuDef{
UINT m_nString; //颜色名（存放在字符串资源中）
UINT m_nID;　　 //菜单ID
COLORREF m_crf; //颜色值
};
结构变量赋值如下所示；
MyMenuDef CMyMenuDoc::m_aColorDef[NUM_TEXTCOLOR]={
{IDS_BLACK,ID_BLACK,RGB(0,0,0)},
{IDS_RED,ID_RED,RGB(128,0,0)},
{IDS_PURLE,ID_PURLE,RGB(128,0,128)},
{IDS_BLUE,ID_BLUE,RGB(0,0,128)},
};
（2）创建文档类的成员函数
创建OnCmdMsg处理函数
由CMyMenuDoc类的OnCmdMsg函数发送Color菜单项的ID命令消息。发送菜单项的ID命令与子窗口的前景色不同，将执行DoSelectColor的处理函数，按新颜色重画文档视类;若发送的菜单项ID与子窗口的文本同色，调用文档类DoupDateSelectColor处理函数，所选颜色的菜单项作出选中标志;命令ID与 m_aColorDef中检索的颜色ID不匹配时，命令消息将调用CCmdTarget类的CCmdMsg进程。OnCmdMsg处理函数显示如下：
BOOL CMyMenuDoc::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
for(int i=NUM_TEXTCOLOR-1;i>=0;i--){
if(nID==m_aColorDef[i].m_nID){
if(nCode==CN_COMMAND)
DoSelectColor(i);
else if(nCode==CN_UPDATE_COM-MAND_UI)
DoUpdateSelectColor(i,(CCmdUI*)pExtra);
return TRUE;
}
}
return CDocument::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
DoSelectColor函数显示如下：
void CMyMenuDoc::DoSelectColor(int iColor)
{
ASSERT(iColor>=0 && iColor<NUM_TEXT-COLOR);
ASSERT(m_abAllowColor[iColor]==TRUE);
m_iColor=iColor;
UpdateAllViews(NULL);
}
DoupDateSelectColor处理函数为：
void CMyMenuDoc::DoUpdateSelectColor(int iColor,CCmdUI*pCmdUI)
{
ASSERT(iColor>=0 && iColor<NUM_TEXT-COLOR);
ASSERT(m_abAllowColor[iColor]==TRUE);
pCmdUI->SetCheck(m_iColor==iColor);
pCmdUI->Enable(TRUE);
}
创建Change options选项的消息处理函数
执行本文所示的多文档框架应用程序，可多次选择Change options选项，弹出的对话框应保持控件上次的状态设置，重新选择检查框状态，按控件状态为m_abAllowColor颜色变量赋值，从文档类成员函数得到视类指针，此指针可调用CWnd类的GetParentFrame成员函数，通过返回子框架的窗口指针，调用MyMenuMDIChildWnd子类框架中的RefreshColorMenu处理函数。OnColorOptions处理函数显示如下：
void CMyMenuDoc::OnColorOptions()
{
CColorOptDlg dlg;
if(m_abAllowColor[0]) dlg.m_black-=TRUE;
else dlg.m_black=FALSE;
if(m_abAllowColor[1]) dlg.m_red=TRUE;
else dlg.m_red=FALSE;
if(m_abAllowColor[2]) dlg.m_purle=-TRUE;
else dlg.m_purle=FALSE;
if(m_abAllowColor[3]) dlg.m_blue=-TRUE;
else dlg.m_blue=FALSE;
if(dlg.DoModal()){
if (dlg.m_black) m_abAllowColor[0]=TRUE;
else m_abAllowColor[0]=FALSE;
if (dlg.m_blue) m_abAllowColor[3]=TRUE;
else m_abAllowColor[3]=FALSE;
if (dlg.m_red) m_abAllowColor[1]=TRUE;
else m_abAllowColor[1]=FALSE;
if (dlg.m_purle) m_abAllowColor[2]=TRUE;
else m_abAllowColor[2]=FALSE;
POSITION pos=GetFirstViewPosition();
CView* pview=GetNextView(pos);
ASSERT(pview!=NULL);
CChildFrame* pwnd=(CChildFrame*)pview->GetParentFrame();
ASSERT_KINDOF(CChildFrame,pwnd);
pwnd->RefreshColorMenu();
}
}
（3）创建子框架类的处理函数
多文档框架应用程序CMyMenuChildWnd类提供管理多文档界面的子窗口功能，其中包括修改子框架窗口菜单条，本文定义子框架类的RefreshColorMenu成员函数，更新Color子菜单。处理函数定位Color子菜单中已标出的颜色项，再删除Change options以外的所有选项，根据模式对话框中选择的颜色种类，将其插入框架菜单条的Color子菜单中，RefreshColorMenu成员函数为：
void CChildFrame::RefreshColorMenu()
{
CMyMenuDoc* pDoc=(CMyMenuDoc*)GetActiveDocument();
ASSERT_KINDOF(CMyMenuDoc,pDoc);
CMenu* pColorMenu=NULL;
CMenu* pTopMenu=AfxGetMainWnd()->GetMenu();
int iPos;
for(iPos=pTopMenu->GetMenuItemCount()-1;iPos>=0;iPos--){
CMenu* pMenu=pTopMenu->GetSubMenu(iPos);
if(pMenu && pMenu->GetMenuItemID(0)==ID_COLOR_OPTIONS){
pColorMenu=pMenu;
break;
}
}
ASSERT(pColorMenu!=NULL);
for(iPos=pColorMenu->GetMenuItemCount()-1;iPos>0;iPos--)
pColorMenu->DeleteMenu(iPos,MF_BYPOSITION);
BOOL bNeedSeparator=TRUE;
for(int i=0;i<NUM_TEXTCOLOR;i++){
if(pDoc->m_abAllowColor[i]==TRUE){
if(bNeedSeparator){
pColorMenu->AppendMenu(MF_SEPARATOR);
bNeedSeparator=FALSE;
}
CString strColor;
strColor.LoadString(pDoc->m_aColorDef[i].m_nString);
pColorMenu->AppendMenu(MF_STRING,
pDoc->m_aColorDef[i].m_nID,strColor);
}
}
}
（4）创建视类的成员函数
多文档框架的CMyMenuView类提供文档-视类结构中文档的显示和用户界面操作，通过重定义OnDraw的成员函数，应用程序在子窗口中显示文本字串，OnDraw函数添加代码显示如下：
void CMyMenuView::OnDraw(CDC* pDC)
{
CMyMenuDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
int iColor=pDoc->m_iColor;
COLORREF crf=CMyMenuDoc::m_aColorDef[iColor].m_crf;

pDC->SetTextColor(crf);
pDC->SetBkMode(TRANSPARENT);
CRect rc;
GetClientRect(rc);
pDC->DrawText(_T("This tet is displayed in the current color."),-1,
rc,DT_CENTER|DT_VCENTER|DT_SINGLELINE);
}
多文档框架应用程序既可通过文档类调用子框架类的成员函数，添加菜单的颜色选项，也可通过其成员函数为已选颜色项作出选择标志，并按此颜色改变子窗口文本的显示颜色。
(廖 琪、廖 彤)