上一篇我们展示了如何使用Windows SDK创建基本控件,本篇来讨论如何输出文本字符。

在使用Win32编程时,我们常常要输出文本到窗口上,Windows所有的文本字符或者图形输出都是通过图形设备接口(GDI)进行的,Windows的三大核心组件之一的GDI32.dll封装了所有的文本和图像输出。

  • GDI基本知识

Windows下要绘图和输出文本,都是通过GDI(Graphics Device Interface,图形设备接口)完成的,GDI是windows在绘制图文时的设备上下文环境,包括画笔、画刷、字体、位图等多种与绘制有关的对象。设备环境(DC)在绘制中起至关重要的作用。几乎所有的绘制(包括图形和文本)都与设备环境相关,注意“环境”的意义,就跟我们在画布上绘画和写字一样,绘制时的画布是哪个,用的什么笔,什么颜色,填充整个画布时用的什么刷子等等,这就是我们的绘制时的环境,而Windows绘图的DC设备上下文就是一样的道理。设备环境句柄(HDC)就是用来描述DC的句柄,可以说,只要有了这个句柄,就具备了在窗口上输出图形和文本的条件。你获得了窗口客户区的HDC,就可以在窗口客户区上画;你获得了窗口的非客户区HDC,就可以在它上面画;你获得了桌面HDC,就可以直接在桌面上画……

获取设备环境句柄的方法有两种:一是处理WM_PAINT消息时,通过BeginPaint函数返回。另外一种就是通过GetDC、GetWindowDC的API函数获取。

  • 通过WM_PAINT消息获取DC

Windows在检测到需要重新绘制或者刷新窗口时,会主动要求处理WM_PAINT消息。比如在如下情况下就会主动求处理:

  1. 用户移动一个窗口,导致原来被盖住的部分窗口显示出来。
  2. 用户调整窗口的大小,并且窗口风格类型设置为CS_HREDRAW和CS_VREDRAW。
  3. 程序调用ScrollWindow或者ScrollDC函数滚动客户区。
  4. 程序调用InvalidateRect或者InvalidateRgn函数,该函数显示生产一条WM_PAINT消息。

我们可以在该消息中完成图文绘制,该消息的处理具有特定的格式,必须在实际绘制前调用BeginPaint,在绘制完成后调用EndPaint函数,也就是说我们需要把所有绘制的功能都放到这两个函数之间,并且HDC也只能在这之间使用,不能保存起来在其它地方使用。使用WM_PAINT有一个好处,就是windows会自己计算哪些区域需要更新,也就是说只有真正变化的地方才会更新,这样更新的代价会降低到最小。

  • 通过API函数获取HDC

我们还可以通过GetDC、GetWindowDC函数来获取HDC,但是要注意,通过这个来获取的HDC,可以保存起来在其它时候使用,但是要记住一旦窗口有更新,必须想办法重新绘制,否则就会消失了。最后在使用完毕后需要调用ReleaseDC来释放,否则会造成资源泄露。

  • 创建特定字体

我们平时最常见的文本输出是不需要自己创建字体的,因为常见的对象都有系统预定义好的。如果想输出点特殊(非系统预定义的)字体,就需要我们创建并自动选入设备环境。创建字体主要有CreateFont和CreateFontIndirect,这两个函数的参数都很多,基本一样,具体用法看后面的实例。

  • 实现文本绘制

有了上面的基础,我们就可以通过Windows的API来完成文本输出了,常用的文本输出函数有TextOut、DrawText、DrawTextExt、ExtTextOut等,这些函数基本都有相似的参数,比如hdc,坐标位置,字符串。下面TextOut、DrawText、ExtTextOut为例来说明如何在Windows窗口中如何输出文本,其它请查看MSDN的用法。

