標(biāo)題: 從零開始學(xué)VC系列教程 二. 對話框及常用控件實(shí)驗(yàn) [打印本頁]

作者: meranda    時(shí)間: 2008-11-6 19:45
標(biāo)題: 從零開始學(xué)VC系列教程 二. 對話框及常用控件實(shí)驗(yàn)
從零開始學(xué)VC系列教程 二. 對話框及常用控件實(shí)驗(yàn)
    恭喜你,進(jìn)入VC學(xué)習(xí)的第二節(jié)了.這一節(jié)是人機(jī)交互的基礎(chǔ).所謂人機(jī)交互,說通俗點(diǎn)就是與機(jī)器對話.然而我們現(xiàn)在的技術(shù)還不能像科幻片里一樣與機(jī)器人直接說話就行了.所以,我們的操作意圖還得通過文本輸入,命令按鈕等等來實(shí)現(xiàn).
本節(jié)內(nèi)容:學(xué)會(huì)對話框調(diào)用及一些常用控件的使用方法.
    學(xué)習(xí)目的:學(xué)習(xí)人機(jī)交互,為軟件開發(fā)提供界面基礎(chǔ).
1.新建工程.參考第一節(jié)的方法新建一個(gè)工程,名字為Eg02完成后如下圖

細(xì)心的朋友一定會(huì)發(fā)現(xiàn).新建的工程里還有一個(gè)對話框,ID名是IDD_ABOUTBOX這個(gè)是做什么用的呢?我們用到的軟件都會(huì)有一個(gè)版權(quán)聲明.通過第一章的學(xué)習(xí),大家應(yīng)該知道怎么觀看這個(gè)IDD_ABOUTBOX對話框了吧.沒錯(cuò)!雙擊IDD_ABOUTBOX就可以了.我們會(huì)看到如下的一個(gè)對話框

這就是我們這個(gè)程序的關(guān)于對話框,一般用于版權(quán)聲明及版本號標(biāo)識.大家看到的這個(gè)對話框里有兩個(gè)靜態(tài)文本框和一個(gè)圖像框(Picture),靜態(tài)文本框我們在前一節(jié)已經(jīng)介紹過了.大家可以修改一下版權(quán)所有這一行,填什么都可以,簽個(gè)大名也行.完成以后你一定想看看效果,這個(gè)對話框怎么打開呢?其實(shí)VC已經(jīng)為我們做好了.先按F7編譯,然后按F5運(yùn)行.大家可以看到程序運(yùn)行了.


單擊應(yīng)用程序圖標(biāo),就會(huì)出現(xiàn)一個(gè)菜單,選最后一個(gè)[關(guān)于Eg02],關(guān)于對話框就彈出來了.
當(dāng)然,這是系統(tǒng)為我們做好的.自己怎么在程序中調(diào)用這個(gè)對話框呢?為了演示,首先參考第一節(jié)的內(nèi)容添加一個(gè)按鈕,然后把按鈕的ID改為IDC_BTN_ABOUTME,把標(biāo)題,也就是Caption改為[關(guān)于].最終效果如下


下面我們?yōu)榘粹o添加代碼.相信大家一定還記得怎么進(jìn)入代碼吧..對了,雙擊[關(guān)于]按鈕,在彈出的對話框中點(diǎn)[確定]就可以了.為了讓大家更好的理解下面的操作,我們先要解釋一下關(guān)于對話框的類.VC向?qū)?huì)為關(guān)于對話框建立一個(gè)類,大家看看下面的圖


