滚动条的使用2

前面简单介绍了滚动条的使用,以及滚动条相关的函数。下面将基于一下内容继续完善滚动条的使用:

        1:GetScrollInfo、SetScrollInfo函数的使用

        2:解决绘制效率问题、以及最后最后一条数据不能在客户区底部显示的问题


1、GetScrollInfo、SetScrollInfo函数的使用

        这两个函数可以完全取代前面所使用的那一系列函数:GetScrollRange、GetScrollPos、SetScrollRange、SetScrollPos。因为GetScrollInfo、SetScrollInfo所使用的参数为SCROLLINFO结构体,该结构体包含了滚动条相关的信息,如下:
typedef struct tagSCROLLINFO { 
    UINT cbSize; 
    UINT fMask; 
    int  nMin; 
    int  nMax; 
    UINT nPage; 
    int  nPos; 
    int  nTrackPos; 
}   SCROLLINFO, *LPSCROLLINFO; 
typedef SCROLLINFO CONST *LPCSCROLLINFO;
现在对这些参数重新说明:
cbSize:结构体大小
fMask:掩码,指定要操作(设置或则获取)哪些滚动条的参数
                        SIF_ALL                                包含 SIF_PAGE, SIF_POS, SIF_RANGE, SIF_TRACKPOS
                        SIF_DISABLENOSCROLL       时滚动条无效
                        SIF_PAGE                            操作也参数
                        SIF_POS                             操作位置
                        SIF_RANGE                         操作范围
                        SIF_TRACKPOS                   获取拖动的位置
nMin:滚动条滚动范围的最小值
nMax:滚动条滚动范围的最大值
nPage:页面大小,即一页所占的内容大小(范围),这个值可以动态变化来改变滑块的大小
nPos:滚动条的位置
nTrackPos:滚动条在拖动过程中的位置
注意:
        正因为有了这个结构体,我们操作滚动条才变得更加方便,MSDN中建议我们在新的应用程序中使用GetScrollInfo、SetScrollInfo来操作滚动条。
        另外,系统会根据滚动条的范围和页(page)大小来计算滚动的真正范围,这就解决了最后一条数据不能现在客户区底部的问题

2、源代码如下

#define  _CRT_SECURE_NO_WARNINGS
#define _CRT_NON_CONFORMING_SWPRINTFS

#define MAX_NUM_ROW 100		//显示文字的最大行数
	
#include <windows.h>
#include <tchar.h>


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

int WINAPI _tWinMain(HINSTANCE hInstance,
					 HINSTANCE hPreInstance,
					 LPTSTR lpCmdLine,
					 int nShowCmd)
{
	TCHAR *pszClassName = _T("MyClass");

	WNDCLASS  wndClass;
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;
	wndClass.hbrBackground = static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hInstance = hInstance;
	wndClass.lpszClassName = pszClassName;
	wndClass.lpszMenuName = NULL;

	BOOL bRet;
	bRet = RegisterClass(&wndClass);
	if (!bRet)
	{
		MessageBox(NULL, _T("注册窗口类失败"), NULL, MB_OK);
		return FALSE;
	}

	HWND hWnd;
	hWnd = CreateWindow(pszClassName,
						_T("MyApplication"),
						WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,		//添加垂直滚动条和水平滚动条
						CW_USEDEFAULT,
						CW_USEDEFAULT,
						600,
						480,
						NULL,
						NULL,
						hInstance,
						NULL);
	if (!hWnd)
	{
		MessageBox(NULL, _T("创建窗口失败!"), NULL, MB_OK);
		return FALSE;
	}

	ShowWindow(hWnd, nShowCmd);
	UpdateWindow(hWnd);

	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}

