客户区是指整个应用程序窗口中没有被标题栏,边框,菜单栏,工具栏,状态栏和滚动条占用的区域。就是窗口中程序可以在上面绘制并向用户传达可视化信息的区域
4.1 绘制和重绘
windows是一个消息驱动的系统,他使用两种方式把事件通知给应用程序。1)把消息放在应用程序消息队列中2)或者适当向窗口过程发送消息。
4.1.1 WM_PAINT消息
在进入消息循环之前调用UpdateWindow函数,向窗口过程发送最初的WM_PAINT消息,通知窗口过程绘制客户区。
以下事件都可能导致窗口收到WM_PAINT消息:
* 用户移动一个窗口,导致原来被遮盖的部分窗口暴露出来。
* 用户调整窗口的大小(当窗口类型设定为CS_HREDRAW 和 CS_VREDRAW)
* 程序调用ScrollWindow或ScrollDC函数滚动客户区
* InvalidateRect 或InvalidateRgn函数显示生成WM_PAINT消息
某些情况下windows会试图保持被覆盖的客户区一遍恢复时使用,但并非每次都成功。以下情形windows有时会发送一条WM_PAINT消息
* Windows 关闭一个覆盖了部分窗口的对话框或消息
* 下拉菜单被拉下然后收回
* 显示提示信息
少数情况下windows总会保存被覆盖区的显示内容
* 鼠标指针在客户区内移动
* 在客户区内拖动图标
4.1.2 有效矩形和无效矩形
需要重新绘制的区域称为“无效区域”,导致windows在消息队列中放置一条WM_PAINT消息,并更新一个“绘制信息结构”,保持着无效区域的矩形。当收到WM_PAINT消息的时候可以使用GetUpdateRect来获得无效矩形的坐标。
在BeginPaint函数以后,整个客户区会变成有效。也可以调用ValidateRect是客户区中任意矩形变得有效,如果该函数让整个无效区都变得有效,那么当前消息队列中的WM_PAINT消息就会被删除。
4.2 GDI简介
最常用的字符输出函数是TextOut
TextOut(hdc, x, y, psText, iLength);
4.2.1 设备环境Device context
设备环境是GDI内部维护的一种数据结构,与特定的显示设备相关联(显示器或打印机)
设备环境中的某些值是图形“属性”,这些属性决定了GDI绘图函数的工作细节。例如在TextOut函数中,设备环境的属性决定着文本颜色,背景颜色。
GDI函数可以改变设备环境的默认属性,也可以获得当前值,还有一些GDI函数可以让你在窗口区绘图。
在绘图完成以后,必须释放设备环境
4.2.2 获取设备环境的方法:方法一
在WM_PAINT消息时 BeginPaint 获取
4.2.3 绘制信息结构
调用BeginPaint函数windows自动填充该结构
typedef struct tagPAINTSTRUCT {
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT, *PPAINTSTRUCT, *NPPAINTSTRUCT, *LPPAINTSTRUCT;
fErase 设为false, 表明在先前BeginPaint函数已经擦除了无效区域。
使用InvalidateRect是客户区矩形无效时,最后一个参数指定背景是否要擦除,fasle windows不会擦除背景
InvalidateRect(hwnd, NULL, TRUE); 是整个客户区无效化,其后调用BeginPaint会擦除原有背景。 设为false会保留原有背景
4.2.4 获取设备环境句柄:方法二
有些时候在非WM_PAINT消息也需要绘制客户区。
hdc = GetDC(hwnd);
Release(hwnd, hdc); 两者成对出现,不要夸消息。
GetDC获取的是整个客户区,不会使无效矩形有效化。如果要是整个客户区有效化,使用
ValidateRect(hwnd, NULL) 函数
通常用户处理键盘鼠标消息,而不用使用BeginPaint使客户区无效化。
GetWindowDC 获取窗口的设备环境句柄。也要处理非客户区绘制 WM_NCPAINT
4.2.5 TextOut函数详解
TextOut(hdc, x, y, psText, iLength);
hdc 设备环境,通过BeginPaint 或者GetDC获得, 默认有一系列字体属性
psText 显示文本的指针,
iLength 文本长度
x,y 逻辑坐标
windows将不会显示字符串落在剪裁区域以外的部分。
4.2.6 系统字体
设备环境定义了在调用TextOut时候的Windows使用的字体,默认系统字体(SYSTEM_FONT)
4.2.7 字符大小
GetSystemMetrics 获取用户界面的尺寸,
GetTextMetrics 获取字体尺寸,返回当前设备环境中选中字体的信息。
typedef struct tagTEXTMETRICW
{
LONG tmHeight;
LONG tmAscent;
LONG tmDescent;
LONG tmInternalLeading;
LONG tmExternalLeading;
LONG tmAveCharWidth;
LONG tmMaxCharWidth;
LONG tmWeight;
LONG tmOverhang;
LONG tmDigitizedAspectX;
LONG tmDigitizedAspectY;
WCHAR tmFirstChar;
WCHAR tmLastChar;
WCHAR tmDefaultChar;
WCHAR tmBreakChar;
BYTE tmItalic;
BYTE tmUnderlined;
BYTE tmStruckOut;
BYTE tmPitchAndFamily;
BYTE tmCharSet;
} TEXTMETRICW, *PTEXTMETRICW, NEAR *NPTEXTMETRICW, FAR *LPTEXTMETRICW;
默认映射模式是MM_TEXT 所以以像素为单位
获取字符相关信息
TEXTMETRIC tm
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm);
ReleaseDC(hwnd, hdc);
4.2.8 文本尺寸的度量
TEXTMETRIC 结构中有设备环境中当前字体的各种信息。
tmHeight 是tmAscent(基线之上)和tmDescent(基线之下)之和
tmInternalLeading(内部间距)
tmExternalLeading 字体设计者建议在两行文字间流出的空间大小。
tmAveCharWidth 小写字符的加权平均宽度,
tmMaxCharWidth 字体中最宽的字符的宽度
不要猜测字体尺寸,不用使用固定值,应该通过GetTextMetrics函数来获得信息。
4.2.9 文本格式化
在WM_CREATE消息时调用一次GetTextMetrics函数
case WM_CREATE:
hdc = getDC(hwnd);
GetTextMetrics(hdc, &tm);
cxChar = tm.tmAveCharWidth;
cyChar = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC(hwnd, hdc);
return 0;
一个wsprintf 和TextOut组合的例子
int iLength;
TCHAR szBuffer[40];
iLength = wsprintf(szBuffer, TEXT("The sum of %i and %i is %i"), iA, iB, iA + iB);
TextOut(HDC, x, y, szBuffer, iLength);
也可以合并成一句
TextOut(hdc, x, y, szBuffer,
wsprintf(szBuffer, TEXT("The sum of %i and %i is %i"), iA, iB, iA + iB));
4.2.10 综合使用
GetSystemMetrics 获得windows中各种图形项(图标,鼠标指针,标题栏和滚动条)的尺寸信息。也称为“索引”参数。
一个使用GetSystemMetrics函数获取系统信息的例子
sysmets.h
#define NUMLINES ((int)(sizeof sysmetrics / sizeof sysmetrics[0]))
struct
{
int iIndex;
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