【转】Windows编程之滚动条—滚动条消息
在用鼠標單擊滾動條或者拖動卷動方塊時,Windows給窗口消息處理程序發送WM_VSCROLL(供上下移動)和WM_HSCROLL(供左右移動)消息。在滾動條上的每個鼠標動作都至少產生兩個消息,一條在按下鼠標按鈕時產生,一條在釋放按鈕時產生。
和所有的消息一樣,WM_VSCROLL和WM_HSCROLL也帶有wParam和lParam消息參數。對于來自作為窗口的一部分而建立的滾動條消息,您可以忽略lParam;它只用于作為子窗口而建立的滾動條(通常在對話框內)。
wParam消息參數被分為一個低字組和一個高字組。wParam的低字組是一個數值,它指出了鼠標對滾動條進行的操作。這個數值被看作一個「通知碼」。通知碼的值由以SB(代表「scroll bar(滾動條)」)開頭的標識符定義。以下是在WINUSER.H中定義的通知碼:
#define SB_LINEUP 0#define SB_LINELEFT 0#define SB_LINEDOWN 1#define SB_LINERIGHT 1#define SB_PAGEUP 2#define SB_PAGELEFT 2#define SB_PAGEDOWN 3#define SB_PAGERIGHT 3#define SB_THUMBPOSITION 4#define SB_THUMBTRACK 5#define SB_TOP 6#define SB_LEFT 6#define SB_BOTTOM 7#define SB_RIGHT 7#define SB_ENDSCROLL 8
???????
包含LEFT和RIGHT的標識符用于水平滾動條,包含UP、DOWN、TOP和BOTTOM的標識符用于垂直滾動條。鼠標在滾動條的不同區域單擊所產生的通知碼如圖4-7所示。
?
?
如果在滾動條的各個部位按住鼠標鍵,程序就能收到多個滾動條消息。當釋放鼠標鍵后,程序會收到一個帶有SB_ENDSCROLL通知碼的消息。一般可以忽略這個消息,Windows不會去改變卷動方塊的位置,而您可以在程序中呼叫SetScrollPos來改變卷動方塊的位置。
當把鼠標的光標放在卷動方塊上并按住鼠標鍵時,您就可以移動卷動方塊。這樣就產生了帶有SB_THUMBTRACK和SB_THUMBPOSITION通知碼的滾動條消息。在wParam的低字組是SB_THUMBTRACK時,wParam的高字組是使用者在拖動卷動方塊時的目前位置。該位置位于卷動列范圍的最小值和最大值之間。在wParam的低字組是SB_THUMBPOSITION時,wParam的高字組是使用者釋放鼠標鍵后卷動方塊的最終位置。對于其它的卷動列操作,wParam的高字組應該被忽略。
為了給使用者提供回饋,Windows在您用鼠標拖動卷動方塊時移動它,同時您的程序會收到SB_THUMBTRACK消息。然而,如果不通過呼叫SetScrollPos來處理SB_THUMBTRACK或SB_THUMBPOSITION消息,在使用者釋放鼠標鍵后,卷動方塊會迅速跳回原來的位置。
程序能夠處理SB_THUMBTRACK或SB_THUMBPOSITION消息,但一般不同時處理兩者。如果處理SB_THUMBTRACK消息,在使用者拖動卷動方塊時您需要移動顯示區域的內容。而如果處理SB_THUMBPOSITION消息,則只需在使用者停止拖動卷動方塊時移動顯示區域的內容。處理SB_THUMBTRACK消息更好一些(但更困難),對于某些型態的數據,您的程序可能很難跟上產生的消息。
WINUSER.H表頭文件還包括SB_TOP、SB_BOTTOM、SB_LEFT和SB_RIGHT通知碼,指出滾動條已經被移到了它的最小或最大位置。然而,對于作為應用程序窗口一部分而建立的滾動條來說,永遠不會接收到這些通知碼。
在滾動條范圍使用32位的值也是有效的,盡管這不常見。然而,wParam的高字組只有16位的大小,它不能適當地指出SB_THUMBTRACK和SB_THUMBPOSITION操作的位置。在這種情況下,需要使用GetScrollInfo函數(在下面描述)來得到信息。
在SYSMETS中加入卷動功能
前面的說明已經很詳盡了,現在,要將那些東西動手做做看了。讓我們開始時簡單些,從垂直卷動著手,因為我們實在太需要垂直卷動了,而暫時還可以不用水平卷動。SYSMET2如程序4-3所示。這個程序可能是滾動條的最簡單的應用。
程序4-3 SYSMETS2.C????????
/*------------------------------------------------------------------SYSMETS2.C -- System Metrics Display Program No. 2(c) Charles Petzold, 1998------------------------------------------------------------------*/#include <windows.h>#include "sysmets.h"LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){static TCHAR szAppName[] = TEXT ("SysMets2") ;HWND hwnd ;MSG msg ;WNDCLASS wndclass ;wndclass.style = CS_HREDRAW | CS_VREDRAW ;wndclass.lpfnWndProc = WndProc ;wndclass.cbClsExtra = 0 ;wndclass.cbWndExtra = 0 ;wndclass.hInstance = hInstance ;wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;wndclass.lpszMenuName = NULL ;wndclass.lpszClassName = szAppName ;if (!RegisterClass (&wndclass)){MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;return 0 ;}hwnd = CreateWindow (szAppName, TEXT ("Get System Metrics No. 2"),WS_OVERLAPPEDWINDOW | WS_VSCROLL,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;ShowWindow (hwnd, iCmdShow) ;UpdateWindow (hwnd) ;while (GetMessage (&msg, NULL, 0, 0)){TranslateMessage (&msg) ;DispatchMessage (&msg) ;}return msg.wParam ;}LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){static int cxChar, cxCaps, cyChar, cyClient, iVscrollPos ;HDC hdc ; int i, y ; PAINTSTRUCT ps ;TCHAR szBuffer[10] ; TEXTMETRIC tm ; switch (message) {case WM_CREATE:hdc = GetDC (hwnd) ;GetTextMetrics (hdc, &tm) ;cxChar = tm.tmAveCharWidth ;cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;cyChar = tm.tmHeight + tm.tmExternalLeading ;ReleaseDC (hwnd, hdc) ;SetScrollRange (hwnd, SB_VERT, 0, NUMLINES - 1, FALSE) ;SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ;return 0 ;case WM_SIZE:cyClient = HIWORD (lParam) ;return 0 ;case WM_VSCROLL:switch (LOWORD (wParam)){case SB_LINEUP:iVscrollPos -= 1 ;break ;case SB_LINEDOWN:iVscrollPos += 1 ;break ;case SB_PAGEUP:iVscrollPos -= cyClient / cyChar ;break ;case SB_PAGEDOWN:iVscrollPos += cyClient / cyChar ;break ;case SB_THUMBPOSITION:iVscrollPos = HIWORD (wParam) ;break ;default :break ;}iVscrollPos = max (0, min (iVscrollPos, NUMLINES - 1)) ;if (iVscrollPos != GetScrollPos (hwnd, SB_VERT)){SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ;InvalidateRect (hwnd, NULL, TRUE) ;}return 0 ;case WM_PAINT:hdc = BeginPaint (hwnd, &ps) ;for (i = 0 ; i < NUMLINES ; i++){y = cyChar * (i - iVscrollPos) ;TextOut (hdc, 0, y,sysmetrics[i].szLabel,lstrlen (sysmetrics[i].szLabel)) ;TextOut (hdc, 22 * cxCaps, y,sysmetrics[i].szDesc,lstrlen (sysmetrics[i].szDesc)) ;SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;TextOut (hdc, 22 * cxCaps + 40 * cxChar, y, szBuffer,wsprintf (szBuffer, TEXT ("%5d"),GetSystemMetrics (sysmetrics[i].iIndex))) ;SetTextAlign (hdc, TA_LEFT | TA_TOP) ;}EndPaint (hwnd, &ps) ;return 0 ;case WM_DESTROY:PostQuitMessage (0) ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;}
?
新的CreateWindow呼叫在第三個參數中包含了WS_VSCROLL窗口樣式,從而在窗口中加入了垂直滾動條,其窗口樣式為:
WS_OVERLAPPEDWINDOW | WS_VSCROLL
???????
WndProc窗口消息處理程序在處理WM_CREATE消息時增加了兩條敘述,以設置垂直滾動條的范圍和初始位置:
SetScrollRange (hwnd, SB_VERT, 0, NUMLINES - 1, FALSE) ;
???????
SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ;
???????
sysmetrics結構具有NUMLINES行文字,所以滾動條范圍被設定為0至NUMLINES-1。滾動條的每個位置對應于在顯示區域頂部顯示的一個文字行。如果卷動方塊的位置為0,則第一行會被放置在顯示區域的頂部。如果位置大于0,其它行就會出現在顯示區域的頂部。當位置為NUMLINES-1時,則最后一行文字出現在顯示區域的頂部。
為了有助于處理WM_VSCROLL消息,在窗口消息處理程序中定義了一個靜態變量iVscrollPos,這一變量是滾動條內卷動方塊的目前位置。對于SB_LINEUP和SB_LINEDOWN,只需要將卷動方塊調整一個單位的位置。對于SB_PAGEUP和SB_PAGEDOWN,我們想移動一整面的內容,或者移動cyClient /cyChar個單位的位置。對于SB_THUMBPOSITION,新的卷動方塊位置是wParam的高字組。SB_ENDSCROLL和SB_THUMBTRACK消息被忽略。
在程序依據收到的WM_VSCROLL消息計算出新的iVscrollPos值后,用min和max宏來調整iVscrollPos,以確保它在最大值與最小值之間。程序然后將iVscrollPos與呼叫GetScrollPos取得的先前位置相比較,如果卷動位置發生了變化,則使用SetScrollPos來進行更新,并且呼叫InvalidateRect使整個窗口無效。
InvalidateRect呼叫產生一個WM_PAINT消息。SYSMETS1在處理WM_PAINT消息時,每一行的y坐標計算公式為:
cyChar * i
???????
在SYSMETS2中,計算公式為:
cyChar * (i - iVscrollPos)
???????
循環仍然顯示NUMLINES行文字,但是對于非零值的iVscrollPos是負數。程序實際上在顯示區域以外顯示這些文字行。當然,Windows不會顯示這些行,因此屏幕顯得干凈和漂亮。
前面說過,我們一開始不想弄得太復雜,這樣的程序代碼很浪費,效率很低。下面我們對此加以修改,但是先要考慮在WM_VSCROLL消息之后更新顯示區域的方法。
<br />本文來自【C語言中文網】:<a href="http://see.xidian.edu.cn/cpp/html/1112.html" target="_blank">http://see.xidian.edu.cn/cpp/html/1112.html</a>
總結
以上是生活随笔為你收集整理的【转】Windows编程之滚动条—滚动条消息的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2020贺岁纪念币多少钱?鼠年贺岁银质纪
- 下一篇: 【转】win32 的DLL中创建wind