卜说的专栏

打死都卜说

WIN32滚动条创建和使用详解

        一直对WIN32编程感兴趣,刚开始是从MFC摸索的,总感觉不带劲,新近网上看了一些博客,自己也看了Window程序设计,终于用原版的WIN32写了窗口并处理了滚动条,在这里给大家分享一下。

        

        图片是最终的窗口效果,下面用一张图来讲解滚动条的作用和区域设置。

        

        黄线矩形为窗口区域,红线区域为图片大小,只有当图片的宽或者高大于客户区的时候才需要相应的滚动条,滚动条的作用即为显示图片在客户区以外的部分,将滚动条位置和范围与像素关联,水平滚动条的最大范围即为cxBitmap-cxClient,最小范围当然是0啦,当滚动条位置发生变化时,图片进行相应的偏移,窗口的大小发生变化时,即cxClient发生变化,需要重新设置水平滚动条的范围,并判断当前滚动条位置是否在新的cxBitmap-cxClient范围内,如果超出范围,需要进行纠正;垂直滚动条的调整原理和水平是一样的。

        实例整体很简单,定义一个典型的WINDOWS窗体:1.定义窗体属性;2.定义窗体处理程序;3.循环处理进程消息队列。详见代码注释。

         

#include <Windows.h>


//WINDOWS程序的标准范式:定义WINDOWS入口函数WinMain
//注册一个窗口对象
//定义窗口的属性,即WNDCLASS结构的队员
//定义窗口对应的处理函数,即WndProc回调函数
//WinMain中循环处理进程消息队列
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

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

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

	//窗口在创建之前需要先注册窗口类
	if(!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("Need Windows NT"), TEXT(""),MB_HELP); 
		return 0;
	}


	//获取屏幕属性的一个很有用的函数,F12可以看到详细用法,这里是获取屏幕的宽和高
	cxclient = GetSystemMetrics(SM_CXSCREEN);
	cyclient = GetSystemMetrics(SM_CYSCREEN);

	
	hwnd = CreateWindow(szAppName,
		TEXT(""),                                //窗口标题,可以设置为空,TEXT()为微软提供的字节处理宏,当定了_UNICODE的时候即代表了UNICODE字符,否则为ASC11字符
		WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,        //第一个宏为窗口有标题栏,有最大化,有最小化,有退出,后面两个指定了窗口具有垂直和水平滚动条
		cxclient/3,            //设定窗口起点为屏幕三分之一高和三分之一宽
		cyclient/3,
		cxclient/3,          //设定窗口宽和高分别为屏幕的三分之一
		cyclient/3,
		NULL,
		NULL,
		NULL,       //hInstance
		NULL);

	//创建窗口以后显示窗口
	ShowWindow(hwnd, TRUE);
	UpdateWindow(hwnd);
	//GetMessage消息循环从进程消息队列中匹配消息并转换后分发给对应窗口,窗口收到消息后调用注册的窗口处理函数处理翻译后的消息。
	//需要注意的是,如果想要从进程消息队列中获取所有消息,需要第二个参数设置成NULL,否则GetMessage无法获取WM_QUIT消息,即无法退出程序。
	//WM_QUIT属于进程但不属于进程的任意一个窗口,只有第二个参数NULL可以获取,GetMessage获取WM_QUIT后进程退出
	while(GetMessage(&msg, NULL, NULL, NULL))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
	static int cxClient, cyClient, cxBitmap, cyBitmap;
	static HBITMAP bitmap;
	static int iHScrollBarPos, iVScrollBarPos;
	

	switch(message)
	{
	case WM_CREATE:
		BITMAP bmpinfo;
		bitmap = (HBITMAP)LoadImage(NULL, TEXT("view.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
		if(!bitmap)
		{
			MessageBox(hwnd, TEXT("Load Image Error"), TEXT("My Demo"), MB_ICONERROR);
		}
		GetObject(bitmap, sizeof(BITMAP), &bmpinfo);
		cxBitmap = bmpinfo.bmWidth;
		cyBitmap = bmpinfo.bmHeight;
		break;
	case WM_PAINT:
		HDC hdc, hdcSrc;
		PAINTSTRUCT ps;
		hdc = BeginPaint(hwnd, &ps);
		hdcSrc = CreateCompatibleDC(hdc);
		SelectObject(hdcSrc, bitmap);
		BitBlt(hdc, -iHScrollBarPos, -iVScrollBarPos, cxBitmap, cyBitmap, hdcSrc, 0, 0, SRCCOPY);
		EndPaint(hwnd, &ps);
		DeleteDC(hdcSrc);
		break;
	case WM_SIZE:
		//窗口大小改变时收到此消息,F12可查看消息参数意思,msdn查到获取新窗口宽和高的方法
		cxClient = LOWORD(lparam);
		cyClient = HIWORD(lparam);
		//设定滚动条的范围
		SetScrollRange(hwnd, SB_HORZ, 0, cxBitmap - cxClient, FALSE);
		SetScrollRange(hwnd, SB_VERT, 0, cyBitmap - cyClient, FALSE);
		//获取滚动条的新位置,即判断窗口大小改变以后滚动条是否超出了应有的最大范围
		iHScrollBarPos = min(cxBitmap - cxClient, max(0, iHScrollBarPos));
		iVScrollBarPos = min(cyBitmap - cyClient, max(0, iHScrollBarPos));
		//如果滚动条超过了最大范围,则重设滚动条范围并刷新窗口
		if(iHScrollBarPos != GetScrollPos(hwnd, SB_HORZ))
		{
			SetScrollPos(hwnd, SB_HORZ, iHScrollBarPos, TRUE);
			InvalidateRect(hwnd, NULL, FALSE);
		}
		if(iVScrollBarPos != GetScrollPos(hwnd, SB_VERT))
		{
			SetScrollPos(hwnd, SB_VERT, iVScrollBarPos, SB_VERT);
			InvalidateRect(hwnd, NULL, FALSE);
		}
		break;
	case WM_VSCROLL:
		//垂直滚动条消息,F12查看消息可以从MSDN获取消息详细参数
		switch(LOWORD(wparam))
		{
		case SB_LINEUP:
			//每次滚动图片变化10个像素
			iVScrollBarPos -= 10;
			break;
		case SB_LINEDOWN:
			iVScrollBarPos += 10;
			break;
		case SB_PAGEUP:
			//每次翻页都滚动一整个客户区的大小
			iVScrollBarPos -= cyClient;
			break;
		case SB_PAGEDOWN:
			iVScrollBarPos += cyClient;
			break;
		case SB_THUMBTRACK:
			iVScrollBarPos = HIWORD(wparam);
			break;
		default :
			break;
		}
		//判断滚动后的滚动条是否超过最大值或最小值,如果超过最大值或者最小值,则取最大值或0,否则等于当前值
		iVScrollBarPos = min(cyBitmap - cyClient, max(0, iVScrollBarPos));
		//如果滚动条位置发生变化,则设置滚动条位置和刷新屏幕
		if(iVScrollBarPos != GetScrollPos(hwnd, SB_VERT))
		{
			SetScrollPos(hwnd, SB_VERT, iVScrollBarPos, TRUE);
			//最后参数设置为FALSE可以大幅度减少屏幕闪烁,可以尝试一下。
			InvalidateRect(hwnd, NULL, FALSE);
		}
		break;
	case WM_HSCROLL:
		switch(LOWORD(wparam))
		{
		case SB_LINEUP:
			iHScrollBarPos -= 10;
			break;
		case SB_LINEDOWN:
			iHScrollBarPos += 10;
			break;
		case SB_PAGEUP:
			iHScrollBarPos -= cxClient;
			break;
		case SB_PAGEDOWN:
			iHScrollBarPos += cxClient;
			break;
		case SB_THUMBTRACK:
			iHScrollBarPos = HIWORD(wparam);
			break;
		default :
			break;
		}
		iHScrollBarPos = min(cxBitmap - cxClient, max(0, iHScrollBarPos));
		if(iHScrollBarPos != GetScrollPos(hwnd, SB_HORZ))
		{
			SetScrollPos(hwnd, SB_HORZ, iHScrollBarPos, TRUE);
			InvalidateRect(hwnd, NULL, FALSE);
		}
		break;
	case WM_DESTROY:
		//点击窗口右上角的X,系统会向窗口发出WM_DESTROY消息,该消息并不能退出窗口,真正退出窗口的是WM_QUIT消息,调用PostQuitMessage向进程消息队列发送WM_QUIT消息
		PostQuitMessage(1);
		DeleteObject(bitmap);
		return 0;
		break;
	default :
		break;
	}
	return DefWindowProc(hwnd, message, wparam, lparam);
}







阅读更多
个人分类: 控件 WIN32
想对作者说点什么? 我来说一句

Win32的滚动条实例

2013年07月30日 2.26MB 下载

没有更多推荐了,返回首页

不良信息举报

WIN32滚动条创建和使用详解

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