• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            兔子的技術(shù)博客

            兔子

               :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
              202 Posts :: 0 Stories :: 43 Comments :: 0 Trackbacks

            留言簿(10)

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            轉(zhuǎn)自:http://blog.csdn.net/skyever2100/archive/2008/11/13/3292480.aspx

            一、             概述

            在Windows Form應(yīng)用中,Windows界面系統(tǒng)通過消息與應(yīng)用程序進(jìn)行交互,每個(gè)窗口都有相應(yīng)的消息處理器,處理各自的用戶輸入及界面重繪等邏輯。窗口都有自己的類名,需要先把該類名及它對(duì)應(yīng)的消息處理器注冊(cè)到Windows界面系統(tǒng)中,再根據(jù)該類名來創(chuàng)建自己的窗口。

            Windows也為我們準(zhǔn)備了文本輸入框,對(duì)于簡(jiǎn)單的文本輸入,這個(gè)功能已經(jīng)很完美了,不過如果我們要做一個(gè)功能強(qiáng)大的文本編輯器,就像開發(fā)環(huán)境的IDE那樣,那么從頭來寫它會(huì)更好,可以實(shí)現(xiàn)我們想要的任何邏輯。

            文本框是這樣一個(gè)窗口,它響應(yīng)鍵盤消息,并實(shí)時(shí)重繪窗口中的文本,還要響應(yīng)鼠標(biāo)消息來移動(dòng)光標(biāo)位置。

            我嘗試著用Windows API來實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的單行文本框,它僅有以下幾個(gè)功能:

            1、  響應(yīng)用戶的普通字符輸入

            2、  可以用光標(biāo)鍵及HOME、END鍵來移動(dòng)光標(biāo)

            3、  可以用鼠標(biāo)鍵來移動(dòng)光標(biāo)

            4、  可以用BACKSPACE及DELETE鍵來刪除輸入的內(nèi)容

            另外,它不具有選擇文本的功能及剪切、復(fù)制、粘貼等功能,這個(gè)文本框是用純C來寫的,不具有對(duì)象化的特征,也就是說,沒有將代碼封裝成類,不能在界面上放置兩個(gè)文本框,這是為了簡(jiǎn)化代碼,只說明它的原理,如果要封裝成類,可以采用MFC等類庫(kù)來編寫這個(gè)文本框。

            在本文的最后,附帶了本程序的全部代碼,為了書寫方便,把所有的代碼都放在了一個(gè)代碼文件中了。

            本文本框運(yùn)行界面如下:

             

             

             

            二、             技術(shù)要點(diǎn)

            1、 注冊(cè)文本框類并創(chuàng)建文本框窗口

            可以使用API函數(shù)RegisterClassEx來注冊(cè)文本框類,如下:

            WNDCLASSEX wc;

                 ::ZeroMemory(&wc, sizeof(wc));

                 wc.cbSize     = sizeof(wc);

                 wc.style      = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;   // 指定當(dāng)窗口尺寸發(fā)生變化時(shí)重繪窗口,并且響應(yīng)鼠標(biāo)雙擊事件

                 wc.hInstance  = _HInstance;

                 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // 指定窗口背景顏色為系統(tǒng)顏色“窗口背景”

                 wc.lpszClassName = _T("MySimpleTextBox"); // 指定要注冊(cè)的窗口類名,創(chuàng)建窗口時(shí)要以此類名為標(biāo)識(shí)符

                 wc.lpfnWndProc     = _TextBoxWndProc; // 處理窗口消息的函數(shù)

            ::RegisterClassEx(&wc);              // 調(diào)用API函數(shù)注冊(cè)文本框窗口

            在注冊(cè)文本框類的時(shí)候,需要為其指定消息處理過程,就是那個(gè)名為_TextBoxWndProc的函數(shù),函數(shù)原型如下:

            LRESULT CALLBACK _TextBoxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

            使用類名“MySimpleTextBox”來注冊(cè)了一個(gè)文本框類,并且為其指定了消息處理過程,下一步就是要使用這個(gè)類名來創(chuàng)建一個(gè)文本框了,使用CreateWindow可以創(chuàng)建該窗口:

            HWND hWnd = ::CreateWindow(__T("MySimpleTextBox"), NULL, WS_CHILDWINDOW | WS_VISIBLE,

            left, top, width, height, hParentWnd, NULL, _HInstance, NULL);

            其中的left、top為、width、height為文本框的位置及尺寸,_HInstance為父窗口的句柄。

             

            2、 繪制文本框及文本

            在文本框的消息處理過程中,響應(yīng)消息WM_PAINT,可以實(shí)現(xiàn)對(duì)文本的繪制,假設(shè)使用默認(rèn)的字體及字號(hào),則代碼如下:

                 static PAINTSTRUCT ps;

                 static RECT rect;

                 HDC hDC = ::BeginPaint(hWnd, &ps);  // 開始繪制操作

                 ::GetClientRect(hWnd, &rect);        // 獲取客戶區(qū)的尺寸

                 ::DrawEdge(hDC, &rect, EDGE_SUNKEN, BF_RECT);  // 繪制邊框,EDGE_SUNKEN表示繪制樣式為內(nèi)嵌樣式,BF_RECT表示繪制矩形邊框

                 int len = ::_tcslen(_String);

            ::TextOut(hDC, 4, 2, _String, len);

            ::EndPaint(hWnd, &ps);               // 結(jié)束繪制操作

            其中,_String為定義的一個(gè)全局變量:TCHAR _String[TEXTBOX_MAXLENGTH+1];

            其中API函數(shù)DrawEdge可以繪制文本框的邊緣。

             

            3、 光標(biāo)操作

            我們可以自己一繪制閃爍的光標(biāo),不過Windows為我們提供了一套和光標(biāo)有關(guān)的API函數(shù),可以省去我們繪制光標(biāo)的繁瑣過程:

            CreateCaret(HWND hWnd, HBITMAP hBitmap, int width, int height);

            該API函數(shù)用于創(chuàng)建一個(gè)光標(biāo),第一個(gè)參數(shù)是窗口的句柄,第二個(gè)參數(shù)是光標(biāo)的位圖,用于定義光標(biāo)的形狀,第三、四個(gè)參數(shù)為光標(biāo)的尺寸。

            我們通常見到的光標(biāo)是一個(gè)黑色的豎線,在Insert模式下(按了Insert鍵)為一個(gè)黑色的方塊,如果是使用這種簡(jiǎn)單的光標(biāo),就把第二個(gè)參數(shù)設(shè)置為NULL就可以了。

            ShowCaret(HWND hWnd);

            該API函數(shù)用于顯示光標(biāo),其中并沒有指定顯示哪個(gè)光標(biāo)的參數(shù),這是因?yàn)楣鈽?biāo)是與當(dāng)前線程有關(guān)的資源,一個(gè)線程只能創(chuàng)建一個(gè)光標(biāo),在該線程中創(chuàng)建的光標(biāo),可以在該線程中對(duì)它執(zhí)行其它的操作。

            SetCaretPos(int x, int y);

            該API函數(shù)用于設(shè)置光標(biāo)的位置,當(dāng)輸入字符后或響應(yīng)光標(biāo)鍵時(shí),可以調(diào)用該函數(shù)重新設(shè)置光標(biāo)的位置。

            在調(diào)用CrateCaret創(chuàng)建了光標(biāo)時(shí),它是隱藏狀態(tài),要顯示它需要調(diào)用ShowCaret函數(shù)。

            HideCaret(HWND hWnd);

            該API函數(shù)用于隱藏光標(biāo)。如果兩次調(diào)用了HideCaret來隱藏光標(biāo),也需要調(diào)用兩次ShowCaret才能顯示它。

            DestroyCaret(HWND hWnd);

            該API函數(shù)用于銷毀光標(biāo)。

             

            通常來說,我們需要在文本框得到焦點(diǎn)的時(shí)候創(chuàng)建并顯示光標(biāo),而在文本框失去焦點(diǎn)的時(shí)候隱藏并銷毀光標(biāo)。

            通過處理兩個(gè)Windows Form消息可以實(shí)現(xiàn)上面的邏輯:

            處理WM_SETFOCUS消息創(chuàng)建并顯示光標(biāo):

            ::CreateCaret(hWnd, (HBITMAP)NULL, 1, TEXTBOX_HEIGHT-5); // 創(chuàng)建光標(biāo)

            ::SetCaretPos(x, y);                  // 設(shè)置光標(biāo)位置

            ::ShowCaret(hWnd);                    // 顯示光標(biāo)

             

            處理WM_KILLFOCUS消息隱藏并銷毀光標(biāo):

            ::HideCaret(hWnd);                    // 隱藏光標(biāo)

            ::DestroyCaret();                     // 銷毀光標(biāo)

            在窗口繪制之前,我們需要先隱藏光標(biāo),繪制完成之后再顯示光標(biāo),否則屏幕上將會(huì)殘留光標(biāo)的痕跡,但在處理WM_PAINT消息時(shí)我們并沒有這樣做,是因?yàn)锽eginPaint和EndPaint已經(jīng)為我們做了這件事情。

             

            4、 響應(yīng)按鍵消息

            Windows有若干與鍵盤相關(guān)的消息,例如:WM_KEYDOWN、WM_KEYUP、WM_CHAR等,我們需要處理WM_CHAR消息在光標(biāo)的位置顯示所輸入的字符,消息處理過程的參數(shù)wParam即為所輸入的字符,顯示出字符之后,需要調(diào)用SetCaretPos來重新設(shè)置光標(biāo)位置。

            如何將字符立即顯示出來呢,首先要指定文本框上的無效區(qū)域,按照Windows Form的編程約定,當(dāng)Windows發(fā)現(xiàn)某個(gè)窗口上出現(xiàn)無效區(qū)域時(shí),會(huì)向該窗口發(fā)送WM_PAINT消息來通知消息處理過程重繪這個(gè)區(qū)域。

            API函數(shù)InvalidateRect可以指定窗口的某個(gè)區(qū)域無效,最簡(jiǎn)單的辦法是讓整個(gè)窗口都無效,如下:

                 RECT rect;

                 ::GetClientRect(hWnd, &rect);

                 ::InvalidateRect(hWnd, &rect, TRUE);

            ::UpdateWindow(hWnd);

            API函數(shù)UpdateWindow在執(zhí)行時(shí)會(huì)立即重繪窗口,以便讓用戶的輸入會(huì)在界面上及時(shí)做出響應(yīng)。

            光標(biāo)鍵及HOME、END等鍵不會(huì)產(chǎn)生WM_CHAR消息,我們可以響應(yīng)WM_KEYDOWN消息來移動(dòng)光標(biāo)的位置。

             

            5、 響應(yīng)鼠標(biāo)消息

            我們需要處理鼠標(biāo)的單擊消息WM_LBUTTONDOWN來移動(dòng)光標(biāo),如何知道鼠標(biāo)點(diǎn)擊在哪個(gè)字符上呢,也就是如何取得指定位置處的字符呢?Windows API并沒有為我們提供現(xiàn)成的功能,好在寫一個(gè)這樣的功能也不復(fù)雜,API函數(shù)GetTextExtentPoint(HDC hDc, LPCSTR lpString, int count, LPSIZE lpSize)可以獲取指定的設(shè)置描述表下,指定字符串的尺寸。

            基于這樣的原理,我們可以逐漸求得每個(gè)字符所在的位置,與鼠標(biāo)單擊的位置來對(duì)照,便可以計(jì)算出鼠標(biāo)是單擊了哪個(gè)字符,如下:

                 int x = LOWORD(lParam);

                 HDC hDc = ::GetDC(hWnd);

                 int strLen = ::_tcslen(_String), strPos = 0;

                 SIZE size;

                 for (strPos=0; strPos<strLen; strPos++)

                 {

                     ::GetTextExtentPoint(hDc, _String, strPos, &size);

                     if(size.cx + 4 >= x)

                          break;

                 }

                 _StringPosition = strPos;

                 ::GetTextExtentPoint(hDc, _String, strPos, &size);

                 ::SetCaretPos(size.cx + 4, 3);

                 ::ReleaseDC(hWnd, hDc);

             

            三、             遺留問題

            1、 文本緩沖區(qū)問題

            本示例中為了簡(jiǎn)單起見,定義了一個(gè)固定大小的文本緩沖區(qū),當(dāng)輸入的字符數(shù)量到達(dá)固定大小時(shí),將忽略字符消息的處理。顯然這種處理方式不實(shí)用,當(dāng)文本較少時(shí)會(huì)造成內(nèi)存緩存區(qū)的浪費(fèi),當(dāng)文本較多時(shí)內(nèi)存緩沖區(qū)不能夠滿足要求,并且插入和刪除字符時(shí),都會(huì)移動(dòng)大量的文本,效率也比較慢。

            我們需要用變長(zhǎng)的字符串來解決字符緩沖區(qū)大小這個(gè)問題,變長(zhǎng)字符串會(huì)有許多邏輯,可以用類來封裝這些邏輯,例如MFC中的CString類。

             

            2、 如何用面向?qū)ο蟮乃季S來寫一個(gè)文本框

            本簡(jiǎn)單的文本框顯示不具有重用特征,字符串緩沖區(qū)、光標(biāo)位置等數(shù)據(jù)都定義為全局變量,這導(dǎo)致無法在界面上放置兩個(gè)文本框。如果采用面向?qū)ο蟮倪壿嫞瑧?yīng)該把這些數(shù)據(jù)封裝在一個(gè)類中,之所以沒有采用面向?qū)ο蟮姆绞絹韺懀且驗(yàn)閃indows API本身就是面向過程的,從整體架構(gòu)上來講,我們需要實(shí)現(xiàn)一套面向?qū)ο蟮拈_發(fā)框架,定義各種窗口共有的基類,在這個(gè)基類上派出生各種窗口,例如MFC就是這樣做的。

             

            3、 文本選擇的邏輯

            實(shí)現(xiàn)這個(gè)邏輯的關(guān)系在于以下兩點(diǎn):

            一是當(dāng)用戶拖拽鼠標(biāo)或用Shift+光標(biāo)鍵等進(jìn)行選擇時(shí),消息處理過程需要對(duì)這些鼠標(biāo)和鍵盤的消息正確地響應(yīng),確定出當(dāng)前所選擇的區(qū)域

            二是如何向用戶呈現(xiàn)所選擇的文本區(qū)域,通常它們具有指定顏色的底色,這牽涉到界面重繪的問題。可以對(duì)這部分文本設(shè)置好背景色和前景色進(jìn)行繪制。

             

            4、 重繪的效率問題

            本示例中每次輸入和刪除都要重繪整個(gè)文本區(qū)域,實(shí)際上,我們可以判斷窗口哪個(gè)位置無效了,一般是光標(biāo)后面的文本無效。在繪制時(shí)先取得其無效區(qū)域,僅對(duì)這一小部分進(jìn)行繪制,可以提高重繪效率。

             

            5、 多行文本的問題

            顯然,該示例程序只能輸入單行文本,如果要輸入多行文本,可以響應(yīng)回車鍵另起一行,在窗口繪制時(shí),如果遇到回車鍵,便跳到下一行的最左側(cè)區(qū)域進(jìn)行繪制。

            也可以采取每行文本使用一個(gè)字符串緩沖區(qū)的辦法,以防止在大量文本時(shí)引起的大量?jī)?nèi)存移動(dòng),這需要定義一個(gè)文本管理器的類來處理多個(gè)緩沖區(qū)的邏輯。

             

            6、 其它問題

            圍繞文本編輯器可以展開若干問題,例如字體、字號(hào)、顏色、行間距等,更高級(jí)的,像開發(fā)環(huán)境的IDE,會(huì)自動(dòng)把關(guān)鍵字突出顯示,如果要做這樣一個(gè)文本編輯器,就是非常復(fù)雜的事情了,不過辦法總比問題多,這些有激情的問題會(huì)帶領(lǐng)我們進(jìn)入一個(gè)廣闊的思維空間。

             

              為了書寫方便,把所有的代碼都放在了一個(gè)代碼文件中了。

             

              關(guān)于對(duì)該代碼技術(shù)要點(diǎn)的解釋,請(qǐng)參見:《用Windows API實(shí)現(xiàn)一個(gè)簡(jiǎn)單的文本輸入框(上)》

             

              該代碼中大部分地方都加了注釋,有不妥之處,敬請(qǐng)批評(píng)指正:

              1 #include <tchar.h>
              2 
              3 #include <windows.h>
              4 
              5  
              6 
              7 HINSTANCE _HInstance;                              // 應(yīng)用程序句柄
              8 
              9 TCHAR _Title[] = _T("簡(jiǎn)單文本框");                 // 定義窗口的標(biāo)題
             10 
             11  
             12 
             13 TCHAR _WindowClass[] = _T("MySimpleTextBoxApp");// 主窗口類名
             14 
             15 ATOM _RegisterClass();                             // 注冊(cè)主窗口類
             16 
             17 HWND _CreateWindow(int nCmdShow);                  // 創(chuàng)建主窗口
             18 
             19 LRESULT CALLBACK _WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);   // 主窗口消息處理函數(shù)
             20 
             21  
             22 
             23 TCHAR _TextBoxClass[] = _T("MySimpleTextBox"); // 文本框的類名
             24 
             25 ATOM _RegisterTextBoxClass();                      // 注冊(cè)文本框的類
             26 
             27 HWND _CreateTextBoxWindow(HWND hParentWnd);        // 創(chuàng)建文本框
             28 
             29 LRESULT CALLBACK _TextBoxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); // 文本框窗口消息處理函數(shù)
             30 
             31 void _DrawText(HDC hDC);                           // 繪制文本
             32 
             33 void _SetCaretPos(HWND hWnd);                      // 設(shè)置光標(biāo)位置
             34 
             35 void _UpdateWindow(HWND hWnd);                     // 更新窗口
             36 
             37  
             38 
             39  
             40 
             41 // 一些常量定義
             42 
             43 #define MAINWINDOW_WIDTH    400      // 主窗口寬度
             44 
             45 #define MAINWINDOW_HEIGHT   200      // 主窗口高度
             46 
             47 #define TEXTBOX_WIDTH       300      // 文本框?qū)挾?/span>
             48 
             49 #define TEXTBOX_HEIGHT      20       // 文本框高度
             50 
             51 #define TEXTBOX_MAXLENGTH   1024 // 文本框中文本的最大長(zhǎng)度
             52 
             53  
             54 
             55 TCHAR _String[TEXTBOX_MAXLENGTH + 1= _T("");     // 文本
             56 
             57 int    _StringPosition = ::_tcslen(_String);        // 光標(biāo)插入點(diǎn)所在的位置
             58 
             59  
             60 
             61 int APIENTRY _tWinMain(HINSTANCE hInstance,        // 當(dāng)前的應(yīng)用程序句柄
             62 
             63                           HINSTANCE hPrevInstance, // 前一個(gè)應(yīng)用程序?qū)嵗木浔ㄔ赪in32上,始終為NULL)
             64 
             65                           LPTSTR lpCmdLine,        // 命令行參數(shù)
             66 
             67                           int        nCmdShow     // 窗口的顯示樣式
             68 
             69                           )
             70 
             71 {
             72 
             73      _HInstance = hInstance;
             74 
             75  
             76 
             77      _RegisterClass();                         // 注冊(cè)窗口類
             78 
             79      if(_CreateWindow(nCmdShow) == NULL)       // 創(chuàng)建窗口
             80 
             81          return FALSE;
             82 
             83  
             84 
             85      MSG msg;
             86 
             87      while (::GetMessage(&msg, NULL, 00))    // 從消息隊(duì)列中獲取消息
             88 
             89      {
             90 
             91          ::TranslateMessage(&msg);            // 轉(zhuǎn)譯一些特殊的消息
             92 
             93          ::DispatchMessage(&msg);             // 執(zhí)行消息處理
             94 
             95      }
             96 
             97  
             98 
             99      return (int)msg.wParam;
            100 
            101 }
            102 
            103  
            104 
            105  
            106 
            107 // 注冊(cè)應(yīng)用程序窗口類
            108 
            109 ATOM _RegisterClass()
            110 
            111 {
            112 
            113      WNDCLASSEX wc;
            114 
            115      ::ZeroMemory(&wc, sizeof(wc));                 // 作為一步清空,是為了讓未賦值的字段的默認(rèn)值為(或NULL)
            116 
            117  
            118 
            119      wc.cbSize     = sizeof(wc);
            120 
            121      wc.style      = CS_HREDRAW | CS_VREDRAW;  // 指定當(dāng)窗口橫向和縱向的尺寸發(fā)生變化時(shí)都會(huì)重繪窗口
            122 
            123      wc.hInstance  = _HInstance;
            124 
            125      wc.hbrBackground = (HBRUSH)( COLOR_APPWORKSPACE + 1);  // 指定主窗口背景為“工作區(qū)域”系統(tǒng)顏色
            126 
            127      wc.lpszClassName = _WindowClass;          // 此為要注冊(cè)的類名,創(chuàng)建窗口時(shí)要以此類名為標(biāo)識(shí)符
            128 
            129      wc.lpfnWndProc     = _WndProc;                      // 此為處理窗口消息的函數(shù)
            130 
            131  
            132 
            133      return ::RegisterClassEx(&wc);                 // 調(diào)用API函數(shù)注冊(cè)窗口類
            134 
            135 }
            136 
            137  
            138 
            139 // 創(chuàng)建窗口
            140 
            141 HWND _CreateWindow(int nCmdShow)
            142 
            143 {
            144 
            145      HWND hWnd = ::CreateWindow(_WindowClass, _Title, WS_OVERLAPPEDWINDOW, 
            146 
            147          CW_USEDEFAULT, CW_USEDEFAULT, MAINWINDOW_WIDTH, MAINWINDOW_HEIGHT, NULL, NULL, _HInstance, NULL);
            148 
            149  
            150 
            151      if(hWnd == NULL)
            152 
            153          return NULL;
            154 
            155  
            156 
            157      ::ShowWindow(hWnd, nCmdShow);
            158 
            159      ::UpdateWindow(hWnd);
            160 
            161  
            162 
            163      return hWnd;
            164 
            165 }
            166 
            167  
            168 
            169 // 窗口處理過程
            170 
            171 LRESULT CALLBACK _WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
            172 
            173 {
            174 
            175      static HWND hTextBoxWnd;
            176 
            177  
            178 
            179      switch (message)
            180 
            181      {
            182 
            183      case WM_CREATE: {
            184 
            185          _RegisterTextBoxClass();    // 注冊(cè)文本框的類
            186 
            187          hTextBoxWnd = _CreateTextBoxWindow(hWnd); // 創(chuàng)建文本框
            188 
            189          } break;
            190 
            191  
            192 
            193      case WM_ACTIVATE:                // 當(dāng)窗口被激活時(shí),將焦點(diǎn)設(shè)置在文本框上
            194 
            195          ::SetFocus(hTextBoxWnd);
            196 
            197          break;
            198 
            199  
            200 
            201      case WM_SETCURSOR: {  // 設(shè)置光標(biāo)形狀
            202 
            203          static HCURSOR hCursor = ::LoadCursor(NULL, IDC_ARROW);
            204 
            205          ::SetCursor(hCursor);
            206 
            207          } break;
            208 
            209  
            210 
            211      case WM_DESTROY:   // 應(yīng)用程序被關(guān)閉
            212 
            213          ::PostQuitMessage(0);
            214 
            215          break;
            216 
            217  
            218 
            219      default:
            220 
            221          return ::DefWindowProc(hWnd, message, wParam, lParam);
            222 
            223      }
            224 
            225  
            226 
            227      return (LRESULT)0;
            228 
            229 }
            230 
            231  
            232 
            233 // 注冊(cè)文本框的類
            234 
            235 ATOM _RegisterTextBoxClass()
            236 
            237 {
            238 
            239      WNDCLASSEX wc;
            240 
            241      ::ZeroMemory(&wc, sizeof(wc));
            242 
            243  
            244 
            245      wc.cbSize     = sizeof(wc);
            246 
            247      wc.style      = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;   // 指定當(dāng)窗口尺寸發(fā)生變化時(shí)重繪窗口,并且響應(yīng)鼠標(biāo)雙擊事件
            248 
            249      wc.hInstance  = _HInstance;
            250 
            251      wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // 指定窗口背景顏色為系統(tǒng)顏色“窗口背景”
            252 
            253      wc.lpszClassName = _TextBoxClass;                  // 指定要注冊(cè)的窗口類名,創(chuàng)建窗口時(shí)要以此類名為標(biāo)識(shí)符
            254 
            255      wc.lpfnWndProc     = _TextBoxWndProc;               // 處理窗口消息的函數(shù)
            256 
            257  
            258 
            259      return ::RegisterClassEx(&wc);                     // 調(diào)用API函數(shù)注冊(cè)文本框窗口
            260 
            261 }
            262 
            263  
            264 
            265  
            266 
            267 // 創(chuàng)建文本框
            268 
            269 HWND _CreateTextBoxWindow(HWND hParentWnd)
            270 
            271 {
            272 
            273      // 之下代碼是為了讓文本框顯示在父窗口中央,而計(jì)算位置
            274 
            275      RECT parentWndRect;
            276 
            277      ::GetClientRect(hParentWnd, &parentWndRect);  // 獲取父窗口客戶區(qū)的位置
            278 
            279      int left = (parentWndRect.right - TEXTBOX_WIDTH) / 2, top = (parentWndRect.bottom - TEXTBOX_HEIGHT) / 2;
            280 
            281  
            282 
            283      // 創(chuàng)建文本框
            284 
            285      HWND hWnd = ::CreateWindow(_TextBoxClass, NULL, WS_CHILDWINDOW | WS_VISIBLE,
            286 
            287          left, top, TEXTBOX_WIDTH, TEXTBOX_HEIGHT, 
            288 
            289          hParentWnd, NULL, _HInstance, NULL);
            290 
            291  
            292 
            293      return hWnd;
            294 
            295 }
            296 
            297  
            298 
            299 // 文本框消息的處理過程
            300 
            301 LRESULT CALLBACK _TextBoxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
            302 
            303 {
            304 
            305      switch (message)
            306 
            307      {
            308 
            309      case WM_PAINT: {  // 繪制這里之所以加一對(duì)大括號(hào),是為了讓之下定義的變量局部化
            310 
            311  
            312 
            313          static PAINTSTRUCT ps;
            314 
            315          static RECT rect;
            316 
            317          HDC hDC = ::BeginPaint(hWnd, &ps);  // 開始繪制操作
            318 
            319  
            320 
            321          ::GetClientRect(hWnd, &rect);        // 獲取客戶區(qū)的尺寸
            322 
            323          ::DrawEdge(hDC, &rect, EDGE_SUNKEN, BF_RECT);  // 繪制邊框,EDGE_SUNKEN表示繪制樣式為內(nèi)嵌樣式,BF_RECT表示繪制矩形邊框
            324 
            325          _DrawText(hDC);                      // 繪制文本
            326 
            327          ::EndPaint(hWnd, &ps);               // 結(jié)束繪制操作
            328 
            329  
            330 
            331          } break;
            332 
            333  
            334 
            335      case WM_SETFOCUS: {    // 獲得焦點(diǎn)
            336 
            337          ::CreateCaret(hWnd, (HBITMAP)NULL, 1, TEXTBOX_HEIGHT-5);     // 創(chuàng)建光標(biāo)
            338 
            339          _SetCaretPos(hWnd);                            // 設(shè)置光標(biāo)位置
            340 
            341          ::ShowCaret(hWnd);                   // 顯示光標(biāo)
            342 
            343          } break;
            344 
            345  
            346 
            347      case WM_KILLFOCUS: // 失去焦點(diǎn)
            348 
            349          ::HideCaret(hWnd);                   // 隱藏光標(biāo)
            350 
            351          ::DestroyCaret();                    // 銷毀光標(biāo)
            352 
            353          break;
            354 
            355  
            356 
            357      case WM_SETCURSOR: {  // 設(shè)置光標(biāo)形狀
            358 
            359          static HCURSOR hCursor = ::LoadCursor(NULL, IDC_IBEAM);
            360 
            361          ::SetCursor(hCursor);
            362 
            363          } break;
            364 
            365  
            366 
            367      case WM_CHAR: {    // 字符消息
            368 
            369          TCHAR code = (TCHAR)wParam;
            370 
            371          int len = ::_tcslen(_String);
            372 
            373          if(code < (TCHAR)' ' || len >= TEXTBOX_MAXLENGTH)
            374 
            375               return 0;
            376 
            377  
            378 
            379          ::MoveMemory(_String + _StringPosition + 1, _String + _StringPosition, (len - _StringPosition + 1* sizeof(TCHAR));
            380 
            381          _String[_StringPosition ++= code;
            382 
            383  
            384 
            385          _UpdateWindow(hWnd);
            386 
            387          _SetCaretPos(hWnd);
            388 
            389  
            390 
            391          } break;
            392 
            393  
            394 
            395      case WM_KEYDOWN: {  // 鍵按下消息
            396 
            397          TCHAR code = (TCHAR)wParam;
            398 
            399  
            400 
            401          switch (code)
            402 
            403          {
            404 
            405          case VK_LEFT: // 左光標(biāo)鍵
            406 
            407               if(_StringPosition > 0)
            408 
            409                    _StringPosition --;
            410 
            411               break;
            412 
            413  
            414 
            415          case VK_RIGHT:     // 右光標(biāo)鍵
            416 
            417               if(_StringPosition < (int)::_tcslen(_String))
            418 
            419                    _StringPosition ++;
            420 
            421               break;
            422 
            423  
            424 
            425          case VK_HOME: // HOME 鍵
            426 
            427               _StringPosition = 0;
            428 
            429               break;
            430 
            431  
            432 
            433          case VK_END:  // END 鍵
            434 
            435               _StringPosition = ::_tcslen(_String);
            436 
            437               break;
            438 
            439  
            440 
            441          case VK_BACK: // 退格鍵
            442 
            443               if(_StringPosition > 0)
            444 
            445               {
            446 
            447                    ::MoveMemory(_String + _StringPosition - 1, _String + _StringPosition, (::_tcslen(_String)-_StringPosition + 1* sizeof(TCHAR));
            448 
            449                    _StringPosition --;
            450 
            451                    _UpdateWindow(hWnd);
            452 
            453               }
            454 
            455               break;
            456 
            457  
            458 
            459          case VK_DELETE: {  // 刪除鍵
            460 
            461               int len = ::_tcslen(_String);
            462 
            463               if(_StringPosition < len)
            464 
            465               {
            466 
            467                    ::MoveMemory(_String + _StringPosition, _String + _StringPosition + 1, (::_tcslen(_String) - _StringPosition + 1* sizeof(TCHAR));
            468 
            469                    _UpdateWindow(hWnd);
            470 
            471               }
            472 
            473  
            474 
            475               } break;
            476 
            477  
            478 
            479          }
            480 
            481  
            482 
            483          _SetCaretPos(hWnd);
            484 
            485  
            486 
            487          } break;
            488 
            489  
            490 
            491      case WM_LBUTTONDOWN: {  // 鼠標(biāo)單擊,設(shè)置光標(biāo)位置
            492 
            493          int x = LOWORD(lParam);
            494 
            495          HDC hDc = ::GetDC(hWnd);
            496 
            497  
            498 
            499          int strLen = ::_tcslen(_String), strPos = 0;
            500 
            501          SIZE size;
            502 
            503  
            504 
            505          for (strPos=0; strPos<strLen; strPos++)
            506 
            507          {
            508 
            509               ::GetTextExtentPoint(hDc, _String, strPos, &size);
            510 
            511  
            512 
            513               if(size.cx + 4 >= x)
            514 
            515                    break;
            516 
            517          }
            518 
            519  
            520 
            521          _StringPosition = strPos;
            522 
            523          ::GetTextExtentPoint(hDc, _String, strPos, &size);
            524 
            525          ::SetCaretPos(size.cx + 43);
            526 
            527  
            528 
            529          ::ReleaseDC(hWnd, hDc);
            530 
            531  
            532 
            533          } break;
            534 
            535  
            536 
            537      default:
            538 
            539          return ::DefWindowProc(hWnd, message, wParam, lParam);
            540 
            541      }
            542 
            543  
            544 
            545      return (LRESULT)0;
            546 
            547 }
            548 
            549  
            550 
            551 // 更新窗口
            552 
            553 void _UpdateWindow(HWND hWnd)
            554 
            555 {
            556 
            557      RECT rect;
            558 
            559      ::GetClientRect(hWnd, &rect);
            560 
            561      ::InvalidateRect(hWnd, &rect, TRUE);
            562 
            563      ::UpdateWindow(hWnd);
            564 
            565 }
            566 
            567  
            568 
            569 // 繪制文本
            570 
            571 void _DrawText(HDC hDC)
            572 
            573 {
            574 
            575      int len = ::_tcslen(_String);
            576 
            577      ::TextOut(hDC, 42, _String, len);
            578 
            579 }
            580 
            581  
            582 
            583 // 設(shè)置光標(biāo)位置
            584 
            585 void _SetCaretPos(HWND hWnd)
            586 
            587 {
            588 
            589      HDC hDC = ::GetDC(hWnd);
            590 
            591  
            592 
            593      SIZE size;
            594 
            595      ::GetTextExtentPoint(hDC, _String, _StringPosition, &size);
            596 
            597      ::SetCaretPos(4 + size.cx, 3);
            598 
            599  
            600 
            601      ::ReleaseDC(hWnd, hDC);
            602 
            603  
            604 
            605 }
            606 
            607 
            posted on 2010-02-16 12:07 會(huì)飛的兔子 閱讀(10656) 評(píng)論(0)  編輯 收藏 引用 所屬分類: 系統(tǒng)API,底層技術(shù)
            青青热久久综合网伊人| 久久99国产精品99久久| 久久se精品一区二区影院 | 久久久久99这里有精品10| 久久福利片| 久久精品视频91| 久久精品成人免费国产片小草| 色综合久久综精品| 青青青伊人色综合久久| 久久久久一区二区三区| 夜夜亚洲天天久久| 久久国产精品无码网站| 久久久艹| 久久亚洲精品国产亚洲老地址| 亚洲人成无码久久电影网站| 色播久久人人爽人人爽人人片aV| 性做久久久久久久久老女人| 亚洲日本久久久午夜精品| 97香蕉久久夜色精品国产| 亚洲午夜久久久影院| 国产精品久久午夜夜伦鲁鲁| 国产精品久久久久天天影视| 天天综合久久久网| 久久久久国色AV免费看图片| 一本综合久久国产二区| 亚洲午夜久久久久久久久久| 久久亚洲精品国产精品| 久久久精品一区二区三区| 久久久久综合国产欧美一区二区| 三级三级久久三级久久| 蜜臀久久99精品久久久久久小说| 精品国产福利久久久| 久久久久亚洲精品男人的天堂| 欧美国产成人久久精品| 91视频国产91久久久| 久久婷婷人人澡人人| 亚洲AV成人无码久久精品老人| 久久综合九色综合97_久久久| 开心久久婷婷综合中文字幕| 色综合久久久久无码专区| 99久久国产亚洲高清观看2024|