格式化输出
如果要使用TextOut函数在Windows应用程序中输出多行文字,我们就必须先取得字符的宽度和高度,通常我们在窗口消息处理函数中处理WM_CREATE消息的时候调用GetTextMetrics函数来获取他们,之所以放到WM_CREATE的部分调用,是因为当系统启动后,系统字体的大小就不会发生改变,我们只需要调用一次GetTextMetrics就可以了。
具体方法如下:
case WM_CREATE:
hdc = GetDC (hwnd); // 取得设备描述表句柄。
GetTextMetrics (hdc, &tm); // 调用GetTextMetrics函数将获得的信息保存在缓冲区中。
iLFx = tm.tmAveCharWidth;
// 获得小写字符的平均宽度,小写字母的加权平均值就是字符的平均宽度。
iCFx = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
// 获得大写字符的平均宽度,对于变宽字体而言,大写字符的平均宽度是iLFx乘以150%,而如果是等宽字体,那么iCFx就等于iLFx。
iFy = tm.tmHeight + tm.tmExternalLeading; // 字符高度等于字符准线上下最大纵向高度加行距。
ReleaseDC (hwnd, hdc) ; // 释放设备描述表句柄。
return 0;
大家可能对上面代码中的,tm.tmPitchAndFamily & 1 ? 3 : 2,这样的写法感到疑惑,TEXTMETRICS结构的tmPitchAndFamily属性是用来判断字体是否为变宽字体的,如果是等宽字体那么它的低位就为0,如果为变宽字体那么它的低位就为1,所以通过位运算我们可以判断是否为变宽字体,如果是就进行乘以150%的操作。
看到这里,我们已经了解了TextOut函数输出所需要的所有知识,下面我们将以一个完整的例子对它进一步的学习。
实例练习
例子目的是要将GetSystemMetrics函数所需要的参数以及这些参数的含义和返回值,以字符串的方式并格式化后循环输出在屏幕上。
该例程我们分为两个文件,一个是用来保存GetSystemMetrics函数所需索引的头文件,另一个则是程序的主文件,代码的详细解释见代码的注释部分。
/* sysmetrics.h 头文件 */
#define NUMLINES ((int) (sizeof sysmetrics / sizeof sysmetrics [0]))
struct
{
int Index; // 索引
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")
};
/* main.cpp 主文件 */
#include
#include
#include "system.h"
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
static char szAppName[] = TEXT("HelloWin");
// 预先定义一个c风格字符串,稍后用于设置窗口类名称。
WNDCLASS wndclass; // 定义窗口类对象。
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc ;
// 指定窗口的处理函数为WndProc,WndProc将处理windows消息。
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; // 窗口类对象的名称。
//-----------------------------------------------------------------------------------------
RegisterClass (&wndclass);
//-------------------------- 实例化过程 -------------------------------------------------
HWND hwnd ;
//创建用于保存窗口句柄的对象,窗口句柄是系统识别不同窗口的依据,它只是个代号。
hwnd = CreateWindow(
szAppName, // 窗口类名称。
"GDI 入门", // 窗口标题。
WS_OVERLAPPEDWINDOW|WS_HSCROLL|WS_VSCROLL, // 窗口样式。
CW_USEDEFAULT, // 初始的窗口x轴位置。
CW_USEDEFAULT, // 初始的窗口y轴位置。
CW_USEDEFAULT, // 初始的窗口x轴大小。
CW_USEDEFAULT, // 初始的窗口y轴大小。
NULL, // 父窗口句柄。
NULL, // 窗口功能表句柄。
hInstance, // 应用程序实例句柄。
NULL // 建立参数,这个参数可以存取后面程序中可能引用到的资料。
);
ShowWindow(hwnd, iCmdShow);
UpdateWindow (hwnd);
//-----------------------------------------------------------------------------------------
//---------------------------- 消息循环 -------------------------------------------------
MSG msg ; // 建立消息对象。
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg);
//把虚拟键盘消息转换到字符消息,满足键盘输入的需要,参数为msg消息对象的地址。
DispatchMessage (&msg);
//把当前的消息发送到消息处理函数中去。
}
//----------------------------------------------------------------------------------------
return msg.wParam;//返回消息结构中的wParam成员信息。
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
// 窗口消息处理函数。
{
static int iLFx, iCFx, iFy ;
HDC hdc; // 创建设备描述句柄对象。
PAINTSTRUCT ps; // 创建绘制结构对象。
int i;
TCHAR szBuffer [10]; // 创建字符数组用于保存GetSystemMetrics函数返回的值。
TEXTMETRIC tm; // 创建TEXTMETRIC结构对象。
char szChar[200];
switch (message)
{
case WM_CREATE: // 当窗口创建的时候获得WM_CREATE消息。
hdc = GetDC (hwnd); // 获取设备描述表句柄。
GetTextMetrics (hdc, &tm);// 调用GetTextMetrics函数获取字符大小相关信息。
iLFx = tm.tmAveCharWidth;
// 获得小写字符的平均宽度,小写字母的加权平均值就是字符的平均宽度。
iCFx = (tm.tmPitchAndFamily & 1 ? 3 : 2) * iLFx / 2;
// 获得大写字符的平均宽度,对于变宽字体而言,大写字符的平均宽度是iLFx乘以150%,而如果是等宽字体,那么iCFx就等于iLFx。
iFy = tm.tmHeight + tm.tmExternalLeading;
// 字符高度等于字符准线上下最大纵向高度加行距。
ReleaseDC (hwnd, hdc) ;
return 0 ;
case WM_PAINT://通知窗口更新显示区域的信息
hdc = BeginPaint (hwnd, &ps);
SetTextColor(hdc,RGB(255,0,0));
for (i = 0; i //通过获取的define定义的NUMLINES进行迭代,以循环输出字符串。 { TextOut (hdc, 0, iFy*i, sysmetrics[i].szLabel,lstrlen (sysmetrics[i].szLabel)); /* 第一个输出由于是从0位置开始,所以x轴为0,y轴高度使用字符高度乘以循环次数获得。 */ TextOut (hdc, 22*iCFx, iFy*i, sysmetrics[i].szDesc, lstrlen(sysmetrics[i].szDesc)); /* 第二个输出由于字符串的起始点有了变化所以必须空出第一列字符串的宽度。 22*iCFx的是由第一列最长字符串长度加上两个空位得来,空位只是为了美观而多加上的。 */ SetTextAlign (hdc, TA_RIGHT | TA_TOP); TextOut (hdc, 22*iCFx+40*iLFx, iFy*i, szBuffer,wsprintf (szBuffer, TEXT("%5d"),GetSystemMetrics (sysmetrics[i].Index))); SetTextAlign (hdc, TA_LEFT | TA_TOP); /* 第三个输出主要是输出GetSystemMetrics函数返回的数值,但其中的确有些微妙。 由于变宽字符由左向右对齐数值的很难实现,所以我们换个思维而选择从右向左的方式对齐。 调用SetTextAlign (hdc, TA_RIGHT | TA_TOP);可以让对齐方式转变为右向左对齐。 22*iCFx+40*iLFx,包含了前两列的宽度,它的值成为了第三列字符串的x轴结束位置。 输出完成后调用SetTextAlign (hdc, TA_LEFT | TA_TOP);让对齐方式恢复默认,以便下一行的输出。 */ } EndPaint (hwnd, &ps); MessageBox(NULL,"此对话框以下遮盖的部分需要更新!","覆盖区域",0); return 0; case WM_DESTROY: // 当窗口销毁的时候会返回此信息,比如ALT+F4或关闭窗口的时候,系统默认调用DestroyWindow()函数撤消窗口。 PostQuitMessage (0); return 0; } return DefWindowProc (hwnd, message, wParam, lParam);//处理不于处理的消息 }