一派护法 十九级 |
【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; }
|