window程序设计学习笔记

第一部分:基础知识

第一章:起步

###4G空间的虚拟内存的概念

1.3第1个windows程序

/***************************************************************************************************
helloMsg.c---Displays "hello,Windows 98! in a message box
(c)2023
**************************************************************************************************/
/*HelloMsg.c*/
#include<Windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
	MessageBox(NULL, TEXT("hello,Windows 98!"), TEXT("helloMsg"), 0);
	return 0;
}	
WinMain函数的返回值被定义为int,WINDEF.H中用以下语句来定义WINAPI标识符
#define WINAPI      __stdcall

1.3第2个windows程序

#include<Windows.h>

int WINAPI WinMain (_In_ HINSTANCE hInstance,
	_In_opt_ HINSTANCE hPrevInstance,
	_In_ LPSTR lpCmdLine,
	_In_ int iCmdShow)
{
	MessageBox(NULL, TEXT("hello,windows,98!"), TEXT("helloMsg"), 0);
	return 0;
}

第二章 Unicode简介

简单来说 Unicode是ASCII字符编码的一个扩展,Unicode用的是16位字母编码,而不是像ASCII那样 用7位表示,已不是计算机上面常用的每个字符八位的方式,这样在计算机通信中,世界上所有书面语言中的字母,象形文字和其他符号都可以用Unicode来表示,人最初的意图是用Unicode作为ASCII的补充,如果运气好的画,希望他可以最终取代ASCII,考虑到ASCII编码仍然是计算机中最有重要位置的标志之一,这显然是一个不太容易完成的任务
Unicode影响着计算机行业的方方面面,最深刻的或许是操作系统和编程语言的影响,在这方面,我们只完成了将近一半的任务。windows NT从底层上支持了Unicode。(不幸的是,windows98 只包含了少量的Unicode支持。)被ANSI规范化的C编程语言已他对宽字符的支援从本质上支持了Unicode,我将在后面详细讨论这一点。
当然。像往常一样,我们作为程序员会面对许多繁复的基本工作,为了减轻大家的负担。我们把本书中所有程序都写成了Unicode兼容版,在本书稍后关于Unicode的讨论会是其含义更加清晰

2.1字符集简史

人类从何时开始用口头语言至今尚无定论,但书面文字貌似已经有六千年的历史,早期文字有着象形的性质。字母表是三千年以前才发明的。它的特征是每个字母有相应的发音。虽然各种世界语言文字貌似很好地发挥着他的用途。几个十九世界的发明家则认为人们还有更多的需要。摩尔斯在1838年到1954年间发明了电报,他还设计了一种用于电报打代码,这套代码中,字母表中每个字母都被赋予一个相应的短和长的停顿系列(点和破折号)。这套代码系统不区分大小写字母,但数字和标点书号有着字节的代码。
摩尔斯并非是用于非符号或非印刷符号来表示书面语言的首列,在1821年到1824年,年轻的莱布尔受军事上一个用于夜间书写和阅读的系统启发。发明了可供盲人阅读的,把凸点印在纸上的代码,盲文本质上是用6位编码来表示字母,常用字母组合,蟾宫词和标点符号。一个特别的转义码表没随后的字母代码是大写:而一个特殊的转换码表示随后的字母代码是数字。

2.1.1美国标准

最重要的是,ASCII码是一种非常可靠的标准,没有其他那种标准像ASCII一样普及,它扎根于我们的键盘,显示器,系统硬件,打印机,字体文件,操作系统和因特网

2.1.2美国以为的世界

不能满足各个国家文字的需求,所有进行了变种,但是没能有效解决国际化语言的最好的方法

2.1.3扩展ASCII

8位代码表示,前面七位ascii占用,只是定义了后面的128个
汉卡就是基于扩展ASCII标准,而存在的

2.1.4 双字节字符集

双字节编码字节的问题

2.1.5 Unicode 的解救方案

unicode彻底解决编码的问题,unicode标准只是解决了编码的问题,但没有告诉大家如何实现的,和存储utf16 utf8是两种实现的方法

2.2 宽字符和C语言

多个字节表示一个字符集
宽字符不一定是Unicode,unicode只是宽字符编码的一种实现,然而本书的重点是window,我会将宽字符和Unicode作为 同义词

2.2.1 char 数据类型

