windows程序设计(5)使用滚动条

本文的程序来自《windows程序设计(第五版)》

设计滚动条的初衷,是因为客户区需要显示的东西太多了,在这个头文件中,我们包含了一大堆的系统的信息:

//总行数
#define NUMLINES  ((int) (sizeof sysmetrics / sizeof sysmetrics [0]))

struct
{
	int Index ;
	TCHAR* szLabel;
	TCHAR* szDesc ;

}
//结构体数组
sysmetrics [] =
        
{
        
	SM_CXSCREEN,			TEXT ("SM_CXSCREEN"),			TEXT ("Screen width in pixels"),        
	SM_CYSCREEN,			TEXT ("SM_CYSCREEN"),			TEXT ("Screen height in pixels"),       
    SM_CXVSCROLL,			TEXT ("SM_CXVSCROLL"),			TEXT ("Vertical scroll width"),        
    SM_CYHSCROLL,			TEXT ("SM_CYHSCROLL"),			TEXT ("Horizontal scroll height"),        
    SM_CYCAPTION,			TEXT ("SM_CYCAPTION"),			TEXT ("Caption bar height"),        
    SM_CXBORDER,			TEXT ("SM_CXBORDER"),			TEXT ("Window border width"),        
    SM_CYBORDER,			TEXT ("SM_CYBORDER"),			TEXT ("Window border height"),        
    SM_CXFIXEDFRAME,		TEXT ("SM_CXFIXEDFRAME"),		TEXT ("Dialog window frame width"),        
    SM_CYFIXEDFRAME,		TEXT ("SM_CYFIXEDFRAME"),		TEXT ("Dialog window frame height"),        
    SM_CYVTHUMB,			TEXT ("SM_CYVTHUMB"),			TEXT ("Vertical scroll thumb height"),        
    SM_CXHTHUMB,			TEXT ("SM_CXHTHUMB"),			TEXT ("Horizontal scroll thumb width"),        
    SM_CXICON,				TEXT ("SM_CXICON"),				TEXT ("Icon width"),        
    SM_CYICON,				TEXT ("SM_CYICON"),				TEXT ("Icon height"),        
    SM_CXCURSOR,			TEXT ("SM_CXCURSOR"),			TEXT ("Cursor width"),        
    SM_CYCURSOR,			TEXT ("SM_CYCURSOR"),			TEXT ("Cursor height"),        
    SM_CYMENU,				TEXT ("SM_CYMENU"),				TEXT ("Menu bar height"),        
    SM_CXFULLSCREEN,		TEXT ("SM_CXFULLSCREEN"),		TEXT ("Full screen client area width"),        
    SM_CYFULLSCREEN,		TEXT ("SM_CYFULLSCREEN"),		TEXT ("Full screen client area height"),        
    SM_CYKANJIWINDOW,		TEXT ("SM_CYKANJIWINDOW"),		TEXT ("Kanji window height"),        
    SM_MOUSEPRESENT,		TEXT ("SM_MOUSEPRESENT"),		TEXT ("Mouse present flag"),        
    SM_CYVSCROLL,			TEXT ("SM_CYVSCROLL"),			TEXT ("Vertical scroll arrow height"),        
    SM_CXHSCROLL,			TEXT ("SM_CXHSCROLL"),			TEXT ("Horizontal scroll arrow width"),        
    SM_DEBUG,				TEXT ("SM_DEBUG"),				TEXT ("Debug version flag"),        
    SM_SWAPBUTTON,			TEXT ("SM_SWAPBUTTON"),			TEXT ("Mouse buttons swapped flag"),        
    SM_CXMIN,				TEXT ("SM_CXMIN"),				TEXT ("Minimum window width"),        
    SM_CYMIN,				TEXT ("SM_CYMIN"),				TEXT ("Minimum window height"),        
    SM_CXSIZE,				TEXT ("SM_CXSIZE"),				TEXT ("Min/Max/Close button width"),        
    SM_CYSIZE,				TEXT ("SM_CYSIZE"),				TEXT ("Min/Max/Close button height"),        
    SM_CXSIZEFRAME,			TEXT ("SM_CXSIZEFRAME"),		TEXT ("Window sizing frame width"),        
    SM_CYSIZEFRAME,			TEXT ("SM_CYSIZEFRAME"),		TEXT ("Window sizing frame height"),        
    SM_CXMINTRACK,			TEXT ("SM_CXMINTRACK"),			TEXT ("Minimum window tracking width"),        
    SM_CYMINTRACK,			TEXT ("SM_CYMINTRACK"),			TEXT ("Minimum window tracking height"),        
    SM_CXDOUBLECLK,			TEXT ("SM_CXDOUBLECLK"),		TEXT ("Double click x tolerance"),        
    SM_CYDOUBLECLK,			TEXT ("SM_CYDOUBLECLK"),		TEXT ("Double click y tolerance"),        
    SM_CXICONSPACING,		TEXT ("SM_CXICONSPACING"),		TEXT ("Horizontal icon spacing"),        
    SM_CYICONSPACING,		TEXT ("SM_CYICONSPACING"),		TEXT ("Vertical icon spacing"),        
    SM_MENUDROPALIGNMENT,	TEXT ("SM_MENUDROPALIGNMENT"),	TEXT ("Left or right menu drop"),        
    SM_PENWINDOWS,			TEXT ("SM_PENWINDOWS"),			TEXT ("Pen extensions installed"),        
    SM_DBCSENABLED,			TEXT ("SM_DBCSENABLED"),		TEXT ("Double-Byte Char Set enabled"),        
    SM_CMOUSEBUTTONS,		TEXT ("SM_CMOUSEBUTTONS"),		TEXT ("Number of mouse buttons"),       
    SM_SECURE,				TEXT ("SM_SECURE"),				TEXT ("Security present flag"),        
    SM_CXEDGE,				TEXT ("SM_CXEDGE"),				TEXT ("3-D border width"),        
    SM_CYEDGE,				TEXT ("SM_CYEDGE"),				TEXT ("3-D border height"),        
    SM_CXMINSPACING,		TEXT ("SM_CXMINSPACING"),		TEXT ("Minimized window spacing width"),        
    SM_CYMINSPACING,		TEXT ("SM_CYMINSPACING"),		TEXT ("Minimized window spacing height"),        
    SM_CXSMICON,			TEXT ("SM_CXSMICON"),			TEXT ("Small icon width"),       
    SM_CYSMICON,			TEXT ("SM_CYSMICON"),			TEXT ("Small icon height"),        
    SM_CYSMCAPTION,			TEXT ("SM_CYSMCAPTION"),		TEXT ("Small caption height"),        
    SM_CXSMSIZE,			TEXT ("SM_CXSMSIZE"),			TEXT ("Small caption button width"),        
    SM_CYSMSIZE,			TEXT ("SM_CYSMSIZE"),			TEXT ("Small caption button height"),        
    SM_CXMENUSIZE,			TEXT ("SM_CXMENUSIZE"),			TEXT ("Menu bar button width"),        
    SM_CYMENUSIZE,			TEXT ("SM_CYMENUSIZE"),			TEXT ("Menu bar button height"),        
    SM_ARRANGE,				TEXT ("SM_ARRANGE"),			TEXT ("How minimized windows arranged"),        
    SM_CXMINIMIZED,			TEXT ("SM_CXMINIMIZED"),		TEXT ("Minimized window width"),        
    SM_CYMINIMIZED,			TEXT ("SM_CYMINIMIZED"),		TEXT ("Minimized window height"),        
    SM_CXMAXTRACK,			TEXT ("SM_CXMAXTRACK"),			TEXT ("Maximum draggable width"),        
    SM_CYMAXTRACK,			TEXT ("SM_CYMAXTRACK"),			TEXT ("Maximum draggable height"),        
    SM_CXMAXIMIZED,			TEXT ("SM_CXMAXIMIZED"),		TEXT ("Width of maximized window"),        
    SM_CYMAXIMIZED,			TEXT ("SM_CYMAXIMIZED"),		TEXT ("Height of maximized window"),        
    SM_NETWORK,				TEXT ("SM_NETWORK"),			TEXT ("Network present flag"),        
    SM_CLEANBOOT,			TEXT ("SM_CLEANBOOT"),			TEXT ("How system was booted"),        
    SM_CXDRAG,				TEXT ("SM_CXDRAG"),				TEXT ("Avoid drag x tolerance"),        
    SM_CYDRAG,				TEXT ("SM_CYDRAG"),				TEXT ("Avoid drag y tolerance"),        
    SM_SHOWSOUNDS,			TEXT ("SM_SHOWSOUNDS"),			TEXT ("Present sounds visually"),        
    SM_CXMENUCHECK,			TEXT ("SM_CXMENUCHECK"),		TEXT ("Menu check-mark width"),        
    SM_CYMENUCHECK,			TEXT ("SM_CYMENUCHECK"),		TEXT ("Menu check-mark height"),        
    SM_SLOWMACHINE,			TEXT ("SM_SLOWMACHINE"),		TEXT ("Slow processor flag"),        
    SM_MIDEASTENABLED,		TEXT ("SM_MIDEASTENABLED"),		TEXT ("Hebrew and Arabic enabled flag"),        
    SM_MOUSEWHEELPRESENT,	TEXT ("SM_MOUSEWHEELPRESENT"),	TEXT ("Mouse wheel present flag"),        
    SM_XVIRTUALSCREEN,		TEXT ("SM_XVIRTUALSCREEN"),		TEXT ("Virtual screen x origin"),        
    SM_YVIRTUALSCREEN,		TEXT ("SM_YVIRTUALSCREEN"),		TEXT ("Virtual screen y origin"),        
    SM_CXVIRTUALSCREEN,     TEXT ("SM_CXVIRTUALSCREEN"),	TEXT ("Virtual screen width"),        
    SM_CYVIRTUALSCREEN,		TEXT ("SM_CYVIRTUALSCREEN"),	TEXT ("Virtual screen height"),        
    SM_CMONITORS,			TEXT ("SM_CMONITORS"),			TEXT ("Number of monitors"),        
    SM_SAMEDISPLAYFORMAT,	TEXT ("SM_SAMEDISPLAYFORMAT"),	TEXT ("Same color format flag")
        
} ;


