鼠标下

来源:互联网 发布:js插件全年日历多选 编辑:程序博客网 时间:2024/04/28 07:11
 

拦截鼠标

一个窗口消息处理程序通常只在鼠标光标位于窗口的显示区域,或非显示区域上时才接收鼠标消息。一个程序也可能需要在鼠标位于窗口外时接收鼠标消息。如果是这样,程序可以自行「拦截」鼠标。别害怕,这么做没什么大不了的。

设计矩形

为了说明拦截鼠标的必要性,请让我们看一下BLOKOUT1程序(如程序7-6所示)。此程序看起来达到了一定的功能,但它却有十分严重的缺陷。

程序7-6 BLOKOUT1
        BLOKOUT1.C        /*----------------------------------------------------------------------------          BLOKOUT1.C --   Mouse Button Demo Program                                                 (c) Charles Petzold, 1998        ----------------------------------------------------------------------------*/        #include <windows.h>        LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;        int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,                                                                PSTR szCmdLine, int iCmdShow)        {                   static TCHAR szAppName[] = TEXT ("BlokOut1") ;                   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 ("Program requires Windows NT!"),                                                                                szAppName, MB_ICONERROR) ;                    return 0 ;                   }                              hwnd = CreateWindow (szAppName, TEXT ("Mouse Button Demo"),                           WS_OVERLAPPEDWINDOW,                            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 ;        }        void DrawBoxOutline (HWND hwnd, POINT ptBeg, POINT ptEnd)        {                   HDC hdc ;                   hdc = GetDC (hwnd) ;                   SetROP2 (hdc, R2_NOT) ;                   SelectObject (hdc, GetStockObject (NULL_BRUSH)) ;                   Rectangle (hdc, ptBeg.x, ptBeg.y, ptEnd.x, ptEnd.y) ;                              ReleaseDC (hwnd, hdc) ;        }        LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam)        {                   static BOOL   fBlocking, fValidBox ;                   static POINT  ptBeg, ptEnd, ptBoxBeg, ptBoxEnd ;                   HDC                                  hdc ;                   PAINTSTRUCT  ps ;                   switch (message)                   {                   case   WM_LBUTTONDOWN :                          ptBeg.x = ptEnd.x = LOWORD (lParam) ;                          ptBeg.y = ptEnd.y = HIWORD (lParam) ;                                          DrawBoxOutline (hwnd, ptBeg, ptEnd) ;                                          SetCursor (LoadCursor (NULL, IDC_CROSS)) ;                                          fBlocking = TRUE ;                          return 0 ;                   case   WM_MOUSEMOVE :                          if (fBlocking)                          {                                          SetCursor (LoadCursor (NULL, IDC_CROSS)) ;                                                               DrawBoxOutline (hwnd, ptBeg, ptEnd) ;                                                              ptEnd.x = LOWORD (lParam) ;                                          ptEnd.y = HIWORD (lParam) ;                                                               DrawBoxOutline (hwnd, ptBeg, ptEnd) ;                          }                          return 0 ;                                   case   WM_LBUTTONUP :                          if (fBlocking)                          {                                          DrawBoxOutline (hwnd, ptBeg, ptEnd) ;                                                               ptBoxBeg                             = ptBeg ;                                          ptBoxEnd.x                           = LOWORD (lParam) ;                                          ptBoxEnd.y                           = HIWORD (lParam) ;                                                              SetCursor (LoadCursor (NULL, IDC_ARROW)) ;                                          fBlocking                            = FALSE ;                                          fValidBox                           = TRUE ;                                                               InvalidateRect (hwnd, NULL, TRUE) ;                          }                          return 0 ;                                   case   WM_CHAR :                          if (fBlocking & wParam == '/x1B')       // i.e., Escape                          {                                          DrawBoxOutline (hwnd, ptBeg, ptEnd) ;                                                             SetCursor (LoadCursor (NULL, IDC_ARROW)) ;                                                              fBlocking = FALSE ;                          }                          return 0 ;                   case   WM_PAINT :                          hdc = BeginPaint (hwnd, &ps) ;                   if (fValidBox)                          {                                                 SelectObject (hdc, GetStockObject (BLACK_BRUSH)) ;                                                 Rectangle     (      hdc, ptBoxBeg.x, ptBoxBeg.y,                                                                                    ptBoxEnd.x, ptBoxEnd.y) ;                          }                                          if (fBlocking)                          {                                          SetROP2 (hdc, R2_NOT) ;                                          SelectObject (hdc, GetStockObject (NULL_BRUSH)) ;                                          Rectangle (hdc, ptBeg.x, ptBeg.y, ptEnd.x, ptEnd.y) ;                          }                                          EndPaint (hwnd, &ps) ;                          return 0 ;                   case   WM_DESTROY :                          PostQuitMessage (0) ;                         return 0 ;            }                   return DefWindowProc (hwnd, message, wParam, lParam) ;        }        

