1.MFC架構結構
vc程式啟動過程: 第一步:建構CMyApp對象 第二步:調用WinMain,WinMain找到CMyApp對象, 第三步:執行CMyApp對象的的成員函數InitInstance,初始化架構Frame 第四步:執行Run
vc程式退出過程: 第一步:刪除CMyFrame對象 第二步:退出Run 第三步:退出WinMain 第四步:刪除CMyApp對象
MFC庫消息映射: 第一步:在.h檔案中聲明函數 afx_msg void OnLButtonDown(UINT nFlags,CPoint point); 第二步:在.cpp檔案中BEGIN_MESSAGE_MAP與END_MESSAGE_MAP之間聲明消息 ON_WM_LBUTTONDOWN() 第三步:在.cpp檔案中實現函數 void CMyFrame::OnLButtonDown(UINT nFlags,CPoint point) { // } 示例1: 新增一專案,選擇Single Document,其它都為默認,然後修改OnDraw函數如下: void CChap3View::OnDraw(CDC* pDC) { CChap3Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc);
//加上下面這句代碼,第一個程式就完成了. pDC->TextOutW(30,30,_T("Hello World!")); }
2.GDI GDI是一個圖形設備接口,它使得開發圖形應用程式時不用考慮顯卡的類型是CGA,EGA,還是VGA,SVGA等等.對于具體的設備隻需再開發相應的設備驅動程式就可以了.
設備描述表分為四種類型:顯示器型,列印機型,內存型及資訊型. 應用程式不能直接訪問設備描述表,但可以通過函數調用檢取設備描述表的句柄,從而間接地訪問設備描述表.
2.1 GDI常用函數: (1). CDC* CWnd::GetDC() 用于獲取指定視窗的工作區的顯示器設備描述表. 對于公共設備描述表,GetDC()為每一次被檢取的設備描述表指定預設屬性. 對于私有設備場境,GetDC()保持它先前所具有的屬性不變.
當類在注冊時,如果指定有CS_CLASSDC,CS_OWNDC,CS_PARENTDC等風格,則GetDC()傳回屬于CWnd類的設備場境.
CWnd::GetWindowDC()用于獲取整個視窗的設備描述表, GetSystemMetrics()用于檢取視窗非客戶區域中各元件的幾何尺寸.
(2).int ReleaseDC(CDC *pDC); 用于釋放一個設備描述表. 除非設備描述表屬于視窗類,否則在完成繪圖後,都要調用Release釋放.
(3).CDC* CWnd::BeginPaint(LPPAINTSTRUCT lpPaint); 用于為在指定視窗中畫圖作準備工作.并將畫圖的有關資訊寫到PAINTSTRUCT結構中.
(4).BOOL CWnd::EndPaint(CONST PAINTSTRUCT* lpPaint); 用于結束指定視窗中的畫圖過程,傳回值總是true
(5).SelectObject() 用于把一個對象選擇進設備描述表. CPen* CDC::SelectObject(CPen* pPen); CBrush* CDC::SelectObject(CBrush* pBrush); virtual CFont* CDC::SelectObject(CFont* pFont); CBitmap* CDC::SelectObject(CBitmap* pBitmap); int CDC::SelectObject(CRgn* pRgn);
(6).BOOL CDC::DeleteObject(HGDIOBJ hObject); 用于刪除邏輯畫筆,畫刷,字型,位圖,區域或調色闆的句柄.
2.2 GDI對象: (1).畫筆 庫存畫筆:BLACK_PEN,WHITE_PEN,NULL_PEN 可使用GetStockObject()來取出庫存畫筆
創建畫筆 BOOL CPen::CreatePen(int nPenStyle,int nWidth,COLORREF crColor) BOOL CPen::CreatePenIndirect(LPLOGPEN lpLogPen)
(2).畫刷 virtual CGdiObject* CDC::SelectStockObject(int nIndex); 用于選擇畫庫存畫刷 如:pDC->SelectStockObject(DKGRAY_BRUSH)
創建畫刷: BOOL CBrush::CreateSolidBrush(COLORREF crColor); BOOL CBrush::CreateHatchBrush(int nIndex,COLORREF crColor); BOOL CBrush::CreatePatternBrush(CBitmap* pBitmap); BOOL CBrush::CreateBrushIndirect(const LOGBRUSH* lpLogBrush);
2.3 繪制基本圖形 (1)畫點 COLORREF CDC::SetPixel(int X,int Y,COLORREF crColor);
(2)畫直線 BOOL CDC::LineTo(int nXEnd,int nYEnd); 用于從當前位置畫線到指定坐標點. BOOL CDC::MoveTo(intX,int Y); 用于移動當前位置
(3)畫折線 BOOL CDC::Polyline(CONST POINT* lppt,int cPoints); 用于畫一條折線 BOOL CDC::PolyPolyline(CONST POINT* lppt,CONST DWORD* lpPolyPoints,DWORD cCount); 用于畫多條折線
(4)畫弧線
示例1: 新增一專案,,選擇Single Document,其它都為默認,然後修改OnDraw函數如下: void CChap4View::OnDraw(CDC* pDC) { CChap4Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return;
int i=0; // //繪制一組彩色點 // pDC->TextOutW(20,20,_T("Point")); pDC->SetPixel(100,20,RGB(255,0,0)); pDC->SetPixel(110,20,RGB(0,255,0)); pDC->SetPixel(120,20,RGB(0,0,255)); pDC->SetPixel(130,20,RGB(255,255,0)); pDC->SetPixel(140,20,RGB(255,0,255)); pDC->SetPixel(150,20,RGB(0,255,255)); pDC->SetPixel(160,20,RGB(0,0,0));
// //繪制一組彩色線 // //定義顏色表 struct tagColor { int r,g,b; } color[7]= { {255,0,0}, {0,255,0}, {0,0,255}, {255,255,0}, {255,0,255}, {0,255,255}, {0,0,0}, }; //定義兩個指向畫筆的指針 CPen* pNewPen; CPen* pOldPen; for(i=0;i<7;i++) { pNewPen=new CPen; if(pNewPen->CreatePen(PS_SOLID,2,RGB(color[i].r,color[i].g,color[i].b))) { //把新畫筆選入設備描述表 //同時存儲設備描述表中的原有畫筆 pOldPen=pDC->SelectObject(pNewPen); //用新畫筆繪圖 pDC->MoveTo(20,60+i*10); pDC->LineTo(160,60-i*10); //恢複設備認描述表中的原有畫筆 pDC->SelectObject(pOldPen); } else { //出錯時給出提示 AfxMessageBox(_T("CreatePen Error!")); } //刪除新創建的畫筆,以便再另外配置設定 delete pNewPen;
} // //用系統預設筆繪制折線和曲線 // //畫一條折線 POINT polyline[4]={{20,190},{70,190},{20,240},{70,240}}; pDC->Polyline(polyline,4);
//畫多條折線,以下畫4條折線 POINT polypolyline[9]={{95,160},{120,185},{120,250},{145,160},{120,185}, {90,185},{150,185},{80,210},{160,210}}; DWORD dwpolypoints[4]={3,2,2,2}; pDC->PolyPolyline(polypolyline,dwpolypoints,4);
//繪制貝齊爾曲線(Bezier) POINT polyBezier[4]={{20,310},{70,250},{110,290},{160,330}}; pDC->PolyBezier(polyBezier,4); // //繪制圓,圓弧和橢圓 // //畫圓圈線 for(i=0;i<8;i++) { //以260,70為圓心 pDC->Arc(260-5*i,70-5*i,260+5*i,70+5*i,260+5*i,70,260+5*i,70); } //畫圓弧線 for(i=4;i<8;i++) { pDC->Arc(260-10*i,70-10*i,260+10*i,70+10*i, (int)260+10*i*cos(60*3.1415926/180), (int)70+10*i*sin(60*3.1415926/180), (int)260+10*i*cos(60*3.1415926/180), (int)70-10*i*sin(60*3.1415926/180));
pDC->Arc(260-10*i,70-10*i,260+10*i,70+10*i, (int)260-10*i*cos(60*3.1415926/180), (int)70-10*i*sin(60*3.1415926/180), (int)260-10*i*cos(60*3.1415926/180), (int)70+10*i*sin(60*3.1415926/180));
} //繪橢圖面 CBrush* pNewBrush; CBrush* pOldBrush; for(i=8;i>1;i--) { pNewBrush=new CBrush;//構造畫刷,然後初始化 if(pNewBrush->CreateSolidBrush(RGB(color[8-i].r,color[8-i].g,color[8-i].b))) { //在設備描述表中選擇新創建的畫刷 //同時存儲設備描述表中原有畫刷 pOldBrush =pDC->SelectObject(pNewBrush); //用畫刷繪橢圖 pDC->Ellipse(260-10*i,200-5*i,260+10*i,200+5*i); //恢複設備描述表的原有畫刷 pDC->SelectObject(pOldBrush); } delete pNewBrush; } //繪矩形和圓角矩形 pDC->Rectangle(190,270,250,310); pDC->RoundRect(260,270,330,310,30,20);
//繪制弧形 pDC->Chord(360-80,70-80,360+80,70+80, (int)360+80*cos(60*3.1415926/180), (int)70+80*sin(60*3.1415926/180), (int)360+80*cos(30*3.1415926/180), (int)70-80*sin(30*3.1415926/180)); //繪扇形 pDC->Pie(480-80,70-80,480+80,70+80, (int)480+80*cos(60*3.1415926/180), (int)70+80*sin(60*3.1415926/180), (int)480+80*cos(30*3.1415926/180), (int)70-80*sin(30*3.1415926/180)); //繪三角形多邊形 POINT polygon[3]={{400,160},{430,220},{350,210}}; pDC->Polygon(polygon,3);
//畫任意多邊形 int polygonpoints[2]={3,3}; POINT polypolygon[6]={{450,120},{540,320},{480,280},{403,300},{540,320},{480,280}}; pDC->PolyPolygon(polypolygon,polygonpoints,2);
}
3.鼠標 若想知道某個鍵被按下,可用對應有位屏蔽值與nFlags參數作按位邏輯"與"動算,所得結果若為非零,則表示鍵被按下. 如: if(nFlags & MK_LBUTTON) { AfxMessageBox("LButton is pressed down!"); } else { AfxMessageBox("LButton up!"); } 鼠標消息的處理 通過重載以下消息映射函數來完成. OnLButtonDown(UINT nFlags,CPoint point) { //檢測Ctrol和Shift是否按下 if((wParam& VK_CONTROL) && (wParam & VK_SHIFT)) break; };
OnMouseMove(UINT nFlags,CPoint point) { }
CWnd::SetCapture(); 用于捕捉鼠標 CWnd::ReleaseCapture; 用于釋放鼠標
設定光標: 當光標未被捕捉時從一個視窗移到另一個視窗,將向視窗發送WM_SETCURSOR消息 protected afx_msg BOOL OnSetCursor(CWnd* pWind,UINT nHitTest,UINT message);
例: BOOL CChap5BView::OnSetCursor(CWnd* pWnd,UINT nHitTest,UINt message) { SetCursor(AfxGetApp()->LoadCursor(IDC_CURSOR1)); return TRUE; } 示例1: 一個用鼠標塗鴉的程式. 第一步:在.h檔案中聲明變量及函數 protected: bool m_bDraw;//是否繪制 public: afx_msg void OnLButtonDown(unsigned int nFlags, CPoint point); afx_msg void OnLButtonUp(unsigned int nFlags, CPoint point); afx_msg void OnMouseMove(unsigned int nFlags, CPoint point); 第二步:在.cpp檔案中BEGIN_MESSAGE_MAP與END_MESSAGE_MAP之間添加消息 BEGIN_MESSAGE_MAP(CChap5AView, CView) ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_MOUSEMOVE() END_MESSAGE_MAP()
第三步:在.cpp檔案中實現如下函數 CChap5AView::CChap5AView() { //在構造函數中初始化成員變量 m_bDraw=false; } void CChap5AView::OnLButtonDown(unsigned int nFlags, CPoint point) { //左鍵按下時,捕捉鼠標 SetCapture(); m_bDraw=true; }
void CChap5AView::OnLButtonUp(unsigned int nFlags, CPoint point) { //左鍵彈起時釋放鼠標 ReleaseCapture(); m_bDraw=false; }
void CChap5AView::OnMouseMove(unsigned int nFlags, CPoint point) { if(m_bDraw) { //取得設備描述表 CDC* pDC=GetDC(); pDC->SetPixel(point,RGB(0,0,0)); } } 示例2:改變光標形狀的程式 第一步:在.h檔案中聲明成員變量及成員函數 protected: CPoint m_prePoint; public: BOOL OnSetCursor(CWnd* pWnd,UINT nHitTest,UINT message); void OnLButtonUp(UINT nFlags,CPoint point); void OnMouseMove(UINT nFlags,CPoint point); 第二步:在.h檔案中聲明消息 BEGIN_MESSAGE_MAP(CChap5BView, CView) ON_WM_SETCURSOR() ON_WM_LBUTTONUP() ON_WM_MOUSEMOVE() END_MESSAGE_MAP()
第三步:在.cpp檔案中實現成員函數 CChap5BView::CChap5BView() { // TODO: 在此加入建構程式碼 m_prePoint.x=m_prePoint.y=-2;
}
BOOL CChap5BView::OnSetCursor(CWnd* pWnd,UINT nHitTest,UINT message) { //需先新增一個空白的光標資料IDC_CURSOR1 SetCursor(AfxGetApp()->LoadCursorW(IDC_CURSOR1)); return true; }
void CChap5BView::OnMouseMove(UINT nFlags,CPoint point) { CDC* pDC=GetDC(); //取當前客戶區大小 CRect rect; GetClientRect(rect); //設置繪圖方式為R2_NOTXORPEN int nDrawMode=pDC->SetROP2(R2_NOTXORPEN); //覆蓋先前的光標線 pDC->MoveTo(rect.left,m_prePoint.y); pDC->LineTo(rect.right,m_prePoint.y); pDC->MoveTo(m_prePoint.x,rect.top); pDC->LineTo(m_prePoint.x,rect.bottom); //繪制新的光標線 pDC->MoveTo(rect.left,point.y); pDC->LineTo(rect.right,point.y); pDC->MoveTo(point.x,rect.top); pDC->LineTo(point.x,rect.bottom); //設置先前的繪圖方式 pDC->SetROP2(nDrawMode); //記錄當前點,以作為下次進入MouseMove的前一個點 m_prePoint=point; } //點鼠標左鍵彈出一個對話框 void CChap5BView::OnLButtonUp(UINT nFlags,CPoint point) { //一個對話框,上面加了很多個按鈕, //當鼠標放在按鈕上時,顯示不同的光標形狀. CCursorDlg dlg; dlg.DoModal(); } 附:CCursorDlg的實現 public: BOOL OnSetCursor(CWnd* pWnd,UINT nHitTest,UINT message);
BEGIN_MESSAGE_MAP(CCursorDlg, CDialog) ON_WM_SETCURSOR() END_MESSAGE_MAP()
BOOL CCursorDlg::OnSetCursor(CWnd* pWnd,UINT nHitTest,UINT message) { //const char * nID; //LPWSTR nID; WCHAR * nID; switch(pWnd->GetDlgCtrlID()) { case IDC_BTN_ARROW: nID=IDC_ARROW;break; case IDC_BTN_IBEAM: nID=IDC_IBEAM;break; case IDC_BTN_WAIT: nID=IDC_WAIT;break; case IDC_BTN_CROSS: nID=IDC_CROSS;break; case IDC_BTN_UPARROW: nID=IDC_UPARROW;break; case IDC_BTN_SIZE: nID=IDC_SIZE;break; case IDC_BTN_SIZEALL: nID=IDC_SIZEALL;break; case IDC_BTN_ICON: nID=IDC_ICON;break; case IDC_BTN_SIZENWSE: nID=IDC_SIZENWSE;break; case IDC_BTN_SIZEWE: nID=IDC_SIZEWE;break; case IDC_HELP: nID=IDC_HELP;break; case IDC_BTN_NO: nID=IDC_NO;break; default: return CDialog::OnSetCursor(pWnd,nHitTest,message); } SetCursor(AfxGetApp()->LoadStandardCursor(nID)); return true; }
4.鍵盤與計時器 系統鍵經常由輸入鍵與Alt鍵組合產生,這種鍵一般由Windows系統內部直接處理,如果應用程式處理了這種擊鍵消息,則在處理完後,還應調用DefWindowProc(),以便不影響Windows對它們的處理.
可通過以下虛函數處理鍵盤消息 OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags); OnKeyUp(UINT nChar,UINT nRepCnt,UINT nFlags); OnSysKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags); OnSysKeyUp(UINT nChar,UINT nRepCnt,UINT nFlags);
字元消息 當按鍵產生可見的字元時,Windows在發送鍵 盤消息的同時還發送字元消息,而有些鍵隻產生鍵盤消息而不產生字元消息,如功能鍵,Shift鍵,Insert鍵,Delete鍵等. 字元消息通過以下函數來映射: OnChar(UINT nChar,UINT nRepCnt,UINT nFlags); OnDeadChar(UINT nChar,UINT nRepCnt,UINT nFlags); OnSysChar(UINT nChar,UINT nRepCnt,UINT nFlags); OnSysDeadChar(UINT nChar,UINT nRepCnt,UINT nFlags); 其中,WM_CHAR和WM_DEADCHAR是由WM_KEYDOWN消息引出的.而WM_SYSCHAR和WM_SYSDEADCHAR是由WM_SYSKEYDOWN消息引出的.
示例1: 一個鼠標點在哪,光標就移到哪,并且光標不斷閃爍,在 第一步:在.h檔案中聲蝗成員變量及成員函數 protected: CPoint m_Position; bool m_bCursorOn; int m_nCount;
void MoveCursor(CPoint point); afx_msg void OnLButtonUp(UINT nFlags,CPoint point); afx_msg void OnChar(UINT nChar,UINT nRepCnt,UINT nFlags); afx_msg void OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags);
afx_msg void OnTimer(UINT nIDEvent); BOOL Create(LPCTSTR lpszClassName,LPCTSTR lpszWindowName, DWORD dwStyle,const RECT& rect, CWnd* pParentWnd,UINT nID, CCreateContext* pContext); BOOL DestroyWindow();
第二步:
BEGIN_MESSAGE_MAP(CChap5CView, CView) ON_WM_LBUTTONUP() ON_WM_CHAR() ON_WM_KEYDOWN()
ON_WM_TIMER() END_MESSAGE_MAP()
第三步: CChap5CView::CChap5CView() : m_Position(0) { // m_Position.x=40; m_Position.y=40; m_nCount=0; m_bCursorOn=true;
}
void CChap5CView::OnDraw(CDC* pDC) { CChap5CDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return;
//設置R2_NOTXORPEN繪圖方式 int nDrawMode=pDC->SetROP2(R2_NOTXORPEN); //繪制輸入光標 pDC->MoveTo(m_Position.x,m_Position.y); pDC->LineTo(m_Position.x,m_Position.y+16); //恢複先前的繪圖方式 pDC->SetROP2(nDrawMode); }
void CChap5CView::OnLButtonUp(UINT nFlags,CPoint point) { //設置文本輸入位置 MoveCursor(point); } void CChap5CView::OnChar(UINT nChar,UINT nRepCnt,UINT nFlags) { //將數字轉化為字元串 //char tbuf[20]; //sprintf(tbuf,"%c",nChar);
//一定要顯式轉換 CString tbuf; tbuf+=(char)nChar;
CDC* pDC=GetDC(); pDC->TextOutW(m_Position.x+1,m_Position.y,tbuf); CSize size=pDC->GetTextExtent(tbuf);//取輸出字元串的大小範圍 CPoint point=m_Position+CPoint(size.cx+1,0); MoveCursor(point);
} void CChap5CView::OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags) { CPoint point; switch(nChar) { case VK_LEFT://向左 point=m_Position+CPoint(-20*nRepCnt,0); MoveCursor(point); break; case VK_RIGHT://向右 point=m_Position+CPoint(20*nRepCnt,0); MoveCursor(point); break; case VK_UP://向上 point=m_Position+CPoint(0,-20*nRepCnt); MoveCursor(point); break; case VK_DOWN://向下 point=m_Position+CPoint(0,20*nRepCnt); MoveCursor(point); break; default: break;
} } void CChap5CView::MoveCursor(CPoint point) {
CDC* pDC=GetDC();
//設置R2_NOTXORPEN繪圖方式 int nDrawMode=pDC->SetROP2(R2_NOTXORPEN); if(m_bCursorOn) { //覆蓋先前的光標線 pDC->MoveTo(m_Position.x,m_Position.y); pDC->LineTo(m_Position.x,m_Position.y+16); } else { m_bCursorOn=true; } //繪制新的光標線 m_Position =point; pDC->MoveTo(m_Position.x,m_Position.y); pDC->LineTo(m_Position.x,m_Position.y+16); //恢複先前的繪圖方式 pDC->SetROP2(nDrawMode);
} void CChap5CView::OnTimer(UINT nIDEvent) { CDC* pDC=GetDC(); switch(nIDEvent) { case 1://標識為1的計時器 { m_bCursorOn=!m_bCursorOn; //設置R2_NOTXORPEN繪圖方式 int nDrawMode=pDC->SetROP2(R2_NOTXORPEN); //覆蓋先前的光標線 pDC->MoveTo(m_Position.x,m_Position.y); pDC->LineTo(m_Position.x,m_Position.y+16); pDC->SetROP2(nDrawMode); } break; case 2://標識為2的計時器 { //char tbuf[20]; //m_nCount+; //sprintf(tbuf,"%d",m_nCount); CString tbuf; tbuf=(char)m_nCount; pDC->TextOutW(20,20,tbuf); } break; default: break; } } BOOL CChap5CView::Create(LPCTSTR lpszClassName,LPCTSTR lpszWindowName, DWORD dwStyle,const RECT& rect, CWnd* pParentWnd,UINT nID, CCreateContext* pContext) { int ret=CWnd::Create(lpszClassName,lpszWindowName,dwStyle,rect,pParentWnd,nID,pContext); //申請兩個計時器,標識為別為1,2,間隔時間分別為500,1000 ASSERT(SetTimer(1,500,NULL)!=0); ASSERT(SetTimer(2,1000,NULL)!=0); return ret;
}
BOOL CChap5CView::DestroyWindow() { //使計時器1和2停止傳送WM_TIMER消息 KillTimer(1); KillTimer(2); return CView::DestroyWindow(); }
5.菜單 SDI程式的指令傳遞順序為: 視->文檔->SDI主架構視窗->應用程式 MDI程式的指令傳遞順序為: 視->文檔->MDI子架構視窗->MDI主架構視窗應用程式.
消息處理指令: 在視類頭檔案中添加函數聲明: afx_msg void OnEditCut(); 在實現檔案中: BEGIN_MESSAGE_MAP(CTestView,CView) ON_COMMAND(ID_EDIT_CUT,OnEditCut) END_MESSAGE_MAP() void CTestView::OnEditCut() {
}
更新指令用戶接口(UI)消息 在視類頭檔案中添加函數聲明: afx_msg void OnUpdateEditCut(CCmdUI* pCmdUI); 在實現檔案中: BEGIN_MESSAGE_MAP(CTestView,CView) ON_UPDATE_COMMAND_UI(ID_EDIT,OnUpdateEditCut) END_MESSAGE_MAP()
void CTestView::OnUpdateEditCut(CCmdUI* pCmdUI) { //更新 pCmdUI->Enable(m_bHaveData); }
擴展指令和範圍指令的處理 ON_COMMAND_EX(id1,memberFnx) ON_COMMAND_EX(id2,memberFnx)
ON_COMMAND_RANGE(id1,id2,memberFnx) ON_UPDATE_COMMAND_UI_RANGE(id1,id2,UpdatememberFnx)
ClassWizard不支援擴展指令消息和範圍指令消息.這些消息要放在//{{AFX_MSG_MAP和//{{AFX_MSG_MAP括號對之外. 程式員必須自已編寫消息映射.