單擊標(biāo)簽ClassView(這里顯示的是[Class…])就可以看到Eg02這個(gè)應(yīng)用程序的類.第一個(gè)CAboutDlg就是關(guān)于對話框的類.CEg02Dlg對應(yīng)IDD_EG02_DIALOG.中間的Ceg02App是應(yīng)用程序的基礎(chǔ)類.所以,如果要對關(guān)于對話框進(jìn)行操作,就要用到類CAboutDlg,因?yàn)榕c此有關(guān)的函數(shù)及變量都封裝在CAboutDlg中.看到這里大家可能又糊涂了,沒關(guān)系,在以后的教程中,通過一些練習(xí)大家就會(huì)慢慢領(lǐng)會(huì)到的.這里還是先為[關(guān)于]按鈕添加代碼.
void CEg02Dlg::OnBtnAboutme()  
{
        // TODO: Add your control notification handler code here
         
}
上面是VC為[關(guān)于]按鈕添加的響應(yīng)函數(shù).我們添加代碼成以下所示
void CEg02Dlg::OnBtnAboutme()  
{
        // TODO: Add your control notification handler code here
        CAboutDlg ADlg;
        ADlg.DoModal();
}
一共有兩句,第一句是CAboutDlg ADlg;作用是定義一個(gè)變量Adlg.第二句是ADlg.DoModal();功能是調(diào)用類CAboutDlg里的一個(gè)函數(shù)DoModal();這個(gè)函數(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.一般我們用于顯示一個(gè)對話框.其實(shí)大家看看CAboutDlg這個(gè)類下面,只有兩個(gè)函數(shù)



  
DoModal()這個(gè)函數(shù)并不在這個(gè)里面.第一章我們提到過類的派生和繼承.其實(shí)CAboutDlg這個(gè)類是派生于CDialog類,DoModal()這個(gè)函數(shù)是CDialog的成員函數(shù),由于CAboutDlg是繼承父類CDialog的,所以CDialog里的函數(shù)在CAboutDlg中也可以使用.
下面我們來說說幾個(gè)常用控件的使用.
首先在IDD_EG02_DIALOG對話框中加入一個(gè)Edit(編輯框)控件.Edit一般用于輸入輸出數(shù)據(jù)文本.相當(dāng)于VB里的TextBox.加入Edit控件后,編輯其屬性為


然后,我們再加一個(gè)按鈕(PushButton),并編輯其屬性為


接下來,我們先說一下要實(shí)現(xiàn)的效果.很簡單,在編輯框里輸入一個(gè)文本,然后按顯示,就把文本顯示在靜態(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);
}
其實(shí)不復(fù)雜,也只有三句,第一句定義一個(gè)CString類變量a 我們來說說GetDlgItemText這個(gè)函數(shù)吧.查查MSDN就知道函數(shù)原型了.
int GetDlgItemText( int nID, LPTSTR lpStr, int nMaxCount ) const;
int GetDlgItemText( int nID, CString& rString ) const;
大家看看就覺得奇怪了,怎么有兩個(gè)原型啊?并且一個(gè)是傳兩個(gè)參數(shù),另一個(gè)是傳三個(gè)參數(shù).在VC里面,同一個(gè)類下是可以存在多個(gè)同名函數(shù)的,具體調(diào)用哪個(gè)函數(shù)要看參數(shù)的不同.在這里我們傳入了兩個(gè)參數(shù),所以VC會(huì)調(diào)用int GetDlgItemText( int nID, CString& rString ) const;這個(gè)函數(shù).第一個(gè)函數(shù)是控件的ID號,第二個(gè)是字串.第二個(gè)傳了地址,所以我們在下一句中用的a已經(jīng)是獲得IDC_EDIT_INPUT的文本了.運(yùn)行效果如下

下面介紹一下進(jìn)度條的使用以及定時(shí)器的使用.
我們要實(shí)現(xiàn)的效果是進(jìn)度從0到滿格,然后再從0到滿格,依次循環(huán).每跳一格間隔500ms,這個(gè)時(shí)間我們用定時(shí)器來實(shí)現(xiàn).
首先從控件條里拖出一個(gè)進(jìn)程條到對話框,修改屬性如下


然后我們要介紹一下VC的定時(shí)器.VC里面使用定時(shí)器有多種方式,我們先介紹一種作為拋磚引玉
首先添加一個(gè)Windows消息處理器.消息這個(gè)詞語可能很陌生,我們會(huì)在后面很多次說明.這里先照圖做

在類管理器里選中Ceg02Dlg這個(gè)類,然后點(diǎn)右鍵,就會(huì)彈出一個(gè)菜單,選擇[Add Windows Message Handler…],接下來會(huì)彈出另一個(gè)菜單


雙擊WM_TIMER然后按[確定]就可以了.大家會(huì)看到,CEg02Dlg類中多了一個(gè)函數(shù)


這個(gè)就是VC中的定時(shí)器響應(yīng)函數(shù).然后雙擊這個(gè)函數(shù)就進(jìn)入代碼了


