什么是GDI? GDI是Graphics Device Interface的缩写,含义是图形设备接口,它的主要任务是负责系统与绘图程序之间的信息交换,处理所有Windows程序的图形输出。 在Windows操作系统下,绝大多数具备图形界面的应用程序都离不开GDI,我们利用GDI所提供的众多函数就可以方便的在屏幕、打印机及其它输出设备上输出图形,文本等操作。GDI的出现使程序员无需要关心硬件设备及设备驱动,就可以将应用程序的输出转化为硬件设备上的输出,实现了程序开发者与硬件设备的隔离,大大方便了开发工作。 GDI是如何实现输出的? 要想在屏幕或者其它输出设备上输出图形或者文字,那么我们就必须先获得一个称为设备描述表( DC:Device Context)的对象的句柄,以它为参数,调用各种GDI函数实现各种文字或图形的输出。 设备描述表是GDI内部保存数据的一种数据结构,此结构中的属性内容与特定的输出设备(显示器,打印机等)相关,属性定义了GDI函数的工作细节,在稍后我们将看到如何使用TextOut函数输出文字,在这里属性确定了文字的颜色,x坐标和y坐标映射到窗口显示区域的方式等。 设备描述表句柄一旦获得,那么系统将使用默认的属性值填充设备描述表结构。 如果有必要,我们可以使用一些GDI函数获取和改变设备描述表中的属性值。 什么是有效矩形什么是无效矩形? 当应用程序接受到WM_PAINT消息后通常就准备更新正个显示区域,但是通常只需要更新一个比较小的区域而不是整个区域,这种情况通常出现在当应用程序的主窗口的一部分被一个对话框覆盖,需要重画的只是被覆盖的矩形区域(见下图)。 EXE示例程序下载:点击这里下载(90K, winzip压缩文件) 阴影以下的部分就是需要更新的矩形区域,该区域就是我们所说的无效区域,正是因为此区域的存在,系统才会向消息队列中放入一个WM_PAINT消息。 Windows内部为每个窗口都保留了一个绘图结构(PAINTSTRUCT),它包含了包围无效区域的最小矩形的坐标和一些其它信息,需要注意的是当窗口消息处理函数在处理WM_PAINT消息之前显示区域中出现了另一个无效区域,那么Windows会计算出一个包围两个无效区域的新无效区域,并把这种变化保存在绘图结构(PAINTSTRUCT)中,Windows是不会同时把多个WM_PAINT消息同时放到消息队列中的。 窗口消息处理函数是通过调用InvalidateRect函数使窗口显示区域内的矩形变为无效的,如果消息队列中已经存在一个WM_PAINT消息,那么Windows将计算出新的无效矩形,在接收到WM_PAINT消息的时候,窗口消息处理函数可以获得无效矩形的座标,通过调用GetUpdateRect,可以在任何时候获得这些坐标。 如何获取或释放设备描述表句柄? 当应用程序需要绘图的时候,必须先获取设备描述表句柄,绘图操作结束后必须释放设备描述表句柄。我们有两种方法获取和释放设备描述表句柄。 1. 使用BeginPaint和Endpaint函数 通常是在应用程序接收到WM_PAINT消息,也就是需要更新窗口的显示区域的时候调用BeginPaint函数获取设备描述表句柄的,使用完后调用Endpaint函数释放设备描述表句柄。 他们的函数原型为: HDC BeginPaint(
从上面BeginPaint函数的原形中我们可以看到需要一个PAINTSTRUCT结构对象的内存地址,PAINTSTRUCT结构包含在WinUser.h头文件中。 定义如下: typedef struct tagPAINTSTRUCT { 事实上当程序调用BeginPaint函数的时候,Windows会自动的填写此结构的各个属性,而程序作者只需要关心前三个属性。 第一个属性hdc表示当前的设备描述表句柄。 第二个属性fErase来说,多数情况下它是被标记成FALSE(0)的,这表示Windows已经擦除了无效矩形的背景,这个擦除动作是是在BeginPaint函数中发生的,而擦除背景用的画刷则是WNDCLASS结构中的hbrBackground属性指定的画刷来擦除背景的,在很多情况下可能程序作者想自己定义一些插除行为,那么可以通过响应消息队列中的WM_ERASEBKGND消息来完成。 第三个属性rcPaint则表示无效矩形座标,它定义了无效矩形的边界。 RECT结构可以在WinDef.h头文件中找到。 定义如下: typedef struct tagRECT 注意,我们在前面提到了InvalidateRect函数,并已经清楚了调用它可以让窗口显示区域内的矩形变为无效,那么我们就可以在处理WM_PAINT消息的时候通过调用它实现在无效矩形外绘图,该调用是在调用BegingPaint函数之前调用的。 使用方法: InvalidateRect(hwnd,NULL,TRUE); 通过上面代码的调用我们让整个显示区域变为了无效,并擦除背景,要注意的是,最后一个参数如果为FALSE,则不擦除背景,原有的东西将保留在原处,这通常是在接受到WM_PAINT消息的时候而不考虑rcPaint属性的情况下简单的重绘整个显示区域最方便的方法,例如,在显示区域内我们输出了一个图形,这个图形的一小部分落在了无效矩形区域内,而这就让绘制这个图形的无效部分变的没有意义,这时就需要重绘整个图形,因为在调用BeginPaint函数传回设备描述表句柄的时候,Windows不会绘制rcPaint也就是无效矩形以外的任何部分。 对于InvalidateRect函数的详细举例,我们将在以后的章节中看到。 2. 使用GetDC和ReleaseDC函数 在很多情况下我们可能需要在接收到非WM_PAINT消息的时候获取设备描述表句柄,通过调用GetDC函数我们可以获得设备描述表句柄,因为程序作者可能要使用设备描述表句柄完成其它工作,例如获得设备描述表属性,或者修改设备描述表属性值等,在最后我们与第一种方法一样要释放句柄,通过调用ReleaseDC函数完成工作。 他们的函数原型为: HDC GetDC(
两种方法的区别: <1> 使用BeginPaint函数获得的的操作区域是显示区域中的无效矩形区域,接下来绘图操作只能在窗口的无效区域范围内进行,无效区域以外的区域将被忽略不能进行操作,而GetDC函数获得的操作区域则是整个窗口的显示区域,之后的操作可以在任何部分进行,而不只限制在无效区域。 <2> BeginPaint函数会自动把无效区域变成有效的区域,而GetDC函数则不会将任何无效区域变得有效,必须强行调用ValidateRect函数,并把第二个参数设置为NULL来完成。 最后我们给出一个可执行程序的例子,当应用程序执行的时候会有一个对话框出现,当你拉动这个对话框的时候又会出现同样的另一个对话框,这就证明了窗口的覆盖会造成无效矩形的出现,系统将会发送WM_PAINT消息。 现在我们对GDI的基础概念有了一定的了解,下面我们将对GDI一些常用的函数进行学习。 TextOut函数TextOut函数的作用是把指定的字符串输出在我们指定的屏幕位置上。 函数原型: BOOL TextOut( 第一参数是设备描述表句柄,它既可以是通过BeginPaint函数获得的,也可以是通过GetDC函数获得的,需要提一下的是,设备描述表中的属性控制了显示的字符串的一些细节特征,比如字体、字体颜色、文字背景等,但要注意的是保存在设备描述表属性中的文字背景颜色和WNDCLASS结构中的屏幕背景是有区别的,文字背景指的是紧靠字符周围的矩形空间,又叫做字符框。而窗口背景则是一个画刷,Windows用它来擦除显示区域,它不是设备描述表结构中的一部分。 第二和第三个参数定义了显示区域内字符串的开始位置,x是水平位置,y是垂直位置,字符串第一个字符位于坐标点(x,y),在设备描述表属性中,原点(x,y)均为为0,是显示区域的左上角,对于坐标来说,坐标的映射方式的不同决定了单位的不同,在通常情况下传递给函数的坐标被称为逻辑坐标,Windows有许多坐标映射方式,它们是用来控制GDI函数指定的逻辑坐标转换为显示器的实际像素坐标的方式。映射方式在设备描述表的属性中定义,默认的映射方式是MM_TEXT,我们可以在WinGdi.h头文件中找到。在MM_TEXT映射模式下,逻辑单位于实际单位都相同,都是像素,对于坐标来说,x的值从左向右递增,y的值则从上向下递增(见下图),MM_TEXT坐标系与Windows在PAINTSTURCT结构中定义的无效矩形所使用的坐标系相同。 第四个参数是指向要输出字符串的长指针。 第五个参数是要输出的字符串的实际长度。 在Windows下输出文字并不如我们所想象的那么容易,在前面我们已经知道输出文字和坐标有关,我们为了精确的输出文字,就必须对系统的字体和字符大小有进一步的认识。 系统字体 对于输出字符串的函数TextOut来说,在默认情况下设备描述表属性中使用的是系统字体(SYSTEM_FONT),系统字体是Windows用来在标题栏,功能表和对话框中显示字符串所使用的默认字体。 但值得注意的是,字体如果按宽度来区分的话,大致可以分为两类: 1. 等宽字体 等宽字体意味着所有的字符宽度都是一致的,但随着计算技术的不断发展和推广,这种等宽字体就不再能够满足需要,于是变宽字体出现了,变宽字体不同的字符宽度都不一定相同。 系统字体是一种点阵字体,字体被定义成了一个个的像素点,字体的确切大小取决于显示器的大小(分辨率的大小)。 字符大小 如果要使用TextOut函数显示多行文字,那么就必须确定字体字符的大小,字体的高度确定了下一行字符的显示位置,字体的宽度确定了下一列的显示位置。 屏幕的分辨率和字符大小是确定如何显示字符的主要依据,为了获得当前系统上各种与视觉属性相关的信息,我们可以调用GetSystemMetrics函数获取,调用GetTextMetrics函数可以获取字体大小。 以下是这两个函数的原形以及参数的详细定义: int GetSystemMetrics( GetSystemMetrics函数是完成Windows图形输出的重要函数,它返回Windows中各种与视觉属性相关的信息,该函数需要只需要一个参数,它是一个索引,这些索引是在Windows头文件中定义的一些常量,这些常量分别指定了不同的与视觉相关的设备属性,这些索引的多少取决与Windows的版本。 以下是这些索引的含义:
BOOL GetTextMetrics( GetTextMetrics函数利用当前选择字体的各种度量值来填充由lptm参数所指向的缓冲区,函数如果运行成功则返回TURE,失败则返回FALSE。 TEXTMETRIC结构定义在WinGdi.h头文件中。 结构如下: typedef struct tagTEXTMETRICA typedef struct tagTEXTMETRICW 该结构拥有大约20个属性,这些属性的值的单位取决于设备描述表的映射方式,默认情况下是MM_TEXT,对于TextOut输出来说我们只需要用到前7个,他们的单位是像素。 LONG tmHeight; // 字符基准线上下最大纵向高度,是tmAscent与tmDescent之和。 字符的纵向大小是由TEXTMETRIC结构的前五个属性决定的。 具体情况见下图: 字体的大小是取决于当前屏幕的分辨率或是所选字体本身的默认大小的,在编写应用程序的时候不要把字体的大小以猜想的方式固定了,因为字体的大小是可变化的,利用GetTextMetrics函数动态的获取它们才是正确的。 格式化输出 如果要使用TextOut函数在Windows应用程序中输出多行文字,我们就必须先取得字符的宽度和高度,通常我们在窗口消息处理函数中处理WM_CREATE消息的时候调用GetTextMetrics函数来获取他们,之所以放到WM_CREATE的部分调用,是因为当系统启动后,系统字体的大小就不会发生改变,我们只需要调用一次GetTextMetrics就可以了。 具体方法如下: 大家可能对上面代码中的,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 <windows.h> #include <stdio.h> #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<NUMLINES; 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);//处理不于处理的消息 }
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; |
|
来自: fisher60 > 《windows api》