Win 32 滾動條的實現 SetScrollInfo和GetScrollInfo(二)

前言:
在上一篇滾動條的使用中,存在一些問題,比如:拉着滾動條快速移動會出現閃爍,還有就是效率不高,有卡頓等。爲優化這一功能,這裏將介紹一種更好的方法。
效果圖:
在這裏插入圖片描述


一、準備知識

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;
}

今日說:
今天刷一下空間,咋都是秀恩愛的啊,原來今天是七夕,但也跟我沒多大關係,哈哈哈

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章