#include <windows.h>
#include <tchar.h> static TCHAR szAppName[] = TEXT("Textout");
static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS wndclass; 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 = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName; if (!RegisterClass(&wndclass))
{
MessageBox (NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
return 0;
} hWnd = CreateWindow(szAppName, // window class name
szAppName, // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
400, // initial x size
300, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters ShowWindow(hWnd, iCmdShow);
UpdateWindow(hWnd); while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
} return msg.wParam;
} static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT ps; switch (message)
{
case WM_CREATE:
return 0;
case WM_PAINT:
{
RECT rect = {10, 30, 100, 50};
TCHAR str[] = TEXT("English and 中文"); hDC = BeginPaint(hWnd, &ps);
TextOut(hDC, 10, 10, str, _tcslen(str)); SetTextColor(hDC, RGB(255,0,0));
DrawText(hDC, str, -1, &rect, DT_LEFT|DT_VCENTER); SetTextColor(hDC, RGB(0,255,0));
INT dx[] = {8,8,8,8,16,8,8,8,16,8,8,8,10};
ExtTextOut(hDC, 10, 50, 0, &rect, str, _tcslen(str), dx); SetTextColor(hDC, RGB(0,0,255));
rect.right = 110;
rect.top = 70;
rect.bottom = 82;
ExtTextOut(hDC, 10, rect.top, ETO_CLIPPED, &rect, str, _tcslen(str), dx);
HFONT hFont = CreateFont(96, // nHeight, 所创建字体的字符高度
0, // nWidth, 字体的字符平均宽度
200, // nEscapement, 字符输出方向与水平向右的方向所成角度,以0.1度为单位
0, // nOrientation, 字符与基线的角度,以0.1度为单位
FW_BOLD, // nWeight, 字符颜色的深浅度
TRUE, // bItalic, 斜体属性标志(FALSE:正常字体,TRUE:斜体)
FALSE, // bUnderline, 下划线属性标志(FALSE:无下划线,TRUE:有下划线)
FALSE, // cStrikeOut, 删除线属性标志(FALSE:无删除线,TRUE:有删除线)
ANSI_CHARSET, // nCharSet, 字符集标识0:ANSI字符集,1:系统缺省字符集
OUT_DEFAULT_PRECIS, // nOutPrecision, 输出精度
CLIP_DEFAULT_PRECIS, // nClipPrecision, 剪切精度
DEFAULT_QUALITY, // nQuality, 输出品质
DEFAULT_PITCH|FF_SWISS, // nPitchAndFamily, 字符间距
TEXT("Arial")); // lpszFacename, 现有系统TrueType字体名称
HFONT hOldFont = (HFONT)SelectObject(hDC, hFont);
SetBkMode(hDC, TRANSPARENT);
SetTextColor(hDC, RGB(0x00, 0xFF, 0xFF));
TextOut(hDC, 0, 150, TEXT("创建Font"), 6);
DeleteObject(hFont);
EndPaint(hWnd, &ps);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0 ;
}
return DefWindowProc (hWnd, message, wParam, lParam);
}

程序运行,点击鼠标左键后效果如下:

程序中的DrawText、ExtTextOut能设置文本输出的矩形范围,超出部分是看不见的,从运行结果我们也可以看出有两行显示不全,就是由于设置的显示范围小的缘故。

另外ExtTextOut函数还可以设置字符的间距,运行结果的第三行就是这种自己设置间距不一样的结果。

本程序还用CreateFont函数创建了一个斜体、右上排列的文本串。通过上例,我们把常用的文本输出作为实例展示给大家,只要好好对照实例代码,在结合MSDN的说明,再加上本系列的第一篇的Windows编程基本框架,一定可以掌握好Windows编程的基本文本输出。

关注微信公众平台:程序员互动联盟(coder_online),你可以第一时间获取原创技术文章,和(java/C/C++/Android/Windows/Linux)技术大牛做朋友,在线交流编程经验,获取编程基础知识,解决编程问题。程序员互动联盟,开发人员自己的家。

转载请注明出处,谢谢合作!