第一行定义的,是数组有多少个元素。之后我们定义了结构体数组。元素个数很多,一行显示不下,那么如何添加滚动条呢?

我们先看程序:

#include <windows.h>
#include "sysmets.h"

LRESULT CALLBACK WndProc (HWND,UINT,WPARAM,LPARAM);

int WINAPI WinMain(HINSTANCE hInstance,		//当前实例句柄
				   HINSTANCE hPrevInstance, //先前实例句柄
				   LPSTR lpCmdLine,			//命令行
				   int iCmdShow)			//显示状态
{
	static TCHAR szAppName[] = TEXT("显示系统内容");
	//窗口句柄
	HWND hwnd;
	//消息
	MSG msg;
	//窗口类
	WNDCLASS wndclass;
	//窗口风格:当移动窗口或者改变大小时重绘窗口
	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) GetStockObject (WHITE_BRUSH);
	//菜单:暂时没有
	wndclass.lpszMenuName  = NULL;
	//窗口类名
	wndclass.lpszClassName = szAppName;

	//注册窗口
	if(!RegisterClass(&wndclass))
	{
		return -1;
	}

	//创建窗口
	hwnd = CreateWindow(szAppName,				//窗口类的名称,必须是已经注册的
						TEXT("系统内容"),		//窗口标题
						WS_OVERLAPPEDWINDOW | WS_VSCROLL,	//窗口风格,加入垂直滚动条
						CW_USEDEFAULT,			//X坐标
						CW_USEDEFAULT,			//Y坐标
						CW_USEDEFAULT,			//宽度
						CW_USEDEFAULT,			//高度
						NULL,					//父窗口句柄
						NULL,					//菜单窗口句柄
						hInstance,				//高级版本的windos忽略
						NULL);					

	//显示窗口
	//ShowWindow(hwnd,SW_SHOWNA);
	ShowWindow (hwnd, iCmdShow);

	//更新窗口
	UpdateWindow(hwnd);

	//消息循环
	while(GetMessage(&msg,NULL,0,0))
	{
		
		TranslateMessage(&msg);
		//将消息给窗口
		DispatchMessage(&msg);

	}

	return msg.wParam;

}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	//字符的宽度,大写字母宽度,字符高度
	static int    cxChar, cxCaps, cyChar ;
	//窗口大小
	static int cxClient, cyClient ;
	//滚动条位置
	static int iVscrollPos; 
	HDC hdc;
	//该变量用于索引sysmets.h中定义的结构体数组sysmetrics[]的每个元素
	int i;
	//输出文本的垂直位置
	int y;
	//绘图结构
	PAINTSTRUCT ps;
	//字符串
	TCHAR szBuffer [10];
	//字体信息结构
	TEXTMETRIC  tm;
	switch(message)
	{
	case WM_CREATE:
		hdc = GetDC(hwnd);
		//取得内定系统字体的文字大小,存在放在tm里
		GetTextMetrics (hdc, &tm);
		//平均字符宽
		cxChar = tm.tmAveCharWidth ;
		//大写字母的平平均宽度
		cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;
		//字符总高度:高度+行间距
		cyChar = tm.tmHeight + tm.tmExternalLeading ;

		ReleaseDC(hwnd,hdc);

		//设置滚动条范围:参数为:窗口句柄,滚动条类型,最小位置,最大位置,是否重绘
		SetScrollRange (hwnd, SB_VERT, 0, NUMLINES - 1, FALSE);
		//滚动条的方块的位置,参数为:窗口句柄,滚动条类型,新的位置
		SetScrollPos   (hwnd, SB_VERT, iVscrollPos, TRUE); 
		
		return 0;
	case WM_SIZE:       
		cxClient = LOWORD (lParam) ;        
		cyClient = HIWORD (lParam) ;       
		return 0; 
	case WM_PAINT:
		hdc = BeginPaint (hwnd, &ps) ;
		for(i = 0;i < NUMLINES;i++ )
		{
			
			//行宽度*变动的行数
			y =  cyChar * (i - iVscrollPos) ;
			//输出字符串到指定的区域(使用当前的字体,背景颜色,颜色)
			//参数为:设备内容句柄,x坐标,y坐标,带输出的字符串(与是否/0结尾无关),字符的个数
			//lstrlen计算字符串长度			
			TextOut(hdc,0,			y,sysmetrics[i].szLabel,lstrlen(sysmetrics[i].szLabel));
			//从22个大写字母以后的位置输出,因为第一列最多只有20个大写字母
			TextOut(hdc,22*cxCaps,	y,sysmetrics[i].szDesc,lstrlen(sysmetrics[i].szDesc));
			//最后一列是右对齐的
			SetTextAlign(hdc,TA_RIGHT | TA_TOP);
			//40*cxChar是第二列及第三列的列宽
			//wsprintf用来给指定的buffer中格式化并存储字符串(0结尾),返回值为字符串的数目
			//GetSystemMetrics用来获取系统参数,返回值为系统参数
			TextOut(hdc,22*cxCaps+40*cxChar,y,szBuffer,wsprintf(szBuffer,TEXT("%5d"),GetSystemMetrics (sysmetrics[i].Index)));
			//再把对齐方式改回去,否则下一次循环时也是右对齐
			SetTextAlign(hdc,TA_LEFT | TA_TOP);
		}
		EndPaint (hwnd, &ps) ;
		return 0;
		
	//滚动条消息
	case WM_VSCROLL:
			
		//通过滚动条消息的wParam表明滚动条的操作
		switch(LOWORD (wParam))
		{
		//点一下上箭头:向下翻一行
		case SB_LINEUP:
			iVscrollPos -=1;
			break;
		//点一下下箭头:向上翻一行
		case SB_LINEDOWN:
			iVscrollPos +=1;
			break;
		//点一下方块上面:向上翻一页,每一页是:(客户区/一行高度)这么多行
		case SB_PAGEUP:
			iVscrollPos -= cyClient / cyChar ;
			break;
		//点一下方块下面:向下翻一页
		case SB_PAGEDOWN:
			iVscrollPos += cyClient / cyChar ;
			break;
		//拖动滚动条:wParam的高字节表明了滚动条的位置
		case SB_THUMBPOSITION:
			iVscrollPos = HIWORD (wParam) ;
			break;
		default:
			break;
		}
		
		//这一句是为了
		iVscrollPos = max (0, min (iVscrollPos, NUMLINES - 1)) ;

		 //如果滚动条位置改变
		 if (iVscrollPos != GetScrollPos (hwnd, SB_VERT))
		 {
			//设置滚动条小方块的位置
			SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ;
			//设为无效区,激励WM_PAINT消息
			InvalidateRect (hwnd, NULL, TRUE) ;
		 }

		return 0;
		

	case  WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc (hwnd, message, wParam, lParam) ;

}