此程序展示了一些,它可以实作在Windows的「画图」程序中的东西。由按下鼠标左键开始确定矩形的一角,然后拖动鼠标。程序将画一个矩形的轮廓,其相对位置是鼠标目前的位置。当您释放鼠标后,程序将填入这个矩形。图7-4显示了一个已经画完的矩形和另一个正在画的矩形。


 

图7-4 BLOKOUT1的屏幕显示

那么,问题在哪里呢?

请试一试下面的操作:在BLOKOUT1的显示区域按下鼠标的左键,然后将光标移出窗口。程序将停止接收WM_MOUSEMOVE消息。现在释放按钮,BLOKOUT1将不再获得WM_BUTTONUP消息,因为光标在显示区域以外。然后将光标移回BLOKOUT1的显示区域,窗口消息处理程序仍然认为按钮处于按下状态。

这样做并不好,因为程序不知道发生了什么事情。

拦截的解决方案

BLOKOUT1显示了一些常见的程序功能,但它的程序代码显然有缺陷。这种问题就是要使用鼠标拦截来对付。如果使用者正在拖曳鼠标,那么当鼠标短时间内被拖出窗口时应该没有什么大问题,程序应该仍然控制着鼠标。

拦截鼠标要比放置一个老鼠夹子容易一些,您只要呼叫:

SetCapture (hwnd) ;        

在这个函数呼叫之后,Windows将所有鼠标消息发给窗口句柄为hwnd的窗口消息处理程序。之后收到鼠标消息都是以显示区域消息的型态出现,即使鼠标正在窗口的非显示区域。lParam参数将指示鼠标在显示区域坐标中的位置。不过,当鼠标位于显示区域的左边或者上方时,这些x和y坐标可以是负的。当您想释放鼠标时,呼叫:

ReleaseCapture () ;        

从而使处理恢复正常。

在32位的Windows中,鼠标拦截要比在以前的Windows版本中有多一些限制。特别是,如果鼠标被拦截,而鼠标按键目前并未被按下,并且鼠标光标移到了另一个窗口上,那么将不是由拦截鼠标的那个窗口,而是由光标下面的窗口来接收鼠标消息。对于防止一个程序在拦截鼠标之后不释放它而引起整个系统的混乱,这是必要的。

换句话说,只有当鼠标按键在您的显示区域中被按下时才拦截鼠标;当鼠标按键被释放时,才释放鼠标拦截。

BLOKOUT2程序

展示鼠标拦截的BLOKOUT2程序如程序7-7所示。

