?? 05.4 字符輸入.txt
字號:
5.4 字符輸入
下面要實現(xiàn)字符的輸入功能,也就是當(dāng)用戶在鍵盤上按下某個字符按鍵后,要把該宇符輸出到程序窗口上。這就需要程序捕獲鍵盤按下這一消息。在第一章中曾介紹過 WM CHAR消息,這里,我們可以捕獲這個消息,在該消息的響應(yīng)函數(shù)中完成字符輸出功能。但在字符輸出時有一個問題需要注意,利用 TextOut函數(shù)在窗口中輸出字符時,需要提供字符顯示位置的 x坐標(biāo)和 y坐標(biāo),例如,我們打算在 ( 0, 0 )位置處輸出用戶按鍵的字符,如果用戶先后按下了 "a飛飛", 66c"這三個字符,對于 " a"字符,輸出的位置是 ( 0,0 ) , 緊接著我們要在 "a"字符之后輸出字符 "b"t但我們?nèi)绾尾拍艽_定 "b"字符的輸出位置呢?這在實現(xiàn)時有一定的難度,因為每個字符在屏幕上所占據(jù)的寬度都是不一樣的,這樣我們要獲得下一個輸入點的坐標(biāo)就不太容易實現(xiàn)。為此,我們可以采取一種簡單的方式,把每次輸入的字符都存儲到一個字符串中,例如,按下了" a"和 "b"字符鍵之后,將這兩個字符組成一個字符串 :"ab飛當(dāng)隨后再按下 "c"鍵后,再把它與 "ab"組成一個字符串: "abc"。在程序中,每當(dāng)按下新的字符時,都在窗口當(dāng)前插入符的位置把這個字符串再重新輸出一次。因為人眼具有視覺殘留的效應(yīng),因此,用戶感覺不到這種重新輸出的變化,只能感覺到每按下一個字符時,窗口中就多了一個字符。
遵照這種思路,我們繼續(xù)在己有的 Text程序中添加功能,首先讓 CTextView類捕獲 WM_CHAR消息,接著為該類定義一個 CString類型的成員變量 : m s位Line,專門用來存儲輸入的字符串,并在 CTextView類的構(gòu)造函數(shù)中將這個變量初始化為空,初始化代碼為:
這里仍有幾個問題需要注意,第一個問題是,程序應(yīng)該在當(dāng)前插入符的位置輸出字符。也就是說,程序運行時,如果用鼠標(biāo)左鍵單擊窗口中某個位置,那么插入符就應(yīng)該移動到這個地方,隨后輸入的字符都應(yīng)在此位置處往后輸出。這樣的話,我們還需要捕獲鼠標(biāo)左鍵按下消息 ( WM_ LBUTTONDOWN),在該消息響應(yīng)函數(shù)中,把插入符移動到鼠標(biāo)左鍵單擊點處。這可以利用 CWnd類的 SetCaretPos函數(shù)來實現(xiàn)。該函數(shù)的聲明形式為:
static void PASCAL SetCaretPos( POINT point };
從該函數(shù)的聲明可以如道,它是一個靜態(tài)函數(shù),并帶有一個 POINT結(jié)構(gòu)體類型的參數(shù),該參數(shù)表示一個點。本例中,這個點就是鼠標(biāo)左鍵單擊點。因此,我們就可以在鼠標(biāo)左鍵按下這一消息的響應(yīng)函數(shù)中添加例 5-11所示代碼中加灰顯示的那行代碼,以完成插入符移動到當(dāng)前鼠標(biāo)左鍵單擊點處的功能。
例5-11
void CTextView : :OnLButtonDown(UINT nFlags , CPoint point)
{ // TODO : Add your message handler code here and/ or call default
SetCaretPos(point);
CView : :OnLButtonDown(nFl ags , point);
Build井運行Text程序,然后用鼠標(biāo)左鍵在程序窗口中任意位置處單擊,將會發(fā)現(xiàn)插入符隨著鼠標(biāo)左鍵的單擊而移動。程序結(jié)果如圖5.17所示。
圖 5.17插入符隨鼠標(biāo)左鍵單擊而移動的效果
第二個需要注意的問題是,用來存儲輸入的字符串的成員變量: m strLine的取值變化問題。當(dāng)用鼠標(biāo)左鍵單擊窗口中一個新的地方時,插入符就會移動到這個新位置,那么以后輸入的字符都應(yīng)從這個位置處開始輸出,而以前輸入的字符不應(yīng)再從此位置處重新輸出,因此,這時就要把m strLine中己有的內(nèi)容清空。這可以利用 CString類的成員函數(shù)
Empty來實現(xiàn)。于是,我們在上述例 5-11所示代碼中添加 Empty函數(shù)調(diào)用, 清空 m s位Line
中的內(nèi)容,結(jié)果如例 5-12所示。
void CTextView : :OnLButtonDown(INT nFlags , CPoint point) {
// TODO: Add your message handler code here and/o工 call default
SetCaretPos(point);
m_strLine.Empty();
CView::OnLButtonDown(nFlags , point);
第三個問題是,因為每次輸入的字符串都應(yīng)在當(dāng)前插入符位置,也就是鼠標(biāo)左鍵單擊點處開始顯示。這樣,就需要把鼠標(biāo)左鍵單擊點的坐標(biāo)保存起來,以便在 OnChar函數(shù)中使用。于是我們?yōu)镃TextView類再增加一個CPoint類型的成員變量來保存這個坐標(biāo)值,將這個變量取名為: m_ptOrigin,井將其訪問權(quán)限設(shè)置為私有的。然后在CTextView類的構(gòu)
造函數(shù)中設(shè)置其初值為 0;接著在鼠標(biāo)左鍵按下這一消息的響應(yīng)函數(shù)中保存當(dāng)前鼠標(biāo)單擊點,代碼如例 5-13所示。
void CTextView: :OnLButtonDown(UINT nFlags , CPoint point )
// TODO : Add your rnessage handler code here and/ or c a ll default
SetCaretPos(point) ;
n_strLine . Enpty () ;
m_ptOrigin = point;
CView: :OnLBut tonDown(nFlags , p o int) ;
第四個問題是,在輸出字符時,還應(yīng)考慮到回車字符的處理。按下回車鍵后,插入符應(yīng)換到下一行,隨后的輸入也應(yīng)從這一新行開始輸出。這樣就需要請空上一行保存的字符,并計算插入符在下一行的新位置。這時插入符的橫坐標(biāo)不變,只有縱坐標(biāo)發(fā)生了變化,而利用己保存的當(dāng)前插入點的縱坐標(biāo)加上當(dāng)前字體的高度就可以得到回車后插入符的新位置的縱坐標(biāo)。使用前面已經(jīng)介紹過的 GetTextMetrics函數(shù),即可獲得當(dāng)前設(shè)備描述表中字體的高度信息。
第五個問題是,在輸出字符時,還要處理一個特殊的字符:退格鍵 ( ep Backspace鍵〉。當(dāng)按下退格鍵后,應(yīng)該刪除屏幕上位于插入符前面的那個字符,也就是將這個字符從屏幕上抹掉,同時,插入符的位置應(yīng)回退一個字符。這個問題的處理也有一定的困難。我們可以采用一種取巧的實現(xiàn)。我們知道刪除一個字符,也就是讓用戶在屏幕上看不見這個字符。另外,如果文本的顏色與背景色一樣的話,在屏幕上就看不到這個文本了,給用戶的感覺就是刪除了這個文本。因此,我們可以先把文本的顏色設(shè)置為背景色,在窗口中把該文本輸出一次。然后從保存輸入字符的字符串變量 ( ID_strLine )中把要刪除的字符刪除,再把文本的顏色設(shè)置為原來的顏色,之后再把字符串在窗口中輸出一次。這時在屏幕上看到的就是正確的刪除效果。因為這些操作都是連續(xù)的操作,而且執(zhí)行的時間很短,給用戶的感覺就是一按退格鍵,就刪除了插入符前面的那個字符。
在具體實現(xiàn)時,為了獲取背景色,可以利用 CDC類的 GetBkColor函數(shù)。而設(shè)置文本顏色,可以利用 CDC類提供的另一個成員函數(shù) SetTextColor,該函數(shù)的聲明如下所示 :
virtual COLORREF SetTextColor( COLORREF crColor );
這個函數(shù)將會返回文本先前的顏色。我們需要把這個返回值保存起來,因為后面還要把文本的顏色設(shè)置回先前的顏色再次顯示。如果想要實現(xiàn)從字符串中刪除一個字符,則可以利用 CString類的Left函數(shù),該函數(shù)的聲明形式如下所示 :
CString Left( int nCount ) const;
這個函數(shù)返回一個 CString對象,即返回指定字符串左邊指定數(shù)目 ( nCount參數(shù)指定 )的字符。例如字符串的值為 "Windows",如果指定參數(shù)值為 3,調(diào)用 Left函數(shù),那么將返
回字符串:"Win",即"Windows "左邊的三個字符。因為本例要刪除字符串最右邊的那個字符,所以可以將Left函數(shù)的參數(shù)指定為待顯示字符串中字符個數(shù)減去數(shù)值1后得到的數(shù)值即可。利用CString類提供的GetLength函數(shù),可以得到指定字符串中字符的個數(shù)。
如果當(dāng)前輸入的字符不是以上這兩種特殊字符(回車鍵和退格鍵),就應(yīng)該把它添加到m strLine變量中,以便在屏幕上輸出。
回車字符的ASCII碼十六進(jìn)制值是OxOd。退格鍵的 ASCII碼十六進(jìn)制值
解決了以上這些問題,就可以在WM CHAR消息響應(yīng)函數(shù)中進(jìn)行字符輸出的處理了,具體實現(xiàn)代碼如例5-14所示。
例5-14
1 . void CTextView : :OnChar(UINT nChar , UINT nRepCnt , UINT nF1ags)
2. {
3. /1 TODO : Add your message hand1er code here and/ or ca11 defau1t
4. CClientDC dc (this) ;
5. TEXTMETRI C tm ;
6. dc . GetTextMetrics (&tm) ;
7. if ( 0x0d == nChar)
8. {
9. m_strLine.Empty();
10. m-ptOrigin.y += tm.tmHeight ;
11. }
12. else if( Ox08 == nChar)
13. {
14. COLORREF clr = dC.SetTextColor(dc.GetBkColor());
15. dc.TextOut(m-ptOrigin.x,m-ptOrigin.y,m_strLine) ;
16. m_strLine = m_strLine.Left(m_strLine.GetLength() -1) ;
17. dc.SetTextColor(clr);
18. }
19. else
20. {
21.m_strLine+=nChar ;
22. }
23 . dc . TextOut(m-ptOrigin . x ,m-ptOrigin.y ,m_strLine);
24 . CView : :OnChar(nChar , nRepCnt , nF1ags) ;
25. }
提示:如果在編程時,不知道某個字符的ASCII值,可以在MSDN中以
"ASC."為索引進(jìn)行查找,即可找到需要的內(nèi)容。 Build并運行Text程序,將會發(fā)現(xiàn)程序現(xiàn)在能夠在當(dāng)前插入符位置處顯示輸入宇符了,
結(jié)果如圖 5.18所示。
圖 5.18字符輸入程序運行結(jié)果
先到此
但是會發(fā)現(xiàn)這個程序仍有問題,當(dāng)在屏幕上輸出字符時,插入符的位置并沒有改變。一般來說,插入符應(yīng)該隨著字符的輸入而移動。我們已經(jīng)知道可以利用 ' SetCaretPos函數(shù)來設(shè)置插入符的位置,但移動的位置如何確定呢?實際上,對于同一行上的輸入來說,插入符橫向移動的距離就是輸入字符的寬度,而其縱坐標(biāo)是不變的。根據(jù)前面的內(nèi)容,我們知道利用 GetTextExtent函數(shù)就可以得到字符串的寬度。因此,在上述例子 14所示代碼的第 23行之前添加例 5-15所示代碼,以實現(xiàn)插入符隨字符的輸入而移動這一功能。
.tl5-15
CSize sz = dc .GetTextExtent(m_strLine);
CPoint pt ;
pt.x = m-ptOrigin.x + sZ .cx;
pt .y = m-ptOrigin.y;
SetCaretPos(pt) ;
再次 Build井運行 Text程序,并任意輸入字符,可以發(fā)現(xiàn)程序屏幕上插入符隨著字符的輸入而移動了。 Text程序運行結(jié)果如圖 5.19所示。這時,我們也可以試試回車鍵和退格鍵的效果,可以發(fā)現(xiàn)均成功實現(xiàn)所需功能。
女事毓程
圖 5.19插入符隨著輸入的字符移動的效果
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -