由于屏幕不夠顯示,滾動(dòng)條成為必備品。我們也習(xí)以為常了,如果沒(méi)有滾動(dòng)條,我們的電腦生活就沒(méi)那么輕松了^_^。要添加滾動(dòng)條,我們必須知道客戶區(qū)的信息,客戶區(qū)是不斷變化的,但是變化是就就有WM_SIZE消息,此時(shí)的lparam的高字節(jié)保存高度,低字節(jié)保存寬度。獲取辦法如下

caseWM_SIZE:       

   cxClient = LOWORD (lParam) ; 

   cyClient = HIWORD (lParam) ; 

   return 0 ; 

行數(shù)ClientLineNumbers=cyClient/cyChar       列數(shù)是ClientColNumbers=cxClient/cxChar

滾動(dòng)條的使用大家都會(huì),可以點(diǎn)箭頭,拖拉滑動(dòng)塊,或者點(diǎn)擊反色條定位。

滾動(dòng)范圍的設(shè)置

SetScrollRange (hwnd, iBar, iMin, iMax, bRedraw) ; 

默認(rèn)上為最小0,下為最大100,也可以自定義。Ibar有兩個(gè)值SB_VERT 和SB_HORIZ,表示垂直或水平滾動(dòng)條。bRedraw設(shè)為T(mén)RUE表示每次滾動(dòng)要重畫(huà)滑動(dòng)塊。滾動(dòng)塊的停留位置是一些離散的值,當(dāng)設(shè)置的位置非常多時(shí)可以看做是連續(xù)的。

可以強(qiáng)制設(shè)定滾動(dòng)條的位置

SetScrollPos (hwnd, iBar, iPos, bRedraw) ; 

也可以使用GetScrollRange和GetScrollPos來(lái)獲取當(dāng)前滾動(dòng)塊的位置和范圍。

關(guān)于滾動(dòng)條的信息保存在wparam中,低字節(jié)是通知碼。wParam的低字組是SB_THUMBPOSITION時(shí),高字節(jié)保存拖動(dòng)滑動(dòng)塊時(shí)的位置;低字節(jié)是SB_THUMBPOSITION時(shí),高字節(jié)保存最終位置。以SB為前綴^_^(scroll bar)。包含LEFT和RIGHT的標(biāo)識(shí)符用于水平滾動(dòng)條,包含UP、DOWN、TOP和BOTTOM的標(biāo)識(shí)符用于垂直滾動(dòng)條。每個(gè)常量都以按下和彈起作為一組。如圖所示:

 

其中最值得注意的是SB_THUMBPOSITION和SB_THUMBTRACK。消息處理函數(shù)只處理兩者中的一個(gè),處理前者,窗口內(nèi)容在鼠標(biāo)停止拖動(dòng)時(shí)顯示。后者則不斷更新內(nèi)容。

關(guān)鍵代碼如下

LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)

{

 static int cxChar,cyChar,cxCaps,cyClient,iVscrollPos;

 int i,y;

 HDC hdc;

 PAINTSTRUCT ps;

 RECT rect;

 TCHAR szBuffer[10];

 TEXTMETRIC tm;

 switch(message)

 {

 case WM_CREATE:

  hdc=GetDC(hWnd);

  GetTextMetrics(hdc,&tm);

        cxChar=tm.tmAveCharWidth;

  cyChar=tm.tmHeight+tm.tmExternalLeading;

  cxCaps=(tm.tmPitchAndFamily&1?3:2)*cxChar/2;

  ReleaseDC(hWnd,hdc);

  SetScrollRange(hWnd,SB_VERT,0,LINENUMBERS-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);

  default:

   break;

  }

  iVscrollPos=max(0,min(iVscrollPos,LINENUMBERS-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<LINENUMBERS;i++)

  {

   y=cyChar*(i-iVscrollPos);

   TextOut(hdc,0,y,sysmetrics[i].szLable,lstrlen(sysmetrics[i].szLable));

   TextOut(hdc,20*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].index)));

   SetTextAlign(hdc,TA_LEFT|TA_TOP);

  }

  EndPaint(hWnd,&ps);

  return 0;

 case WM_DESTROY:

  PostQuitMessage(0);

  return 0;

 }

return DefWindowProc(hWnd,message,wParam,lParam);

}

iVscrollPos=max(0,min(iVscrollPos,LINENUMBERS-1))用來(lái)保證值在(0,LINENUMBERS-1)之間。下面是判斷位置的變化,然后更具位置更新。

更好的滾動(dòng)條

前面介紹的四個(gè)函數(shù)都是過(guò)時(shí)的,不過(guò)依然可以使用。現(xiàn)在只需兩個(gè)函數(shù)實(shí)現(xiàn)滾動(dòng)條的功能。

為了更加人性化,滾動(dòng)條的大小應(yīng)該隨著內(nèi)容的多少而發(fā)生變化,這也是我們習(xí)以為常的。

(滾動(dòng)塊大小/滾動(dòng)長(zhǎng)度)=(頁(yè)面大小/范圍)=(顯示文件數(shù)量/文件的總大小)

這兩個(gè)函數(shù)是

SetScrollInfo (hwnd, iBar, &si, bRedraw) ;

GetScrollInfo (hwnd, iBar, &si) ; 

其中一個(gè)參數(shù)是我們沒(méi)有見(jiàn)過(guò)的,他是一種新的數(shù)據(jù)結(jié)構(gòu)變量

