您的位置:首页 > 其它

[Win32]重绘的基本概念以及简单的文本输出文本输出

2014-10-13 11:42 549 查看
1. 客户区的完整概念:

即应用程序窗口中没有被标题栏、边框、菜单栏、工具栏、状态栏和滚动条占据的中间的一片区域,用户可以在这片区域内绘制任意信息

注意!我们不能假定它有特定的尺寸或者是它的尺寸不会发生变化,窗口必须和其它应用程序窗口共用仅有的一块显示屏幕,因此要着重处理窗口不够用(输出的信息太多)以及窗口太大(输出信息少,空白区域多)这两种情形以使窗口资源得到充分利用

!由于窗口可以在屏幕中任意移动或放缩,因此不能假定位置以及字体的大小,而必须通过Windows的工具获得运行时的环境信息(即一种相对量)来进行相关问题的处理

2. 关于WM_PAINT消息的补充:

以下事件发生时会产生WM_PAINT消息要求客户区重画(WM_PAINT消息总是非队列消息,要求立即响应的):

被遮盖部分暴露出来、尺寸改变(必须要设定CS_HREDRAW和CS_VREDRAW,否则就是固定窗口)、滑条滚动窗口、关闭遮住部分窗口的对话框和消息框、下拉菜单被收回(下拉时遮住部分客户区)、显示提示信息、直接使用InvalidateRect和ValidateRgn函数显示生成WM_PAINT消息(之后会介绍)

在以上情形中系统都不会保存别遮住部分的信息,因为特别耗资源,因此都是直接对客户区中需要重绘的区域进行重现,接下来两个情况系统会保存遮盖部分,因为遮盖面积非常小,为了速度值得保存:

鼠标在客户区内移动、客户区内拖动图标

3. 有效区域以及有效矩形的概念:

WM_PAINT重绘时并不需要重绘整个客户区重绘而只需要重绘那些被抹掉的部分,而需要重新绘制的部分就成为“无效区域”或“更新区域”

在处理WM_PAINT响应的时候必须使用BeginPaint让无效区域有效化(即刷新无效区域的背景),否则永远也无法使无效区域有效,即使直接使用DrawText等函数直接绘制(因为背景还没有抹干净),如果存在无效区域就会在消息队列中投递WM_PAINT消息,如果响应WM_PAINT中不写BeginPaint和EndPaint则无效区域永远不可能有效,因此会不停发送WM_PAINT消息时程序进入死循环!

即使是DefWindowProc也是如此处理的:

case WM_PAINT:
BeginPaint( hWnd, &ps );
EndPaint( hWnd, &ps );
return 0;

有效矩形:

首先RECT保存有四个LONG型值:left、top、right、bottom,表示矩形的左上角坐标以及右下角坐标,但都是逻辑值而不是绝对值(即窗口左上角坐标设为(0, 0)),计算绝对坐标时会利用窗口左上角坐标的绝对值,这样有利于通用编程,因为不同显示屏分辨率不一样、大小不一样、像素点个数不一样,难以用统一的坐标来表示

Windows内部会为每个窗口保存一个绘制信息结构,保存着可以覆盖无效区域的最小矩形的信息(保存在RECT里,还有就是无效区域形状可能不规则,只能用一个最小面积矩形把这些区域统统覆盖,因此无效矩形>无效区域),这就是无效矩形,如果在处理消息队列中的WM_PAINT消息之前又多了一块无效区域,则Windows会将这块区域和之前的无效区域整合并算出一个新的无效矩形更新WM_PAINT消息的相关信息,因为消息队列里只能有一个WM_PAINT消息而不能有多个!

4. 绘制信息结构——PAINTSTRUCT:

typedef struct tagPAINTSTRUCT{ // 绘制结构
HDC		hDC;		// 即BeginPaint获得的hDC,表现出来Windows典型的数据冗余性
BOOL	rErase;		// 标志背景是否该被擦除(即重刷),TRUE表示要重刷
// FALSE一般表示已经重刷过了不必重刷
// 当调用完BeginPaint之后该项就被设为了FALSE了,因为已经重刷过了
RECT	rcPaint;	// 保存无效矩形区域的边界信息(逻辑值)
// 即在接下来的各种使用hDC绘制信息的时候就会限制在这里指定的矩形区域中进行绘制
...					// 接下来的字段供Windows内部使用,用户用不到
} PAINTSTRUCT, *PPAINTSTRUCT;
在BeginPaint之前可以简单暴力地使用InvalidateRect函数将整个客户区标记为无效(需要重新绘制整个客户区):InvalidateRect( hWnd, NULL, TRUE );