然后我們?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);
}
首先定義一個(gè)整型的變量nPos用于記錄進(jìn)度條的進(jìn)度值.默認(rèn)時(shí),進(jìn)度條0為空,100為滿格.從后的程序大家可以看到,這個(gè)變量自加到100就變?yōu)?.最難理解的就是
((CProgressCtrl*)(GetDlgItem(IDC_PROGRESS)))->SetPos(nPos);
首先, GetDlgItem(IDC_PROGRESS)這個(gè)函數(shù)用來獲取IDC_PROGRESS的句柄,在VC里面引入了句柄這個(gè)詞語,我們將在下一章中對消息和句柄進(jìn)行詳細(xì)的說明, 句柄是WINDOWS用來標(biāo)識被應(yīng)用程序所建立或使用的對象的唯一整數(shù),WINDOWS使用各種各樣的句柄標(biāo)識諸如應(yīng)用程序?qū)嵗,窗口,控制,位圖,GDI對象等等。WINDOWS句柄有點(diǎn)象C語言中的文件句柄。在這里,GetDlgItem獲取了窗體句柄,大家應(yīng)該還記得,在VC里面,控制就看成窗體。(CProgressCtrl*)這個(gè)地方是將返回的句柄強(qiáng)制轉(zhuǎn)換為CProgressCtrl類型,與C語言的中的強(qiáng)制轉(zhuǎn)換是一樣的.進(jìn)度條的控制類是CprogressCtrl.而前面返回的是一個(gè)窗體類型,所以先要強(qiáng)制轉(zhuǎn)換.在第一章中,我們提到過,VC中的控件都認(rèn)為是窗體,在這里就體現(xiàn)出來了. SetPos(nPos);這個(gè)函數(shù)是類CprogressCtrl的成員函數(shù),用來指定當(dāng)前進(jìn)度條的進(jìn)度.最后還有一步,就是激活這個(gè)定時(shí)器.像我們的C51或AVR一樣,要初始化定時(shí)器. 而void CEg02Dlg::OnTimer(UINT nIDEvent)這個(gè)函數(shù)就像我們單片機(jī)的定時(shí)器中斷服務(wù)函數(shù)一樣.時(shí)間到了就會(huì)自動(dòng)執(zhí)行.
參考上面的圖,雙擊OnInitDialog(),就可以進(jìn)入對話框初始化函數(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); //這里是添加的,別的都是自動(dòng)生成的

        return TRUE;  // return TRUE  unless you set the focus to a control
}
在這里我們只添加SetTimer(0,500,NULL);其中,參數(shù)0代表定時(shí)器的ID號為0,.第二個(gè)參數(shù)500是定時(shí)器的時(shí)間,單位為ms,后面的NULL是指不要回調(diào)函數(shù).
按F7編譯后運(yùn)行就可以看到運(yùn)行效果了.