声明定义和初始哈一个包含单一字符的变量,变量C需要一个字节的存储空间而且用十六进制0x41来初始哈,也就是ASCII字符的A
char c =‘A’;

char* p
指针变量需要四个字节的存储空间
char *p =“hello!”;
7个字节的存储空间,其中有一个字符串的结束标志0

2.2.2 更宽的字符
2.2.3 宽字符库函数
2.2.4 维护一个源代码文件

2.3 宽字符和windows

2.3.1 windows头文件类型
2.3.2 windows函数调用
2.3.3 windows的字符串函数
2.3.4 windows中使用printf
2.3.5 格式化的消息框
2.3.4 国际化之于本书

第三章 窗口与信息

前面我们学习了MessageBox(),MessageBox函数虽然能创建窗口,但是它比较特殊,其灵活性有限,消息窗口带有一个标题,一个可选图标,一行文字,几个按钮,这是非常有限的
下面我们学习并创建属于字节的窗口,还可以为窗口添加菜单等

3.1窗口的创建

创建窗口,只需要调用CreateWindow函数

3.1.1 系统结构概述

windows程序的执行并不是从上到下顺序执行的,这里是按消息的机制执行,内核的内容,这里先不涉及

3.1.2 Hellowin程序

要创建窗口,首先需要注册一个窗口类,而窗口类有需要窗口过程来处理窗口信息。
这些是几乎所有windows程序都会包含的一些常规的步骤。

/*----------------------------------------------------------
hellowin.c---Displays "hello,Windows 98!"in client area 
2023
-----------------------------------------------------------*/
#include<Windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
	PSTR lpCmdLine,int iCmdShow)//PSTR非宽字符串指针char*
{
	static TCHAR szAppName[] = TEXT("helloWin");//窗口类名
	HWND hwnd;//窗口句柄
	MSG msg;//消息 结构变量-F1
	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))
	{
		MessageBox(NULL, TEXT("this program requires windows NT!"),
			szAppName,MB_ICONERROR);
		return 0;
	}

	hwnd= CreateWindow(szAppName,//窗口类名
		TEXT("The Hello Program"),//窗口标题
		WS_OVERLAPPEDWINDOW,//窗口样式
		CW_USEDEFAULT,//initial x position
		CW_USEDEFAULT,//initial y position 
		CW_USEDEFAULT,//initial x size
		CW_USEDEFAULT,//initial y size
		NULL,//父窗口句柄
		NULL,//窗口菜单句柄
		hInstance,//程序控制句柄
		NULL);//创建参数

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);//更新窗口

	while (GetMessage(&msg, NULL, 0, 0))//消息列队中获取信息,message字段为WM_QUIT返回
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return	msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	RECT rect;

	switch (message)
	{/*
	case WM_CREATE:
		PlaySound(TEXT("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC);
		return 0;*/
	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		GetClientRect(hwnd, &rect);

		DrawText(hdc, TEXT("hello,windows 98!"), -1, &rect,
			DT_SINGLELINE | DT_CENTER | DT_VCENTER);
		EndPaint(hwnd, &ps);
		return 0;

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

3.1.3 通盘考虑

一般的,windows程序结构都和这个hellowin.c大体类似,没有人会真正记住这种框架的所有细节。一般就是复制现有代码到新程序中,然后加以修改,你可以这样自由的组合光盘中的代码。

**window 函数调用**

hellowin 用到直接调用的函数有18个
loadIcon
LoadCursor
GetStockObject
RegsiterClass
MessageBox
CreateWindows
ShowWindow
UpdateWindow
GetMessage
TranslateMessage
DispatchMessage
PlaySound
BeginPaint
GetClientRect
DrawText
EndPaint
PostQuitMessage
DefWindowProc

大写标识符

CS
CW
DT
IDI
IDC
MB
SND
WM
WS
这些前缀都在头文件中有定义

新数据类型

第四章 文本输出

/*----------------------------------------------------------
013.c 每日一练
	TextOut()
	Ellipse()

注意:关闭窗口后,程序还在运行
	2023.6.24
-----------------------------------------------------------*/
#include<Windows.h>
#pragma comment(lib,"winmm.lib")//playSound函数在winmm.lib

/*************************回调函数*****************************
LRESULT:返回值,被宏定义未long型
CALLBACK:函数参数进栈顺序(从右到左,已反汇编证实)----stdcall调用约定
4个参数:未MSG结构体的前4个成员变量
*************************************************************/
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);//消息回调函数


