一派掌門 二十級 |
【C++代碼】 #include <tchar.h> #include <Windows.h> #include "resource.h"
#define SCROLL_LINE 20 // 點擊滾動條的箭頭時, 位圖滾動多少像素 //#define SCROLL_MAX(si) (si.nMax - (int)si.nPage + 1) // 獲取滑塊左端最大位置的宏
HBITMAP hbmp; BITMAP bmp;
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { HDC hdc, hdcMem; HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE); int nLines, nNewPos, nOldPos, nOldHorzPos, nOldVertPos; PAINTSTRUCT ps; RECT rect, rcClient; SCROLLBARINFO sbi; SCROLLINFO si; ULONG ulScrollLines; switch (uMsg) { case WM_CREATE: hbmp = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP1)); // 加載位圖資源 GetObject(hbmp, sizeof(bmp), &bmp); // 獲取位圖尺寸
// 為了方便測試, 可以手動將bmWidth和bmHeight改小, 模擬顯示小圖片 //bmp.bmWidth = 800; //bmp.bmHeight = 600;
// 設置滾動條的滾動範圍 // 只要位圖的大小不變, 那麼滾動範圍就不會發生變化 // 由於滾動位置的最小值為0, 而bmWidth和bmHeight的最小值卻為1, 因此在指定滾動位置的最大值時必須減1 // 比如一張10x10的圖片顯示到窗口中,圖片最右下角點的顯示坐標為(9, 9) // 因此此時滾動條滑塊最右端只能滾動到9,不能滾動到10 SetScrollRange(hWnd, SB_HORZ, 0, bmp.bmWidth - 1, FALSE); SetScrollRange(hWnd, SB_VERT, 0, bmp.bmHeight - 1, FALSE); break; case WM_DESTROY: DeleteObject(hbmp); PostQuitMessage(0); break;
// 水平滾動條消息的處理 case WM_HSCROLL: si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; GetScrollInfo(hWnd, SB_HORZ, &si); // 下面的這些值無需作任何判斷處理, 系統會自動糾正無效的值 switch (LOWORD(wParam)) { case SB_LEFT: nNewPos = si.nMin; break; case SB_LINELEFT: nNewPos = si.nPos - SCROLL_LINE; break; case SB_LINERIGHT: nNewPos = si.nPos + SCROLL_LINE; break; case SB_PAGELEFT: nNewPos = si.nPos - si.nPage; break; case SB_PAGERIGHT: nNewPos = si.nPos + si.nPage; break; case SB_RIGHT: nNewPos = si.nMax; // 執行SetScrollPos函數後會由系統自動調整為SCROLL_MAX break;
// 下面兩個case要二選一 //case SB_THUMBPOSITION: // 僅當用戶拖動滾動條完成並釋放鼠標左鍵後才更新內容 case SB_THUMBTRACK: // 只要用戶拖動了滾動條就立即更新內容 nNewPos = HIWORD(wParam); break;
// 忽略其他滾動條消息 default: return FALSE; } nOldHorzPos = SetScrollPos(hWnd, SB_HORZ, nNewPos, TRUE); // 這裏設置的是滑塊左端的位置 nNewPos = GetScrollPos(hWnd, SB_HORZ); // 若設置後滾動條位置有變化就更新窗口內容 if (nNewPos != nOldHorzPos) InvalidateRect(hWnd, NULL, FALSE); break;
// 鼠標滾輪消息處理 case WM_MOUSEWHEEL: SystemParametersInfo(SPI_GETWHEELSCROLLLINES, NULL, &ulScrollLines, NULL); // 獲取用戶在控制面板中設置的滾動速度 nLines = GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA * ulScrollLines; // 計算要滾動多少單位
sbi.cbSize = sizeof(SCROLLBARINFO); GetScrollBarInfo(hWnd, OBJID_VSCROLL, &sbi); // 獲取垂直滾動條的狀態信息 if (sbi.rgstate[0] == STATE_SYSTEM_INVISIBLE || sbi.rgstate[0] == STATE_SYSTEM_OFFSCREEN) { // 如果沒有垂直滾動條,就滾動水平滾動條 nNewPos = GetScrollPos(hWnd, SB_HORZ) - nLines * SCROLL_LINE; nOldPos = SetScrollPos(hWnd, SB_HORZ, nNewPos, TRUE); nNewPos = GetScrollPos(hWnd, SB_HORZ); } else { // 默認滾動垂直滾動條 nNewPos = GetScrollPos(hWnd, SB_VERT) - nLines * SCROLL_LINE; nOldPos = SetScrollPos(hWnd, SB_VERT, nNewPos, TRUE); nNewPos = GetScrollPos(hWnd, SB_VERT); } // 如果滾動條位置發生變化就刷新窗口 if (nOldPos != nNewPos) InvalidateRect(hWnd, NULL, FALSE); break;
case WM_PAINT: hdc = BeginPaint(hWnd, &ps); SetWindowOrgEx(hdc, GetScrollPos(hWnd, SB_HORZ), GetScrollPos(hWnd, SB_VERT), NULL); // 告訴hdc現在滾動到什麼位置上了
// 將位圖顯示到(0, 0)處 // 其中的坐標都無需根據滾動條的位置改變,該往哪兒畫就往哪兒畫 hdcMem = CreateCompatibleDC(hdc); SelectObject(hdcMem, hbmp); BitBlt(hdc, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcMem, 0, 0, SRCCOPY); DeleteDC(hdcMem); EndPaint(hWnd, &ps); break; case WM_SIZE: // 記錄重設nPage值之前的滾動條位置 nOldHorzPos = GetScrollPos(hWnd, SB_HORZ); nOldVertPos = GetScrollPos(hWnd, SB_VERT);
// 根據新的窗口尺寸重新設置nPage值的大小 // 如果設置後某個滾動條消失或重新出現,那麼系統會自動調整滾動條的位置 SetRect(&rcClient, 0, 0, LOWORD(lParam), HIWORD(lParam)); do { rect = rcClient; si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_PAGE; si.nPage = rect.right - rect.left; SetScrollInfo(hWnd, SB_HORZ, &si, TRUE); si.nPage = rect.bottom - rect.top; SetScrollInfo(hWnd, SB_VERT, &si, TRUE); GetClientRect(hWnd, &rcClient); } while (memcmp(&rcClient, &rect, sizeof(RECT)) != 0); // 如果滾動條消失或重新出現導致客戶區尺寸發生變化,則需要重新設置nPage
// 設置了nPage的值後,若滾動條位置已經自動發生了變化,則必須刷新窗口內容 // 如果在註冊窗口類時指定了CS_HREDRAW | CS_VREDRAW,則可以刪除下面這兩行 if (nOldHorzPos != GetScrollPos(hWnd, SB_HORZ) || nOldVertPos != GetScrollPos(hWnd, SB_VERT)) InvalidateRect(hWnd, NULL, FALSE); // 注意: InvalidateRect並不會立即刷新窗口, 而是要等到消息隊列中沒有消息了才執行WM_PAINT // 所以執行兩次InvalidateRect並不會影響性能 break;
// 垂直滾動條消息的處理,這個和上面的水平滾動條差不多 case WM_VSCROLL: si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; GetScrollInfo(hWnd, SB_VERT, &si); switch (LOWORD(wParam)) { case SB_TOP: nNewPos = si.nMin; break; case SB_LINEUP: nNewPos = si.nPos - SCROLL_LINE; break; case SB_LINEDOWN: nNewPos = si.nPos + SCROLL_LINE; break; case SB_PAGEUP: nNewPos = si.nPos - si.nPage; break; case SB_PAGEDOWN: nNewPos = si.nPos + si.nPage; break; case SB_BOTTOM: nNewPos = si.nMax; break; case SB_THUMBTRACK: nNewPos = HIWORD(wParam); break; default: return FALSE; } nOldVertPos = SetScrollPos(hWnd, SB_VERT, nNewPos, TRUE); nNewPos = GetScrollPos(hWnd, SB_VERT); if (nNewPos != nOldVertPos) InvalidateRect(hWnd, NULL, FALSE); break; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return FALSE; }
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { WNDCLASS wc; wc.cbClsExtra = wc.cbWndExtra = 0; wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hInstance = hInstance; wc.lpfnWndProc = WndProc; wc.lpszClassName = TEXT("ImageViewer"); wc.lpszMenuName = NULL; wc.style = NULL; RegisterClass(&wc);
// 創建窗口時,指定WS_HSCROLL或WS_VSCROLL就可以開啟窗口滾動條 HWND hWnd = CreateWindow(wc.lpszClassName, TEXT("Image Viewer"), WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) return 1; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd);
MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
|