程序的思路是这样的:在创建窗口时,就要告诉系统你要加一个滚动条(垂直的),在WM_CREATE消息中,就要对滚动条进行一些基本的设置。WM_VSCROLL对滚动条做出相应,可以分为3部分:第一部分接收滚动条的消息;第二部分是让滚动条的那个小方块滚动到特定的位置,第三部分是页面的文字出现“滚动”的效果。

先看第一部分:滚动条的作用是什么:当点击一下向上或者向下的按钮时,会滚动一行文字;当点击滚动的小方块的上面或者下面时,会滚动一页文字;而拖着滚动条走,能滚动到指定的地方。到底是这几种操作的哪一种,是通过WM_VSCROLL消息的wParam的低字节传过来的。在程序中,使用iVscrollPos这个全局变量来记录滚动的位置,每一次对滚动条都会改变位置。

iVscrollPos = max (0, min (iVscrollPos, NUMLINES - 1)) ;是为了不让滚动条的位置超出一定为范围,大家可以把它屏蔽之后,将滚动条向上或者向下滚,就可以理解结果了。
而通过SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ;就将滚动条的方块放到了可定位位置。而第三部分让页面滚动,则是WM_PAINT的作用了:其实当你看到第二行显示在原来第一行为位置上时,其实并不是系统没有输出第一行,而是第一行输出的位置已经到了客户区的上面,所以你看不到!第二行紧跟在第一行下面,刚好就放到了开始的位置。

