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!"), -, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hwnd, &ps);//结束窗口绘制
return ;

4.2.3 绘制信息结构

PAINTSTRUCT结构包含了应用程序用来绘制它所拥有的窗口客户区所需要的信息。PAINTSTRUCT的结构定义如下:

typedef struct tagPAINTSTRUCT {
  HDC hdc;
  BOOL fErase;
  RECT rcPaint;
  BOOL fRestore;
  BOOL fIncUpdate;
  BYTE rgbReserved[];
} 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 = ;//窗口实例扩展
wndclass.cbWndExtra = ;//窗口类扩展
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 ;
} 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, , )) {//从消息队列中获取消息
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 ;
case WM_PAINT://处理窗口绘制
hdc = GetDC(hwnd);
GetClientRect(hwnd, &rect);//获取窗口客户区的尺寸
DrawText(hdc, TEXT("Hello Hk_Mayfly!"), -, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
ReleaseDC(hwnd, hdc);
ValidateRect(hwnd, NULL);//因为会不断更新整个客户区,不断刷新,要使其他消息能接收,需要使客户区有效
return ;
case WM_LBUTTONDOWN:
MessageBox(hwnd, TEXT("左键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION);
return ;
case WM_RBUTTONDOWN:
MessageBox(hwnd, TEXT("右键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION);
return ;
case WM_DESTROY://处理窗口关闭时的消息
MessageBox(hwnd, TEXT("主窗口关闭"), TEXT("Windows"), MB_OK | MB_ICONINFORMATION);//显示一个文本字符串
PostQuitMessage();//将退出消息插入消息队列,程序从消息循环退出,return msg.wParam
return ;
}
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[];
int iLength = wsprintf(szBuffer, TEXT("Unicode形式输出!"));
case WM_PAINT://处理窗口绘制
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
TextOut(hdc, , , szBuffer, iLength);
EndPaint(hwnd, &ps);
return ;

第二种:

在switch前面定义一个结构变量tm

TEXTMETRIC tm;
TCHAR szBuffer[];
int iLength = wsprintf(szBuffer, TEXT("Unicode形式输出!"));
case WM_PAINT://处理窗口绘制
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm);
TextOut(hdc, , , szBuffer, iLength);
ReleaseDC(hwnd, hdc);
ValidateRect(hwnd, NULL);
return ;

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("刘然"), , TEXT("软件工程"),
TEXT("黄鹤"), , TEXT("计算机科学"),
TEXT("马平"), , TEXT("网络工程"),
TEXT("评然"), , TEXT("软件工程"),
TEXT("里鹤"), , TEXT("计算机科学"),
TEXT("高平"), , TEXT("网络工程"),
TEXT("偶然"), , TEXT("软件工程"),
TEXT("炮打"), , TEXT("计算机科学"),
TEXT("马平"), , TEXT("网络工程"),
TEXT("刘然"), , TEXT("软件工程"),
TEXT("黄鹤"), , TEXT("计算机科学"),
TEXT("马平"), , 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 = ;//窗口实例扩展
wndclass.cbWndExtra = ;//窗口类扩展
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 ;
} hwnd = CreateWindow(szAppName, //Windows类名
TEXT("窗口绘制成功!"), //窗口标题
WS_OVERLAPPEDWINDOW, //窗口风格
CW_USEDEFAULT, //初始化窗口位置的X坐标
CW_USEDEFAULT, //初始化窗口位置的Y坐标
, //初始化窗口长度大小
, //初始化窗口宽度大小
NULL, //父类窗口句柄
NULL, //窗口菜单句柄
hInstance, //程序实例句柄
NULL); //创建参数
ShowWindow(hwnd, iCmdShow);//显示窗口
UpdateWindow(hwnd);//更新窗口 while (GetMessage(&msg, NULL, , )) {//从消息队列中获取消息
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 = ;
TCHAR szBuffer[];
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 ;
case WM_PAINT://处理窗口绘制
hdc = BeginPaint(hwnd, &ps);
for (i = ; i < NUMLINES; ++i) {
//SetTextAlign(hdc, TA_RIGHT | TA_TOP);//设置TextOut使用的坐标将从右上角开始
TextOut(hdc, , 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, , cyChar * i, szBuffer, wsprintf(szBuffer, TEXT("%2d"), sysmetrics[i].iAge));
TextOut(hdc, , cyChar * i, sysmetrics[i].szMajor, lstrlen(sysmetrics[i].szMajor));
}
     EndPaint(hwnd,&ps);
return ;
case WM_LBUTTONDOWN:
MessageBox(hwnd, TEXT("左键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION);
return ;
case WM_RBUTTONDOWN:
MessageBox(hwnd, TEXT("右键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION);
return ;
case WM_DESTROY://处理窗口关闭时的消息
MessageBox(hwnd, TEXT("主窗口关闭"), TEXT("Windows"), MB_OK | MB_ICONINFORMATION);//显示一个文本字符串
PostQuitMessage();//将退出消息插入消息队列,程序从消息循环退出,return msg.wParam
return ;
}
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("刘然"), , TEXT("软件工程"),
TEXT("黄鹤"), , TEXT("计算机科学"),
TEXT("马平"), , TEXT("网络工程"),
TEXT("评然"), , TEXT("软件工程"),
TEXT("里鹤"), , TEXT("计算机科学"),
TEXT("高平"), , TEXT("网络工程"),
TEXT("偶然"), , TEXT("软件工程"),
TEXT("炮打"), , TEXT("计算机科学"),
TEXT("马平"), , TEXT("网络工程"),
TEXT("刘然"), , TEXT("软件工程"),
TEXT("黄鹤"), , TEXT("计算机科学"),
TEXT("马平"), , TEXT("网络工程"),
TEXT("评然"), , TEXT("软件工程"),
TEXT("里鹤"), , TEXT("计算机科学"),
TEXT("高平"), , TEXT("网络工程"),
TEXT("偶然"), , TEXT("软件工程"),
TEXT("炮打"), , TEXT("计算机科学"),
TEXT("马平"), , TEXT("网络工程"),
TEXT("刘然"), , TEXT("软件工程"),
TEXT("黄鹤"), , TEXT("计算机科学"),
TEXT("马平"), , TEXT("网络工程"),
TEXT("评然"), , TEXT("软件工程"),
TEXT("里鹤"), , TEXT("计算机科学"),
TEXT("高平"), , TEXT("网络工程"),
TEXT("偶然"), , TEXT("软件工程"),
TEXT("炮打"), , TEXT("计算机科学"),
TEXT("马平"), , TEXT("网络工程"),
TEXT("刘然"), , TEXT("软件工程"),
TEXT("黄鹤"), , TEXT("计算机科学"),
TEXT("马平"), , TEXT("网络工程"),
TEXT("评然"), , TEXT("软件工程"),
TEXT("里鹤"), , TEXT("计算机科学"),
TEXT("高平"), , TEXT("网络工程"),
TEXT("偶然"), , TEXT("软件工程"),
TEXT("炮打"), , TEXT("计算机科学"),
TEXT("马平"), , 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 = ;//窗口实例扩展
wndclass.cbWndExtra = ;//窗口类扩展
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 ;
} hwnd = CreateWindow(szAppName, //Windows类名
TEXT("窗口绘制成功!"), //窗口标题
WS_OVERLAPPEDWINDOW, //窗口风格
CW_USEDEFAULT, //初始化窗口位置的X坐标
CW_USEDEFAULT, //初始化窗口位置的Y坐标
, //初始化窗口长度大小
, //初始化窗口宽度大小
NULL, //父类窗口句柄
NULL, //窗口菜单句柄
hInstance, //程序实例句柄
NULL); //创建参数
ShowWindow(hwnd, iCmdShow);//显示窗口
UpdateWindow(hwnd);//更新窗口 while (GetMessage(&msg, NULL, , )) {//从消息队列中获取消息
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 = , y;
TCHAR szBuffer[];
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, , NUMLINES - , FALSE);//设置垂直滚动条的范围
SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);//设置垂直滚动条的位置
return ;
case WM_PAINT://处理窗口绘制
hdc = BeginPaint(hwnd, &ps);
for (i = ; i < NUMLINES; ++i) {
y = cyChar * (i - iVscrollPos);//刚开始为0,小于iVscrollPos都被显示到了内容区域之外
//SetTextAlign(hdc, TA_RIGHT | TA_TOP);
TextOut(hdc, , y, sysmetrics[i].szName, lstrlen(sysmetrics[i].szName));
//TextOut(hdc, 400, cyChar * i, szBuffer, wsprintf(szBuffer, TEXT("%2d"), GetSystemMetrics(sysmetrics[i].iAge)));
TextOut(hdc, , y, szBuffer, wsprintf(szBuffer, TEXT("%2d"), sysmetrics[i].iAge));
TextOut(hdc, , y, sysmetrics[i].szMajor, lstrlen(sysmetrics[i].szMajor));
}
return ;
case WM_LBUTTONDOWN:
MessageBox(hwnd, TEXT("左键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION);
return ;
case WM_RBUTTONDOWN:
MessageBox(hwnd, TEXT("右键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION);
return ;
case WM_SIZE:
//cxClient = LOWORD(lParam);//客户区宽度
cyClient = HIWORD(lParam);//客户区的高度
return ;
case WM_VSCROLL:
switch (LOWORD(wParam)) {
case SB_LINEUP:
iVscrollPos -= ;//表示向上移动一行
break;
case SB_LINEDOWN:
iVscrollPos += ;
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(, min(iVscrollPos, NUMLINES - ));
if (iVscrollPos != GetScrollPos(hwnd, SB_VERT)) {//如果滚动跳当前位置和我更新之后的iVscrollPos位置不同,就需要设置新的滚动条位置,并绘制数据
SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);//设置显示位置
InvalidateRect(hwnd, NULL, TRUE);//将要绘制区域无效
}
return ;
case WM_DESTROY://处理窗口关闭时的消息
MessageBox(hwnd, TEXT("主窗口关闭"), TEXT("Windows"), MB_OK | MB_ICONINFORMATION);//显示一个文本字符串
PostQuitMessage();//将退出消息插入消息队列,程序从消息循环退出,return msg.wParam
return ;
}
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("刘然"), , TEXT("软件工程"),
TEXT("黄鹤"), , TEXT("计算机科学"),
TEXT("马平"), , TEXT("网络工程"),
TEXT("评然"), , TEXT("软件工程"),
TEXT("里鹤"), , TEXT("计算机科学"),
TEXT("高平"), , TEXT("网络工程"),
TEXT("偶然"), , TEXT("软件工程"),
TEXT("炮打"), , TEXT("计算机科学"),
TEXT("马平"), , TEXT("网络工程"),
TEXT("刘然"), , TEXT("软件工程"),
TEXT("黄鹤"), , TEXT("计算机科学"),
TEXT("马平"), , TEXT("网络工程"),
TEXT("评然"), , TEXT("软件工程"),
TEXT("里鹤"), , TEXT("计算机科学"),
TEXT("高平"), , TEXT("网络工程"),
TEXT("偶然"), , TEXT("软件工程"),
TEXT("炮打"), , TEXT("计算机科学"),
TEXT("马平"), , TEXT("网络工程"),
TEXT("刘然"), , TEXT("软件工程"),
TEXT("黄鹤"), , TEXT("计算机科学"),
TEXT("马平"), , TEXT("网络工程"),
TEXT("评然"), , TEXT("软件工程"),
TEXT("里鹤"), , TEXT("计算机科学"),
TEXT("高平"), , TEXT("网络工程"),
TEXT("偶然"), , TEXT("软件工程"),
TEXT("炮打"), , TEXT("计算机科学"),
TEXT("马平"), , TEXT("网络工程"),
TEXT("刘然"), , TEXT("软件工程"),
TEXT("黄鹤"), , TEXT("计算机科学"),
TEXT("马平"), , TEXT("网络工程"),
TEXT("评然"), , TEXT("软件工程"),
TEXT("里鹤"), , TEXT("计算机科学"),
TEXT("高平"), , TEXT("网络工程"),
TEXT("偶然"), , TEXT("软件工程"),
TEXT("炮打"), , TEXT("计算机科学"),
TEXT("马结束"), , 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 = ;//窗口实例扩展
wndclass.cbWndExtra = ;//窗口类扩展
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 ;
} hwnd = CreateWindow(szAppName, //Windows类名
TEXT("窗口绘制成功!"), //窗口标题
WS_OVERLAPPEDWINDOW, //窗口风格
CW_USEDEFAULT, //初始化窗口位置的X坐标
CW_USEDEFAULT, //初始化窗口位置的Y坐标
, //初始化窗口长度大小
, //初始化窗口宽度大小
NULL, //父类窗口句柄
NULL, //窗口菜单句柄
hInstance, //程序实例句柄
NULL); //创建参数
ShowWindow(hwnd, iCmdShow);//显示窗口
UpdateWindow(hwnd);//更新窗口 while (GetMessage(&msg, NULL, , )) {//从消息队列中获取消息
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 = , x, y;
SCROLLINFO si;
TCHAR szBuffer[];
HDC hdc;//设备环境句柄
PAINTSTRUCT ps;//绘制结构
TEXTMETRIC tm; switch (message) {//处理得到的消息
case WM_CREATE://窗口创建发来消息
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm);
cxChar = tm.tmAveCharWidth; //得到字体平均宽度
cxCaps = (tm.tmPitchAndFamily & ? : ) * cxChar / ;//判断字符类型,Unicode是字符型的1.5倍大小
cyChar = tm.tmHeight + tm.tmExternalLeading;//字体高度, 总高度tmHeight + 两行文字之间的建议间距大小tmExternalLeading
ReleaseDC(hwnd, hdc);
iMaxWidth = * cxChar + * cxCaps;//最大宽度
return ;
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(, 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 + * ps.rcPaint.bottom / cyChar / );
for (i = iPaintBeg; i < iPaintEnd; ++i) {
//小于当前横iHorzPos,纵iVertPos滚动条位置的数据都被隐藏
//0和i相当于输出的起始位置
x = cxChar * ( - 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+ * cxCaps, y, sysmetrics[i].szMajor, lstrlen(sysmetrics[i].szMajor));
//SetTextAlign(hdc, TA_RIGHT | TA_TOP);
TextOut(hdc, x+ * cxCaps + * cxChar, y, szBuffer, wsprintf(szBuffer, TEXT("%2d"), sysmetrics[i].iAge));
}
return ;
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 - ;//纵向范围最大值为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
si.nMax = + iMaxWidth / cxChar;//横向范围最大值
si.nPage = cxClient / cxChar;//每页列数
SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
return ;
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 -= ;//表示向上移动一行
break;
case SB_LINEDOWN:
si.nPos += ;
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, , cyChar * (iVertPos - si.nPos), NULL, NULL);
UpdateWindow(hwnd);
}
return ;
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 -= ;
break;
case SB_LINERIGHT:
si.nPos += ;
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), , NULL, NULL);
}
return ;
case WM_DESTROY://处理窗口关闭时的消息
PostQuitMessage();//将退出消息插入消息队列,程序从消息循环退出,return msg.wParam
return ;
}
return DefWindowProc(hwnd, message, wParam, lParam);//执行默认消息处理
}

