MFC 進行全屏顯示並動態載入選單進行還原
首先說一下全屏的概念:大家應該都有過軟體全屏的使用經歷,無非就是讓其客戶區域顯示在整個螢幕上,原先視窗上的工具欄和狀態列都隱藏了罷了。
下面我只說一下核心程式碼。(注:大家實驗前,應該先建一個單文件工程,並新增一個選單項:全屏:IDR_FULLSCREEN,點擊這個選單項就進行全屏顯示,所以我們應該編寫這個選單項的事件程式碼,當然是再CMainFrame類中)。
1:先說一下我們暫時(後邊還需要其他的)需要的成員變數:因為我要進行全屏和還原工作,所以我們需要兩個變數,一個用來儲存全屏顯示時的資訊,另一個用來儲存還原時的資訊,所以,我們在CMainFrame類中增加個成員變數:m_wpOld,m_wpNew;這兩個變數都是WINDOWPLACEMENT型的。
再就是我們需要設定一個BOOL變數來記錄是否已經全屏顯示了 bool m_FullScreen=false;
下面在選單事件中寫程式碼:
RECT rectDeskTop;這個變數用來記錄整個螢幕的大小資訊。
if(!m_FullScreen) //如果不是全屏,那我們就要進行全屏顯示
{
m_wndStatusBar.ShowWindow(SW_HIDE);
m_wndToolBar.ShowWindow(SW_HIDE); //先隱藏工具欄和狀態列
m_wpPrev.length=sizeof(m_wpOld);
GetWindowPlacement(&m_wpOld); //獲取當前視窗位置資訊 (全屏顯示前的,儲存在m_wpOld中,以便於還原)
::GetWindowRect(::GetDesktopWindow(),&rectDesktop);//得到整個桌面視窗的資訊
AdjustWindowRectEx(&rectDesktop,GetStyle(),TRUE,GetExStyle());//根據傳遞進來的客戶區域大小計算視窗的大小。因為,我們要進行全屏顯示,所以我們要把整個螢幕設定為我們的客戶區域,所以我們把上面獲取到的rectDesktop作為我們的客戶區域大小傳遞進去,函式返回後,rectDesktop裡面就包含了我們進行全屏設定所需要的視窗大小(在這裡要注意:客戶區域和視窗
m_wpNew=m_wpOld;//把m_wpOld的一些資訊(比如length,ptMinPosition,ptMaxPosition)來填充m_wpNew,(實際上在這裡沒什麼用,因為m_wpNew需要的引數,在下邊會設定)
m_wpNew.showCmd=SW_NORMAL/;
m_wpNew.rcNormalPosition=rectDesktop;//設定全屏顯示時的視窗的正常顯示的大小,對應上面的SW_NORMAL。上面為什麼要設定wpNew.showCmd=SW_NORMAL/;呢?從GetWindowPlacement(&m_wpOld); 獲取到的m_wpOld的showCmd引數實際上已經是SW_NORMAL了,執行這條語句m_wpNew=m_wpOld後,wpNew.showCmd就已經等於SW_NORMAL了,為什麼我們這裡還要設定一下呢,(1)因為當視窗剛創建出來的時候,當我們直接點選選單項進行全屏時,GetWindowPlacement(&m_wpOld); 這個獲取的m_wpOld的引數showCmd確實是SW_NORMAL,而當m_wpNew=m_wpOld後,m_wpNew的引數showCmd也是SW_NORMAL,因此,當進行全屏設定時(下面的SetWindowPlacement(&wpNew);)設定的確實是在m_wpNew.rcNormalPosition=rectDesktop 中設定的視窗的正常顯示的大小。。(2)而當視窗創建出來的時候,這個時候如果我們點選最大化按鈕,這個時候再點選全屏選單項,GetWindowPlacement(&m_wpOld); 這個獲取的m_wpOld的引數showCmd就不是SW_NORMAL了,而是SW_MAXIMIZE,這個時候當m_wpNew=m_wpOld後,m_wpNew的引數showCmd也是SW_MAXIMIZE,因此,當進行全屏設定時(下面的SetWindowPlacement(&wpNew);)設定的就不是在m_wpNew.rcNormalPosition=rectDesktop 中設定的視窗的正常顯示的大小(即:不是我們想要的全屏效果),而是原先你的視窗最大化時的大小,只不過是狀態列和工具欄沒有了。。(大家可以試一下,以加深理解)..(3)所以,為了視窗在任何狀態下都能有我們想要的全屏顯示效果,在這裡要進行wpNew.showCmd=SW_NORMAL/;設定,(而當進行還原的時候我們就不必重新設定了,因為還原的就是全屏之前的大小,即:(如果之前是最大化顯示,那還原之後還是最大化))
m_FullScreen=TRUE;//全屏完了以後要設定狀態
}
else //如果是全屏狀態下,即進行還原操作
{
m_wndStatusBar.ShowWindow(SW_NORMAL);
m_wndToolBar.ShowWindow(SW_NORMAL);//還原後要把之前隱藏的工具欄選單欄給顯示出來
m_wpNew=m_wpOld; //還原之後的視窗就是之前儲存的那個
m_FullScreen=0;、//設定狀態
}
SetWindowPlacement(&m_wpNew);//根據要全屏或要還原的視窗大小進行設定。。
到這裡實際上,全屏效果已經顯示出來了,但是,我們要再全屏後進行還原,還需要動態載入一個下拉選單,當需要還原的時候,我們只要按下一個鍵,動態選單就顯示出來了,然後點選進行還原操作。
所以呢,我們還需要一個成員變數CMenu m_menu.來進行動態載入選單。。這裡假設當我們按下ALT+S組合鍵的時候選單就在全屏中顯示出來。。因為要ALT鍵的資訊,普通的WM_KEYDOWN捕獲不到,這個ALT的訊息是WM_SYSKEYDOWN,所以我們要再CMainFrame類中重寫BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) 函式,在PreTranslateMessage(MSG* pMsg)裡面進行捕獲。下面是動態載入彈出選單程式碼:
BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class
if(m_bFullScreen && pMsg->message==WM_SYSKEYDOWN)//只有在全屏模式下按ALT+S組合鍵才彈出選單。 然後再檢測是否為WM_SYSKEYDOWN訊息,即是否按下了ALT鍵
{
if(pMsg->wParam=='S')//這裡檢測當按下了ALT鍵後 是否又按下了“S”鍵。
{
m_menu.CreatePopupMenu(); //建立彈出選單
m_menu.AppendMenu(0,IDR_FULLSCREEN,"還原");//追加一個選單項,這個選單項就是我們之前設定的全屏:IDR_FULLSCREEN選單,因為我們的還原始碼也寫到這個事件中去了,大家可以修改。
CPoint Pos;
Pos.x=0;
Pos.y=0;//設定選單彈出時的位置,假設我們要它在左上角彈出
m_menu.TrackPopupMenu(TPM_RIGHTBUTTON,Pos.x,Pos.y,this);//在指定位置彈出選單
m_menu.DestroyMenu();//設定完後銷燬選單
}
}
return CFrameWnd::PreTranslateMessage(pMsg);//MFC生成的程式碼,不礙咱事
}
OK,大功告成了。。。。。
怎麼樣,實驗了嗎?是不是出問題了啊?哈哈。。。全屏是不是全的不徹底啊,螢幕底邊還保留了桌面的狀態列。。沒關係,我們來解決這個問題,解決之前,首先,讓我們來看一個WINDOWS訊息WM_GETMINMAXINFO。。(拷貝一段說明)在Windows中,無論什麼時候以何種方式改變視窗的尺寸或大小,是拖拽視窗邊緣也好,或是在程式碼中呼叫改變視窗尺寸的函式也好,總之不管你用什麼方法,Windows都會首先發送WM_GETMINMAXINFO訊息。這個訊息的意思是說:“嘿,如果你要強迫我的尺寸變大或變小,就附上詳細的MINMAXINFO結構資訊,否則我用預設值處理。”大多數應用程式都不用顯式處理這個 WM_GETMINMAXINFO訊息(也就是說讓DefWindowProc視窗過程進行預設處理)(那我們什麼時候需要自己處理呢?答案是:當我們要限制一個視窗的最大尺寸或最小尺寸時就需要自己處理),而Windows在進行預設處理時是不會讓一個視窗檢視比螢幕還大的,所以我們會碰上前面那個問題。解決的方法是:不要讓Windows對WM_GETMINMAXINFO訊息進行預設處理,而是由我們自己處理,方法如下:新增訊息處理事件:
void CMainFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
// TODO: Add your message handler code here and/or call default
if(m_FullScreen)//我們只在全屏的時候才處理這個訊息,還原的時候不不要處理
{
//下面的m_FullScreenWindowRect是什麼在後邊介紹
lpMMI->ptMaxTrackSize.y=m_FullScreenWindowRect.Height();//設定視窗能用滑鼠拖拽的最大尺寸y
lpMMI->ptMaxTrackSize.x=m_FullScreenWindowRect.Width();//設定視窗能用滑鼠拖拽的最大尺寸x
}
CFrameWnd::OnGetMinMaxInfo(lpMMI);
}
MINMAXINFO的結構體:
typedef struct {POINT ptReserved; //不用,系統保留
POINT ptMaxSize; //視窗最大化顯示時的大小
POINT ptMaxPosition; //視窗最大化時的放置點
POINT ptMinTrackSize; //當用滑鼠拖動改變視窗大小時的最小拖動範圍,也就是說你用滑鼠拖動時,它會實時地跟著你的滑鼠改變視窗大小,當達到一定值之後,你再拖它,它也不會變小,下面的同理
POINT ptMaxTrackSize; //當用滑鼠拖動改變視窗大小時的最大拖動範圍,(我們要設定的就是這個引數)
} MINMAXINFO;
既然有了上面的分析,那麼我們應該再再CMainFrame類中新增一個成員變數m_FullScreenWindowRect,儲存全屏時的RECT:CRect m_FullScreenWindowRect ;(注意這裡的型別為MFC封裝類:CRect,而不是RECT)
新增完以後就要為它賦值,在哪裡賦?賦什麼值呢?對,就在當初次獲取到要全屏顯示的視窗的大小時那裡: AdjustWindowRectEx(&rectDesktop,GetStyle(),TRUE,GetExStyle());//就在這後面賦值:
m_FullScreenWindowRect=rectDesktop;
做完這項工作以後,再把上面WM_GETMINMAXINFO訊息的處理程式碼加上!
OK,這回是真的大功告成啦。。。