程序7-7 BLOKOUT2
        BLOKOUT2.C        /*----------------------------------------------------------------------------          BLOKOUT2.C --         Mouse Button & Capture Demo Program                                                  (c) Charles Petzold, 1998        ----------------------------------------------------------------------------*/        #include <windows.h>        LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;        int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,                                                  PSTR szCmdLine, int iCmdShow)        {                   static TCHAR szAppName[] = TEXT ("BlokOut2") ;                   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 ("Program requires Windows NT!"),                                                          szAppName, MB_ICONERROR) ;                    return 0 ;           }                   hwnd = CreateWindow (szAppName, TEXT ("Mouse Button & Capture Demo"),                                           WS_OVERLAPPEDWINDOW,                           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 ;        }        void DrawBoxOutline (HWND hwnd, POINT ptBeg, POINT ptEnd)        {                   HDC hdc ;                   hdc = GetDC (hwnd) ;                   SetROP2 (hdc, R2_NOT) ;                   SelectObject (hdc, GetStockObject (NULL_BRUSH)) ;                   Rectangle (hdc, ptBeg.x, ptBeg.y, ptEnd.x, ptEnd.y) ;                              ReleaseDC (hwnd, hdc) ;        }        LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam)        {                   static BOOL  fBlocking, fValidBox ;                   static POINT ptBeg, ptEnd, ptBoxBeg, ptBoxEnd ;                   HDC          hdc ;                   PAINTSTRUCT  ps ;                              switch (message)                   {                   case   WM_LBUTTONDOWN :                          ptBeg.x = ptEnd.x = LOWORD (lParam) ;                          ptBeg.y = ptEnd.y = HIWORD (lParam) ;                                          DrawBoxOutline (hwnd, ptBeg, ptEnd) ;                          SetCapture (hwnd) ;                          SetCursor (LoadCursor (NULL, IDC_CROSS)) ;                                          fBlocking = TRUE ;                          return 0 ;                                   case   WM_MOUSEMOVE :                          if (fBlocking)                          {                                          SetCursor (LoadCursor (NULL, IDC_CROSS)) ;                                                               DrawBoxOutline (hwnd, ptBeg, ptEnd) ;                                                               ptEnd.x = LOWORD (lParam) ;                                          ptEnd.y = HIWORD (lParam) ;                                                               DrawBoxOutline (hwnd, ptBeg, ptEnd) ;                          }                          return 0 ;                                   case   WM_LBUTTONUP :                          if (fBlocking)                          {                                          DrawBoxOutline (hwnd, ptBeg, ptEnd) ;                                                               ptBoxBeg                      = ptBeg ;                                          ptBoxEnd.x                    = LOWORD (lParam) ;                                          ptBoxEnd.y                    = HIWORD (lParam) ;                                                               ReleaseCapture () ;                                          SetCursor (LoadCursor (NULL, IDC_ARROW)) ;                                                               fBlocking                     = FALSE ;                                          fValidBox                     = TRUE ;                                                             InvalidateRect (hwnd, NULL, TRUE) ;                          }                          return 0 ;                                   case   WM_CHAR :                          if (fBlocking & wParam == '/x1B')       // i.e., Escape                          {                                          DrawBoxOutline (hwnd, ptBeg, ptEnd) ;                                          ReleaseCapture () ;                                          SetCursor (LoadCursor (NULL, IDC_ARROW)) ;                                          fBlocking = FALSE ;                          }                          return 0 ;                                   case   WM_PAINT :                         hdc = BeginPaint (hwnd, &ps) ;                                          if (fValidBox)                 {                                          SelectObject (hdc, GetStockObject (BLACK_BRUSH)) ;                                          Rectangle (hdc, ptBoxBeg.x, ptBoxBeg.y,                                          ptBoxEnd.x, ptBoxEnd.y) ;                          }                                                  if (fBlocking)                          {                                          SetROP2 (hdc, R2_NOT) ;                                         SelectObject (hdc, GetStockObject (NULL_BRUSH)) ;                                          Rectangle (hdc, ptBeg.x, ptBeg.y, ptEnd.x, ptEnd.y) ;                         }                                          EndPaint (hwnd, &ps) ;                          return 0 ;                                   case   WM_DESTROY :                          PostQuitMessage (0) ;                          return 0 ;                   }                   return DefWindowProc (hwnd, message, wParam, lParam) ;        }        

BLOKOUT2程序和BLOKOUT1程序一样,只是多了三行新程序代码:在WM_LBUTTONDOWN消息处理期间呼叫SetCapture,而在WM_LBUTTONDOWN和WM_CHAR消息处理期间呼叫ReleaseCapture。检查画出窗口:使窗口小于屏幕大小,开始在显示区域画出一块矩形,然后将鼠标光标移出显示区域的右边或下边,最后释放鼠标按键。程序将获得整个矩形的坐标。但是需要扩大窗口才能看清楚它。

