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

今日说:
今天刷一下空间,咋都是秀恩爱的啊,原来今天是七夕,但也跟我没多大关系,哈哈哈

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