從零開始學VC系列教程 二. 對話框及常用控件實驗
恭喜你,進入VC學習的第二節(jié)了.這一節(jié)是人機交互的基礎.所謂人機交互,說通俗點就是與機器對話.然而我們現(xiàn)在的技術還不能像科幻片里一樣與機器人直接說話就行了.所以,我們的操作意圖還得通過文本輸入,命令按鈕等等來實現(xiàn).
本節(jié)內(nèi)容:學會對話框調(diào)用及一些常用控件的使用方法.
學習目的:學習人機交互,為軟件開發(fā)提供界面基礎.
1.新建工程.參考第一節(jié)的方法新建一個工程,名字為Eg02完成后如下圖
細心的朋友一定會發(fā)現(xiàn).新建的工程里還有一個對話框,ID名是IDD_ABOUTBOX這個是做什么用的呢?我們用到的軟件都會有一個版權(quán)聲明.通過第一章的學習,大家應該知道怎么觀看這個IDD_ABOUTBOX對話框了吧.沒錯!雙擊IDD_ABOUTBOX就可以了.我們會看到如下的一個對話框
這就是我們這個程序的關于對話框,一般用于版權(quán)聲明及版本號標識.大家看到的這個對話框里有兩個靜態(tài)文本框和一個圖像框(Picture),靜態(tài)文本框我們在前一節(jié)已經(jīng)介紹過了.大家可以修改一下版權(quán)所有這一行,填什么都可以,簽個大名也行.完成以后你一定想看看效果,這個對話框怎么打開呢?其實VC已經(jīng)為我們做好了.先按F7編譯,然后按F5運行.大家可以看到程序運行了.
單擊應用程序圖標,就會出現(xiàn)一個菜單,選最后一個[關于Eg02],關于對話框就彈出來了.
當然,這是系統(tǒng)為我們做好的.自己怎么在程序中調(diào)用這個對話框呢?為了演示,首先參考第一節(jié)的內(nèi)容添加一個按鈕,然后把按鈕的ID改為IDC_BTN_ABOUTME,把標題,也就是Caption改為[關于].最終效果如下
下面我們?yōu)榘粹o添加代碼.相信大家一定還記得怎么進入代碼吧..對了,雙擊[關于]按鈕,在彈出的對話框中點[確定]就可以了.為了讓大家更好的理解下面的操作,我們先要解釋一下關于對話框的類.VC向?qū)䴙殛P于對話框建立一個類,大家看看下面的圖
單擊標簽ClassView(這里顯示的是[Class…])就可以看到Eg02這個應用程序的類.第一個CAboutDlg就是關于對話框的類.CEg02Dlg對應IDD_EG02_DIALOG.中間的Ceg02App是應用程序的基礎類.所以,如果要對關于對話框進行操作,就要用到類CAboutDlg,因為與此有關的函數(shù)及變量都封裝在CAboutDlg中.看到這里大家可能又糊涂了,沒關系,在以后的教程中,通過一些練習大家就會慢慢領會到的.這里還是先為[關于]按鈕添加代碼.
void CEg02Dlg::OnBtnAboutme()
{
// TODO: Add your control notification handler code here
}
上面是VC為[關于]按鈕添加的響應函數(shù).我們添加代碼成以下所示
void CEg02Dlg::OnBtnAboutme()
{
// TODO: Add your control notification handler code here
CAboutDlg ADlg;
ADlg.DoModal();
}
一共有兩句,第一句是CAboutDlg ADlg;作用是定義一個變量Adlg.第二句是ADlg.DoModal();功能是調(diào)用類CAboutDlg里的一個函數(shù)DoModal();這個函數(shù)在MSDN里的解釋是Call this member function to invoke the modal dialog box and return the dialog-box result when done. This member function handles all interaction with the user while the dialog box is active. This is what makes the dialog box modal; that is, the user cannot interact with other windows until the dialog box is closed.一般我們用于顯示一個對話框.其實大家看看CAboutDlg這個類下面,只有兩個函數(shù)
DoModal()這個函數(shù)并不在這個里面.第一章我們提到過類的派生和繼承.其實CAboutDlg這個類是派生于CDialog類,DoModal()這個函數(shù)是CDialog的成員函數(shù),由于CAboutDlg是繼承父類CDialog的,所以CDialog里的函數(shù)在CAboutDlg中也可以使用.
下面我們來說說幾個常用控件的使用.
首先在IDD_EG02_DIALOG對話框中加入一個Edit(編輯框)控件.Edit一般用于輸入輸出數(shù)據(jù)文本.相當于VB里的TextBox.加入Edit控件后,編輯其屬性為
然后,我們再加一個按鈕(PushButton),并編輯其屬性為
接下來,我們先說一下要實現(xiàn)的效果.很簡單,在編輯框里輸入一個文本,然后按顯示,就把文本顯示在靜態(tài)文本框中.所以,這里要把靜態(tài)文本編輯框的ID改為IDC_DISPLABEL
下面我們?yōu)閇顯示]按鈕添加代碼
void CEg02Dlg::OnBtnShow()
{
// TODO: Add your control notification handler code here
CString a;
GetDlgItemText(IDC_EDIT_INPUT,a);
SetDlgItemText(IDC_DISPLABEL,a);
}
其實不復雜,也只有三句,第一句定義一個CString類變量a 我們來說說GetDlgItemText這個函數(shù)吧.查查MSDN就知道函數(shù)原型了.
int GetDlgItemText( int nID, LPTSTR lpStr, int nMaxCount ) const;
int GetDlgItemText( int nID, CString& rString ) const;
大家看看就覺得奇怪了,怎么有兩個原型啊?并且一個是傳兩個參數(shù),另一個是傳三個參數(shù).在VC里面,同一個類下是可以存在多個同名函數(shù)的,具體調(diào)用哪個函數(shù)要看參數(shù)的不同.在這里我們傳入了兩個參數(shù),所以VC會調(diào)用int GetDlgItemText( int nID, CString& rString ) const;這個函數(shù).第一個函數(shù)是控件的ID號,第二個是字串.第二個傳了地址,所以我們在下一句中用的a已經(jīng)是獲得IDC_EDIT_INPUT的文本了.運行效果如下
下面介紹一下進度條的使用以及定時器的使用.
我們要實現(xiàn)的效果是進度從0到滿格,然后再從0到滿格,依次循環(huán).每跳一格間隔500ms,這個時間我們用定時器來實現(xiàn).
首先從控件條里拖出一個進程條到對話框,修改屬性如下
然后我們要介紹一下VC的定時器.VC里面使用定時器有多種方式,我們先介紹一種作為拋磚引玉
首先添加一個Windows消息處理器.消息這個詞語可能很陌生,我們會在后面很多次說明.這里先照圖做
在類管理器里選中Ceg02Dlg這個類,然后點右鍵,就會彈出一個菜單,選擇[Add Windows Message Handler…],接下來會彈出另一個菜單
雙擊WM_TIMER然后按[確定]就可以了.大家會看到,CEg02Dlg類中多了一個函數(shù)
這個就是VC中的定時器響應函數(shù).然后雙擊這個函數(shù)就進入代碼了
然后我們?yōu)門imer事件添加代碼.
void CEg02Dlg::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default IDC_PROGRESS
static int nPos=0;
((CProgressCtrl*)(GetDlgItem(IDC_PROGRESS)))->SetPos(nPos);
if(nPos<100) nPos+=10;
else nPos=0;
CDialog::OnTimer(nIDEvent);
}
首先定義一個整型的變量nPos用于記錄進度條的進度值.默認時,進度條0為空,100為滿格.從后的程序大家可以看到,這個變量自加到100就變?yōu)?.最難理解的就是
((CProgressCtrl*)(GetDlgItem(IDC_PROGRESS)))->SetPos(nPos);
首先, GetDlgItem(IDC_PROGRESS)這個函數(shù)用來獲取IDC_PROGRESS的句柄,在VC里面引入了句柄這個詞語,我們將在下一章中對消息和句柄進行詳細的說明, 句柄是WINDOWS用來標識被應用程序所建立或使用的對象的唯一整數(shù),WINDOWS使用各種各樣的句柄標識諸如應用程序?qū)嵗翱冢刂疲粓D,GDI對象等等。WINDOWS句柄有點象C語言中的文件句柄。在這里,GetDlgItem獲取了窗體句柄,大家應該還記得,在VC里面,控制就看成窗體。(CProgressCtrl*)這個地方是將返回的句柄強制轉(zhuǎn)換為CProgressCtrl類型,與C語言的中的強制轉(zhuǎn)換是一樣的.進度條的控制類是CprogressCtrl.而前面返回的是一個窗體類型,所以先要強制轉(zhuǎn)換.在第一章中,我們提到過,VC中的控件都認為是窗體,在這里就體現(xiàn)出來了. SetPos(nPos);這個函數(shù)是類CprogressCtrl的成員函數(shù),用來指定當前進度條的進度.最后還有一步,就是激活這個定時器.像我們的C51或AVR一樣,要初始化定時器. 而void CEg02Dlg::OnTimer(UINT nIDEvent)這個函數(shù)就像我們單片機的定時器中斷服務函數(shù)一樣.時間到了就會自動執(zhí)行.
參考上面的圖,雙擊OnInitDialog(),就可以進入對話框初始化函數(shù),只要添加一句就可以了.完成后如下
BOOL CEg02Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application’s main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
SetTimer(0,500,NULL); //這里是添加的,別的都是自動生成的
return TRUE; // return TRUE unless you set the focus to a control
}
在這里我們只添加SetTimer(0,500,NULL);其中,參數(shù)0代表定時器的ID號為0,.第二個參數(shù)500是定時器的時間,單位為ms,后面的NULL是指不要回調(diào)函數(shù).
按F7編譯后運行就可以看到運行效果了.
或許大家有個問題,為什么開始時是添加了一個WM_TIMER的消息處理器呢?我們使用SetTimer開始定時器后,如果沒有回調(diào)函數(shù),系統(tǒng)會在每次定時時間到后發(fā)送一個WM_TIMER消息到窗體.窗體收到這個信息后,調(diào)用OnTimer()函數(shù)進行處理.系統(tǒng)定義這個函數(shù)為afx_msg void OnTimer(UINT nIDEvent);可以看到是以afx_msg修飾的,這種函數(shù)會與其中一個信息關聯(lián),或者說是消息影射到這個函數(shù).每當有消息發(fā)過來,都會執(zhí)行這個函數(shù).大家只要好好想想單片機的定時器中斷就會明白的,原理一樣,只是傳輸機制不同.
如果上面的內(nèi)容你制作成功了,那么恭喜你,第二節(jié)就基本學完了.
下面我們也來說說多任務和消息機制吧.
Windows是基于消息機制的,它是一個多任務的操作系統(tǒng),也就是說,同一時間內(nèi),系統(tǒng)會掛起多個任務.為了說明多任務,我們先來看一段單片機程序.
Void main(void)
{
While(1)
{
TaskA();
}
}
這個程序很簡單,單片機工作后就進入while()循環(huán)了,單片機這個時候就干一件事,那就是執(zhí)行任務TaskA.這樣的工作總是在一個主循環(huán)內(nèi)實施,一次只執(zhí)行一個任務的我們稱為單任務系統(tǒng).單片機程序只要不引入操作系統(tǒng)并且由一個主循環(huán)一直執(zhí)行完畢的基本都是單任務的.有些程序也是這樣寫的.
Void main(void)
{
While(1)
{
TaskA();
TaskB();
TaskC();
}
}
這樣的程序看起來似乎是有三個任務了.這三個任務是順序執(zhí)行的,也就是說,必須讓A完成后,才能到B,B完成后才能到C.如果A有一個長時間的延時,系統(tǒng)就會在A中空等,然而B與A本來是無關的,這樣空等的時間就算是浪費了.如果我們的windows也是順序執(zhí)行的就麻煩了,那時我們不僅僅會說Windows有點慢,而是說Windows像蝸牛一樣在爬.如果我們讓這些任務更合理的安排一下,在執(zhí)行A的時候,有空就去執(zhí)行B,而在B的空閑時間去執(zhí)行C或者A,那么時間就節(jié)省下來了.如果時間切換夠快,那么我們可以認為A,B,C三個任務在同時進行.所以,如果我們把單片機的執(zhí)行時間分成若干等份,每份1ms或者更小,這種時間等份我們稱為時間片,每次時間到了就換一個任務.也就是說,第一毫秒執(zhí)行A,這時我們并不等A全部執(zhí)行完,并記住這個斷點,到了第二毫秒執(zhí)行B,第三毫秒執(zhí)行C,第四毫秒又執(zhí)行A并從原來的斷點開始執(zhí)行,依次直至三個任務都完成.大家可以看到,任務A每三毫秒執(zhí)行了一次.這就是多任務的模型了.
Windows其實就是這么干的.只是每次任務時間不一定是我們上面說的3ms,因為系統(tǒng)同一時間內(nèi)可能會有很多待執(zhí)行的任務,這些任務就被系統(tǒng)按優(yōu)先級排成隊,一個個取出來執(zhí)行.比如我們現(xiàn)在打開VC,系統(tǒng)在加載VC,同時我們還可以移動鼠標,這些好像都是在同時進行,其實它們也是按時間片分時執(zhí)行的.
現(xiàn)在分析一下這個工作過程吧.現(xiàn)在我們要打開VC,系統(tǒng)開始加載VC了,可以看到VC的LOGO界面,但這個時候我們還可以動一下鼠標,硬件首先會響應,并給軟件發(fā)個通知,而軟件這個時候可能還沒有輪到鼠標程序的執(zhí)行,怎么辦呢?系統(tǒng)會先把這個鼠標操作保存起來,輪到鼠標時間片時再來處理鼠標操作.大家可能覺得,這樣鼠標操作不是滯后了嗎?確實是這樣的,但這個滯后時間是很短的,我們基本不會察覺出來.
我們來看看消息的定義.
消息系統(tǒng)對于一個win32程序來說十分重要,它是一個程序運行的動力源泉。一個消息,是系統(tǒng)定義的一個32位的值,他唯一的定義了一個事件,向Windows發(fā)出一個通知,告訴應用程序某個事情發(fā)生了。例如,單擊鼠標、改變窗口尺寸、按下鍵盤上的一個鍵都會使Windows發(fā)送一個消息給應用程序。
消息本身是作為一個記錄傳遞給應用程序的,這個記錄中包含了消息的類型以及其他信息。例如,對于單擊鼠標所產(chǎn)生的消息來說,這個記錄中包含了單擊鼠標時的坐標。
綜合前面的分析,消息就好理解了,鼠標動一下會產(chǎn)生一個事件,這個事件必須要告訴系統(tǒng)啊,怎么告訴系統(tǒng)呢?Windows把這個定義為Message也就是消息,所以,消息可以認為是一個操作記號,記錄上一步所發(fā)生的或者說是下一步所要處理的事件.系統(tǒng)為消息安排了一個隊列,里面可以存放很多個消息,我們稱為消息隊列.有鍵盤操作時,把鍵盤操作放到消息隊列里面,相當于做一個記號,我們動了鍵盤,有鼠標操作時,把鼠標操作放到消息隊列,也便記住我們在鍵盤后還動了鼠標,系統(tǒng)就把這些東西一個個按時間取出來執(zhí)行.在Windows里面,鼠標消息可以有幾種,例如單擊落了左鍵,系統(tǒng)會產(chǎn)生一個消息為WM_LBUTTONDOWN如果我們移動了鼠標,就像產(chǎn)生一個消息為WM_MOUSEMOVE消息入隊后,系統(tǒng)會依次處理.當然,我們這樣的解釋雖然容易懂,但不是非常專業(yè),并且Windows內(nèi)部處理消息也比這復雜多了,但基本原理就是這樣的了,并且,我們在進行操作時,并不用關心Windows的底層是怎么處理這些事件的,除非我們的操作真的讓Windows生氣了,就必須去查查出什么問題了.
說了這么多,消息可能還是一個十分飄渺的概念,倒底要怎么用呢?我們來舉個例子說明一下.給系統(tǒng)發(fā)消息一般會用到兩個函數(shù),一個是SendMessage,另一個是PostMessage,這兩個是有點區(qū)別的,具體區(qū)別作為這次的作品,大家去網(wǎng)上查查.例如我們要退出當前的應用程序,最簡單的方法當然是按一下關閉的那個紅X,如果我們要自己讓程序退出,可以給系統(tǒng)發(fā)一個消息,其實那個關閉程序的紅X也是這么干的.為了演示,首先在上次的對話框中加一個按鈕,修改標題也就是Caption為”關閉程序”,ID號改為IDC_QUIT_ME雙擊為按鈕添加以下代碼.
PostMessage(WM_CLOSE);
編譯運行試試,如果沒有寫錯代碼就可以用那個按鈕退出程序了.
這一節(jié)也就寫到這里了,可以大家還是有點糊涂,慢慢體會一下就好了.還是不明白可以來這里提問. |