这个滚动条有一个明显的缺点,就是他并不是我们希望看到的那样:滚动条的长度反应的当前页面占显示的内容总的内容的百分比,具体的说,就是:

页面方块的大小/滚动的长度=页面大小/整个范围=显示文件的数量/整个文件的数量

别看这只是一点小小的改动,但是他的实现方法却与上面的差别很大。总体上来说,他需要使用SCROLLINFO结构来记录滚动条的信息,然后使用GetScrollInfo获取信息,通过SetScrollInfo设置信息。在对滚动条的响应消息中,通过ScrollWindow来实现内容的滚动(而不像前面那个程序,在WM_PAINT消息下实现)。

我们在看看程序:

#include <windows.h>
#include "sysmets.h"

LRESULT CALLBACK WndProc (HWND,UINT,WPARAM,LPARAM);

int WINAPI WinMain(HINSTANCE hInstance,		//当前实例句柄
				   HINSTANCE hPrevInstance, //先前实例句柄
				   LPSTR lpCmdLine,			//命令行
				   int iCmdShow)			//显示状态
{
	static TCHAR szAppName[] = TEXT("显示系统内容");
	//窗口句柄
	HWND hwnd;
	//消息
	MSG msg;
	//窗口类
	WNDCLASS wndclass;
	//窗口风格:当移动窗口或者改变大小时重绘窗口
	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) GetStockObject (WHITE_BRUSH);
	//菜单:暂时没有
	wndclass.lpszMenuName  = NULL;
	//窗口类名
	wndclass.lpszClassName = szAppName;

	//注册窗口
	if(!RegisterClass(&wndclass))
	{
		return -1;
	}

	//创建窗口
	hwnd = CreateWindow(szAppName,				//窗口类的名称,必须是已经注册的
						TEXT("系统内容"),		//窗口标题
						WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,	//窗口风格,加入滚动条
						CW_USEDEFAULT,			//X坐标
						CW_USEDEFAULT,			//Y坐标
						CW_USEDEFAULT,			//宽度
						CW_USEDEFAULT,			//高度
						NULL,					//父窗口句柄
						NULL,					//菜单窗口句柄
						hInstance,				//高级版本的windos忽略
						NULL);					

	//显示窗口
	//ShowWindow(hwnd,SW_SHOWNA);
	ShowWindow (hwnd, iCmdShow);

	//更新窗口
	UpdateWindow(hwnd);

	//消息循环
	while(GetMessage(&msg,NULL,0,0))
	{
		
		TranslateMessage(&msg);
		//将消息给窗口
		DispatchMessage(&msg);

	}

	return msg.wParam;

}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	//字符的宽度,大写字母宽度,字符高度
	static int    cxChar, cxCaps, cyChar ;
	//窗口大小
	static int cxClient, cyClient ;
	//最大宽度
	static int iMaxWidth;
	//滚动条位置
	static int iVertPos,iHorzPos,iPaintBeg,iPaintEnd;
	HDC hdc;
	//该变量用于索引sysmets.h中定义的结构体数组sysmetrics[]的每个元素
	int i;
	//输出文本的位置
	int x,y;
	//绘图结构
	PAINTSTRUCT ps;
	//这个结构包含了滚动条的信息
	//通过SetScrollInfo函数设置信息,通过 GetScrollInfo 函数获取信息
	SCROLLINFO si;
	//字符串
	TCHAR szBuffer [10];
	//字体信息结构
	TEXTMETRIC  tm;
	switch(message)
	{
	case WM_CREATE:
		hdc = GetDC(hwnd);
		//取得内定系统字体的文字大小,存在放在tm里
		GetTextMetrics (hdc, &tm);
		//平均字符宽
		cxChar = tm.tmAveCharWidth ;
		//大写字母的平均宽度
		cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;
		//字符总高度:高度+行间距
		cyChar = tm.tmHeight + tm.tmExternalLeading ;

		ReleaseDC(hwnd,hdc);
		//最大跨度 = 40个字符+22个大写字母
		iMaxWidth = 40*cxChar+22*cxCaps;
		return 0;
	case WM_SIZE:
		cxClient = LOWORD (lParam) ;        
		cyClient = HIWORD (lParam) ;   
		//设置垂直滚动条信息
		//结构体的大小
		si.cbSize  = sizeof(si);
		//指明将要设置和获取的参数:这里是最大最小值组成的范围和页面的大小
		si.fMask = SIF_RANGE | SIF_PAGE ;
		//滚动条位置的最小值
		si.nMin = 0;
		//滚动条位置的最大值
		si.nMax = NUMLINES - 1 ;
		//页面大小
		si.nPage = cyClient / cyChar ;
		//设置滚动条的参数
		SetScrollInfo(hwnd, SB_VERT,&si,TRUE);

		//设置水平滚动条信息
		si.cbSize  = sizeof(si);
		si.fMask = SIF_RANGE | SIF_PAGE ;
		si.nMin = 0;
		si.nMax = 2+ iMaxWidth/cxChar ;
		si.nPage = cxClient / cxChar ;
		SetScrollInfo (hwnd, SB_HORZ, &si, TRUE) ;

		return 0; 
		
	case WM_PAINT:
		hdc = BeginPaint (hwnd, &ps) ;

		si.cbSize = sizeof(si);
		si.fMask = SIF_POS;
		GetScrollInfo(hwnd,SB_VERT,&si);
		iVertPos = si.nPos;
		GetScrollInfo(hwnd,SB_HORZ,&si);
		iHorzPos = si.nPos;
		//max(0,当前位置+需要绘图的矩形区的最高点/字符的高度)
		//iPaintBeg = max(0,iVertPos+ps.rcPaint.top/cyChar);
		iPaintBeg = max(0,iVertPos);
		//绘图结束的地方 = 当前位置+绘制去取的高度
		iPaintEnd = min(NUMLINES -1,iVertPos+ps.rcPaint.bottom/cyChar);

		for(i = iPaintBeg; i <= iPaintEnd;i++)
		{
			//绘图的x起始位置:1是自己设置的,设置越大离左边越宽
			x = cxChar * (1 - iHorzPos) ;
			//绘图的y起始位置。
			y = cyChar * (i - iVertPos) ;
			TextOut(hdc,x, y,sysmetrics[i].szLabel,lstrlen(sysmetrics[i].szLabel));  
			//从22个大写字母以后的位置输出,因为第一列最多只有20个大写字母  
			TextOut(hdc,x+22*cxCaps,y,sysmetrics[i].szDesc,lstrlen(sysmetrics[i].szDesc));  
			SetTextAlign(hdc,TA_RIGHT | TA_TOP);
			TextOut(hdc,x+22*cxCaps+40*cxChar,y,szBuffer,wsprintf(szBuffer,TEXT("%5d"),GetSystemMetrics (sysmetrics[i].Index))); 
			SetTextAlign(hdc,TA_LEFT | TA_TOP);  
		}
		EndPaint (hwnd, &ps) ;
		return 0;
		
	//垂直滚动条消息
	case WM_VSCROLL:
		//获取垂直滚动条信息

		si.cbSize = sizeof(si);
		//所有参数
		si.fMask = SIF_ALL;
		GetScrollInfo(hwnd,SB_VERT,&si);
		//垂直位置
		iVertPos = si.nPos ;
		//通过滚动条消息的wParam表明滚动条的操作
		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;
		case SB_PAGEUP:
			si.nPage -= 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(si.nPos != iVertPos)
		{
			//滚动指定窗口的内容
			ScrollWindow(hwnd,0,cyChar*(iVertPos-si.nPos),NULL,NULL);
			UpdateWindow(hwnd);
		}
		return 0;
	//水平滚动条消息
	case WM_HSCROLL:
		si.cbSize = sizeof(si);
		si.fMask = SIF_ALL;
		GetScrollInfo(hwnd,SB_HORZ,&si);
		iHorzPos = si.nPos;
		switch(LOWORD(wParam))
		{
		case SB_LINELEFT:
			si.nPos -= 1;
			break;
		case SB_LINERIGHT:
			si.nPos +=1;
			break;
		case SB_PAGELEFT:
			si.nPos -= si.nPage;
			break;
		case SB_PAGERIGHT:
			si.nPos += si.nPage;
			break;
		case SB_THUMBPOSITION:
			si.nPos = si.nTrackPos;
			break;
		default:
			break;
			
		}
		si.fMask = SIF_POS;
		SetScrollInfo(hwnd,SB_HORZ,&si,TRUE);
		GetScrollInfo(hwnd,SB_HORZ,&si);
		if(si.nPos != iHorzPos)
		{
			ScrollWindow(hwnd,cxChar*(iHorzPos-si.nPos),0,NULL,NULL);
			//UpdateWindow (hwnd) ;

		}
		return 0;
	case  WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc (hwnd, message, wParam, lParam) ;

}


程序中有几点需要注意:

1.程序中设置了垂直滚动条和水平滚动条。在WM_SIZE消息中,随着窗口的变化,滚动条的那个小方块的大小也在变化。

2.在每次使用GetScrollInfo或者SetScrollInfo之前,都必须有si.cbSize = sizeof(si);这是因为兼容的缘故。而且还得指明操作的是SCROLLINFO中的哪些内容,这通过fMask来控制。

3.程序中有几行代码很费解:

iPaintBeg = max(0,iVertPos+ps.rcPaint.top/cyChar);实际上,由于ps.rcPaint.top=0,这行代码也可以写为:iPaintBeg = max(0,iVertPos);

iPaintEnd = min(NUMLINES -1,iVertPos+ps.rcPaint.bottom/cyChar);画图的结束位置=滚动条当前的位置+绘制的行数,而行数=客户区宽度/每一行的宽度;

x = cxChar * (1 - iHorzPos) ;绘图的x坐标,那个1是为了不是太“顶格”,你也可以把它设置大一点,结果就很明显了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值