/**********************************************************
WINAPI:		函数调用约定,_stdcall
hInstance:	用用程序(进程 )句柄
hPrvInstance:兼容windows16
lpCmdLine:	命令行参数
nCmdShow:	窗体显示方式-正常,最大化,最小化,全屏等
********************************************************/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR lpCmdLine, int iCmdShow)//PSTR非宽字符串指针char*
{
	static TCHAR szAppName[] = TEXT("helloWin");//窗口类名---一个以0结尾的字符串数组

	/*****************************1,设计窗口类****************************************/
	WNDCLASS wndclass;//定义窗口结构对象,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;

	//************************2,注册窗口类*****************************************
	if (!RegisterClass(&wndclass))//未成功注册
	{
		MessageBox(NULL, TEXT("---未成功注册一个窗口类!"),
			szAppName, MB_ICONEXCLAMATION);
		return 0;
	}
	//******************************3,创建窗口************************************************
	HWND hwnd;
	hwnd = CreateWindow(
		szAppName,//窗口类名
		TEXT("标题我的第一个窗口程序!"),//窗口标题
		//TEXT("add is test!"),		//通过这里知道wc设计好的窗口
		WS_OVERLAPPEDWINDOW,//窗口样式
		CW_USEDEFAULT,//initial x position
		CW_USEDEFAULT,//initial y position 
		CW_USEDEFAULT,//initial x size
		CW_USEDEFAULT,//initial y size
		NULL,//父窗口句柄
		NULL,//窗口菜单句柄
		hInstance,//程序控制句柄
		NULL
	);//创建参数
/*创建窗口完成(只存在于内存),系统会发下哦那个一条消息WM_CREATE,注意此时,程序并未直线到消息循环处,
操作系统烧过应用程序的消息队列,直接向应用曾向发出,该消息在所有的消息之前,目的是为了在程序直线之前
可以有机会进行一些初始化工作或装在动态链接库操作。
a)CreatWindow的第一个参数就是窗口类的名字,通过这个名字可以找到仓才注册的窗口类,然后再根据他来创建
b)显示器上的坐标与数字中的不同,显示器的左上角是远点坐标,从原点坐标向右是X轴,向下是Y轴,都是正坐标,没有负坐标
c)参数hInstance是通过主函数winmain传入的
注意hInstance()函数创建窗口后,仅仅是位了窗口分配了内存空间,获得了句柄,
但窗口并没有显示出来,所有还需要调用ShowWindows()函数来显示窗口
*/

//********************************4,显示窗口和更新窗口*************************************
	ShowWindow(hwnd, iCmdShow);
	/*1,单纯一个ShowWindow,照样会正确画出旷课内容,只不过是再消息队列区孔之后才画的,
	又是,我们虚妄唱K被立即重画,而不是等待那个不确定的消息队列,此时就需要
	用到updataWindow,updateWindow的作用只有一个:假若当时被标记位重画的区域邨彩(不
	存在的画声明也不做),那么立刻让windows使用SendMessage的方式来对你的窗口
	发送WM_PAINT
	2,ShowWindow本身不发送重绘信息,它的作用仅仅是吧窗口显示出来。
	所有还需要调用updatawindow()函数,生产VM_PAINT消息,将客户区中的各种控件绘制出来。
	不过,当窗口显示的时候,windows会自动探测窗口的内容是否需要重画,已经需要重画的区域。
	3,Showwindow调用中会发哦是那个WM_SIZE 和SHOWWINDOW消息给窗口过程
	4,第一次调用和以后的调用时右区别的。第一次调用时,它的输入参数nCmdShow时需要输入
	winMain函数里传入来的nCmdShow参数,而不能时其它参数。
	*/

	UpdateWindow(hwnd);
	/*直线updatewindow时会立即发送一条消息WM_PAIN,同样右操作系统直接发送,updatewindow执行
	时,先判断无效区域是否位空,部位空,则发送,为空为不发送,可见,将上面的shouwindow注释掉,则
	不发送,应为为显示出来,当然没有无效区域,即无效区域为空
	*/
	/*updataWindow和Invalidata的区别:
	1,updatawindow()的作用时使用窗口立即重绘(因为操作系统绕过应用程序的消息队列,直接给就目标窗口发送WM_Pain,
	而Invalidata等函数后窗口不会立即重绘,这是由于WM_PAINT消息的优先级很低,他需要等消息队里中的其他直线完毕
	2,updatewindow直线时,先判断无效区域是否为空,部位空才发送消息, invalidate无效区为整个客户区
	*/

	//*****************************消息循环**********************************************
	MSG msg;
	/*1,调用GetMsessage函数,传入msg的地址,从应用程序饿消息队里中获取消息, 其中的
	消息结构的内容由操作系统自动填充
	2,这是windows核心的,消息循环处理过程,只有在收到WM_QUIT里才退出,结束程序
	*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);//将虚拟键消息转换为字符消息
		DispatchMessage(&msg); //发送消息给函数,先把msg发送给系统,然后由系统
								//dispatch函数的内部实现的答题流程
								//push 
								//.....
								//call wndproc 
								//.....
								//结束

	}

	return msg.wParam;//msg.wParam来自一条表示退出的消息,返回这个值给系统,从而退出
}

//******************************回调函数---消息处理过程************************************
//窗口回调函数,此函数只有声明和定义,没有回调!这说明此函数确实有操作系统调用的!
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hDC;//设备环境---绘图的地方
	PAINTSTRUCT ps;//绘图结构体变量
	RECT rect;//绘图区范围

	switch (message)
	{
	case WM_CREATE://创建窗口消息,此消息时一个应用程序发送的第一个消息,也是唯一的一次
		PlaySound(TEXT("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC);//播放声音文件
		//return 0;
		break;
	case WM_PAINT:
		hDC = BeginPaint(hwnd, &ps);
		/*ps结构的第三个成员rect rcPaint,时值一个无效区域当调用BeginPaint函数时,由操
		作系统填充,因此,严格来讲hDC为无效区域的“设备环境”,在此区域外的作图,都
		被忽略(即x,y坐标在此区域外的)*/
		//获取客户区大小
		GetClientRect(hwnd, &rect);
		//绘制字符串
		TextOut(hDC, 200, 200, TEXT("江江!"), lstrlen(TEXT("江江!")));
		//绘制椭圆图形
		Ellipse(hDC, 500, 50, 800, 500);//左上角坐标,右下角坐标
		//绘制格式化文本,客户区中间垂直居中对齐
		DrawText(hDC, TEXT("这是我的第一个窗口程序!"), -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

		EndPaint(hwnd, &ps);
		break;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);//未处理任何窗口消息
}