但一般都不使用,因为重绘整个客户区会比较费时费资源

BOOL InvalidateRect(
HWND hWnd,           // 需要标记的窗口
CONST RECT* lpRect,  // 需要处理的矩形区域
BOOL bErase          // 是否要被擦除
);


5. 处理非WM_PAINT消息时获取DC以及获取非客户区的DC:

由于WM_PAINT消息处理时必须要使无效区域有效化而不得不使用BeginPaint,但是在处理其它消息,如键盘和鼠标消息的时候为了及时绘制信息而不需要重绘,可以直接使用GetDC函数获取客户区DC,注意GetDC( hWnd )和ReleaseDC( hWnd, hDC )必须成对出现,用完一定要及时释放DC资源

但有时候不得不处理整个窗口的绘制(比如标题栏等),这是就得用GetWindowDC了(也和ReleaseDC配合使用),可以响应WM_NCPAINT消息(即None-Client Area的意思,即绘制非客户区的消息)

DC作用的区域:

如果是从BeginPaint中获得的DC,则其作用区域(即裁剪区)就是无效矩形区域,如果是从GetDC中获得的DC,则其裁剪区域就是整个客户区

6. TextOut函数详解:

是显示文本的一个最为重要的GDI函数,位于wingdi.h头文件中,其原型为:

BOOL TextOut(
HDC hdc,           // 利用该hDC进行绘制
int nXStart,       // 文本第一个字符的左上角坐标定位于该逻辑坐标上
int nYStart,       // 即客户区的坐标系中(客户区左上角设为(0, 0))
LPCTSTR lpString,  // 输出的文本
// 不能含有ASCII控制符,如\n等,会将其显示为空心或实心的方块!
int cbString       // 文本的长度,必须指定长度,不能以'\0'作为结束标志
); // 成功返回非0,失败返回0
关于字符框:

即DC中保存的默认的文本字符的背景色,在输出文本的时候Windows将用该背景色填充字符周围的矩形区域,如果字符框颜色和客户区背景色相同则看不出来,如果将背景画刷的颜色改成深色(而字符框的颜色的默认值为白色),就可以看出来了

7. 文本的尺寸:

首先介绍一下系统定义的默认字体:默认值为SYSTEM_FONT,定义在wingdi.h中,是标题栏、菜单、对话框中使用的默认字体,这里现不做改变,以后会详述

变宽字体:最早Windows系统中的字体都是等宽字体,之后更改为变宽字体,比如'W'就比'i'要宽,变宽字体可读性更强更美观,因此在编程的时候必须注意到这点,在计算平均宽度时需要格外小心,因此需要掌握变宽字体的处理技术

字符大小的重要性:在显示多行文本的时候格外重要,由高度信息可以决定下一行文本的垂直位置,由平均宽度可以决定下一栏文本的水平的水平位置

获取文本的度量(即尺寸值):绝对不能凭空猜测或经验定夺,因为不同屏幕分辨率不一样,写出来的程序不具有可移植性和通用性,因此不需通过GetTextMetrics函数来获取文本的度量参数,并将信息保存在TEXTMETRIC结构中:

typedef struct tagTEXTMETRIC { // 前缀tm表示Text Metric
LONG tmHeight;			// 整个字符的高度
LONG tmAscent;			// 基线之上高度
LONG tmDescent;			// 基线之下高度
LONG tmInternalLeading; // 内部间距,用于显示重音符号(欧洲字符)
LONG tmExternalLeading;	// 外部间距,行之间的间距
LONG tmAveCharWidth;	// 小写字符的加权平均宽度,变宽字符的大写字符的平均宽度为该值乘1.5,等宽字符大写就等于该值
LONG tmMaxCharWidth;	// 字体中最宽字符的跨度
...	// 只列举以上最常用的7个字段
} TEXTMETRIC, *PTEXTMETRIC;