//窗口过程
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static int cxClient, cyClient, cxChar, cyChar, cxCaps;
	HDC hdc;
	PAINTSTRUCT ps;
	TEXTMETRIC tm;
	SCROLLINFO scrollInfo;
	int iVertPos, iHorzPos;
	int iBegin, iEnd;
	
	switch (uMsg)
	{
	case WM_CREATE:
		hdc = GetDC(hWnd);
		GetTextMetrics(hdc, &tm);
		cxChar = tm.tmAveCharWidth;
		cyChar = tm.tmHeight + tm.tmExternalLeading;
		cxCaps = (tm.tmPitchAndFamily & TMPF_FIXED_PITCH ? 3 : 2) * cxChar / 2;
		ReleaseDC(hWnd, hdc);
		return 0;
	case WM_SIZE:
		//第一条WM_SIZE消息是在WinMain函数调用CraeteWindow后,在ShowWindow时产生的,
		//所以这里获取客户区的大小进行初始化是可以的
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);

		scrollInfo.cbSize = sizeof(SCROLLINFO);
		scrollInfo.fMask = SIF_PAGE | SIF_RANGE;
		//设置垂直滚动条
		scrollInfo.nMin = 0;
		scrollInfo.nMax = MAX_NUM_ROW -1;
		scrollInfo.nPage = cyClient / cyChar;		//更新
		SetScrollInfo(hWnd, SB_VERT, &scrollInfo, TRUE);
		//设置水平滚动条
		scrollInfo.nMin = 0;
		scrollInfo.nMax = 100;
		scrollInfo.nPage = cxClient / cxChar;
		SetScrollInfo(hWnd, SB_HORZ, &scrollInfo, TRUE);
		return 0;
	case WM_VSCROLL:
		scrollInfo.cbSize = sizeof(SCROLLINFO);
		scrollInfo.fMask = SIF_ALL;
		GetScrollInfo(hWnd, SB_VERT, &scrollInfo);
		iVertPos = scrollInfo.nPos;
		switch (LOWORD(wParam))
		{
		case SB_LINEUP:
			scrollInfo.nPos -= 1;
			break;
		case SB_LINEDOWN:
			scrollInfo.nPos += 1;
			break;
		case SB_PAGEUP:
			scrollInfo.nPos -= scrollInfo.nPage;
			break;
		case SB_PAGEDOWN:
			scrollInfo.nPos += scrollInfo.nPage;
			break;
		case SB_THUMBTRACK:
			scrollInfo.nPos = scrollInfo.nTrackPos;
			break;
		}
		scrollInfo.fMask = SIF_POS;
		SetScrollInfo(hWnd, SB_VERT, &scrollInfo, TRUE);
		GetScrollInfo(hWnd, SB_VERT, &scrollInfo);
		if (iVertPos != scrollInfo.nPos)
		{
			//滚动客户区的内容,这里参数设置代表了整个客户区,滚动后,没有覆盖的区域将参与重绘
			ScrollWindow(hWnd, 
						 0, 
						 (iVertPos - scrollInfo.nPos) * cyChar, //这里为内容的滚动方向,向上则加上负值
						 NULL, 
						 NULL);
			UpdateWindow(hWnd);	//立即更新无效的区域,此处为未被覆盖的局域
		}
		return 0;
	case WM_HSCROLL:
		scrollInfo.cbSize = sizeof(SCROLLINFO);
		scrollInfo.fMask = SIF_ALL;
		GetScrollInfo(hWnd, SB_HORZ, &scrollInfo);
		iHorzPos = scrollInfo.nPos;
		switch (LOWORD(wParam))
		{
		case SB_LINELEFT:
			scrollInfo.nPos -= 1;
			break;
		case SB_LINERIGHT:
			scrollInfo.nPos += 1;
			break;
		case SB_PAGELEFT:
			scrollInfo.nPos -= scrollInfo.nPage;
			break;
		case SB_PAGERIGHT:
			scrollInfo.nPos += scrollInfo.nPage;
			break;
		case SB_THUMBTRACK:
			scrollInfo.nPos = scrollInfo.nTrackPos;
			break;
		}
		scrollInfo.fMask = SIF_POS;
		SetScrollInfo(hWnd, SB_HORZ, &scrollInfo, TRUE);
		GetScrollInfo(hWnd, SB_HORZ, &scrollInfo);
		if (scrollInfo.nPos != iHorzPos)
		{
			ScrollWindow(hWnd, cxChar * (iHorzPos - scrollInfo.nPos),0, NULL, NULL);
		}
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);

		scrollInfo.cbSize = sizeof(SCROLLINFO);
		scrollInfo.fMask = SIF_POS;
		GetScrollInfo(hWnd, SB_VERT, &scrollInfo);
		iVertPos = scrollInfo.nPos;		//当前垂直滚动条的位置

		GetScrollInfo(hWnd, SB_HORZ, &scrollInfo);
		iHorzPos = scrollInfo.nPos;		//当前水平滚动条的位置

		//确保滚动条滑块位置在 0--MAX_NUM_ROW之间
		iBegin = max(0, iVertPos + ps.rcPaint.top / cyChar);//无效区域对应的开始行,这句话是多余的,
															//因为开始设置的iVertPos大于0,而无效区域的矩形的坐标一般也是个正直
															//(InValidate可以设置坐标为负的矩形,但windows会做处理。对于不同的映射模式也一样吗?)
		iEnd = min(MAX_NUM_ROW - 1, iVertPos + ps.rcPaint.bottom / cyChar);	//无效区域对应的结束行
		
		for (int row = iBegin; row <= iEnd; ++row)
		{
				TCHAR buffer[100] = {0};
				TextOut(hdc,
						-iHorzPos * cxChar,
						(row - iVertPos) * cyChar,
						buffer,
						_stprintf(buffer, _T("第%3d行"), row + 1));
		}
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

3、运行结果如下图



显然,解决了最后一行不能显示在客户区底部的问题。此外这里绘制是根据客户区无效的区域来确定对应内容的显示。当行号设置得更大时,绘制的效率也是一样的。

4、ScrollWindow的使用

BOOL ScrollWindow(  HWND hWnd,              // handle to window
  int XAmount,            // horizontal scrolling
  int YAmount,            // vertical scrolling
  CONST RECT *lpRect,     // client area
  CONST RECT *lpClipRect  // clipping rectangle);
见下图:


左边为滚动前的内容,右边为滚动后的内容。
其中红色区域为滚动区域,将内容向上滚动一行后,右边蓝色矩形区域为未被滚动区域覆盖的,那么这部分会被重绘。而红色区域不参与重绘,只是滚动之后的结果,内容不变,只是位置发生了改变而已!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值