Windows程序设计--(四)文本输出
4.1 绘制和重绘
4.1.2 有效矩阵和无效矩阵
在擦除对话框之后,需要重画的被对话框遮住的矩形区域,这个区域称为「无效区域」或「更新区域」。正是显示区域内无效区域的存在,才会让Windows将一个WM_PAINT消息放在应用程序的消息队列中。只有在显示区域的某一部分失效时,窗口才会接受WM_PAINT消息。
Windows内部为每个窗口保存一个「绘图信息结构」,这个结构包含了包围无效区域的最小矩形的坐标以及其它信息,这个矩形就叫做「无效矩形」,有时也称为「无效区域」。如果在窗口消息处理程序处理WM_PAINT消息之前显示区域中的另一个区域变为无效,则Windows计算出一个包围两个区域的新的无效区域(以及一个新的无效矩形),并将这种变化后的信息放在绘制信息结构中。Windows不会将多个WM_PAINT消息都放在消息队列中。
4.2 GDI 简介
4.2.2 获取设备环境句柄:方法一
HDC hdc;//设备环境句柄 PAINTSTRUCT ps;//绘制结构 RECT rect;//矩形结构 case WM_PAINT: hdc = BeginPaint(hwnd, &ps);//标明窗口绘制开始,设备环境句柄 GetClientRect(hwnd, &rect);//获取窗口客户区的尺寸 DrawText(hdc, TEXT("Hello Hk_Mayfly!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER); EndPaint(hwnd, &ps);//结束窗口绘制 return 0;
4.2.3 绘制信息结构
PAINTSTRUCT结构包含了应用程序用来绘制它所拥有的窗口客户区所需要的信息。PAINTSTRUCT的结构定义如下:
typedef struct tagPAINTSTRUCT { HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; BYTE rgbReserved[32]; } PAINTSTRUCT, *PPAINTSTRUCT;
当调用BeginPaint函数时, Windows将自动填充这个结构体中的成员相关属性, 程序仅能使用前三个成员, 其他为Windows内部使用。 参数一HDC hdc即为设备环境句柄, BeginPaint函数的返回值也就是这里的设备环境句柄, 简单来说就是先填充再返回; 参数二fErase决定是否擦出客户区背景, 如果为非零值则擦除背景,否则不擦除背景; 参数三rcPaint 通过指定客户区左上角和右下角的坐标确定一个要绘制的矩形范围, 即使需要更新的无效区域不是一个矩形, Windows也会把需要重绘的部分裁剪为一个矩形。
如果你仍想重绘整个客户区, 可以在BeginPaint函数之前调用InvalidateRect( hwnd, NULL, TRUE );使整个客户区无效化。
4.2.4 获取设备环境句柄:方法二
#include <Windows.h> #include <iostream> using namespace std; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);//消息函数声明 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)//主函数 { static TCHAR szAppName[] = TEXT("WNDCLASS NAME");//窗口类名称 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(DKGRAY_BRUSH);//主窗口背景色 wndclass.lpszMenuName = NULL;//窗口菜单 wndclass.lpszClassName = szAppName;//窗口类名 if (!RegisterClass(&wndclass)) {//注册窗口类,如果注册失败弹出窗口 MessageBox(NULL, TEXT("窗口创建失败!程序需要Windows NT!(传递窗口消息为UNICODE)"), szAppName, MB_ICONERROR);//消息窗口 return 0; } hwnd = CreateWindow(szAppName, //Windows类名 TEXT("窗口绘制成功!"), //窗口标题 WS_OVERLAPPEDWINDOW, //窗口风格 CW_USEDEFAULT, //初始化窗口位置的X坐标 CW_USEDEFAULT, //初始化窗口位置的Y坐标 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) { HDC hdc;//设备环境句柄 PAINTSTRUCT ps;//绘制结构 /* typedef struct tagRECT { LONG left; LONG top; LONG right; LONG bottom; } RECT 其中left,top赋为0,因此right和bottom表示客户区的宽度和高度(像素) */ RECT rect;//矩形结构 switch (message) {//处理得到的消息 case WM_CREATE://窗口创建发来消息 MessageBox(hwnd, TEXT("创建成功,音乐播放"), TEXT("Windows"), MB_OK | MB_ICONINFORMATION); /* 1.波形文件的名称 2.只有当声音文件是一个资源时才有用,此处NULL表示不使用 3.指定一组选项,表示指定了第一个参数为文件名且该段声音是以异步方式播放 */ PlaySound(TEXT("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC); return 0; case WM_PAINT://处理窗口绘制 hdc = GetDC(hwnd); GetClientRect(hwnd, &rect);//获取窗口客户区的尺寸 DrawText(hdc, TEXT("Hello Hk_Mayfly!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER); ReleaseDC(hwnd, hdc); ValidateRect(hwnd, NULL);//因为会不断更新整个客户区,不断刷新,要使其他消息能接收,需要使客户区有效 return 0; case WM_LBUTTONDOWN: MessageBox(hwnd, TEXT("左键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION); return 0; case WM_RBUTTONDOWN: MessageBox(hwnd, TEXT("右键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION); return 0; case WM_DESTROY://处理窗口关闭时的消息 MessageBox(hwnd, TEXT("主窗口关闭"), TEXT("Windows"), MB_OK | MB_ICONINFORMATION);//显示一个文本字符串 PostQuitMessage(0);//将退出消息插入消息队列,程序从消息循环退出,return msg.wParam return 0; } return DefWindowProc(hwnd, message, wParam, lParam);//执行默认消息处理 }
4.2.5 TEXTOUT函数详解
TextOut (hdc, x, y, psText, iLength) ;
以下将详细地讨论这个函数。
第一个参数是设备内容句柄,它既可以是GetDC的传回值,也可以是在处理WM_PAINT消息时BeginPaint的传回值。
psText参数是指向字符串的指针,iLength是字符串中字符的个数。如果psText指向Unicode字符串,则字符串中的字节数就是iLength值的两倍。字符串中不能包含任何ASCII控制字符(如回车、换行、制表或退格),Windows会将这些控制字符显示为实心块。Text0ut不识别作为字符串结束标志的内容为零的字节(对于Unicode,是一个短整数型态的0),而需要由nLength参数指明长度。
TextOut中的x和y定义显示区域内字符串的开始位置,x是水平位置,y是垂直位置。字符串中第一个字符的左上角位于坐标点(x,y)。在内定的设备内容中,原点(x和y均为0的点)是显示区域的左上角。如果在TextOut中将x和y设为0,则将从显示区域左上角开始输出字符串。
4.2.6 系统字体
设备内容还定义了在您呼叫TextOut显示文字时Windows使用的字体。内定字体为「系统字体」,或用Windows表头文件中的标识符,即SYSTEM_FONT。系统字体是Windows用来在标题列、菜单和对话框中显示字符串的内定字体。
4.2.7 字符大小
GetTextMetrics需要的TEXTMETRIC型态的结构:
typedef struct tagTEXTMETRIC { LONG tmHeight; //字符高度 LONG tmAscent; //字符上部高度(基线以上) LONG tmDescent; //字符下部高度(基线以下) LONG tmInternalLeading, //由tmHeight定义的字符高度的顶部空间数目 LONG tmExternalLeading, //夹在两行之间的空间数目 LONG tmAveCharWidth, //平均字符宽度 LONG tmMaxCharWidth, //最宽字符的宽度 LONG tmWeight; //字体的粗细轻重程度 LONG tmOverhang, //加入某些拼接字体上的附加高度 LONG tmDigitizedAspectX, //字体设计所针对的设备水平方向 LONG tmDigitizedAspectY, //字体设计所针对的设备垂直方向 BCHAR tmFirstChar; //为字体定义的第一个字符 BCHAR tmLastChar; //为字体定义的最后一个字符 BCHAR tmDefaultChar; //字体中所没有字符的替代字符 BCHAR tmBreakChar; //用于拆字的字符 BYTE tmItalic, //字体为斜体时非零 BYTE tmUnderlined, //字体为下划线时非零 BYTE tmStruckOut, //字体被删去时非零 BYTE tmPitchAndFamily, //字体间距(低4位)和族(高4位) BYTE tmCharSet; //字体的字符集 } TEXTMETRIC;
总共20个字段,我们只关心前7个。默认的映射模式为MM_TEXT
TextOut的两种运用,将上面代码的WM_PAINT部分替换即可。
第一种
TEXTMETRIC tm; TCHAR szBuffer[100]; int iLength = wsprintf(szBuffer, TEXT("Unicode形式输出!"));
case WM_PAINT://处理窗口绘制 hdc = BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rect); TextOut(hdc, 400, 200, szBuffer, iLength); EndPaint(hwnd, &ps); return 0;
第二种:
在switch前面定义一个结构变量tm
TEXTMETRIC tm; TCHAR szBuffer[100]; int iLength = wsprintf(szBuffer, TEXT("Unicode形式输出!"));
case WM_PAINT://处理窗口绘制 hdc = GetDC(hwnd); GetTextMetrics(hdc, &tm); TextOut(hdc, 400, 200, szBuffer, iLength); ReleaseDC(hwnd, hdc); ValidateRect(hwnd, NULL); return 0;
4.2.8 文本尺寸大小
tmHeight,它是tmAscent和tmDescent的和。这两个值表示了基准在线下字符的最大纵向高度。「间距」(leading)指打印机在两行文字间插入的空间。在TEXTMETRIC结构中,内部的间距包括在tmAscent中(因此也在tmHeight中),并且它经常是重音符号出现的地方。tmInternalLeading字段可被设成0,在这种情况下,加重音的字母会稍稍缩短以便容纳重音符号。
字段tmExternalLeading,它是字体设计者建议加在横向字符之间的空间大小。
TEXTMETRICS结构包含有描述字符宽度的两个字段,即tmAveCharWidth(小写字母加权平均宽度)和tmMaxCharWidth(字体中最宽字符的宽度)。
4.2.9 文本的格式化
4.2.10 综合使用
SYSMETS.h
#include <Windows.h> #define NUMLINES ((int)(sizeof(sysmetrics) / sizeof(sysmetrics [0]))) struct { const TCHAR* szName; int iAge; const TCHAR* szMajor; } sysmetrics[]={ TEXT("刘然"), 20, TEXT("软件工程"), TEXT("黄鹤"), 21, TEXT("计算机科学"), TEXT("马平"), 20, TEXT("网络工程"), TEXT("评然"), 20, TEXT("软件工程"), TEXT("里鹤"), 21, TEXT("计算机科学"), TEXT("高平"), 20, TEXT("网络工程"), TEXT("偶然"), 20, TEXT("软件工程"), TEXT("炮打"), 21, TEXT("计算机科学"), TEXT("马平"), 20, TEXT("网络工程"), TEXT("刘然"), 20, TEXT("软件工程"), TEXT("黄鹤"), 21, TEXT("计算机科学"), TEXT("马平"), 20, TEXT("网络工程") };
#include <Windows.h> #include <stdio.h> #include <stdlib.h> #include "SYSMETS.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);//消息函数声明 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)//主函数 { static TCHAR szAppName[] = TEXT("WNDCLASS NAME");//窗口类名称 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("窗口创建失败!程序需要Windows NT!(传递窗口消息为UNICODE)"), szAppName, MB_ICONERROR);//消息窗口 return 0; } hwnd = CreateWindow(szAppName, //Windows类名 TEXT("窗口绘制成功!"), //窗口标题 WS_OVERLAPPEDWINDOW, //窗口风格 CW_USEDEFAULT, //初始化窗口位置的X坐标 CW_USEDEFAULT, //初始化窗口位置的Y坐标 500, //初始化窗口长度大小 300, //初始化窗口宽度大小 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, cxCaps, cyChar; int i = 0; TCHAR szBuffer[100]; HDC hdc;//设备环境句柄 PAINTSTRUCT ps;//绘制结构 TEXTMETRIC tm; switch (message) {//处理得到的消息 case WM_CREATE://窗口创建发来消息 hdc = GetDC(hwnd); GetTextMetrics(hdc, &tm); cxChar = tm.tmAveCharWidth; //得到字体平均宽度 //cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;//判断字体是等宽字体还是变宽字体,变宽字体是cxChar的1.5倍 cyChar = tm.tmHeight + tm.tmExternalLeading;// //字体高度, 总高度tmHeight + 两行文字之间的建议间距大小tmExternalLeading ReleaseDC(hwnd, hdc); return 0; case WM_PAINT://处理窗口绘制 hdc = BeginPaint(hwnd, &ps); for (i = 0; i < NUMLINES; ++i) { //SetTextAlign(hdc, TA_RIGHT | TA_TOP);//设置TextOut使用的坐标将从右上角开始 TextOut(hdc, 0, cyChar * i, sysmetrics[i].szName, lstrlen(sysmetrics[i].szName)); //TextOut(hdc, 400, cyChar * i, szBuffer, wsprintf(szBuffer, TEXT("%2d"), GetSystemMetrics(sysmetrics[i].iAge))); TextOut(hdc, 400, cyChar * i, szBuffer, wsprintf(szBuffer, TEXT("%2d"), sysmetrics[i].iAge)); TextOut(hdc, 200, cyChar * i, sysmetrics[i].szMajor, lstrlen(sysmetrics[i].szMajor)); }
EndPaint(hwnd,&ps); return 0; case WM_LBUTTONDOWN: MessageBox(hwnd, TEXT("左键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION); return 0; case WM_RBUTTONDOWN: MessageBox(hwnd, TEXT("右键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION); return 0; case WM_DESTROY://处理窗口关闭时的消息 MessageBox(hwnd, TEXT("主窗口关闭"), TEXT("Windows"), MB_OK | MB_ICONINFORMATION);//显示一个文本字符串 PostQuitMessage(0);//将退出消息插入消息队列,程序从消息循环退出,return msg.wParam return 0; } return DefWindowProc(hwnd, message, wParam, lParam);//执行默认消息处理 }
SYSMETS.h
#include <Windows.h> #define NUMLINES ((int)(sizeof(sysmetrics) / sizeof(sysmetrics [0]))) struct { const TCHAR* szName; int iAge; const TCHAR* szMajor; } sysmetrics[]={ TEXT("刘然"), 20, TEXT("软件工程"), TEXT("黄鹤"), 21, TEXT("计算机科学"), TEXT("马平"), 20, TEXT("网络工程"), TEXT("评然"), 20, TEXT("软件工程"), TEXT("里鹤"), 21, TEXT("计算机科学"), TEXT("高平"), 20, TEXT("网络工程"), TEXT("偶然"), 20, TEXT("软件工程"), TEXT("炮打"), 21, TEXT("计算机科学"), TEXT("马平"), 20, TEXT("网络工程"), TEXT("刘然"), 20, TEXT("软件工程"), TEXT("黄鹤"), 21, TEXT("计算机科学"), TEXT("马平"), 20, TEXT("网络工程"), TEXT("评然"), 20, TEXT("软件工程"), TEXT("里鹤"), 21, TEXT("计算机科学"), TEXT("高平"), 20, TEXT("网络工程"), TEXT("偶然"), 20, TEXT("软件工程"), TEXT("炮打"), 21, TEXT("计算机科学"), TEXT("马平"), 20, TEXT("网络工程"), TEXT("刘然"), 20, TEXT("软件工程"), TEXT("黄鹤"), 21, TEXT("计算机科学"), TEXT("马平"), 20, TEXT("网络工程"), TEXT("评然"), 20, TEXT("软件工程"), TEXT("里鹤"), 21, TEXT("计算机科学"), TEXT("高平"), 20, TEXT("网络工程"), TEXT("偶然"), 20, TEXT("软件工程"), TEXT("炮打"), 21, TEXT("计算机科学"), TEXT("马平"), 20, TEXT("网络工程"), TEXT("刘然"), 20, TEXT("软件工程"), TEXT("黄鹤"), 21, TEXT("计算机科学"), TEXT("马平"), 20, TEXT("网络工程"), TEXT("评然"), 20, TEXT("软件工程"), TEXT("里鹤"), 21, TEXT("计算机科学"), TEXT("高平"), 20, TEXT("网络工程"), TEXT("偶然"), 20, TEXT("软件工程"), TEXT("炮打"), 21, TEXT("计算机科学"), TEXT("马平"), 20, TEXT("网络工程") };
#include <Windows.h> #include <stdio.h> #include <stdlib.h> #include "SYSMETS.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);//消息函数声明 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)//主函数 { static TCHAR szAppName[] = TEXT("WNDCLASS NAME");//窗口类名称 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("窗口创建失败!程序需要Windows NT!(传递窗口消息为UNICODE)"), szAppName, MB_ICONERROR);//消息窗口 return 0; } hwnd = CreateWindow(szAppName, //Windows类名 TEXT("窗口绘制成功!"), //窗口标题 WS_OVERLAPPEDWINDOW, //窗口风格 CW_USEDEFAULT, //初始化窗口位置的X坐标 CW_USEDEFAULT, //初始化窗口位置的Y坐标 500, //初始化窗口长度大小 300, //初始化窗口宽度大小 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, cxCaps, cyChar; static int cxClient, cyClient,iVscrollPos; int i = 0, y; TCHAR szBuffer[100]; HDC hdc;//设备环境句柄 PAINTSTRUCT ps;//绘制结构 TEXTMETRIC tm; switch (message) {//处理得到的消息 case WM_CREATE://窗口创建发来消息 hdc = GetDC(hwnd); GetTextMetrics(hdc, &tm); cxChar = tm.tmAveCharWidth; //得到字体平均宽度 //cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2; cyChar = tm.tmHeight + tm.tmExternalLeading;// //字体高度, 总高度tmHeight + 两行文字之间的建议间距大小tmExternalLeading ReleaseDC(hwnd, hdc); SetScrollRange(hwnd, SB_VERT, 0, NUMLINES - 1, FALSE);//设置垂直滚动条的范围 SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);//设置垂直滚动条的位置 return 0; case WM_PAINT://处理窗口绘制 hdc = BeginPaint(hwnd, &ps); for (i = 0; i < NUMLINES; ++i) { y = cyChar * (i - iVscrollPos);//刚开始为0,小于iVscrollPos都被显示到了内容区域之外 //SetTextAlign(hdc, TA_RIGHT | TA_TOP); TextOut(hdc, 0, y, sysmetrics[i].szName, lstrlen(sysmetrics[i].szName)); //TextOut(hdc, 400, cyChar * i, szBuffer, wsprintf(szBuffer, TEXT("%2d"), GetSystemMetrics(sysmetrics[i].iAge))); TextOut(hdc, 400, y, szBuffer, wsprintf(szBuffer, TEXT("%2d"), sysmetrics[i].iAge)); TextOut(hdc, 200, y, sysmetrics[i].szMajor, lstrlen(sysmetrics[i].szMajor)); } return 0; case WM_LBUTTONDOWN: MessageBox(hwnd, TEXT("左键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION); return 0; case WM_RBUTTONDOWN: MessageBox(hwnd, TEXT("右键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION); return 0; case WM_SIZE: //cxClient = LOWORD(lParam);//客户区宽度 cyClient = HIWORD(lParam);//客户区的高度 return 0; case WM_VSCROLL: switch (LOWORD(wParam)) { case SB_LINEUP: iVscrollPos -= 1;//表示向上移动一行 break; case SB_LINEDOWN: iVscrollPos += 1; break; case SB_PAGEUP: iVscrollPos -= cyClient / cyChar;//cyClient / cyChar表示客户区一列能显示的字符数,因此这里表示向上一屏 break; case SB_PAGEDOWN: iVscrollPos += cyClient / cyChar; break; case SB_THUMBPOSITION: iVscrollPos = HIWORD(wParam); break; default: break; } /* min中判断是否到达最后一组数据, max如果在第一行向上移动,iVscrollPos为负,所以使用max表示向上还是显示第一组数据。 */ iVscrollPos = max(0, min(iVscrollPos, NUMLINES - 1)); if (iVscrollPos != GetScrollPos(hwnd, SB_VERT)) {//如果滚动跳当前位置和我更新之后的iVscrollPos位置不同,就需要设置新的滚动条位置,并绘制数据 SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);//设置显示位置 InvalidateRect(hwnd, NULL, TRUE);//将要绘制区域无效 } return 0; case WM_DESTROY://处理窗口关闭时的消息 MessageBox(hwnd, TEXT("主窗口关闭"), TEXT("Windows"), MB_OK | MB_ICONINFORMATION);//显示一个文本字符串 PostQuitMessage(0);//将退出消息插入消息队列,程序从消息循环退出,return msg.wParam return 0; } return DefWindowProc(hwnd, message, wParam, lParam);//执行默认消息处理 }
但是上面的最后一个信息,显示在最后一页,可以将最后一个信息显示在最后一页的最后一行。
#include <Windows.h> #define NUMLINES ((int)(sizeof(sysmetrics) / sizeof(sysmetrics [0]))) struct { const TCHAR* szName; int iAge; const TCHAR* szMajor; } sysmetrics[]={ TEXT("刘然"), 20, TEXT("软件工程"), TEXT("黄鹤"), 21, TEXT("计算机科学"), TEXT("马平"), 20, TEXT("网络工程"), TEXT("评然"), 20, TEXT("软件工程"), TEXT("里鹤"), 21, TEXT("计算机科学"), TEXT("高平"), 20, TEXT("网络工程"), TEXT("偶然"), 20, TEXT("软件工程"), TEXT("炮打"), 21, TEXT("计算机科学"), TEXT("马平"), 20, TEXT("网络工程"), TEXT("刘然"), 20, TEXT("软件工程"), TEXT("黄鹤"), 21, TEXT("计算机科学"), TEXT("马平"), 20, TEXT("网络工程"), TEXT("评然"), 20, TEXT("软件工程"), TEXT("里鹤"), 21, TEXT("计算机科学"), TEXT("高平"), 20, TEXT("网络工程"), TEXT("偶然"), 20, TEXT("软件工程"), TEXT("炮打"), 21, TEXT("计算机科学"), TEXT("马平"), 20, TEXT("网络工程"), TEXT("刘然"), 20, TEXT("软件工程"), TEXT("黄鹤"), 21, TEXT("计算机科学"), TEXT("马平"), 20, TEXT("网络工程"), TEXT("评然"), 20, TEXT("软件工程"), TEXT("里鹤"), 21, TEXT("计算机科学"), TEXT("高平"), 20, TEXT("网络工程"), TEXT("偶然"), 20, TEXT("软件工程"), TEXT("炮打"), 21, TEXT("计算机科学"), TEXT("马平"), 20, TEXT("网络工程"), TEXT("刘然"), 20, TEXT("软件工程"), TEXT("黄鹤"), 21, TEXT("计算机科学"), TEXT("马平"), 20, TEXT("网络工程"), TEXT("评然"), 20, TEXT("软件工程"), TEXT("里鹤"), 21, TEXT("计算机科学"), TEXT("高平"), 20, TEXT("网络工程"), TEXT("偶然"), 20, TEXT("软件工程"), TEXT("炮打"), 21, TEXT("计算机科学"), TEXT("马结束"), 20, TEXT("网络工程") };
#include <Windows.h> #include <stdio.h> #include <stdlib.h> #include "SYSMETS.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);//消息函数声明 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)//主函数 { static TCHAR szAppName[] = TEXT("WNDCLASS NAME");//窗口类名称 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("窗口创建失败!程序需要Windows NT!(传递窗口消息为UNICODE)"), szAppName, MB_ICONERROR);//消息窗口 return 0; } hwnd = CreateWindow(szAppName, //Windows类名 TEXT("窗口绘制成功!"), //窗口标题 WS_OVERLAPPEDWINDOW, //窗口风格 CW_USEDEFAULT, //初始化窗口位置的X坐标 CW_USEDEFAULT, //初始化窗口位置的Y坐标 500, //初始化窗口长度大小 300, //初始化窗口宽度大小 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, cxCaps, cyChar; static int cxClient, cyClient,iMaxWidth, iVertPos, iHorzPos, iPaintBeg, iPaintEnd; int i = 0, x, y; SCROLLINFO si; TCHAR szBuffer[100]; HDC hdc;//设备环境句柄 PAINTSTRUCT ps;//绘制结构 TEXTMETRIC tm; switch (message) {//处理得到的消息 case WM_CREATE://窗口创建发来消息 hdc = GetDC(hwnd); GetTextMetrics(hdc, &tm); cxChar = tm.tmAveCharWidth; //得到字体平均宽度 cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;//判断字符类型,Unicode是字符型的1.5倍大小 cyChar = tm.tmHeight + tm.tmExternalLeading;//字体高度, 总高度tmHeight + 两行文字之间的建议间距大小tmExternalLeading ReleaseDC(hwnd, hdc); iMaxWidth = 40 * cxChar + 22 * cxCaps;//最大宽度 return 0; case WM_PAINT://处理窗口绘制 hdc = BeginPaint(hwnd, &ps); si.cbSize = sizeof(si);//必须设置cbSize为结构体大小 si.fMask = SIF_POS;//指定滚动条位置 GetScrollInfo(hwnd, SB_VERT, &si);//获取当前纵向滚动条位置 iVertPos = si.nPos;//设置iVertPos为当前纵向滚动条位置 GetScrollInfo(hwnd, SB_HORZ, &si);//获取当前横向滚动条位置 iHorzPos = si.nPos;//设置iHorzPos为当前横向滚动条位置 iPaintBeg = max(0, iVertPos + ps.rcPaint.top / cyChar);//ps.rcPaint.top这里为0,处理UP操作 //ps.rcPaint.bottom这里为cyClient,处理DOWN操作,因为...top为0,所以不考虑Unicode字符影响 //而...bottom表示cyClient大小,实际上的Unicode字符是原来的1.5倍 iPaintEnd = min(NUMLINES, iVertPos + 3 * ps.rcPaint.bottom / cyChar / 2); for (i = iPaintBeg; i < iPaintEnd; ++i) { //小于当前横iHorzPos,纵iVertPos滚动条位置的数据都被隐藏 //0和i相当于输出的起始位置 x = cxChar * (0 - iHorzPos); y = cyChar * (i - iVertPos); //SetTextAlign(hdc, TA_LEFT | TA_TOP); TextOut(hdc, x, y, sysmetrics[i].szName, lstrlen(sysmetrics[i].szName)); TextOut(hdc, x+ 22 * cxCaps, y, sysmetrics[i].szMajor, lstrlen(sysmetrics[i].szMajor)); //SetTextAlign(hdc, TA_RIGHT | TA_TOP); TextOut(hdc, x+ 22 * cxCaps + 40 * cxChar, y, szBuffer, wsprintf(szBuffer, TEXT("%2d"), sysmetrics[i].iAge)); } return 0; case WM_SIZE: cxClient = LOWORD(lParam);//客户区宽度 cyClient = HIWORD(lParam);//客户区的高度 si.cbSize = sizeof(si); si.fMask = SIF_RANGE | SIF_PAGE;//指定滚动范围和页面大小 si.nMin = 0;//纵向范围最小为0 si.nMax = NUMLINES - 1;//纵向范围最大值为NUMLINES-1 si.nPage = cyClient / cyChar;//页面行数 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);//设置滚动范围和滚动页码到si中 si.cbSize = sizeof(si); si.fMask = SIF_RANGE | SIF_PAGE; si.nMin = 0;//横向范围最小为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; 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;//cyClient / cyChar表示客户区一列能显示的字符数,因此这里表示向上一屏 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) {//如果滚动跳当前位置和我更新之后的iVscrollPos位置不同,就需要设置新的滚动条位置,并绘制数据 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;//截获SB_THUMBPOSITION通知码 break; default: break; } si.fMask = SIF_POS; SetScrollInfo(hwnd, SB_HORZ, &si, TRUE); GetScrollInfo(hwnd, SB_HORZ, &si); if (si.nPos != iHorzPos) {//如果滚动跳当前位置和我更新之后的iVscrollPos位置不同,就需要设置新的滚动条位置,并绘制数据 ScrollWindow(hwnd, cxChar * (iHorzPos - si.nPos), 0, NULL, NULL); } return 0; case WM_DESTROY://处理窗口关闭时的消息 PostQuitMessage(0);//将退出消息插入消息队列,程序从消息循环退出,return msg.wParam return 0; } return DefWindowProc(hwnd, message, wParam, lParam);//执行默认消息处理 }
iPaintBeg和iPaintEnd那两行删了,下面使用for(i = 0; i < NUMLINES; ++i)也能够正常输出