可利用GetTextMetric( hDC, &tm );将文本尺寸信息保存在TEXTMETRIC结构tm中

8. 文本的格式化:

在Windows运行时系统字体是不会变化的,因此只需要调用一次GetTextMetric就行了,最佳的时机就是处理WM_CREATE消息,可以用两个变量保存字体的宽和高,为了使过程函数调用更快、函数栈更小可以将以上两个变量声明为静态的,实例代码如下:

static int cxChar, cxCaps, cyChar;

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;

ReleaseDC( hWnd, hDC );
return 0;


TEXTMETRIC的字段tmPitchAndFamily的最低位标志字体是否为变宽字体,如果为变宽字体则为1,否则为0,变宽字体大写的平均宽度为tmAveCharWidth的1.5倍

输出的文本一般为格式化字符串(包含数字和字符串),则可以先用wprintf现得到格式字符串,再用TextOut等函数输出

9. 简单文本输出的小例子:

利用GetTextMetric函数获得Windows中各种对象的大小信息,并格式化输出,每个对象占据一行,为此现定义一个头文件,将这些信息组织起来:

/*-----------------------------------------------
SYSMETS.H -- System metrics display structure
-----------------------------------------------*/

#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_CYHSCROLL,            TEXT ("SM_CYHSCROLL"),
TEXT ("Horizontal scroll height"),
SM_CYCAPTION,            TEXT ("SM_CYCAPTION"),
TEXT ("Caption bar height"),
SM_CXBORDER,             TEXT ("SM_CXBORDER"),
TEXT ("Window border width"),
SM_CYBORDER,             TEXT ("SM_CYBORDER"),
TEXT ("Window border height"),
SM_CXFIXEDFRAME,         TEXT ("SM_CXFIXEDFRAME"),
TEXT ("Dialog window frame width"),
SM_CYFIXEDFRAME,         TEXT ("SM_CYFIXEDFRAME"),
TEXT ("Dialog window frame height"),
SM_CYVTHUMB,             TEXT ("SM_CYVTHUMB"),
TEXT ("Vertical scroll thumb height"),
SM_CXHTHUMB,             TEXT ("SM_CXHTHUMB"),
TEXT ("Horizontal scroll thumb width"),
SM_CXICON,               TEXT ("SM_CXICON"),
TEXT ("Icon width"),
SM_CYICON,               TEXT ("SM_CYICON"),
TEXT ("Icon height"),
SM_CXCURSOR,             TEXT ("SM_CXCURSOR"),
TEXT ("Cursor width"),
SM_CYCURSOR,             TEXT ("SM_CYCURSOR"),
TEXT ("Cursor height"),
SM_CYMENU,               TEXT ("SM_CYMENU"),
TEXT ("Menu bar height"),
SM_CXFULLSCREEN,         TEXT ("SM_CXFULLSCREEN"),
TEXT ("Full screen client area width"),
SM_CYFULLSCREEN,         TEXT ("SM_CYFULLSCREEN"),
TEXT ("Full screen client area height"),
SM_CYKANJIWINDOW,        TEXT ("SM_CYKANJIWINDOW"),
TEXT ("Kanji window height"),
SM_MOUSEPRESENT,         TEXT ("SM_MOUSEPRESENT"),
TEXT ("Mouse present flag"),
SM_CYVSCROLL,            TEXT ("SM_CYVSCROLL"),
TEXT ("Vertical scroll arrow height"),
SM_CXHSCROLL,            TEXT ("SM_CXHSCROLL"),
TEXT ("Horizontal scroll arrow width"),
SM_DEBUG,                TEXT ("SM_DEBUG"),
TEXT ("Debug version flag"),
SM_SWAPBUTTON,           TEXT ("SM_SWAPBUTTON"),
TEXT ("Mouse buttons swapped flag"),
SM_CXMIN,                TEXT ("SM_CXMIN"),
TEXT ("Minimum window width"),
SM_CYMIN,                TEXT ("SM_CYMIN"),
TEXT ("Minimum window height"),
SM_CXSIZE,               TEXT ("SM_CXSIZE"),
TEXT ("Min/Max/Close button width"),
SM_CYSIZE,               TEXT ("SM_CYSIZE"),
TEXT ("Min/Max/Close button height"),
SM_CXSIZEFRAME,          TEXT ("SM_CXSIZEFRAME"),
TEXT ("Window sizing frame width"),
SM_CYSIZEFRAME,          TEXT ("SM_CYSIZEFRAME"),
TEXT ("Window sizing frame height"),
SM_CXMINTRACK,           TEXT ("SM_CXMINTRACK"),
TEXT ("Minimum window tracking width"),
SM_CYMINTRACK,           TEXT ("SM_CYMINTRACK"),
TEXT ("Minimum window tracking height"),
SM_CXDOUBLECLK,          TEXT ("SM_CXDOUBLECLK"),
TEXT ("Double click x tolerance"),
SM_CYDOUBLECLK,          TEXT ("SM_CYDOUBLECLK"),
TEXT ("Double click y tolerance"),
SM_CXICONSPACING,        TEXT ("SM_CXICONSPACING"),
TEXT ("Horizontal icon spacing"),
SM_CYICONSPACING,        TEXT ("SM_CYICONSPACING"),
TEXT ("Vertical icon spacing"),
SM_MENUDROPALIGNMENT,    TEXT ("SM_MENUDROPALIGNMENT"),
TEXT ("Left or right menu drop"),
SM_PENWINDOWS,           TEXT ("SM_PENWINDOWS"),
TEXT ("Pen extensions installed"),
SM_DBCSENABLED,          TEXT ("SM_DBCSENABLED"),
TEXT ("Double-Byte Char Set enabled"),
SM_CMOUSEBUTTONS,        TEXT ("SM_CMOUSEBUTTONS"),
TEXT ("Number of mouse buttons"),
SM_SECURE,               TEXT ("SM_SECURE"),
TEXT ("Security present flag"),
SM_CXEDGE,               TEXT ("SM_CXEDGE"),
TEXT ("3-D border width"),
SM_CYEDGE,               TEXT ("SM_CYEDGE"),
TEXT ("3-D border height"),
SM_CXMINSPACING,         TEXT ("SM_CXMINSPACING"),
TEXT ("Minimized window spacing width"),
SM_CYMINSPACING,         TEXT ("SM_CYMINSPACING"),
TEXT ("Minimized window spacing height"),
SM_CXSMICON,             TEXT ("SM_CXSMICON"),
TEXT ("Small icon width"),
SM_CYSMICON,             TEXT ("SM_CYSMICON"),
TEXT ("Small icon height"),
SM_CYSMCAPTION,          TEXT ("SM_CYSMCAPTION"),
TEXT ("Small caption height"),
SM_CXSMSIZE,             TEXT ("SM_CXSMSIZE"),
TEXT ("Small caption button width"),
SM_CYSMSIZE,             TEXT ("SM_CYSMSIZE"),
TEXT ("Small caption button height"),
SM_CXMENUSIZE,           TEXT ("SM_CXMENUSIZE"),
TEXT ("Menu bar button width"),
SM_CYMENUSIZE,           TEXT ("SM_CYMENUSIZE"),
TEXT ("Menu bar button height"),
SM_ARRANGE,              TEXT ("SM_ARRANGE"),
TEXT ("How minimized windows arranged"),
SM_CXMINIMIZED,          TEXT ("SM_CXMINIMIZED"),
TEXT ("Minimized window width"),
SM_CYMINIMIZED,          TEXT ("SM_CYMINIMIZED"),
TEXT ("Minimized window height"),
SM_CXMAXTRACK,           TEXT ("SM_CXMAXTRACK"),
TEXT ("Maximum draggable width"),
SM_CYMAXTRACK,           TEXT ("SM_CYMAXTRACK"),
TEXT ("Maximum draggable height"),
SM_CXMAXIMIZED,          TEXT ("SM_CXMAXIMIZED"),
TEXT ("Width of maximized window"),
SM_CYMAXIMIZED,          TEXT ("SM_CYMAXIMIZED"),
TEXT ("Height of maximized window"),
SM_NETWORK,              TEXT ("SM_NETWORK"),
TEXT ("Network present flag"),
SM_CLEANBOOT,            TEXT ("SM_CLEANBOOT"),
TEXT ("How system was booted"),
SM_CXDRAG,               TEXT ("SM_CXDRAG"),
TEXT ("Avoid drag x tolerance"),
SM_CYDRAG,               TEXT ("SM_CYDRAG"),
TEXT ("Avoid drag y tolerance"),
SM_SHOWSOUNDS,           TEXT ("SM_SHOWSOUNDS"),
TEXT ("Present sounds visually"),
SM_CXMENUCHECK,          TEXT ("SM_CXMENUCHECK"),
TEXT ("Menu check-mark width"),
SM_CYMENUCHECK,          TEXT ("SM_CYMENUCHECK"),
TEXT ("Menu check-mark height"),
SM_SLOWMACHINE,          TEXT ("SM_SLOWMACHINE"),
TEXT ("Slow processor flag"),
SM_MIDEASTENABLED,       TEXT ("SM_MIDEASTENABLED"),
TEXT ("Hebrew and Arabic enabled flag"),
SM_MOUSEWHEELPRESENT,    TEXT ("SM_MOUSEWHEELPRESENT"),
TEXT ("Mouse wheel present flag"),
SM_XVIRTUALSCREEN,       TEXT ("SM_XVIRTUALSCREEN"),
TEXT ("Virtual screen x origin"),
SM_YVIRTUALSCREEN,       TEXT ("SM_YVIRTUALSCREEN"),
TEXT ("Virtual screen y origin"),
SM_CXVIRTUALSCREEN,      TEXT ("SM_CXVIRTUALSCREEN"),
TEXT ("Virtual screen width"),
SM_CYVIRTUALSCREEN,      TEXT ("SM_CYVIRTUALSCREEN"),
TEXT ("Virtual screen height"),
SM_CMONITORS,            TEXT ("SM_CMONITORS"),
TEXT ("Number of monitors"),
SM_SAMEDISPLAYFORMAT,    TEXT ("SM_SAMEDISPLAYFORMAT"),
TEXT ("Same color format flag")
} ;


