目錄
- 核心技術
- 需求分析
- 程序設計
- 程序展示
(一)核心技術
MFC(Micosoft Foundation Class Libay,微基礎類庫)是微基於Windows平台下的C++類庫集合,MFC包含了所有與系統相關的類,其中封裝了大多數的API(Application Pogam Inteface)方法,提供了應用程序設計的框架以及開發應用程序所需要使用的工具,應用程序向導、類向導、可視化資源設計等高效工具,使用消息映射處理進行對消息的響應,極大的簡化了Windows中應用程序的開發工作,使程序員可以從繁重的編輯工作中抽身出來,提高了程序員的工作效率。
1、MFC與Windows編程
Windows操作系統采用了圖形用戶界面,借助它提供的API方法,用戶可以編出具有圖形用戶界面的程序。Windows操作系統下的應用程序和控制台方式(MS-DOS)下的應用程序相比,具有下特點:
(1)用戶界面統一、友好。Windows應用程序擁有相似的基本外觀,包括窗口、菜單欄、工具、狀態欄、滾動條等標准元素。
(2)獨立於設備的圖形操作。Windows下的應用程序使用圖形設備接口(Gaphic D evice Inteface),該接口屏蔽了不同的設備之間的差異,提供了與設備無關的圖形輸出能力。
(3)支持多事務處理機制。Windows是一個多事務的操作環境,允許用戶在同一時間運行多個獨立的應用程序。
(4)事件驅動程序的程序設計。Windows程序不是由事件的順序來控制,而是由事件的發生與否來控制程序執行邏輯。而事件與消息之間的關系是關聯的,Windows應用程序的消息來源主要有以下4種:
輸入消息:包括鍵盤和鼠標的輸入。
控制消息:用來與Windows的控制對象,如列表框、按鈕、復選框等進行雙向通信。
系統消息:對程序化的事件或系統時鍾中斷做出反應。
用戶消息:這是程序員自己定義並在應用程序中主動發出的。
在VC++中編寫Windows應用程序有以下兩種方法。
(1)直接使用了Windows操作系統中所提供的Windows API方法來對Windows應用程序進行編寫。通過Windows API創建的Windows應用程序包含了兩個基本部分:應用程序主方法WinMain和窗口方法。WinMain方法是應用程序的入口點,相當於C++控制台應用程序的主方法main。與main方法一樣,WinMain方法名也是固定的。窗口方法的名字是用戶自定義的,由系統調用,主要功能是用來處理窗口的消息,以此來完成某些特定的任務。使用Windows API編寫Windows應用程序時,大量的繁雜瑣碎的程序代碼必須由程序員自己親自動手編寫,工作量十分大。
(2)使用MFC類庫編寫Windows應用程序。MFC提供了大量預先編寫好的類及支持代 碼,用於處理多項標准的Windows編程任務,如窗口的創建、消息的處理、工具欄的添加和對話框等。因此,使用MFC類庫可以簡化Windows應用程序的開發工作量。
2.MFC應用程序框架
MFC封裝了大部分Windows API方法、數據結構和宏,以面向對象的類提供給程序員,並提供了一個應用程序框架,簡化和標准化了Windows程序設計。
MFC中的各種類加起來有幾百個,其中只有5個核心類對應用程序框架有影響:CWinApp、CDocument、CView、CFrameWnd和 CDocTemplate。這5個類之中只有CWinApp是必不可少的類,CWinApp的對象在應用程序中必須有一個,也只有一個,並且是一個全局對象。全局對象是在Windows操作系統調用WinMain之前建立的,它開通了程序執行的路徑。在MFC編程中,入口方法WinMain被封裝在MFC的應用程序框架內,稱為AfxWinMain,不需要也不可以再定義另一個WinMain方法。
應用程序框架(Application Framework)是一組類構造起來的大模型。它的出現使得開發人員不需要構建程序框架結構,其初始代碼可以由應用程序向導自動完成。
3.MFC應用程序向導
MFC應用程序向導(MFC AppWizard)可以幫助程序員創建一個MFC應用程序框架,並且自動生成這個MFC應用程序框架所需要的全部文件。然后,程序員利用資源管理器和類向導(ClassWizard),為應用程序添加實現特定功能的代碼,以實現應用程序所要求的功能。
在Visual C++中,程序員可以創建以下3種典型的Windows應用程序,它們都是通過MFC應用程序向導(AppWizard)進行創建的:
(1)基於對話框的應用程序:該類應用程序比較適合於文檔文獻比較少而交互操作需求量比較多的應用場合,比如Windows自帶的計算器程序。
(2)單文檔界面(SDI)應用程序:該類程序一次只能打開一個文檔,如Windows自帶的Notepad程序。
(3)多文檔界面(MDI)應用程序:該類程序可以同時打開多個文檔並對文檔進行處理,處理的過程中很容易進行切換,如Microsoft Word。
下面通過構建應用程序myprogl,簡單介紹如何使用MFC應用程序向導。
啟動Microsoft Visual C++ 6.0應用軟件,進入到Visual C++的集成的開發環境(IDE)中,並進行如下操作:
(1)選擇“文件(File)”→“新建(New)”菜單,彈出“新建”對話框。
(2)在“新建”對話框中,切換到“工程(Projects)”選項卡,從左邊窗口所示的“項目類型”列表框中選擇需要進行創建的項目類型,這里需要選擇的類型是“MFC AppWizard (exe)”。在“工程名稱(Projects name)”文本框中鍵入應用程序名,這里為myprogl。在“位置(Location)”文本框中鍵入用於存放應用程序的目錄,這里為F:\MYPROJ\myprog 1。從“平台(Platforms)”列表框中選擇可用的目標平台,默認平台為Win32。最后單擊“確定”按鈕,彈出MFC App Wizard-Step1對話框。
(3) MFC應用程序向導-步驟一(AppWizard-Step1of6)對話框的作用在於它是用戶確定應用程序的結構:是單文檔還是多文檔亦或者是基本對話框,並為對應的資源選擇一種需要使用的語言。
這里所選擇的結構是單文檔(Single document),其余步驟(步驟二至步驟六)取默認設置,單擊“完成”按鈕,顯示應用程序所具有的特征。單擊“確定”按鈕,確認之前所做的所有選擇沒有出現錯誤,則MFC AppWizard將會根據這些選擇自行生成應用程序的相關源文件。
(4) 完成上述步驟后,應用程序的框架即被生成,並在開發環境(Developer Studio)的程序工作區窗口中打開生成的程序。其中Class View面板顯示的是所創建的類和成員方法;Resource View面板顯示的是所創建的資源;FileView面板顯示的是所創建的初始文件。
(5)選擇“組建(Build)”→“組建<myprog1.exe>”命令,編譯、連接、建立運行程序。
(6)選擇“組建(Build)”→“執行<myprog1.exe>”命令,運行應用程序。
從運行結果可以看出,盡管還未寫入一句代碼,但myprogl程序已經是一個完整的可執行程序了,其整個的運行結果包括了標題欄、工具欄、菜單欄和一個打開的文檔邊框窗口。
生成應用程序框架后,這僅僅是一個程序的最基本的骨架,往往還需往項目中添加大量的代碼,包括類、資源、消息處理方法等。
4. MFC與消息映射
Windows應用程序都是消息(Message)驅動的,消息處理是Windows應用程序的核心部分。消息是用來請求對象執行某一處理、某一行為的信息。某對象在執行相應的處理時,如果需要,它可以通過傳遞消息請求其他對象完成某些處理工作或回答某些信息。其他對象在執行所要求的處理活動時,同樣可以通過傳遞消息與別的對象聯系,即Windows應用程序的運行是靠對象間傳遞消息來完成的。消息實現了對象與外界,對象與其他對象之間的聯系。
消息主要有3種類型:標准Windows消息、控件通知消息和命令消息。
(1)標准Windows消息。
除了一個特定的WM-COMMAND外,所有其他的以“WM-”為前綴的消息都是標准的Windows消息。標准Windows消息是由窗口和視圖進行處理,該類消息通常會含有用於確定如何對消息迸行處理的一些參數。標准Windows消息都有缺省的處理方法,這些方法在CWnd類中進行了預定義。MFC類庫以消息名為基礎形成這些處理方法的名稱,這些處理方法的名稱都有前綴“On”。
(2)控件通知消息。
控件通知消息包含從控件和其他子窗口傳送給父窗口的WM-COMMAND的通知消息。與其他標准的Windows消息相同,控件通知消息由窗口和視圖進行處理,但當用戶單擊按鈕控件時,發生的BN-CLICKED控件通知消息將會被系統作為命令消息來處理。
(3)命令消息。
命令消息包含來自用戶界面對象(如菜單項、工具欄按鈕和加速鍵等)的WM-COMMAND 通知消息。命令消息的處理與其他消息的處理不同,命令消息可以被更廣泛的對象(如文檔、文檔模板、應用程序對象、窗口和視圖)處理。如果某條命令直接影響某個特定的對象,則應該讓該對象來處理這條命令。
Windows程序這種“接收消息-處理消息-再等消息”的往復過程即稱為“消息循環”。 消息循環是Windows應用程序與MS-DOS應用程序的最大差異點。
在Windows平台,程序員不能決定程序執行的流程,而只能決定接收到消息時的程序的動作(編寫完成局部的代碼)。在Visual C++中編程不是考慮要讓程序按照什么樣的順序執行,而應考慮在某一消息下程序應該干什么。
(二)需求分析
查閱相關資料得知本題涉及到的主要知識點有:
時鍾指針運動算法、屏幕重繪方法、定時器消息、鼠標消息、菜單命令、對話框、畫筆/畫刷、顯示文字等。指針運動算法和屏幕重繪方法是本程序主要難點所在。
時針分針秒針,每次轉動均以弧度(一秒的角度)為基本單位,且都以表盤中心為轉動圓心。計算指針端點的公式如下:
:表示圓心坐標
:表示指針長度
:表示指針方向角
注意,指針長度是指自圓心至指針一個端點的長度(是整個指針的一部分),為了方便顯示每一個指針都需要計算兩個端點。
三個指針的運動是相關聯的,秒針轉一圈引起分針運動一格,分針轉一圈引起時針運動一格,因此只需要使用一個定時器消息來處理指針的運動即可。
由於屏幕的重繪速度很快(50微秒一次),如果采用全屏刪除式重繪則閃爍十分明顯,顯示效果不佳。在參閱多方資料之后,作者本人程序采用網上常用的方法——非刪除式重繪,即假定指針將要移動一格,則先采用背景色(這里是白色)重繪原來指針以刪除原來位置的指針,再采用指針的顏色在當前位置繪制指針;如果指針沒有動,則直接繪制指針。
另外,秒表需要采用單獨的定時器消息控制。
(三)程序設計
在這次設計的時鍾程序中,實現了指針圖形時鍾,數字電子時鍾,當前日期以及當前星期的顯示。繪制當前數字日期的代碼如下:
void CClockDate::DrawDate(CDC *pDc, CRect rectClient, CTime oTime)
{
SetPosition(rectClient);
UINT nYear,nMonth, nDay,nDayOfWeek;
nYear = oTime.GetYear();
nMonth = oTime.GetMonth();
nDay = oTime.GetDay();
CString strDate,strYear,strMonth,strDay,strDayOfWeek;
strYear.Format("%d",nYear);
strMonth.Format("%d",nMonth);
if (nMonth <10)
{
strMonth.Format("0%d",nMonth);
}
strDay.Format("%d",nDay);
if (nDay <10)
{
strDay.Format("0%d",nDay);
}
strDate.Format("%s年%s月%s日",strYear,strMonth,strDay);
strDayOfWeek = weekDay( oTime);
int nBKMode = pDc->SetBkMode(TRANSPARENT);
pDc->SetTextColor(m_dColor);
pDc->TextOut(m_DX,m_DY,strDate);
pDc->SetTextColor(m_wColor);
pDc->TextOut(m_WX,m_WY,strDayOfWeek);
pDc->SetBkMode(nBKMode);
}
繪制星期的代碼如下:
CString CClockDate::weekDay(CTime oTime)
{
CString str;
int nDayOfWeek = oTime.GetDayOfWeek();
switch(nDayOfWeek )
{
case 1:
str = "星期日";
break;
case 2:
str = "星期一";
break;
case 3:
str = "星期二";
break;
case 4:
str = "星期三";
break;
case 5:
str = "星期四";
break;
case 6:
str = "星期五";
break;
case 7:
str = "星期六";
break;
}
return str;
}
繪制圖形時鍾模塊代碼如下:
繪制指針(分針、時針以及秒針)代碼如下:
void CClockHand::DrawHand(CDC *pDC, int nValue,HANDTYPE typeHand,CPoint &ptMiddle,CTime oTime)
{
m_ptMiddle.x = ptMiddle.x;
m_ptMiddle.y = ptMiddle.y;
m_nRidius = min(m_ptMiddle.x,m_ptMiddle.y);
m_nPointWidth = (int)m_nRidius/20;
CPoint ptHand[4];
//得到指針的位置
GetHandPoints(nValue,typeHand,ptHand, oTime);
CBrush brHandH(m_HColor);
CPen penHandH(PS_SOLID,1,m_HbordColor);
CBrush brHandM(m_MColor);
CPen penHandM(PS_SOLID,1,m_MbordColor);
CPen penrgb(PS_SOLID,1,m_SbordColor);
switch(typeHand)
{
case HOUR_HAND:
//設置畫刷、畫筆
pDC->SelectObject(&brHandH);
pDC->SelectObject(&penHandH);
//繪制一個四邊形
pDC->Polygon(ptHand,4);
break;
case MINUTE_HAND:
//設置畫刷、畫筆
pDC->SelectObject(&brHandM);
pDC->SelectObject(&penHandM);
//繪制一個四邊形
pDC->Polygon(ptHand,4);
break;
case SECOND_HAND:
pDC->SelectObject(&penrgb);
pDC->MoveTo(ptHand[0]);
pDC->LineTo(ptHand[1]);
break;
}
}
繪制好了指針后我們將會根據不同的指針確定指針的形態:
void CClockHand::GetHandPoints(int nValue, HANDTYPE typeHand, CPoint *pptHand,CTime oTime)
{
UINT nMinute = oTime.GetMinute();
CClockScale Scale;
Scale.m_ptMiddle.x = m_ptMiddle.x;
Scale.m_ptMiddle.y = m_ptMiddle.y;
int nLength = 0;
//根據指針的類型區分
switch(typeHand)
{
case HOUR_HAND:
//時針長為鍾面半徑的一半
nLength = MulDiv(m_nRidius, 50, 100);
//因為繪制時針按照分針進行,故需要一些變換
nValue *= 5;
nValue += (nMinute/12);
break;
case MINUTE_HAND:
nLength = MulDiv(m_nRidius, 70, 100);
break;
case SECOND_HAND:
nLength = MulDiv(m_nRidius, 80, 100);
break;
default:
ASSERT(false);
}
//得到時針和分針外形的四個點
if (typeHand == HOUR_HAND || typeHand == MINUTE_HAND)
{
pptHand[0] = Scale.ComputerFacePoint(nValue+30,m_nPointWidth*2);
pptHand[1] = Scale.ComputerFacePoint(nValue+15,m_nPointWidth);
pptHand[2] = Scale.ComputerFacePoint(nValue,nLength);
pptHand[3] = Scale.ComputerFacePoint(nValue-15,m_nPointWidth);
}
//得到秒針的兩個端點
else
{
pptHand[0] = m_ptMiddle;
pptHand[1] = Scale.ComputerFacePoint(nValue,nLength);
}
}
繪制完指針的形態后對不同的指針進行上色:
void CClockHand::SetHandColor(COLORREF hBColor, COLORREF hColor, COLORREF mBColor, COLORREF mColor, COLORREF sColor)
{
m_HbordColor = hBColor;
m_HColor = hColor;
m_MbordColor = mBColor;
m_MColor = mColor;
m_SbordColor = sColor;
}
void CClockHand::GetHandColor(COLORREF &hBColor, COLORREF &hColor, COLORREF &mBColor, COLORREF &mColor, COLORREF &sColor)
{
hBColor = m_HbordColor;
hColor = m_HColor;
mBColor = m_MbordColor;
mColor = m_MColor;
sColor = m_SbordColor;
}
(四)運行截圖
1 <html> 2 <head> 3 項目下載地址 4 </head> 5 <body> 6 <a href="http://webcodeschool.hrxxkj.com/webindex">PC端</a> 7 <h3> 移動端關注VX:校猿碼。 8 </body> 9 </html>