【Windows编程】系列第三篇:文本字符输出的更多相关文章

  1. Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) JAVA日志的前世今生 .NET MVC采用SignalR更新在线用户数 C#多线程编程系列(五)- 使用任务并行库 C#多线程编程系列(三)- 线程同步 C#多线程编程系列(二)- 线程基础 C#多线程编程系列(一)- 简介

    Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) 一.前言 由于本篇文章较长,所以下面给出内容目录方便跳转阅读,当然也可以用博客页面最右侧的文章目录导航栏进行跳转查阅. 一.前言 ...

  2. javascript面向对象系列第三篇——实现继承的3种形式

    × 目录 [1]原型继承 [2]伪类继承 [3]组合继承 前面的话 学习如何创建对象是理解面向对象编程的第一步,第二步是理解继承.本文是javascript面向对象系列第三篇——实现继承的3种形式 [ ...

  3. 深入理解javascript函数系列第三篇——属性和方法

    × 目录 [1]属性 [2]方法 前面的话 函数是javascript中的特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本 ...

  4. 深入理解javascript函数系列第三篇

    前面的话 函数是javascript中特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本文是深入理解javascript函数 ...

  5. 深入理解javascript作用域系列第三篇——声明提升(hoisting)

    × 目录 [1]变量 [2]函数 [3]优先 前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javasc ...

  6. Webpack系列-第三篇流程杂记

    系列文章 Webpack系列-第一篇基础杂记 Webpack系列-第二篇插件机制杂记 Webpack系列-第三篇流程杂记 前言 本文章个人理解, 只是为了理清webpack流程, 没有关注内部过多细节 ...

  7. 深入理解javascript作用域系列第三篇

    前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javascript作用域系列第三篇——声明提升(hois ...

  8. 并发编程之第三篇(synchronized)

    并发编程之第三篇(synchronized) 3. 自旋优化 4. 偏向锁 撤销-其它线程使用对象 撤销-调用wait/notify 批量重偏向 批量撤销 5. 锁消除 4.7 wait/notify ...

  9. api (三)文本字符输出 (转)

    在使用Win32编程时,我们常常要输出文本到窗口上,Windows所有的文本字符或者图形输出都是通过图形设备接口(GDI)进行的,Windows的三大核心组件之一的GDI32.dll封装了所有的文本和 ...

随机推荐

  1. 安装Entity Framework【Setup Entity Framework Environment】(EF基础系列篇4)

    Entity Framework 5.0 API是分布在两个地方:NuGet和.NET Framework中,这个.NET framework 4.0/4.5包含EF核心的API,然而通过NuGet包 ...

  2. CodeFirst时使用T4模板(你肯定没用过的笨方法,还望园友指教)

    我们都知道T4模板用于生成相似代码. 在DBFirst和ModelFirst条件下我们很容易从.edmx下获取所有实体类和其名称,并且通过我们定义的模板和某些遍历工作为我们生成所需要的相似代码. 但是 ...

  3. python处理空格脚本

    博客园上传代码时拷贝vs里面的代码不能直接粘贴,否则空格会不符合要求 去掉空格代码 # -*- coding: utf-8 -*- '''打开delSpace.txt文本并删除每行开头的八个空格''' ...

  4. Hadoop JAVA 开发说明

    作为Hadoop程序员,他要做的事情就是: 1.定义Mapper,处理输入的Key-Value对,输出中间结果.2.定义Reducer,可选,对中间结果进行规约,输出最终结果.3.定义InputFor ...

  5. 【Python大系】Python快速教程

    感谢原作者:Vamei 出处:http://www.cnblogs.com/vamei 怎么能快速地掌握Python?这是和朋友闲聊时谈起的问题. Python包含的内容很多,加上各种标准库.拓展库, ...

  6. IT基础架构规划方案二(计算机系统与机房规划规划)

    计算机系统规划       服务器硬件选型规划方案       根据对某集团的实际调研,获取了企业业务应用系统的建设情况,随着企业信息化建设的推进,需要对各种信息化管理系统和应用系统的服务器选型进行选 ...

  7. ElasticSearch+NLog+Elmah实现Asp.Net分布式日志管理

    本文将介绍使用NLOG.Elmah结合ElasticSearch实现分布式日志管理. 一.ElasticSearch简介 ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布 ...

  8. sqlserver性能调优第一步

    相信不少的朋友,无论是做开发.架构的,还是DBA等,都经常听说“调优”这个词.说起“调优”,可能会让很多技术人员心头激情澎湃,也可能会让很多人感觉苦恼,不知道如何入手.当然,也有很多人对此不屑一顾,因 ...

  9. 基于 Eclipse 的 MapReduce 开发环境搭建

    文 / vincentzh 原文连接:http://www.cnblogs.com/vincentzh/p/6055850.html 上周末本来要写这篇的,结果没想到上周末自己环境都没有搭起来,运行起 ...

  10. JAVA理解逻辑程序的书上全部重要的习题

    今天随便翻翻看以前学过JAVA理解逻辑程序的书上全部练习,为了一些刚学的学弟学妹,所以呢就把这些作为共享了. 希望对初学的学弟学妹有所帮助! 例子:升级“我行我素购物管理系统”,实现购物结算功能 代码 ...