// sysmets1.c

#include <windows.h>

#include "sysmets.h"

#define CLINE(i)		( ( cyChar ) * ( i ) )

LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam );

int WINAPI WinMain(
HINSTANCE	hInstance,
HINSTANCE	hPrevInstance,
LPSTR		lpszCmdline,
int			nCmdShow
)
{
static TCHAR	szAppName[] = TEXT("sysmets1");

WNDCLASS	wndclass;
HWND		hWnd;
MSG			msg;

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		= LoadIcon( NULL, IDC_ARROW );
wndclass.hbrBackground	= (HBRUSH)GetStockObject( WHITE_BRUSH );
wndclass.lpszMenuName	= NULL;
wndclass.lpszClassName	= szAppName;

RegisterClass( &wndclass );

hWnd = CreateWindow(
szAppName,
TEXT("Get System Metrics Version 1"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);

ShowWindow( hWnd, nCmdShow );
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		col1, col2, col3; // 三栏的起始x轴位置

TCHAR		szBuffer[30];

HDC				hDC;
PAINTSTRUCT		ps;
TEXTMETRIC		tm;

int		i;

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;
col1 = 0; // 第一栏为大写字母
col2 = 22 * cxCaps; // 空出第一栏的距离,第二栏是小写字母
col3 = col1 + col2 + 40 * cxChar; // 第三栏是数字,空出第一栏和第二栏的距离

ReleaseDC( hWnd, hDC );
return 0;
case WM_PAINT:
hDC = BeginPaint( hWnd, &ps );

for ( i = 0; i < NUMLINES; i++ )
{
// 显示第一栏
TextOut( hDC, col1, CLINE(i), sysmetrics[i].szLabel, lstrlen( sysmetrics[i].szLabel ) );

// 显示第二栏
TextOut( hDC, col2, CLINE(i), sysmetrics[i].szDesc, lstrlen( sysmetrics[i].szDesc ) );

// 显示第三栏
SetTextAlign( hDC, TA_RIGHT | TA_TOP ); // 表示右上对齐
TextOut( hDC, col3, CLINE(i), szBuffer, wsprintf( szBuffer, TEXT("%5d"),
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 );
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: