标准滚动栏msdn示例简化版

滚动栏是Windows中重要的UI元素,它有两种类型:标准滚动栏、滚动栏控制。二者比较起来,标准滚动栏简单但功能有限制。它只在窗口客户区的边界上,作为窗口的一个部分不含有独立的窗口句柄,消息由所在窗口的WndProc处理。滚动栏控制功能更强大一点,相对复杂一些。它可以在窗口的任何地方,是独立的子窗口有自己的窗口句柄,消息由自己处理。

本文的目的是展示滚动栏的消息处理方法,不涉及复杂的应用,所以例子比较简单,是对msdn上滚动栏示例的简化:只含有垂直滚动栏,不含有水平滚动栏。

下面是这个例子的运行截图:

对右侧的滚动栏进行交互,客户区的文本可以正确地显示出来。这个示例程序代码非常简单:

1. 头文件和声明

// Scrollable.cpp
// 2012-12-04 by btwsmile

#include <Windows.h>
#include <tchar.h>
// WndProc pre-declaration
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

因为要调用Windows API函数,所以需包含Windows.h头文件。tchar.h是提供通用字符串处理的,程序中调用了_tcslen()函数计算字符串的长度,为了支持Unicode这样做是很有必要的。WndProc是窗口过程函数的声明式,其实现被放在WinMain后。

2. WinMain函数的定义

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
	static TCHAR szAppName[] = TEXT("Scrollable");
	WNDCLASS wndclass;
	MSG msg;

	wndclass.style			= CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc	= WndProc;
	wndclass.cbClsExtra		= 0;
	wndclass.cbWndExtra		= 0;
	wndclass.hInstance		= hInstance;
	wndclass.hIcon			= ::LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor		= ::LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wndclass.lpszMenuName	= NULL;
	wndclass.lpszClassName	= szAppName;

	if( !::RegisterClass(&wndclass) )
	{
		MessageBox(NULL, TEXT("Register wndclass failed!"), TEXT("ERROR"), MB_OK);
		return 1;
	}
	HWND hWnd = ::CreateWindow(szAppName, szAppName, WS_OVERLAPPEDWINDOW | WS_VSCROLL,
							200, 200, 320, 240,
							NULL, NULL, hInstance, NULL);
	::ShowWindow(hWnd, iCmdShow);
	::UpdateWindow(hWnd);

	while( ::GetMessage(&msg, NULL, 0, 0) )
	{
		::TranslateMessage(&msg);
		::DispatchMessage(&msg);
	}
	return msg.wParam;
}

遵循了窗口的一般创建过程,即:

a. 定义窗口类wndclass;

b. 注册窗口类;

c. 创建窗口并获得句柄hWnd;

d. 显示和更新窗口;

e. 启动消息循环。

需要特别注意的是,在调用CreateWindow函数创建窗口时,窗口风格需指定WS_VSCROLL,这样系统才会在窗口的右侧增加一个滚动栏。

3. WndProc的实现

WndProc有点长,但容易理解,主要分成两个部分:变量声明,消息处理。

3.1 变量声明
// WndProc definition
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	HDC hdc; 
	PAINTSTRUCT ps;
	TEXTMETRIC tm;
	SCROLLINFO si;

	static int yClient;     // height of client area
	static int yChar;       // vertical scrolling unit 
	static int yPos;        // current vertical scrolling position
	// Create an array of lines to display. 
	#define LINES 28 
	static TCHAR *abc[] = { 
		   TEXT("anteater"),  TEXT("bear"),      TEXT("cougar"), 
		   TEXT("dingo"),     TEXT("elephant"),  TEXT("falcon"), 
		   TEXT("gazelle"),   TEXT("hyena"),     TEXT("iguana"), 
		   TEXT("jackal"),    TEXT("kangaroo"),  TEXT("llama"), 
		   TEXT("moose"),     TEXT("newt"),      TEXT("octopus"), 
		   TEXT("penguin"),   TEXT("quail"),     TEXT("rat"), 
		   TEXT("squid"),     TEXT("tortoise"),  TEXT("urus"), 
		   TEXT("vole"),      TEXT("walrus"),    TEXT("xylophone"), 
		   TEXT("yak"),       TEXT("zebra"),
		   TEXT("This line contains words, but no character. Go figure."),
		   TEXT("")
		 }; 

