從VC提供的MFC類派生圖中我們可以看出視窗的派生關係,所有的視窗類別都是由CWnd派生。所有CWnd的成員函數在其衍生類別中都可以使用。本節介紹一些常用的功能給大家。
改變視窗狀態:
BOOL EnableWindow( BOOL bEnable = TRUE );可以設定視窗的禁止/允許狀態。BOOL IsWindowEnabled( );可以查詢時段的禁止/允許狀態。
BOOL ModifyStyle( DWORD dwRemove, DWORD dwAdd, UINT nFlags = 0 )/BOOL ModifyStyleEx( DWORD dwRemove, DWORD dwAdd, UINT nFlags = 0 );可以修改視窗的風格,而不需要調用SetWindowLong
BOOL IsWindowVisible( ) 可以檢查視窗是否被顯示。
BOOL ShowWindow( int nCmdShow );將改變視窗的顯示狀態,nCmdShow可取如下值:
SW_HIDE 隱藏視窗
SW_MINIMIZE SW_SHOWMAXIMIZED 已最小化的視窗
SW_RESTORE 恢複視窗
SW_SHOW 顯示視窗
SW_SHOWMINIMIZED 已最大化的視窗
改變視窗位置:
void MoveWindow( LPCRECT lpRect, BOOL bRepaint = TRUE );可以移動視窗。
void GetWindowRect( LPRECT lpRect ) ;可以得到視窗的矩形位置。
BOOL IsIconic( ) ;可以檢測視窗是否已經縮為表徵圖。
BOOL SetWindowPos( const CWnd* pWndInsertAfter, int x, int y, int cx, int cy, UINT nFlags );可以改變視窗的Z次序,此外還可以移動視窗位置。
使視窗失效,印發重繪:
void Invalidate( BOOL bErase = TRUE );使整個視窗失效,bErase將決定視窗是否產生重繪。
void InvalidateRect( LPCRECT lpRect, BOOL bErase = TRUE )/void InvalidateRgn( CRgn* pRgn, BOOL bErase = TRUE );將使指定的矩形/多邊形地區失效。
視窗尋找:
static CWnd* PASCAL FindWindow( LPCTSTR lpszClassName, LPCTSTR lpszWindowName );可以以視窗的類名和視窗名尋找視窗。任一參數設定為NULL表對該參數代表的資料進行任意匹配。如FindWindow("MyWnd",NULL)表明尋找類名為MyWnd的所有視窗。
BOOL IsChild( const CWnd* pWnd ) 檢測視窗是否為子視窗。
CWnd* GetParent( ) 得到父視窗指標。
CWnd* GetDlgItem( int nID ) 通過子視窗ID得到視窗指標。
int GetDlgCtrlID( ) 得到視窗ID值。
static CWnd* PASCAL WindowFromPoint( POINT point );將從螢幕上某點座標得到包含該點的視窗指標。
static CWnd* PASCAL FromHandle( HWND hWnd );通過HWND構造一個CWnd*指標,但該指標在空閑時會被刪除,所以不能儲存供以後使用。
時鐘:
UINT SetTimer( UINT nIDEvent, UINT nElapse, void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT, UINT, DWORD) );可以建立一個時鐘,如果lpfnTimer回呼函數為NULL,視窗將會收到WM_TIMER訊息,並可以在afx_msg void OnTimer( UINT nIDEvent );中安排處理代碼
BOOL KillTimer( int nIDEvent );刪除一個指定時鐘。
可以利用重載來添加訊息處理的虛函數:
afx_msg int OnCreate( LPCREATESTRUCT lpCreateStruct );視窗被建立時被調用
afx_msg void OnDestroy( );視窗被銷毀時被調用
afx_msg void OnGetMinMaxInfo( MINMAXINFO FAR* lpMMI );需要得到視窗尺寸時被調用
afx_msg void OnSize( UINT nType, int cx, int cy );視窗改變大小後被調用
afx_msg void OnMove( int x, int y );視窗被移動後時被調用
afx_msg void OnPaint( );視窗需要重繪時時被調用,你可以填如繪圖代碼,對於視圖類不需要重載OnPaint,所有繪圖代碼應該在OnDraw中進行
afx_msg void OnChar( UINT nChar, UINT nRepCnt, UINT nFlags );接收到字元輸入時被調用
afx_msg void OnKeyDown/OnKeyUp( UINT nChar, UINT nRepCnt, UINT nFlags );鍵盤上鍵被按下/放開時被調用
afx_msg void OnLButtonDown/OnRButtonDown( UINT nFlags, CPoint point );滑鼠左/右鍵按下時被調用
afx_msg void OnLButtonUp/OnRButtonUp( UINT nFlags, CPoint point );滑鼠左/右鍵放開時被調用
afx_msg void OnLButtonDblClk/OnRButtonDblClk( UINT nFlags, CPoint point );滑鼠左/右鍵雙擊時被調用
afx_msg void OnMouseMove( UINT nFlags, CPoint point );滑鼠在視窗上移動時被調用
使用資源編輯器編輯對話方塊
在Windows開發中彈出對話方塊是一種常用的輸入/輸出手段,同時編輯好的對話方塊可以儲存在資源檔中。Visual C++提供了對話方塊編輯工具,利用編輯工具可以方便的添加各種控制項到對話方塊中,而且利用ClassWizard可以方便的產生新的對話方塊類和映射訊息。
首先資源清單中按下右鍵,可以在快顯功能表中選擇“插入對話方塊”,然後再開啟該對話方塊進行編輯,你會在螢幕上看到一個控制項板,你可以將所需要添加的控制項拖到對話方塊上,或是先選中後再在對話方塊上用滑鼠畫出所佔的地區。
接下來我們在對話方塊上產生一個輸入框,和一個用於顯示表徵圖的圖片框。之後我們使用滑鼠右鍵單擊產生的控制項並選擇其屬性,我們可以在屬性對話方塊中編輯控制項的屬性同時也需要指定控制項ID,如果在選擇對話方塊本身的屬性那麼你可以選擇對話方塊的一些屬性,包括字型,外觀,是否有系統功能表等等。最後我們編輯圖片控制項的屬性,我們設定控制項的屬性為顯示表徵圖並指明一個表徵圖ID。
接下來我們添加一些其他的控制項,最後的效果按下Ctrl-T可以測試該對話方塊。此外在對話方塊中還有一個有用的特性,就是可以利用Tab鍵讓輸入焦點在各個控制項間移動,要達到這一點首先需要為控制項設定在Tab鍵按下時可以接受焦點移動的屬性Tab Stop,如果某一個控制項不打算利用這一特性,你需要清除這一屬性。然後從菜單“Layout”選擇Tab Order來確定焦點移動順序,使用滑鼠依此點擊控制項就可以重新規定焦點移動次序。最後按下Ctrl-T進行測試。
最後我們需要為對話方塊產生新的類,ClassWizard可以替我們完成大部分的工作,我們只需要填寫幾個參數就可以了。在編輯好的對話方塊上雙擊,然後系統回詢問是否添加新的對話方塊,選擇是並在接下來的對話方塊中輸入類名就可以了。ClassWizard會為你產生所需要的標頭檔和CPP檔案。然後在需要使用的地方包含相應的標頭檔,對於有強制回應對話方塊使用DoModal()產生,對於無強制回應對話方塊使用Create()產生。相關代碼如下;
void CMy51_s1View::OnCreateDlg()
{//產生無強制回應對話方塊
CTestDlg *dlg=new CTestDlg;
dlg->Create(IDD_TEST_DLG);
dlg->ShowWindow(SW_SHOW);
}
void CMy51_s1View::OnDoModal()
{//產生有強制回應對話方塊
CTestDlg dlg;
int iRet=dlg.DoModal();
TRACE("dlg return %d/n",iRet);
}
下載例子。如果你在調試這個程式時你會發現程式在退出後會有記憶體流失,這是因為我沒有釋放無強制回應對話方塊所使用的記憶體,這一問題會在以後的章節5.3 建立無強制回應對話方塊中專門講述。
關於在使用對話方塊時Enter鍵和Escape鍵的處理:在使用對話方塊是你會發現當你按下Enter鍵或Escape鍵都會退出對話方塊,這是因為Enter鍵會引起CDialog::OnOK()的調用,而Escape鍵會引起CDialog::OnCancel()的調用。而這兩個調用都會引起對話方塊的退出。在MFC中這兩個成員函數都是虛擬函數,所以我們需要進行重載,如果我們不希望退出對話方塊那麼我們可以在函數中什麼都不做,如果需要進行檢查則可以添加檢查代碼,然後調用父類的OnOK()或OnCancel()。相關代碼如下;
void CTestDlg::OnOK()
{
AfxMessageBox("你選擇確定");
CDialog::OnOK();
}
void CTestDlg::OnCancel()
{
AfxMessageBox("你選擇取消");
CDialog::OnCancel();
}
建立有強制回應對話方塊
使用有強制回應對話方塊時在對話方塊彈出後調用函數不會立即返回,而是等到對話方塊銷毀後才會返回(請注意在對話方塊彈出後其他視窗的訊息依然會被傳遞)。所以在使用對話方塊時其他視窗都不能接收使用者輸入。建立有強制回應對話方塊的方法是調用CDialogDoModal()。下面的代碼示範了這種用法:
CYourView::OnOpenDlg()
{
CYourDlg dlg;
int iRet=dlg.DoModal();
}
CDialogDoModal()的傳回值為IDOK,IDCANCEL。表明操作者在對話方塊上選擇“確認”或是“取消”。由於在對話方塊銷毀前DoModal不會返回,所以可以使用局部變數來引用對象。在退出函數體後對象同時也會被銷毀。而對於無強制回應對話方塊則不能這樣使用,下節5.3 建立無強制回應對話方塊中會詳細講解。
你需要根據DoModal()的傳回值來決定你下一步的動作,而得到傳回值也是使用有強制回應對話方塊的一個很大原因。
使用有強制回應對話方塊需要注意一些問題,比如說不要在一些反覆出現的事件處理過程中產生有強制回應對話方塊,比如說在定時器中產生有強制回應對話方塊,因為在上一個對話方塊還未退出時,定時器訊息又會引起下一個對話方塊的彈出。
同樣的在你的對話方塊類中為了向調用者返回不同的值可以調用CDialog::OnOK()或是CDialogOnCancel()以返回IDOK或IDCANCEL,如果你希望返回其他的值,你需要調用
CDialogEndDialog( int nResult );其中nResult會作為DoModal()調用的傳回值。
下面的代碼示範了如何使用自己的函數來退出對話方塊:
void CMy52_s1View::OnLButtonDown(UINT nFlags, CPoint point)
{//建立對話方塊並得到傳回值
CView::OnLButtonDown(nFlags, point);
CTestDlg dlg;
int iRet=dlg.DoModal();
CString szOut;
szOut.Format("return value %d",iRet);
AfxMessageBox(szOut);
}
//重載OnOK,OnCancel
void CTestDlg::OnOK()
{//什麼也不做
}
void CTestDlg::OnCancel()
{//什麼也不做
}
//在對話方塊中對三個按鈕訊息進行映射
void CTestDlg::OnExit1()
{
CDialog::OnOK();
}
void CTestDlg::OnExit2()
{
CDialog::OnCancel();
}
void CTestDlg::OnExit3()
{
CDialog::EndDialog(0XFF);
}
由於重載了OnOK和OnCancel所以在對話方塊中按下Enter鍵或Escape鍵時都不會退出,只有按下三個按鈕中的其中一個才會返回。
此外在對話方塊被產生是會自動調用BOOL CDialog::OnInitDialog(),你如果需要在對話方塊顯示前對其中的控制項進行初始化,你需要重載這個函數,並在其中填入相關的初始化代碼。利用ClassWizard可以方便的產生一些預設代碼,首先開啟ClassWizard,選擇相應的對話方塊類,在右邊的訊息列表中選擇WM_INITDIALOG並雙擊,,ClassWizard會自動產生相關代碼,代碼如下:
BOOL CTestDlg::OnInitDialog()
{
/*先調用父類的同名函數*/
CDialog::OnInitDialog();
/*填寫你的初始化代碼*/
return TRUE;
}
有關對對話方塊中控制項進行初始化會在5.4 在對話方塊中進行訊息映射中進行更詳細的講解。
建立無強制回應對話方塊
無強制回應對話方塊與有強制回應對話方塊不同的是在建立後其他視窗都可以繼續接收使用者輸入,因此無強制回應對話方塊有些類似一個快顯視窗。建立無強制回應對話方塊需要調用
BOOL CDialog::Create( UINT nIDTemplate, CWnd* pParentWnd = NULL );之後還需要調用
BOOL CDialog::ShowWindow( SW_SHOW);進行顯示,否則無強制回應對話方塊將是不可見的。相關代碼如下:
void CYourView::OnOpenDlg(void)
{
/*假設IDD_TEST_DLG為已經定義的對話方塊資源的ID號*/
CTestDlg *dlg=new CTestDlg;
dlg->Create(IDD_TEST_DLG,NULL);
dlg->ShowWindows(SW_SHOW);
/*不要調用 delete dlg;*/
}
在上面的代碼中我們新產生了一個對話方塊對象,而且在退出函數時並沒有銷毀該對象。因為如果此時銷毀該對象(對象被銷毀時視窗同時被銷毀),而此時對話方塊還在顯示就會出現錯誤。那麼這就提出了一個問題:什麼時候銷毀該對象。我時常使用的方法有兩個:
在對話方塊退出時銷毀自己:在對話方塊中重載OnOK與OnCancel在函數中調用父類的同名函數,然後調用DestroyWindow()強制銷毀視窗,在對話方塊中映射WM_DESTROY訊息,在訊息處理函數中調用delete this;強行刪除自身對象。相關代碼如下:
void CTestDlg1::OnOK()
{
CDialog::OnOK();
DestroyWindow();
}
void CTestDlg1::OnCancel()
{
CDialog::OnCancel();
DestroyWindow();
}
void CTestDlg1::OnDestroy()
{
CDialog::OnDestroy();
AfxMessageBox("call delete this");
delete this;
}
這種方法的要點是在視窗被銷毀的時候,刪除自身對象。所以你可以在任何時候調用DestroyWindow()以達到徹底銷毀自身對象的作用。(DestroyWindow()的調用會引起OnDestroy()的調用)
通過向父親視窗發送訊息,要求其他視窗對其進行銷毀:首先需要定義一個訊息用於進行通知,然後在對話方塊中映射WM_DESTROY訊息,在訊息處理函數中調用訊息發送函數通知其他視窗。在接收訊息的視窗中利用ON_MESSAGE映射處理訊息的函數,並在訊息處理函數中刪除對話方塊對象。相關代碼如下:
/*更改對話方塊的有關檔案*/
CTestDlg2::CTestDlg2(CWnd* pParent /*=NULL*/)
: CDialog(CTestDlg2::IDD, pParent)
{/*m_pParent為一成員變數,用於儲存通知視窗的指標,
所以該指標不能是一個臨時指標*/
ASSERT(pParent);
m_pParent=pParent;
//{{AFX_DATA_INIT(CTestDlg2)
// NOTE: the ClassWizard will add member
initialization here
//}}AFX_DATA_INIT
}
void CTestDlg2::OnOK()
{
CDialog::OnOK();
DestroyWindow();
}
void CTestDlg2::OnCancel()
{
CDialog::OnCancel();
DestroyWindow();
}
void CTestDlg2::OnDestroy()
{
CDialog::OnDestroy();
/*向其他視窗發送訊息,將自身指標作為一個參數發送*/
m_pParent->PostMessage(WM_DELETE_DLG,
(WPARAM)this);
}
/*在訊息接收視窗中添加訊息映射*/
/*在標頭檔中添加函數定義*/
afx_msg LONG OnDelDlgMsg(WPARAM wP,
LPARAM lP);
/*添加訊息映射代碼*/
ON_MESSAGE(WM_DELETE_DLG,OnDelDlgMsg)
END_MESSAGE_MAP()
/*實現訊息處理函數*/
LONG CMy53_s1View::OnDelDlgMsg(WPARAM wP,LPARAM lP)
{
delete (CTestDlg2*)wP;
return 0;
}
/*建立對話方塊*/
void CMy53_s1View::OnTest2()
{
CTestDlg2 *dlg=new CTestDlg2(this);
dlg->Create(IDD_TEST_DLG_2);
dlg->ShowWindow(SW_SHOW);
}
在這種方法中我們利用訊息來進行通知,在Window系統中利用訊息進行通知和傳遞資料的用法是很多的。
同樣無強制回應對話方塊的另一個作用還可以用來在使用者在對話方塊中的輸入改變時可以及時的反映到其他視窗。下面的代碼示範了在對話方塊中輸入一段文字,然後將其更新到視圖的顯示地區中,這同樣也是利用了訊息進行通知和資料傳遞。
/*在對話方塊中取出資料,並向其他視窗發送訊息和資料,
將資料指標作為一個參數發送*/
void CTestDlg2::OnCommBtn()
{
char szOut[30];
GetDlgItemText(IDC_OUT,szOut,30);
m_pParent->SendMessage(WM_DLG_NOTIFY,
(WPARAM)szOut);
}
/*在訊息接收視窗中*/
/*映射訊息處理函數*/
ON_MESSAGE(WM_DLG_NOTIFY,OnDlgNotifyMsg)
/*在視圖中繪製出字串 m_szOut*/
void CMy53_s1View::OnDraw(CDC* pDC)
{
CMy53_s1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
pDC->TextOut(0,0,"Display String");
pDC->TextOut(0,20,m_szOut);
}
/*處理通知訊息,儲存資訊並更新顯示*/
LONG CMy53_s1View::OnDlgNotifyMsg(WPARAM wP,LPARAM lP)
{
m_szOut=(char*)wP;
Invalidate();
return 0;
}
此外這種用法利用訊息傳遞資料的方法對有強制回應對話方塊和其他的視窗間通訊也一樣有效。
本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/JamesXing/archive/2007/08/03/1724381.aspx