一派掌门 二十级              | 
          
            
            
             
              【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; }              
             |