拦截鼠标并非只适用于那些古怪的应用程序。如果您需要鼠标按键在显示区域按下时都能够追踪WM_MOUSEMOVE消息,并直到鼠标按键被释放为止,那么您就应该拦截鼠标。这样将简化您的程序,同时又符合使用者的期望。

鼠标滑轮

与传统的鼠标相比,Microsoft IntelliMouse的特点是在两个键之间多了一个小滑轮。您可以按下这个滑轮,这时它的功能相当于鼠标按键的中键;或者您也可以用食指来转动它,这会产生一条特殊的消息,叫做WM_MOUSEWHEEL。使用鼠标滑轮的程序通过滚动或放大文件来响应此消息。它最初听起来像一个不必要的隐藏机关,但我必须承认,我很快就习惯于使用鼠标滑轮来滚动Microsoft Word和Microsoft Internet Explorer了。

我不想讨论鼠标滑轮的所有使用方法。实际上,我只是想告诉您如何在现有的程序(例如程序SYSMETS4)中添加鼠标滑轮处理程序,以便在显示区域中卷动数据。最终的SYSMETS程序如程序7-8所示。

程序7-8 SYSMETS4
        SYSMETS.C        /*----------------------------------------------------------------------------        SYSMETS.C -- Final System Metrics Display Program                                           (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 ("SysMets") ;                   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 ("Program requires Windows NT!"),                                                                                szAppName, MB_ICONERROR) ;                          return 0 ;                   }                       hwnd = CreateWindow (szAppName, TEXT ("Get System Metrics"),                                 WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,                                   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, cxClient, cyClient, iMaxWidth ;            static        int           iDeltaPerLine, iAccumDelta ;         // for mouse wheel logic                          HDC         hdc ;                          int         i, x, y, iVertPos, iHorzPos, iPaintBeg, iPaintEnd ;                          PAINTSTRUCT           ps ;                          SCROLLINFO            si ;                          TCHAR                         szBuffer[10] ;                          TEXTMETRIC            tm ;                          ULONG                         ulScrollLines ;       // for mouse wheel logic                   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) ;                                                         // Save the width of the three columns                                          iMaxWidth = 40 * cxChar + 22 * cxCaps ;                                                         // Fall through for mouse wheel information                   case   WM_SETTINGCHANGE:                          SystemParametersInfo (SPI_GETWHEELSCROLLLINES, 0, &ulScrollLines, 0) ;                                             // ulScrollLines usually equals 3 or 0 (for no scrolling)                           // WHEEL_DELTA equals 120, so iDeltaPerLine will be 40                          if (ulScrollLines)                                                 iDeltaPerLine = WHEEL_DELTA / ulScrollLines ;                          else                                                 iDeltaPerLine = 0 ;                    return 0 ;                                   case   WM_SIZE:                          cxClient = LOWORD (lParam) ;                   cyClient = HIWORD (lParam) ;                                                         // Set vertical scroll bar range and page size                          si.cbSize                        = sizeof (si) ;                          si.fMask                             = SIF_RANGE | SIF_PAGE ;                          si.nMin                              = 0 ;                          si.nMax                              = NUMLINES - 1 ;                          si.nPage                             = cyClient / cyChar ;                          SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;                                                         // Set horizontal scroll bar range and page size                          si.cbSize                        = sizeof (si) ;                          si.fMask                             = SIF_RANGE | SIF_PAGE ;                         si.nMin                              = 0 ;                          si.nMax                              = 2 + iMaxWidth / cxChar ;                         si.nPage                             = cxClient / cxChar ;                   SetScrollInfo (hwnd, SB_HORZ, &si, TRUE) ;                          return 0 ;                                   case WM_VSCROLL:                                                         // Get all the vertical scroll bar information                          si.cbSize = sizeof (si) ;                          si.fMask  = SIF_ALL ;                          GetScrollInfo (hwnd, SB_VERT, &si) ;                                                         // Save the position for comparison later on                          iVertPos = si.nPos ;                    switch (LOWORD (wParam))                          {                          case   SB_TOP:                                                 si.nPos = si.nMin ;                                                break ;                                               case   SB_BOTTOM:                                                 si.nPos = si.nMax ;                                                 break ;                                               case SB_LINEUP:                                                 si.nPos -= 1 ;                                                 break ;                                               case   SB_LINEDOWN:                                                 si.nPos += 1 ;                                                 break ;                                               case SB_PAGEUP:                                                 si.nPos -= si.nPage ;                                                 break ;                                               case   SB_PAGEDOWN:                                                 si.nPos += si.nPage ;                                                 break ;                                               case   SB_THUMBTRACK:                                                si.nPos = si.nTrackPos ;                                                 break ;                          default:                                                 break ;                                 }                                                 // Set the position and then retrieve it.  Due to adjustments                                   //   by Windows it may not be the same as the value set.                          si.fMask = SIF_POS ;                          SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;                          GetScrollInfo (hwnd, SB_VERT, &si) ;                                                 // If the position has changed, scroll the window and update it                          if (si.nPos != iVertPos)                          {                                                            ScrollWindow (hwnd, 0, cyChar * (iVertPos - si.nPos),                                                 NULL, NULL) ;                                          UpdateWindow (hwnd) ;                         }                          return 0 ;                                   case   WM_HSCROLL:                                                 // Get all the vertical scroll bar information                          si.cbSize = sizeof (si) ;                          si.fMask  = SIF_ALL ;                                                 // Save the position for comparison later on                          GetScrollInfo (hwnd, SB_HORZ, &si) ;                          iHorzPos = si.nPos ;                          switch (LOWORD (wParam))                          {                          case   SB_LINELEFT:                                                 si.nPos -= 1 ;                                                 break ;                                              case   SB_LINERIGHT:                                                 si.nPos += 1 ;                                                 break ;                          case   SB_PAGELEFT:                                                 si.nPos -= si.nPage ;                                                 break ;                          case   SB_PAGERIGHT:                                                 si.nPos += si.nPage ;                                                 break ;                                               case   SB_THUMBPOSITION:                                                 si.nPos = si.nTrackPos ;                                                break ;                                               default:                                          break ;                          }                                         // Set the position and then retrieve it.  Due to adjustments                           //   by Windows it may not be the same as the value set.                    si.fMask = SIF_POS ;                          SetScrollInfo (hwnd, SB_HORZ, &si, TRUE) ;                          GetScrollInfo (hwnd, SB_HORZ, &si) ;                                                         // If the position has changed, scroll the window                          if (si.nPos != iHorzPos)                          {                                                 ScrollWindow (hwnd, cxChar * (iHorzPos - si.nPos), 0,                                                                 NULL, NULL) ;                         }                          return 0 ;                   case   WM_KEYDOWN :                          switch (wParam)                          {                          case   VK_HOME :                                                 SendMessage (hwnd, WM_VSCROLL, SB_TOP, 0) ;                                                break ;                                               case   VK_END :                                                 SendMessage (hwnd, WM_VSCROLL, SB_BOTTOM, 0) ;                                                 break ;                                               case   VK_PRIOR :                                                 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0) ;                                                 break ;                                            case   VK_NEXT :                                                SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0) ;                                                 break ;                          case   VK_UP :                                                 SendMessage (hwnd, WM_VSCROLL, SB_LINEUP, 0) ;                                                 break ;                                               case   VK_DOWN :                                                SendMessage (hwnd, WM_VSCROLL, SB_LINEDOWN, 0) ;                                                 break ;                                               case   VK_LEFT :                                                 SendMessage (hwnd, WM_HSCROLL, SB_PAGEUP, 0) ;                                                 break ;                                               case   VK_RIGHT :                                               SendMessage (hwnd, WM_HSCROLL, SB_PAGEDOWN, 0) ;                                                 break ;                          }                          return 0 ;                   case   WM_MOUSEWHEEL:                          if (iDeltaPerLine == 0)                                                break ;                          iAccumDelta += (short) HIWORD (wParam) ;     // 120 or -120                          while (iAccumDelta >= iDeltaPerLine)                          {                                                              SendMessage (hwnd, WM_VSCROLL, SB_LINEUP, 0) ;                                                 iAccumDelta -= iDeltaPerLine ;                          }                          while (iAccumDelta <= -iDeltaPerLine)                         {                                                SendMessage (hwnd, WM_VSCROLL, SB_LINEDOWN, 0) ;                                                 iAccumDelta += iDeltaPerLine ;                          }                          return 0 ;                   case   WM_PAINT :                          hdc = BeginPaint (hwnd, &ps) ;                                                 // Get vertical scroll bar position                                  si.cbSize             = sizeof (si) ;                                  si.fMask                      = SIF_POS ;                                  GetScrollInfo (hwnd, SB_VERT, &si) ;                                  iVertPos                      = si.nPos ;                                                 // Get horizontal scroll bar position                                  GetScrollInfo (hwnd, SB_HORZ, &si) ;                                  iHorzPos = si.nPos ;                                                 // Find painting limits                                  iPaintBeg = max (0, iVertPos + ps.rcPaint.top / cyChar) ;                                  iPaintEnd = min (NUMLINES - 1,                            iVertPos + ps.rcPaint.bottom / cyChar) ;                                                  for (i = iPaintBeg ; i <= iPaintEnd ; i++)                                  {                                                x = cxChar * (1 - iHorzPos) ;                                                 y = cyChar * (i - iVertPos) ;                                                                      TextOut (     hdc, x, y,                                  sysmetrics[i].szLabel,                                   lstrlen (sysmetrics[i].szLabel)) ;                                                                      TextOut (     hdc, x + 22 * cxCaps, y,                                  sysmetrics[i].szDesc,                                  lstrlen (sysmetrics[i].szDesc)) ;                                                                     SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;                                              TextOut  (    hdc, x + 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) ;        }        

转动滑轮会导致Windows在有输入焦点的窗口(不是鼠标光标下面的窗口)产生WM_MOUSEWHEEL消息。与平常一样,lParam将获得鼠标的位置,当然坐标是相对于屏幕左上角的,而不是显示区域的。另外,wParam的低字组包含一系列的旗标,用于表示鼠标按键、Shift与Ctrl键的状态。

新的信息保存在wParam的高字组。其中有一个「delta」值,该值目前可以是120或-120,这取决于滑轮的向前转动(也就是说,向鼠标的前面,即带有按钮与电缆的一端)还是向后转动。值120或-120表示文件将分别向上或向下卷动三行。这里的构想是,以后版本的鼠标滑轮能有比现在的鼠标产生更精确的移动速度信息,并且用delta值,例如40和-40,来产生WM_MOUSEWHEEL消息。这些值能使文件只向上或向下卷动一行。

为使程序能在一般化环境执行,SYSMETS将在WM_CREATE和WM_SETTINGCHANGE消息处理时,以SPI_GETWHEELSCROLLLINES作为参数来呼叫SystemParametersInfo。此值说明WHEEL_DELTA的delta值将滚动多少行,WHEEL_DELTA在WINUSER.H中定义。WHEEL_DELTA等于120,并且,在内定情况下SystemParametersInfo传回3,因此与卷动一行相联系的delta值就是40。SYSMETS将此值保存在iDeltaPerLine。

在WM_MOUSEWHEEL消息处理期间,SYSMETS将delta值给静态变量iAccumDelta。然后,如果iAccumDelta大于或等于iDeltaPerLine(或者是小于或等于-iDeltaPerLin),SYSMETS用SB_LINEUP或SB_LINEDOWN值产生WM_VSCROLL消息。对于每一个WM_VSCROLL消息,iAccumDelta由iDeltaPerLine增加(或减少)。此代码允许delta值大于、小于或等于滚动一行所需要的delta值。

下面还有

还有一个引人注目的鼠标问题:建立自订鼠标光标。我将在第十章,与其它Windows资源一起讨论此问题。

原创粉丝点击