4.1 绘制和重绘

4.1.1 WM_PAINT
4.1.2 有效矩形和无效矩形

4.2 GDI简介

4.2.1 设备环境
4.2.2 获取设备环境句柄:方法一
4.2.3 绘制消息结构
4.2.4 获取设备环境句柄:方法二
4.2.5 TEXT函数详解
4.2.6 系统字体
4.2.7 字符大小
4.2.8 文本尺寸的度量
4.2.9 文本的格式化
4.2.10 综合使用
4.2.11 SYSTEMS1.C窗口过程
4.2.13 客户区的尺寸
/*---------------------------------------------------------
	sysmets2.c---system metrics display program no -3
	第二十一个例子:获取系统配置信息3,改进:适当大小的窗口滚动条
		SCROLLINFO结构
		SetScrollInfo函数
		GetScrollInfo函数
		ScrollWindows函数   滚动窗口
		2023 06 26 18:16
-----------------------------------------------------------*/

#include<Windows.h>
#include<tchar.h>
//#include<sysmets.h>
#include "SYSMETS.H"//定义系统信息结构数组即NUMLINES(显示行数 )

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

int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	static TCHAR szAppName[] = TEXT("SysMets3");//移植你更改的地方
	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))
	{
		MessageBox(NULL, TEXT("this program requires Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	hwnd = CreateWindow(szAppName, TEXT("Get System Metrics No.3"),//更新
		WS_OVERLAPPEDWINDOW | WS_VSCROLL|WS_HSCROLL,//增加水平滚动条WS_HSCROLL
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hinstance, NULL);

	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;//字符平均宽度
	static int cxCaps;//字符间距
	static int cyChar;//字符行距
	static int cxClient,cyClient;//新客户区高度
	static int iMaxWidth;
	HDC hdc;
	int i,x,y,iVertPos,iHorzPos,iPaintBeg,iPaintEnd;
	SCROLLINFO si;//滚动条参数结构变量
	PAINTSTRUCT ps;
	TCHAR szBuffer[10];
	TEXTMETRIC tm;	//物理字体的基本信息

	switch (message)
	{
	case WM_CREATE:
		hdc = GetDC(hwnd);//获取设备环境句柄
		GetTextMetrics(hdc, &tm);
		cxChar = tm.tmAveCharWidth;//字符平均宽度
		//tm.tmPitchAndFamily字体间距,tm.tmPitchAndFamily字段的地位决定是否为等宽字符,1代表是变宽字符,0代表等宽字符
		cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
		//字体简介cxCaps设为cxcar的1.5倍,1表示宽字符
		cyChar = tm.tmHeight + tm.tmExternalLeading;//字符行距=字符高+字符顶部空间数目
		ReleaseDC(hwnd, hdc);//释放设备环境句柄

		//保留三列文本的宽度,水平方向一页足以显示全部文本,因此会 隐藏了水平滚动条
		iMaxWidth = 40 * cxChar + 22 * cxCaps;
		return 0;

	case WM_SIZE:
		cxClient = LOWORD(lParam);//客户区的新高度
		cyClient = HIWORD(lParam);//客户区的新高度
		//set vertical scroll bar range and page size
		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);

		
		//SET horizontal scroll bar range and page size
		si.cbSize = sizeof(si);
		//SIF_DISABLENOSCROLL :滚动条不需要时,禁用而不是删除,则可以显示滚动条
		//si.fMask = SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL;
		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_VSCROLL:
		//获取所有垂直滚动条信息之前,先设置下面两个参数
		si.cbSize = sizeof(si);
		si.fMask = SIF_ALL;
		GetScrollInfo(hwnd, SB_VERT, &si);

		//保存位置以便稍后进行比较
		iVertPos = si.nPos;//指定当前的滚动框的位置
		/*******************nPos滚动条位置计算方法***************************
		//NUMLINES = 75
		//cyClient / cyChar = 50
		//iVscrollMax = 75 - 50 = 25;
		//上面两行代码将滚动条的范围设置为从0---25
		//当滚动条位置是0时,程序显示0--49行
		//当滚动条位置是1时 ,程序显示1---50行
		//当滚动条位置是25最大值时,显示25---74行
		*******************************************************************/
		switch (LOWORD(wParam))//wParam低16位是滚动条值
		{
		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 (si.nPage!=iVertPos)
		{
			//华东整个客户区的内容,第二个参数为水平滑动多少像素,第三个参数为春之滑动像素
			ScrollWindow(hwnd, 0, cyChar * (iVertPos - si.nPos),NULL,NULL);//下断点测试
			//InvalidateRect(hwnd, NULL, TRUE);//并擦除背景
			UpdateWindow(hwnd);//立即更新,发送一个WM_pAINT消息
		}
		return 0;

	case WM_HSCROLL:
		//Get all the vertical scroll bar information
		si.cbSize = sizeof(si);
		si.fMask = SIF_ALL;

		//save the position for comparison later on 
		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);
		}
		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;

		//find painting limits 
		iPaintBeg = max(0, iVertPos + ps.rcPaint.top / cyChar);
		iPaintEnd = min(NUMLINES - 1,
			iVertPos + ps.rcPaint.bottom / cyChar);

		for (i = iPaintBeg; i <= iPaintEnd; i++)
		{
			x = cxChar * (0 - iHorzPos);//这里用0,1都没问题
			y = cyChar * (i - iVertPos);

			TextOut(hdc, x, y,
				sysmetrics[i].szLabel,
				lstrlen(sysmetrics[i].szLabel));

			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"),//tchar.h头文件通用函数
					GetSystemMetrics(sysmetrics[i].iIndex)));

			SetTextAlign(hdc, TA_LEFT | TA_TOP);
		}
		EndPaint(hwnd, &ps);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}

	return DefWindowProc(hwnd, message, wParam, lParam);
}

4.3 滚动条

4.3.1 滚动条的范围和位置
4.3.2 滚动条消息
4.3.3 加入滚动条的SYSTEMS
4.3.4 程序的绘制代码的结构

4.3 效果更好的滚动

4.4.1 滚动条信息函数
4.4.2 最远可以卷动到哪里
4.4.3 新的SYSMETS
4.4.4 可我不想用鼠标

第五章 绘图基础

更新中

更新中

更新中

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值