这几天的任务是:在一个MDI应用程序中打开多个view(子窗口),要求通过菜单实现子窗口的水平、垂直和重叠排列,在实现之前,先看一下效果图吧。
这个功能在VC++6.0中可以通过添加菜单项后将在菜单项的ID设置为指定ID,即可由系统自动实现这些功能,不需要添加事件处理函数(ID_WINDOW_CASCADE , ID_WINDOW_HORZ , ID_WINDOW_VERT),但是在VS2010中按照添加缺省ID的方法却无法实现这些功能。前几天一直在想怎么让系统自动实现这些功能,或者调用现成的API函数实现,但是都没研究出想要的效果,昨天又看了一下VC++6.0的实现效果,才想出一个简单的实现思路,其实水平排列和垂直排列也就是在重叠排列的基础上,重新设定一下窗口的大小和位置而已,我们可以用MoveWindow来实现这个功能,而由默认的排列方式变成重叠的排列方式,需要用到EnableMDITabbedGroups,将第一个参数设为FALSE即可,而从重叠的排列方式转换为默认的排列方式时,将第一个参数设置为TRUE就行。
另一个问题就是,在重叠的排列方式下如何遍历所有的子窗口,逐个设置各个子窗口的位置,这里主要用到连个函数,MDIGetactive和MDINext,获取活动子窗口,然后依次获取其他窗口,具体看下面的代码吧。
void CMainFrame::OnWindowTileHorz()
{
ArrangeChildwindow(TRUE);
}
void CMainFrame::OnWindowCascade()
{
// TODO: Add your command handler code here
//先将窗口转换为重叠排列
CMDITabInfo params;
EnableMDITabbedGroups(FALSE,params);
//获取已打开view窗口个数
int count = GetOpenedViewCount();
CRect rectClient;
GetClientRect(&rectClient);
if (count)
{
int temp = 0;
CMDIFrameWnd* pFrame = (CMDIFrameWnd*)AfxGetMainWnd();
if(pFrame)
{
CMDIChildWnd* pOldChildFrame = pFrame->MDIGetActive();
CMDIChildWnd* pChildFrame = pOldChildFrame;
if(pChildFrame)
{
do
{
MDIActivate(pChildFrame);
::MoveWindow(pChildFrame->GetSafeHwnd(),rectClient.left +(count -temp-1)*rectClient.Width()*0.05,rectClient.top+(count -temp-1)*rectClient.Height()*0.05,rectClient.Width()*0.7,rectClient.Height()*0.7,TRUE);
pFrame->MDINext();
pChildFrame = pFrame->MDIGetActive();
temp++;
}
while (pChildFrame != pOldChildFrame);
}
}
}
}
void CMainFrame::OnWindowTileVer()
{
// TODO: Add your command handler code here
ArrangeChildwindow(FALSE);
}
//“默认排列”菜单项的响应函数
void CMainFrame::OnWindowRecoverry()
{
// TODO: Add your command handler code here
CMDITabInfo mdiTabParams;
mdiTabParams.m_style = CMFCTabCtrl::STYLE_3D_ONENOTE;
// set to FALSE to place close button at right of tab area
mdiTabParams.m_bActiveTabCloseButton = /*FALSE*/TRUE;
// set to TRUE to enable document icons on MDI taba
mdiTabParams.m_bTabIcons = /*TRUE*/FALSE;
// set to FALSE to disable auto-coloring of MDI tabs
mdiTabParams.m_bAutoColor = TRUE;
// set to TRUE to enable the document menu at the right edge of the tab area
mdiTabParams.m_bDocumentMenu = TRUE;
//set to TRUE to enable the user to change the tabs positions by dragging the tabs
mdiTabParams.m_bEnableTabSwap = TRUE;
// set to TRUE to give each tab window has a flat frame
mdiTabParams.m_bFlatFrame = TRUE;
// set to TRUE to enable each tab window to display the Close button on the right edge of the tab.
mdiTabParams.m_bTabCloseButton = FALSE;
// set to TRUE to enable the tabs to display tooltips.
mdiTabParams.m_bTabCustomTooltips = TRUE;
// Specifies that the tabs labels are located at the top of the page
mdiTabParams.m_tabLocation = CMFCTabCtrl::LOCATION_TOP;
EnableMDITabbedGroups(TRUE, mdiTabParams);
}
void CMainFrame::ArrangeChildwindow(BOOL IsHoriz)
{
//先将窗口转换为重叠排列
CMDITabInfo params;
EnableMDITabbedGroups(FALSE,params);
//获取已打开view窗口个数
int count = GetOpenedViewCount();
CRect rectClient;
GetClientRect(&rectClient);
if (count)
{
int temp = 0;
CMDIFrameWnd* pFrame = (CMDIFrameWnd*)AfxGetMainWnd();
if(pFrame)
{
CMDIChildWnd* pOldChildFrame = pFrame->MDIGetActive();
CMDIChildWnd* pChildFrame = pOldChildFrame;
if(pChildFrame)
{
do
{
if (!IsHoriz)//窗口垂直排列
{
::MoveWindow(pChildFrame->GetSafeHwnd(),rectClient.left + temp * rectClient.Width()/count,rectClient.top,rectClient.Width()/count,rectClient.Height(),TRUE);
}
else//窗口水平排列
{
::MoveWindow(pChildFrame->GetSafeHwnd(),rectClient.left,rectClient.top + temp * rectClient.Height()/count,rectClient.Width(),rectClient.Height()/count,TRUE);
}
pFrame->MDINext();
pChildFrame = pFrame->MDIGetActive();
temp++;
}
while (pChildFrame != pOldChildFrame);
}
}
}
}
int CMainFrame::GetOpenedViewCount(void)
{
//计算子窗口个数
int count = 0;
CMDIFrameWnd* pFrame = (CMDIFrameWnd*)AfxGetMainWnd();
if(pFrame)
{
CMDIChildWnd* pOldChildFrame = pFrame->MDIGetActive();
CMDIChildWnd* pChildFrame = pOldChildFrame;
if(pChildFrame)
{
do
{
pFrame->MDINext();
pChildFrame = pFrame->MDIGetActive();
count++;
}
while (pChildFrame != pOldChildFrame);
}
}
return count;
}
到这里,已经可以实现上面的功能了。
另外,在我们项目中,主框架客户区左右两边各有一个面板,是的我用上面的方法获取的rect总是不对,后来这么改了下就行了:
CMDIChildWnd *pWndChild = ((CMainFrame*)AfxGetMainWnd())->MDIGetActive();
MDIMaximize(pWndChild);
pWndChild->GetClientRect(&m_rectClient);
MDIRestore(pWndChild);
(先将子窗口最大化,获取rect之后再还原回来)代码里面可能有的地方写得有些古怪(比如MoveWindow里面的一些参数),但都是遇到问题后一步步改出来的,如果你有简单的方法,欢迎留言指教!