hdc是设备描述表句柄,想要在客户区输出文本或图形都需要hdc;ps是绘制结构,在处理WM_PAINT消息的时候使用,其rcPaint成员表示无效矩形区域,在计算TextOut的垂直坐标时会用到;tm表示字体的尺寸信息;si表示滚动栏信息。yClient表示客户区的高度,yChar表示每一行的高度,yPos表示当前垂直滚动栏的位置。LINES是常量宏,表示abc所含字符串数量。abc数组保存着将在窗口中显示字符串,每行一个字符串。

3.2 消息处理
	switch (uMsg) 
	{ 
		case WM_CREATE : 
			hdc = GetDC (hwnd); 
			GetTextMetrics (hdc, &tm);	// get text metric info
			yChar = tm.tmHeight + tm.tmExternalLeading; 
			ReleaseDC (hwnd, hdc); 
			return 0; 
 
		case WM_SIZE: 
			yClient = HIWORD (lParam);
			si.cbSize = sizeof(si); 
			si.fMask  = SIF_RANGE | SIF_PAGE; 
			si.nMin   = 0; 
			si.nMax   = LINES - 1; 
			si.nPage  = yClient / yChar; 
			SetScrollInfo(hwnd, SB_VERT, &si, TRUE); // Set the vertical scrolling range and page size
			return 0; 

		case WM_VSCROLL:
			 si.cbSize = sizeof (si);
			 si.fMask  = SIF_ALL;
			 GetScrollInfo (hwnd, SB_VERT, &si);
			 yPos = 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;
				  break;
			 default:
				  break; 
			 }

			 si.fMask = SIF_POS;
			 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
			 GetScrollInfo (hwnd, SB_VERT, &si);
			 // If the position has changed, scroll window and update it
			 if (si.nPos != yPos)
			 {
				ScrollWindow(hwnd, 0, yChar * (yPos - si.nPos), NULL, NULL);
				UpdateWindow (hwnd);
			 }
			 return 0;

		case WM_PAINT :
			 hdc = BeginPaint (hwnd, &ps);
			 si.cbSize = sizeof (si);
			 si.fMask  = SIF_POS;
			 GetScrollInfo (hwnd, SB_VERT, &si);
			 yPos = si.nPos;
			 {
				 // Find painting limits
				 int iFirstLine = max (0, yPos + ps.rcPaint.top / yChar);
				 int iLastLine = min (LINES - 1, yPos + ps.rcPaint.bottom / yChar);
				 for (int i = iFirstLine; i <= iLastLine; i++)
					 ::TextOut(hdc, 0, ps.rcPaint.top + (i-iFirstLine)*yChar, abc[i], _tcslen(abc[i]));
			 }
			 EndPaint (hwnd, &ps);
			 return 0;
         
		case WM_DESTROY :
			 PostQuitMessage (0);
			 return 0;
	}
	return DefWindowProc (hwnd, uMsg, wParam, lParam);
}

在处理WM_CREATE消息时,调用GetTextMetrics函数获取字体尺寸信息,yChar等于tmHeight与tmExternalLeading之和。

在处理WM_SIZE消息时,调用SetScrollInfo函数设置滚动栏的range和page size,可以看出是以“行”为单位的,而非像素。

接下来处理WM_VSCROLL消息,当用户与右侧滚动栏交互会触发这一消息,其wParam参数的低16位表示交互请求类型,有8种,这里只处理了其中的7种,没处理的一种是SB_THUMBPOSITION,这种消息其实是当用户拖动滚动块松开鼠标后发出的。

处理WM_VSCROLL消息分成三大步:一是更新si.nPos,二是调用SetScrollInfo设置滚动栏的信息,三是判断是否发生了变更,若变更则调用ScrollWindow滚动窗口并调用UpdateWindow里面更新无效的矩形区域。更新的实质是直接向WndProc发送WM_PAINT消息,从而可以看出WndProc在返回前被重入的。

接着处理WM_PAINT消息,对无效的矩形区域进行绘制。注意TextOut附近的代码被专门放入一个语句块中,用大括号标识出来,不这么做编译器会报错,这与作用域有关。

需要特别注意TextOut输出字符串时所使用的坐标,水平方向上总是0,垂直方向上是ps.rcPaint.top + (i-iFirstLine)*yChar,即以无效矩形区域的上侧(top)为基准,偏移i-iFirstLine行后的起始坐标值。