typedef struct tagSCROLLINFO {  

    UINT cbSize;  

    UINT fMask;  

    int  nMin;  

    int  nMax;  

    UINT nPage;  

    int  nPos;  

    int  nTrackPos;  

}   SCROLLINFO, *LPSCROLLINFO;  

typedef SCROLLINFO CONST *LPCSCROLLINFO;

在調(diào)用這兩個(gè)函數(shù)前cbSize=sizeof(SCROLLINFO),這個(gè)屬性指名結(jié)構(gòu)的大小,許多windows數(shù)據(jù)結(jié)構(gòu)都有這個(gè)特點(diǎn)。fMask是一個(gè)旗標(biāo),通俗點(diǎn)就是一個(gè)功能的標(biāo)志,當(dāng)取不同的值(SIF為前綴)時(shí),兩個(gè)函數(shù)的功能是不一樣的,填充的屬性也就不同。更好的滾動(dòng)條最大的不同點(diǎn)不僅是函數(shù),還在于顯示范圍上。書(shū)上用文字描述,我覺(jué)得用圖示更好說(shuō)明

有圖很容易知道范圍是0到25.

關(guān)鍵代碼

 case WM_SIZE:

  cxClient=LOWORD(lParam);

  cyClient=HIWORD(lParam);

  si.cbSize=sizeof(SCROLLINFO);

  si.fMask=SIF_RANGE|SIF_PAGE;

  si.nMin=0;

  si.nMax=2+iMaxWidth/cxChar;

  si.nPage=cyClient/cyChar;

  SetScrollInfo(hWnd,SB_VERT,&si,TRUE);

  return 0;

 case WM_VSCROLL:

  si.cbSize=sizeof(SCROLLINFO);

  si.fMask=SIF_ALL;

  GetScrollInfo(hWnd,SB_VERT,&si);

  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;

  default:

   break;

  }

  si.fMask=SIF_POS;

  SetScrollInfo(hWnd,SB_VERT,&si,TRUE);

  GetScrollInfo(hWnd,SB_VERT,&si);

  if(iVertPos!=si.nPos)

  {

   ScrollWindow(hWnd,0,cyChar*(iVertPos-si.nPos),NULL,NULL);

   UpdateWindow(hWnd);

  }

  return 0;

    case WM_HSCROLL:

  si.cbSize=sizeof(SCROLLINFO);

  si.fMask=SIF_ALL;

  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;

  default:

   break;

  }

  si.fMask=SIF_POS;

  SetScrollInfo(hWnd,SB_HORZ,&si,TRUE);

  GetScrollInfo(hWnd,SB_HORZ,&si);

  if(iHorzPos!=si.nPos)

  {

   ScrollWindow(hWnd,cxChar*(iHorzPos-si.nPos),0,NULL,NULL);

   UpdateWindow(hWnd);

  }

  return 0;

 case WM_PAINT:

  hdc=BeginPaint(hWnd,&ps);

  si.cbSize=sizeof(SCROLLINFO);

  si.fMask=SIF_POS;

  GetScrollInfo(hWnd,SB_VERT,&si);

  iVertPos=si.nPos;

  GetScrollInfo(hWnd,SB_HORZ,&si);

  iHorzPos=si.nPos;

  iPaintBeg=max(0,iVertPos+ps.rcPaint.top/cyChar);

  iPaintEnd=min(iVertPos+ps.rcPaint.bottom/cyChar,LINENUMBERS-1);

  for(i=iPaintBeg;i<=iPaintEnd;i++)

  {

   x=cxChar*(1-iHorzPos);

   y=cyChar*(i-iVertPos);

   TextOut(hdc,x,y,sysmetrics[i].szLable,lstrlen(sysmetrics[i].szLable));

   TextOut(hdc,x+20*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].index)));

   SetTextAlign(hdc,TA_LEFT|TA_TOP);

  }

  EndPaint(hWnd,&ps);

  return 0;

理解關(guān)鍵一

iPaintBeg=max(0,iVertPos+ps.rcPaint.top/cyChar);

iPaintEnd=min(iVertPos+ps.rcPaint.bottom/cyChar,LINENUMBERS-1);

iVertPos是當(dāng)前滾動(dòng)條位置,那么開(kāi)始繪制的地方就應(yīng)該是滾動(dòng)條所在的位置,對(duì)應(yīng)到要顯示的整個(gè)區(qū)域的位置也是這個(gè)iVertPos,后面的top可以去掉,因?yàn)轱@示區(qū)top等于0。繪制結(jié)束的地方應(yīng)該是iVertPos加上繪制區(qū)的高度。這些都需要在顯示屏后面的整個(gè)顯示范圍上看,以繪制區(qū)為尺度。

理解關(guān)鍵二

x=cxChar*(1-iHorzPos);

y=cyChar*(i-iVertPos);

先看y,比如i=iPaintBeg事,即i=iVertPos,此時(shí)對(duì)應(yīng)繪制區(qū)0的位置,隨著循環(huán)遞加,逐行繪制。x的值令人比較迷惑,這是由于兩個(gè)原因的干擾,一個(gè)是橫向拉動(dòng)的話還是原來(lái)那幾行文字,和縱向是不一樣的,如果把他們認(rèn)為是相同的就很難理解了。另一個(gè)原因是函數(shù)ScrollWindow造成的,作者沒(méi)有詳細(xì)介紹這個(gè)函數(shù),他的實(shí)現(xiàn)肯定決定了這個(gè)值的取法。關(guān)于1,是自己定義的,保證了文字不太靠近邊緣。