iPaintBeg和iPaintEnd那两行删了,下面使用for(i = 0; i < NUMLINES; ++i)也能够正常输出

Windows程序设计--(四)文本输出的更多相关文章

  1. windows程序设计获取文本框(窗口、对话框)文本

    就是这样一个简单的界面,窗口上重绘的对话框(这种写法参考我之前博文): 需要做到的就是点击确定,获取文本框中内容. // 处理对话框消息 INT_PTR CALLBACK NewDlgProc(HWN ...

  2. Windows程序设计(第五版)学习:第四章 文本输出

    第四章 文本输出 1,客户区:整个应用程序窗口中没有被标题栏.边框.菜单栏.工具栏.状态栏和滚动条占用的区域.简而言之,客户区就是窗口中程序可以在上面绘制并向用户传达可视化信息的区域.   2,大多数 ...

  3. 【C语言】控制台窗口图形界面编程(四):文本输出

    目录 00. 目录 01. FillConsoleOutputAttribute函数 02. FillConsoleOutputCharacter函数 03. WriteConsoleOutputCh ...

  4. Windows程序设计学习笔记(1):一个简单的windows程序

    <Windows程序设计>(第五版)(美Charles Petzold著) #include<windows.h> LRESULT CALLBACK WndProc(HWND, ...

  5. Windows 程序设计(4) MFC 03 -系列学习

    本文整体目录和绝大部门内容来自 [鸡啄米网站]的MFC系列文章,欢迎支持原创 (一)VS2010/MFC编程入门之前言 VC++全称是Visual C++,是由微软提供的C++开发工具,它与C++的根 ...

  6. Direct3D 10学习笔记(三)——文本输出

    本篇将简单整理Direct3D 10的文本输出的实现,具体内容参照< Introduction to 3D Game Programming with DirectX 10>(中文版有汤毅 ...

  7. Windows 程序设计

    一.Win32 API /******************************************************************** created: 2014/04/1 ...

  8. CMD命令之 :修改windows的CMD窗口输出编码格式为UTF-8

    修改windows的CMD窗口输出编码格式为UTF-8 转载自 http://xuliduo.iteye.com/blog/639923 dos命令: chcp 65001  就是换成UTF-8代码页 ...

  9. MFC字体与文本输出

    字体 成员函数 1.CFont( ); 构造一个CFont对象.此对象在使用之前应该先使用CreateFont.CreateFontIndirect.CreatePointFont或CreatePoi ...

  10. windows 程序设计 SetPolyFillMode关于ALTERNATE、WINDING的详细解释

    看windows程序第五章GDI编程部分.一直卡壳在这里了. 下面我来说下自己的想法.看是否对您有帮助. 首先我们来看一个图. SetPolyFillMode(ALTERNATE);  // 系统默认 ...

随机推荐

  1. css 响应式(媒介查询)

    1.CSS 来实现响应式 CSS实现响应式网站的布局要用到的就是CSS中的媒体查询接下来来简单介绍一下: @media 类型 and (条件1) and (条件二){css样式} <link r ...

  2. django 多条数据显示的坑(怪自己)

    今天的问题是,一个接口执行了很多次,每次都会在结果表里面记录一条结果信息,在查看接口详情页面,我想只展示一条,然后就进入误区了 第一个是怪自己手残,api_id  被自己写成app_id了 第二个是筛 ...

  3. 对calc()的研究

    1.calc是英文单词calculate(计算)的缩写,是css3的一个新增的功能,用来指定元素的长度 calc()最大的好处就是用在流体布局上 2.calc()使用通用的数学运算规则 使用“+”.“ ...

  4. 对vueloader的研究

    vue-loader是webpack的加载器,允许您以称为单文件组件(SFC)的格式创作Vue组件: <template> <div class="example" ...

  5. 奇异值分解(SVD)(基础知识)

    参考:https://www.cnblogs.com/pinard/p/6251584.html 参考:http://blog.csdn.net/u010099080/article/details/ ...

  6. 图片没有.png或者jpg后缀能不能加载?

    是可以的,如:http://mmbiz.qpic.cn/mmbiz_png/MW1VecOrnw6HUMvEUNUxibLVtbY2vHF8QkFyviaickh8pnsapQ8gOosdq13SBX ...

  7. ES6中的export和import

    1.ES6中的模块加载 ES6 模块是编译时加载,编译时就能确定模块的依赖关系,以及输入和输出的变量,相比于CommonJS 和 AMD 模块都只能在运行时确定输入输出变量的加载效率要高. 1.1.严 ...

  8. 台哥原创:java 俄罗斯方块源码

    大四的时候,用java开发,耗时一周 界面参照当时用的联想手机里的俄罗斯方块 ​ 这里的级别,标识难度,1是初级,方块下降速度很慢,5是最高级,下降速度最快 ​ 得分:每消除一行,会给10分,同时消除 ...

  9. php函数名后冒号+数据类型(返回值类型限制/php新特性)

    在PHP7,一个新的功能,返回类型声明已被引入.返回类型声明指定的一个函数返回值的类型. int float bool string interfaces array callable 对象实例 如下 ...

  10. ST表——————一失足成千古恨系列2

    在此先祝自己这个系列写的越少越好qwq(保证不超过4篇(flag已立)) 考试原题:(绝壁是看完复联出的) 第一反应:线段树??不对,是st表.嗯,没错.哎,st表咋写来着??完了凉了. 结果:写暴搜 ...