最后是WM_DESTROY消息,当用户关闭程序,窗口被销毁后就会抛出此消息。这时Post退出消息号0,消息循环获取它,正好可以退出循环,整个程序运行结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: VC6.0是微软公司推出的一款集成开发工具,对应的开发环境是Microsoft Visual C++ 6.0。而MSDN则是微软开发者网络的缩写,包含了丰富的开发文档、示例代码、API参考等资源。在VC6.0中,MSDN的帮助文档基本以CHM(Compiled HTML Help)格式提供。 精简版的MSDN是对原版MSDN进行了一些精简和压缩,以减少文件的大小,并去除一些相对冷门或不常用的内容。精简版的好处在于节省了硬盘空间,下载和安装的时间也相对较快。对于那些只需要基本功能和常用的参考资料的开发者来说,精简版的MSDN是一个不错的选择。 然而,需要注意的是精简版的MSDN可能缺少一些较为特殊或高级的内容,对于一些复杂的开发任务可能无法提供全面的支持。因此,如果开发者需要更全面的文档和更丰富的资源,建议使用完整版的MSDN。 总的来说,VC6.0 MSDN精简版是一种更小巧、更快速、节省资源的选择,适合对硬盘空间有限或只关注基本功能和常用参考资料的开发者。但对于一些更高级或特殊开发需求的用户来说,完整版的MSDN可能更加适合。 ### 回答2: VC6.0 MSDN CHM精简版是在VC6.0开发环境中内置的帮助文档,它提供了开发者在使用VC6.0进行编程过程中需要的相关信息和指导。这个精简版的CHM格式的帮助文档相对于完整版的MSDN文档而言,主要是在内容上进行了简化,删除了一些相对不常用或不必要的信息,使得整个文档更加精练且易于浏览。 这个精简版的VC6.0 MSDN CHM主要囊括了VC6.0编程环境、C++编程语言、MFC框架、Windows API等方面的核心内容,提供了丰富的实例和指导,帮助开发者快速上手和解决常见问题。在这个帮助文档中,开发者可以找到关于编写代码、调试程序、编译构建、界面设计、资源管理等方面的详细说明和示例。 通过VC6.0 MSDN CHM精简版,开发者可以轻松地查询函数、类、结构、宏等的定义和用法,了解相关的参数、返回值和使用注意事项,还可以学习到一些编程技巧和最佳实践。这个帮助文档的结构清晰,搜索功能强大,可以快速定位所需信息,并提供了丰富的链接,帮助开发者更好地了解和使用VC6.0编程环境。 总之,VC6.0 MSDN CHM精简版是VC6.0开发环境中一份很有价值的帮助文档,它提供了开发者在使用VC6.0进行编程过程中所需要的关键信息和指导,帮助开发者更高效地进行软件开发工作。 ### 回答3: VC6.0 MSDN CHM精简版是指一种精简的VC 6.0开发环境的帮助文件。MSDN是微软开发者网络(Microsoft Developer Network)的缩写,它提供了丰富的开发文档和帮助资源,方便开发人员学习和使用微软的开发工具和技术。 而VC6.0是微软的一款经典的集成开发环境,用于开发Windows平台上的应用程序。它拥有强大的编译器、调试器和工具集,被广泛应用于过去的软件开发。 MSDN CHM精简版是对VC6.0的帮助文档进行了精简处理,去除了一些冗余和不常用的内容,使得文件更加简洁和高效。这对于开发者来说可以提高查找和阅读文档的效率,不必在庞大的文档中浪费时间。 精简版的CHM文件可能会去除一些高级的和专业的内容,因此对于初学者或者只需要了解基础知识的开发者来说是一个很好的选择。它提供了VC6.0开发环境的基本知识,例如语法、编程概念、函数库等。通过阅读这些文档,开发者可以更好地理解和使用VC6.0进行开发。 然而,使用精简版的同时也需要注意一些局限性。由于去除了一些内容,可能会导致某些高级和特定问题的文档无法得到解答。因此,在遇到复杂或者深入的问题时,可能需要查看完整版的MSDN文档或者其他更全面的参考资料。 总的来说,VC6.0 MSDN CHM精简版是一种方便开发者快速查阅VC6.0开发环境文档的资源。尤其对于初学者来说,这是一种高效学习和使用VC6.0的方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值