找回密码
 注册
搜索
查看: 650|回复: 1

[讨论] windows sdk编程系列文章 ---- 绘制文本

[复制链接]
发表于 2008-4-15 15:19:52 | 显示全部楼层 |阅读模式
windows sdk编程系列文章 ---- 绘制文本

来源:http://edu.teamsourcing.com.cn


本课中,我们将学习如何在窗口的客户区“绘制”字符串。我们还将学习关于“设备环境”的概念。

理论:

Windows 中的文本是一个GUI(图形用户界面)对象。每一个字符实际上是由许多的像素点组成,这些点在有笔画的地方显示出来,这样就会出现字符。这也是为什么我说“绘制”字符,而不是写字符。通常您都是在您应用程序的客户区“绘制”字符串(尽管您也可以在客户区外“绘制”)。Windows 下的“绘制”字符串方法和 Dos 下的截然不同,在 Dos 下,您可以把屏幕想象成 85 x 25 的一个平面,而 Windows 下由于屏幕上同时有几个应用程序的画面,所以您必须严格遵从规范。Windows 通过把每一个应用程序限制在他的客户区来做到这一点。当然客户区的大小是可变的,您随时可以调整。

在您在客户区“绘制”字符串前,您必须从 Windows 那里得到您客户区的大小,确实您无法像在 DOS 下那样随心所欲地在屏幕上任何地方“绘制”,绘制前您必须得到 Windows 的允许,然后 Windows 会告诉您客户区的大小,字体,颜色和其它 GUI 对象的属性。您可以用这些来在客户区“绘制”。

什么是“设备环境”(DC)呢? 它其实是由 Windows 内部维护的一个数据结构。一个“设备环境”和一个特定的设备相连。像打印机和显示器。对于显示器来说,“设备环境”和一个个特定的窗口相连。

“设备环境”中的有些属性和绘图有关,像:颜色,字体等。您可以随时改动那些缺省值,之所以保存缺省值是为了方便。您可以把“设备环境”想象成是Windows 为您准备的一个绘图环境,而您可以随时根据需要改变某些缺省属性。

当应用程序需要绘制时,您必须得到一个“设备环境”的句柄。通常有几种方法。

在 WM_PAINT 消息中使用 BeginPaint
在其他消息中使用 GetDC
CreateDC 建立你自己的 DC
您必须牢记的是,在处理单个消息后你必须释放“设备环境”句柄。不要在一个消息处理中获得 “设备环境”句柄,而在另一个消息处理中在释放它。

我们在Windows 发送 WM_PAINT 消息时处理绘制客户区,Windows 不会保存客户区的内容,它用的是方法是“重绘”机制(譬如当客户区刚被另一个应用程序的客户区覆盖),Windows 会把 WM_PAINT 消息放入该应用程序的消息队列。重绘窗口的客户区是各个窗口自己的责任,您要做的是在窗口过程处理 WM_PAINT 的部分知道绘制什么和何如绘制。

您必须了解的另一个概念是“无效区域”。Windows 把一个最小的需要重绘的正方形区域叫做“无效区域”。当 Windows 发现了一个”无效区域“后,它就会向该应用程序发送一个 WM_PAINT 消息,在 WM_PAINT 的处理过程中,窗口首先得到一个有关绘图的结构体,里面包括无效区的坐标位置等。您可以通过调用BeginPaint 让“无效区”有效,如果您不处理 WM_PAINT 消息,至少要调用缺省的窗口处理函数 DefWindowProc ,或者调用 ValidateRect 让“无效区”有效。否则您的应用程序将会收到无穷无尽的 WM_PAINT 消息。

下面是响应该消息的步骤:

取得“设备环境”句柄
绘制客户区
释放“设备环境”句柄
注意,您无须显式地让“无效区”有效,这个动作由 BeginPaint 自动完成。您可以在 BeginPaint 和 Endpaint 之间,调用所有的绘制函数。几乎所有的GDI 函数都需要“设备环境”的句柄作为参数。

例子: (见光盘FirstWindow2)

下面我们将写一个应用程序,它会在客户区的中心显示一行 "Win32 program, Simple and powerful !"

#include "Windows.h"
#include "tchar.h"

HWND hWinMain;
TCHAR szClassName[] = _T("MyClass");
TCHAR szCaptionMain[] = _T("My First Window!");
TCHAR szText[] = _T("Win32 program, Simple and powerful !");
WNDCLASSEX stdWndClass;