或許大家有個(gè)問題,為什么開始時(shí)是添加了一個(gè)WM_TIMER的消息處理器呢?我們使用SetTimer開始定時(shí)器后,如果沒有回調(diào)函數(shù),系統(tǒng)會(huì)在每次定時(shí)時(shí)間到后發(fā)送一個(gè)WM_TIMER消息到窗體.窗體收到這個(gè)信息后,調(diào)用OnTimer()函數(shù)進(jìn)行處理.系統(tǒng)定義這個(gè)函數(shù)為afx_msg void OnTimer(UINT nIDEvent);可以看到是以afx_msg修飾的,這種函數(shù)會(huì)與其中一個(gè)信息關(guān)聯(lián),或者說是消息影射到這個(gè)函數(shù).每當(dāng)有消息發(fā)過來,都會(huì)執(zhí)行這個(gè)函數(shù).大家只要好好想想單片機(jī)的定時(shí)器中斷就會(huì)明白的,原理一樣,只是傳輸機(jī)制不同.
如果上面的內(nèi)容你制作成功了,那么恭喜你,第二節(jié)就基本學(xué)完了.
下面我們也來說說多任務(wù)和消息機(jī)制吧.
Windows是基于消息機(jī)制的,它是一個(gè)多任務(wù)的操作系統(tǒng),也就是說,同一時(shí)間內(nèi),系統(tǒng)會(huì)掛起多個(gè)任務(wù).為了說明多任務(wù),我們先來看一段單片機(jī)程序.
Void main(void)
{
        While(1)
{
        TaskA();
}
}
這個(gè)程序很簡單,單片機(jī)工作后就進(jìn)入while()循環(huán)了,單片機(jī)這個(gè)時(shí)候就干一件事,那就是執(zhí)行任務(wù)TaskA.這樣的工作總是在一個(gè)主循環(huán)內(nèi)實(shí)施,一次只執(zhí)行一個(gè)任務(wù)的我們稱為單任務(wù)系統(tǒng).單片機(jī)程序只要不引入操作系統(tǒng)并且由一個(gè)主循環(huán)一直執(zhí)行完畢的基本都是單任務(wù)的.有些程序也是這樣寫的.
Void main(void)
{
        While(1)
{
        TaskA();
        TaskB();
TaskC();
}
}
這樣的程序看起來似乎是有三個(gè)任務(wù)了.這三個(gè)任務(wù)是順序執(zhí)行的,也就是說,必須讓A完成后,才能到B,B完成后才能到C.如果A有一個(gè)長時(shí)間的延時(shí),系統(tǒng)就會(huì)在A中空等,然而B與A本來是無關(guān)的,這樣空等的時(shí)間就算是浪費(fèi)了.如果我們的windows也是順序執(zhí)行的就麻煩了,那時(shí)我們不僅僅會(huì)說Windows有點(diǎn)慢,而是說Windows像蝸牛一樣在爬.如果我們讓這些任務(wù)更合理的安排一下,在執(zhí)行A的時(shí)候,有空就去執(zhí)行B,而在B的空閑時(shí)間去執(zhí)行C或者A,那么時(shí)間就節(jié)省下來了.如果時(shí)間切換夠快,那么我們可以認(rèn)為A,B,C三個(gè)任務(wù)在同時(shí)進(jìn)行.所以,如果我們把單片機(jī)的執(zhí)行時(shí)間分成若干等份,每份1ms或者更小,這種時(shí)間等份我們稱為時(shí)間片,每次時(shí)間到了就換一個(gè)任務(wù).也就是說,第一毫秒執(zhí)行A,這時(shí)我們并不等A全部執(zhí)行完,并記住這個(gè)斷點(diǎn),到了第二毫秒執(zhí)行B,第三毫秒執(zhí)行C,第四毫秒又執(zhí)行A并從原來的斷點(diǎn)開始執(zhí)行,依次直至三個(gè)任務(wù)都完成.大家可以看到,任務(wù)A每三毫秒執(zhí)行了一次.這就是多任務(wù)的模型了.
Windows其實(shí)就是這么干的.只是每次任務(wù)時(shí)間不一定是我們上面說的3ms,因?yàn)橄到y(tǒng)同一時(shí)間內(nèi)可能會(huì)有很多待執(zhí)行的任務(wù),這些任務(wù)就被系統(tǒng)按優(yōu)先級排成隊(duì),一個(gè)個(gè)取出來執(zhí)行.比如我們現(xiàn)在打開VC,系統(tǒng)在加載VC,同時(shí)我們還可以移動(dòng)鼠標(biāo),這些好像都是在同時(shí)進(jìn)行,其實(shí)它們也是按時(shí)間片分時(shí)執(zhí)行的.
現(xiàn)在分析一下這個(gè)工作過程吧.現(xiàn)在我們要打開VC,系統(tǒng)開始加載VC了,可以看到VC的LOGO界面,但這個(gè)時(shí)候我們還可以動(dòng)一下鼠標(biāo),硬件首先會(huì)響應(yīng),并給軟件發(fā)個(gè)通知,而軟件這個(gè)時(shí)候可能還沒有輪到鼠標(biāo)程序的執(zhí)行,怎么辦呢?系統(tǒng)會(huì)先把這個(gè)鼠標(biāo)操作保存起來,輪到鼠標(biāo)時(shí)間片時(shí)再來處理鼠標(biāo)操作.大家可能覺得,這樣鼠標(biāo)操作不是滯后了嗎?確實(shí)是這樣的,但這個(gè)滯后時(shí)間是很短的,我們基本不會(huì)察覺出來.
我們來看看消息的定義.
消息系統(tǒng)對于一個(gè)win32程序來說十分重要,它是一個(gè)程序運(yùn)行的動(dòng)力源泉。一個(gè)消息,是系統(tǒng)定義的一個(gè)32位的值,他唯一的定義了一個(gè)事件,向Windows發(fā)出一個(gè)通知,告訴應(yīng)用程序某個(gè)事情發(fā)生了。例如,單擊鼠標(biāo)、改變窗口尺寸、按下鍵盤上的一個(gè)鍵都會(huì)使Windows發(fā)送一個(gè)消息給應(yīng)用程序。
消息本身是作為一個(gè)記錄傳遞給應(yīng)用程序的,這個(gè)記錄中包含了消息的類型以及其他信息。例如,對于單擊鼠標(biāo)所產(chǎn)生的消息來說,這個(gè)記錄中包含了單擊鼠標(biāo)時(shí)的坐標(biāo)。
綜合前面的分析,消息就好理解了,鼠標(biāo)動(dòng)一下會(huì)產(chǎn)生一個(gè)事件,這個(gè)事件必須要告訴系統(tǒng)啊,怎么告訴系統(tǒng)呢?Windows把這個(gè)定義為Message也就是消息,所以,消息可以認(rèn)為是一個(gè)操作記號,記錄上一步所發(fā)生的或者說是下一步所要處理的事件.系統(tǒng)為消息安排了一個(gè)隊(duì)列,里面可以存放很多個(gè)消息,我們稱為消息隊(duì)列.有鍵盤操作時(shí),把鍵盤操作放到消息隊(duì)列里面,相當(dāng)于做一個(gè)記號,我們動(dòng)了鍵盤,有鼠標(biāo)操作時(shí),把鼠標(biāo)操作放到消息隊(duì)列,也便記住我們在鍵盤后還動(dòng)了鼠標(biāo),系統(tǒng)就把這些東西一個(gè)個(gè)按時(shí)間取出來執(zhí)行.在Windows里面,鼠標(biāo)消息可以有幾種,例如單擊落了左鍵,系統(tǒng)會(huì)產(chǎn)生一個(gè)消息為WM_LBUTTONDOWN如果我們移動(dòng)了鼠標(biāo),就像產(chǎn)生一個(gè)消息為WM_MOUSEMOVE消息入隊(duì)后,系統(tǒng)會(huì)依次處理.當(dāng)然,我們這樣的解釋雖然容易懂,但不是非常專業(yè),并且Windows內(nèi)部處理消息也比這復(fù)雜多了,但基本原理就是這樣的了,并且,我們在進(jìn)行操作時(shí),并不用關(guān)心Windows的底層是怎么處理這些事件的,除非我們的操作真的讓W(xué)indows生氣了,就必須去查查出什么問題了.
說了這么多,消息可能還是一個(gè)十分飄渺的概念,倒底要怎么用呢?我們來舉個(gè)例子說明一下.給系統(tǒng)發(fā)消息一般會(huì)用到兩個(gè)函數(shù),一個(gè)是SendMessage,另一個(gè)是PostMessage,這兩個(gè)是有點(diǎn)區(qū)別的,具體區(qū)別作為這次的作品,大家去網(wǎng)上查查.例如我們要退出當(dāng)前的應(yīng)用程序,最簡單的方法當(dāng)然是按一下關(guān)閉的那個(gè)紅X,如果我們要自己讓程序退出,可以給系統(tǒng)發(fā)一個(gè)消息,其實(shí)那個(gè)關(guān)閉程序的紅X也是這么干的.為了演示,首先在上次的對話框中加一個(gè)按鈕,修改標(biāo)題也就是Caption為”關(guān)閉程序”,ID號改為IDC_QUIT_ME雙擊為按鈕添加以下代碼.
PostMessage(WM_CLOSE);
編譯運(yùn)行試試,如果沒有寫錯(cuò)代碼就可以用那個(gè)按鈕退出程序了.
這一節(jié)也就寫到這里了,可以大家還是有點(diǎn)糊涂,慢慢體會(huì)一下就好了.還是不明白可以來這里提問.
作者: meranda    時(shí)間: 2008-11-6 19:46
我的貼,有人看,沒人回!
沒信心發(fā)了。
作者: yas123    時(shí)間: 2008-11-6 22:27

沙發(fā),外菜來頂一下!!!!






歡迎光臨 (http://www.torrancerestoration.com/bbs/) Powered by Discuz! X3.1