前言:
在上一篇滾動條的使用中,存在一些問題,比如:拉着滾動條快速移動會出現閃爍,還有就是效率不高,有卡頓等。爲優化這一功能,這裏將介紹一種更好的方法。
效果圖:
一、準備知識
1. 結構SCROLLINFO
結構原型:
typedef struct tagSCROLLINFO
{
UINT cbSize; // SCROLLINFO結構體本身的字節大小
UINT fMask; // 見下面的說明
int nMin; // 最小滾動位置
int nMax; // 最大滾動位置
UINT nPage; // 頁面尺寸
int nPos; // 滾動塊的位置
int nTrackPos; // 滾動塊當前被拖動的位置,不能在SetScrollInfo中指定
} SCROLLINFO;
參數解釋:
cbSize: SCROLLINFO結構長度字節數,該值在設置和查詢參數時都必須填寫。
fMask: 指定結構中的哪些成員是有效,該值共有如下5種選擇,可以選擇多種用“OR”組合起來,該值在設置和查詢參數時都必須填寫。
值 | 含義 |
---|---|
SIF_ALL | 整個結構都有效 |
SIF_DISABLENOSCROLL | 該值僅在設定參數時使用,視控件參數設定的需要來對本結構的成員進行取捨。 |
SIF_PAGE | nPage成員有效 |
SIF_POS | nPos成員有效 |
SIF_RANGE | nMin和nMax成員有效 |
nMin: 滾動範圍最小值
nMax: 滾動範圍最大值
nPage: 頁尺寸,用來確定比例滾動框的大小
nPos: 滾動框的位置
nTrackPos: 拖動時滾動框的位置,該參數只能查詢,不能設置。
2. 函數SetScrollInfo
函數功能:
該函數設置滾動條參數,包括滾動位置的最大值和最小值,頁面大小,滾動按鈕的位置。如被請求,函數也可以重畫滾動條。
函數原型:
int SetScrollInfo(HWND hWnd;int fnBar,LPSCROLLINFO lpsi,BOOL fRedraw);
參數解釋:
hWnd:滾動條控制或帶標準滾動條的窗體句柄,由fnBar參數決定。
fnBar:指定被設定參數的滾動條的類型。這個參數可以是下面值,含義如下:
SB_CTL:設置滾動條控制。而參數hwnd必須是滾動條控制的句柄。
SB_HORZ:設置所給定的窗體上標準水平滾動條參數。
SB_VERT:設置所給定的窗體上標準垂直滾動條參數。
IPBI:指向SCROLLINFO結構。在調用SetScrognfo之前,設置SCROLLINFO結構中cbSize成員以標識結構大小,設置成員fMask以說明待設置的滾動條參數,並且在適當的成員中制定新的參數值。
fRedraw:指定滾動條是否重畫以反映滾動條的變化。如果這個參數爲TRUE,滾動條將被重畫,否則不被重畫。
3. 函數GetScrollInfo
函數功能:
該函數找到滾動條的參數,包括滾動條位置的最小值、最大值,頁面大小,滾動按鈕的位置等。
函數原型:
BOOL GetScrollInfo( HWND hWnd, int fnBar, LPSCROLLINFO lpsi );
參數解釋:
hWnd: 滾動條控制或有標準滾動條的窗體句柄,由fnBar參數確定。
fnBar: 指定待找回滾動條參數的類型,此參數可以爲如下值,其值含義:
SB_CTL|找回滾動條控制參數。其中參數hwnd一定是處理滾動條控制的句柄。
SB_HORZ|找回所指定窗體的標準水平滾動條參數。
SB_VERT|找回所指定窗體的標準垂直滾動條參數。
lpsi:指向SCROLLINFO結構。
4. 函數ScrollWindow
函數功能:
該函數滾動所指定的窗口客戶區域內容。該函數存在向後兼容性,新的應用程序應使用ScrollWindowEX。
函數原型:
BOOL ScrollWindow(HWND hWnd, int XAmount, int YAmount, CONST RECT *IpRect, CONST RECT *lpClipRect);
參數解釋:
hWnd 客戶區域將被滾動的窗口的句柄。
XAmount
指定水平滾動的距離,以設備單位計。如果窗口類風格爲CS_OWNDC或CS_CLASSDC,則此參數則使用邏輯單位而非設備單位。當向左滾動窗體內容時,參數值必須爲負。
YAmount
指定垂直滾動的距離,以設備單位計。如果窗口類風格爲CS_OWNDC或CS_CLASSDC,則此參數則使用邏輯單位而非設備單位。當向上滾動窗體內容時,參數值必須爲負。
lpRect
指向RECT結構的指針,該結構指定了將要滾動的客戶區範圍。若此參數爲NULL,則整個客戶區域將被滾動。
lpClipRect
指向RECT結構的指針,該結構指定了要滾動的裁剪區域。只有這個矩形中的位纔會被滾動。在矩形之外的位不會被影響,即使它們是在lpRect矩形之內。(見代碼"測試一")假如lpClipRect爲NULL,則不會在滾動矩形上進行裁剪。
5. 核心要點
其實SetScrollInfo是SetScrollRange和SetScrollPos的結合,GetScrollInfo是GetScrollRange和GetScrollPos的結合。
無論是Set還是Get,都得先設置si結構的第一個域的值,即賦給cbSize結構的大小。之後根據設置的fMask域的值進行Set或Get,當Set時,需要根據fMask的值將相關的域填充後再調用SetScrollInfo(),這樣si結構就被Set成功。當Get時,直接調用GetScrollInfo(),具體能使用哪些域的值是根據所設置的fMask域的值定的。
當要設置滾動條的範圍和頁面大小時(SetScrollInfo的使用):
si.cbSize = sizeof (SCROLLINFO) ;
si.fMask = SIF_RANGE | SIF_PAGE ;
si.nMin = 0 ;
si.nMax = NUMLINES - 1 ;
si.nPage = cyClient / cyChar ;
SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;
而若要用到滾動條的位置時(GetScrollInfo的使用):
先si.cbSize = sizeof (si) ;
si.fMask = SIF_ALL ; // 表示Get後將使用si結構的位置、頁面大小等量
GetScrollInfo (hwnd, SB_VERT, &si) ;
// 然後就可直接使用si.nPos、si.nPage、si.nTrackPos等量,
// 這些量就是從si結構中通過Get函數獲得的,也是之前通過Set函數設置的值。
二、代碼如下
這裏提供的是相對Win32應用程序基本框架有修改添加的部分,其他部分一樣,你只需此代碼覆蓋原來這部分代碼即可。
CreateWindowW函數
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW | WS_VSCROLL,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
WndProc函數
RESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int i;
size_t j;
TCHAR str[100];
TEXTMETRIC tm;
SCROLLINFO si;
static int cxChar, cyChar, iVscrollPos, cxClient, cyClient, y;
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 分析菜單選擇:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_CREATE: // 獲得字體的大小、高度、寬度等信息
{
HDC hdc;
hdc = GetDC(hWnd);
GetTextMetrics(hdc, &tm);
cxChar = tm.tmAveCharWidth;
cyChar = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC(hWnd, hdc);
}
case WM_SIZE:
{
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
y = cyClient / cyChar; // y表示一頁裏有多少行
si.cbSize = sizeof(SCROLLINFO); // 以下爲設置滾動條
si.fMask = SIF_RANGE | SIF_PAGE;
si.nMin = 0;
si.nMax = 50;
si.nPage = y;
SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
si.cbSize = sizeof(si); // 以下獲得滾動條信息
si.fMask = SIF_POS;
GetScrollInfo(hWnd, SB_VERT, &si);
iVscrollPos = si.nPos;
for (i = 0; i < 50; i++)
{
SetTextAlign(hdc, TA_LEFT | TA_TOP);
StringCchPrintf(str, 100, TEXT("%d:I love you!"), i + 1);
StringCchLength(str, 100, &j);
TextOut(hdc, 0, (i - iVscrollPos)* cyChar, str, j);
TextOut(hdc, 20 * cxChar, (i - iVscrollPos)*cyChar, str, j);
SetTextAlign(hdc, TA_RIGHT | TA_TOP);
TextOut(hdc, 50 * cxChar, (i - iVscrollPos)*cyChar, str, j);
}
EndPaint(hWnd, &ps);
}
break;
case WM_VSCROLL:
{
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
GetScrollInfo(hWnd, SB_VERT, &si);
iVscrollPos = si.nPos;
switch (LOWORD(wParam))
{
case SB_LINEDOWN:
si.nPos += 1;
break;
case SB_LINEUP:
si.nPos -= 1;
break;
case SB_PAGEDOWN:
si.nPos += y;
break;
case SB_PAGEUP:
si.nPos -= y;
break;
case SB_THUMBTRACK:
si.nPos = si.nTrackPos ;
break;
}
si.nPos = min(50 - y, max(0, si.nPos));
si.fMask = SIF_POS;
SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
GetScrollInfo(hWnd, SB_VERT, &si);
if (iVscrollPos != si.nPos)
{
ScrollWindow(hWnd, 0, cyChar * (iVscrollPos - si.nPos), nullptr, nullptr);
UpdateWindow(hWnd);
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
今日說:
今天刷一下空間,咋都是秀恩愛的啊,原來今天是七夕,但也跟我沒多大關係,哈哈哈