這幾天的任務是:在一個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裏面的一些參數),但都是遇到問題後一步步改出來的,如果你有簡單的方法,歡迎留言指教!