LRESULT CALLBACK ProcWinMain(   HWND hWnd,
                       UINT Msg,
                       WPARAM wParam,
                       LPARAM lParam
)
{
    PAINTSTRUCT stPs;
    RECT stRect;
    HDC hDC;
    switch(Msg)
    {
        case WM_PAINT:
        {
            hDC = BeginPaint(hWnd,&stPs);
            GetClientRect(hWnd,&stRect);
            DrawText(hDC, szText,-1,&stRect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
            EndPaint(hWnd,&stPs);
        }
        break;

        case WM_CLOSE:
        {
            DestroyWindow(hWinMain);
            PostQuitMessage(NULL);
        }
        break;

        default:
            return DefWindowProc(hWnd, Msg, wParam, lParam );
    }
    return 0;
}

int WINAPI WinMain(     HINSTANCE hInstance,
                        HINSTANCE hPrevInstance,
                        LPSTR lpCmdLine,
                        int nCmdShow
)
{
    MSG stMsg;
    WNDCLASSEX stdWndClass;
    RtlZeroMemory(&stdWndClass, sizeof(stdWndClass));
    stdWndClass.hCursor = LoadCursor(0,IDC_ARROW);
    stdWndClass.cbSize = sizeof(stdWndClass);
    stdWndClass.style = CS_HREDRAW|CS_VREDRAW;
    stdWndClass.lpfnWndProc = ProcWinMain;
    stdWndClass.hbrBackground = (HBRUSH)COLOR_WINDOW;
    stdWndClass.lpszClassName = szClassName;
    stdWndClass.hInstance = hInstance;

    RegisterClassEx(&stdWndClass);

    hWinMain = CreateWindowEx(WS_EX_CLIENTEDGE,szClassName,szCaptionMain,        WS_OVERLAPPEDWINDOW,100,100,600,400,NULL,NULL,hInstance,NULL);

    if(!hWinMain)
        return 0;
   
    ShowWindow(hWinMain,SW_SHOWNORMAL);
    UpdateWindow(hWinMain);


    while(GetMessage(&stMsg,NULL,0,0))
    {
        TranslateMessage(&stMsg);
        DispatchMessage(&stMsg);
    }

    return stMsg.wParam;
}

分析:

这里的大多数代码和第三课中的一样。我只解释其中一些不相同的地方。

    PAINTSTRUCT stPs;
    RECT stRect;
    HDC hDC;

这些局部变量由处理 WM_PAINT 消息中的 GDI 函数调用。hdc 用来存放调用 BeginPaint 返回的“设备环境”句柄。ps 是一个 PAINTSTRUCT 数据类型的变量。通常您不会用到其中的许多值,它由 Windows 传递给 BeginPaint,在结束绘制后再原封不动的传递给 EndPaint。rect 是一个 RECT 结构体类型参数,它的定义如下:

typedef struct tagRECT
{
    LONG left;
    LONG top;
    LONG right;
    LONG bottom;
}RECT;

left 和 top 是正方形左上角的坐标。right 和 bottom 是正方形右下角的坐标。客户区的左上角的坐标是 x=0,y=0,这样对于 x=0,y=10 的坐标点就在它的下面。

            hDC = BeginPaint(hWnd,&stPs);
            GetClientRect(hWnd,&stRect);
            DrawText(hDC, szText,-1,&stRect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
            EndPaint(hWnd,&stPs);

在处理 WM_PAINT 消息时,您调用BeginPaint函数,传给它一个窗口句柄和未初始化的 PAINTSTRUCT 型参数。调用成功后在 hDC中得到返回“设备环境”的句柄。下一次,调用 GetClientRect 以得到客户区的大小,大小放在 rect 中,然后把它传给 DrawText。DrawText 的语法如下:

WINUSERAPI int WINAPI DrawTextA(
    HDC hDC,
    LPCSTR lpString,
    int nCount,
    LPRECT lpRect,
    UINT uFormat);

DrawText是一个高层的调用函数。它能自动处理像换行、把文本放到客户区中间等这些杂事。所以您只管集中精力“绘制”字符串就可以了。我们会在下一课中讲解低一层的函数 TextOut,该函数在一个正方形区域中格式化一个文本串。它用当前选择的字体、颜色和背景色。它处理换行以适应正方形区域。它会返回以设备逻辑单位度量的文本的高度,我们这里的度量单位是像素点。让我们来看一看该函数的参数:

hdc: “设备环境”的句柄。
lpString:要显示的文本串,该文本串要么以NULL结尾,要么在nCount中指出它的长短。
nCount:要输出的文本的长度。若以NULL结尾,该参数必须是-1。
lpRect: 指向要输出文本串的正方形区域的指针,该方形必须是一个裁剪区,也就是说超过该区域的字符将不能显示。
uFormat:指定如何显示。我们可以用 or 把以下标志或到一块:
DT_SINGLELINE:是否单行显示。
DT_CENTER:是否水平居中。
DT_VCENTER :是否垂直居中。

结束绘制后,必须调用 EndPaint 释放“设备环境”的句柄。 好了,现在我们把“绘制”文本串的要点总结如下:

必须在开始和结束处分别调用 BeginPaint 和 EndPaint;
在 BeginPaint 和 EndPaint 之间调用所有的绘制函数;
如果在其它的消息处理中重新绘制客户区,您可以有两种选择:
(1)用GetDC和ReleaseDC代替BeginPaint和EndPaint;
(2)调用InvalidateRect或UpdateWindow让客户区无效,这将迫使WINDOWS把WM_PAINT放入应用程序消息队列,从而使得客户区重绘。
 楼主| 发表于 2008-4-22 14:56:58 | 显示全部楼层
网站更新了,教程演示DEMO可以下载,欢迎下载!
点评回复

使用道具 举报

高级模式
B Color Image Link Quote Code Smilies

本版积分规则

Archiver|手机版|小黑屋|52RD我爱研发网 ( 沪ICP备2022007804号-2 )

GMT+8, 2024-10-4 20:29 , Processed in 0.065221 second(s), 17 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表