鸡啄米在上一节中讲了CFont字体类,本节主要讲解文本输出的方法和实例。

文本输出过程

在文本输出到设备以前,我们需要确定字体、字体颜色和输出的文本内容等信息。Windows窗口的客户区由应用程序管理,所以我们还要在应用程序中控制输出文本的格式,例如后续字符的位置、换行等格式。

由此,文本的输出过程大致包括确定字体信息、格式化文本和执行输出操作三个步骤。下面分别讲解。

1、确定字体信息

文本在输出以前应该先确定字体信息,或者是当前正在使用的字体,或者是自定义的字体,之后就可以根据确定的字体来显示文本或者利用字体信息来设定文本的格式了,例如,我们可以根据当前字体的字符高度来确定下一行字符在什么位置输出。

自定义字体可以通过CFont类的创建字体的几个成员函数完成。获取当前选择字体的信息可以使用API函数GetTextMetrics实现,此函数的原型如下:

BOOL GetTextMetrics(__in   HDC hdc,__out  LPTEXTMETRIC lptm);

参数hdc为设备上下文的句柄;参数lptm是指向TEXTMETRIC结构体变量的指针,此结构体变量用于接收字体信息。TEXTMETRIC结构体的定义如下:

C++代码
  1. typedef struct tagTEXTMETRIC {
  2. LONG  tmHeight;        // 字符高度
  3. LONG  tmAscent;        // 字符基线以上的高度
  4. LONG  tmDescent;       // 字符基线以下的高度
  5. LONG  tmInternalLeading; // 由tmHeight成员指定的字符高度顶部的空间
  6. LONG  tmExternalLeading; // 行间距
  7. LONG  tmAveCharWidth;  // 字符的平均宽度
  8. LONG  tmMaxCharWidth;  // 字符的最大宽度
  9. LONG  tmWeight;        // 字符的粗度
  10. LONG  tmOverhang;      // 合成字体间附加的宽度
  11. LONG  tmDigitizedAspectX; // 为输出设备设计的x轴尺寸
  12. LONG  tmDigitizedAspectY; // 为输出设备设计的y轴尺寸
  13. TCHAR tmFirstChar;     // 字体中第一个字符值
  14. TCHAR tmLastChar;      // 字体中最后一个字符值
  15. TCHAR tmDefaultChar;   // 替换字体中没有的字符
  16. TCHAR tmBreakChar;     // 作为分隔符的字符
  17. BYTE  tmItalic;        // 非0则表示字体为斜体
  18. BYTE  tmUnderlined;    // 非0则表示字体有下划线
  19. BYTE  tmStruckOut;     // 非0则表示字符带有删除线
  20. BYTE  tmPitchAndFamily;// 字体间距和字体族
  21. BYTE  tmCharSet;       // 字符集
  22. } TEXTMETRIC, *PTEXTMETRIC;

 2、格式化文本

格式化文本一般包括两种,一种是确定文本行中后续文本的位置,另一种是确定换行时下一行文本的位置。

确定后续文本的位置

一般我们可以先获取当前字符串的宽度,根据此宽度确定文本行中后续文本的位置。当前字符串的宽度可以通过API函数GetTextExtentPoint32获得。GetTextExtentPoint32函数的原型如下:

BOOL GetTextExtentPoint32(__in   HDC hdc,__in   LPCTSTR lpString,__in   int c,__out  LPSIZE lpSize);

参数hdc为设备上下文的句柄;参数lpString为指向文本字符串缓存的指针,此字符串不是必须以结束符结尾的,因为参数c指定了长度;参数c为lpString指向的字符串的长度;参数lpSize为指向SIZE结构体变量的指针,此SIZE结构体变量用于接收字符串的宽度和高度信息。SIZE结构体定义如下:

C++代码
  1. typedef struct tagSIZE {
  2. LONG cx;   // 宽度
  3. LONG cy;   // 高度
  4. } SIZE, *PSIZE;

已知本字符串的起始水平坐标和宽度,两者相加即是后续文本的起始坐标。

确定换行时下一行文本的位置

由GetTextMetrics函数获取了当前字体的信息并存入TEXTMETRIC结构体后,通过计算当前文本行的垂直坐标、当前字体的高度和行间距之和,就可以得到换行时下一行的垂直坐标。

3、执行文本输出操作

最后,通过API函数TextOut执行文本输出操作。TextOut函数的原型如下:

BOOL TextOut(__in  HDC hdc,__in  int nXStart,__in  int nYStart,__in  LPCTSTR lpString,__in  int cbString);

参数hdc为设备上下文的句柄;参数nXStart为起始点x坐标;参数nYStart为起始点y坐标;参数lpString为要输出的文本字符串;参数cbString为字符串中要输出的字符的数量。

当然也可以使用设备上下文类CDC的成员函数TextOut来输出,CDC::TextOut函数的两种重载形式如下:

virtual BOOL TextOut(int x,int y,LPCTSTR lpszString,int nCount);
       BOOL TextOut(int x,int y,const CString& str);

参数x指定文本起始点的x坐标;参数y指定文本起始点的y坐标;参数lpszString为要输出的文本字符串;参数nCount指定字符串中的字节个数;参数str为包含要输出的字符的CString对象。

字体和文本输出的应用实例

鸡啄米下面给大家演示一个简单的关于字体和文本输出的实例。功能就是实现两个字符串分别在水平方向和垂直方向上定时滚动。实现步骤如下:

1、创建一个基于对话框的MFC工程,名字设置为“Example48”。

2、在自动生成的对话框模板IDD_EXAMPLE48_DIALOG中,删除“TODO: Place dialog controls here.”静态文本框

3、在Example48Dlg.h文件中为CExample48类添加成员变量:

C++代码
  1. int m_nTextX;   // 水平滚动文本的起始点的x坐标
  2. int m_nTextY;   // 垂直滚动文本的起始点的y坐标
  3. CFont m_newFont;   // 新字体
  4. CFont *m_pOldFont; // 选择新字体之前的字体

4、在CExample48Dlg类的构造函数中,初始化新添加的成员变量:

C++代码
  1. CExample48Dlg::CExample48Dlg(CWnd* pParent /*=NULL*/)
  2. : CDialogEx(CExample48Dlg::IDD, pParent)
  3. {
  4. m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
  5. m_nTextX = 260;
  6. m_nTextY = 10;
  7. m_pOldFont = NULL;
  8. }

5、在CExample48Dlg对话框初始化函数中,创建新的字体,并开启定时器

C++代码
  1. BOOL CExample48Dlg::OnInitDialog()
  2. {
  3. CDialogEx::OnInitDialog();
  4. // Add "About..." menu item to system menu.
  5. // IDM_ABOUTBOX must be in the system command range.
  6. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
  7. ASSERT(IDM_ABOUTBOX < 0xF000);
  8. CMenu* pSysMenu = GetSystemMenu(FALSE);
  9. if (pSysMenu != NULL)
  10. {
  11. BOOL bNameValid;
  12. CString strAboutMenu;
  13. bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
  14. ASSERT(bNameValid);
  15. if (!strAboutMenu.IsEmpty())
  16. {
  17. pSysMenu->AppendMenu(MF_SEPARATOR);
  18. pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  19. }
  20. }
  21. // Set the icon for this dialog.  The framework does this automatically
  22. //  when the application's main window is not a dialog
  23. SetIcon(m_hIcon, TRUE);         // Set big icon
  24. SetIcon(m_hIcon, FALSE);        // Set small icon
  25. // TODO: Add extra initialization here
  26. // 创建一种新的字体(18点,隶书)
  27. m_newFont.CreatePointFont(180, _T("隶书"));
  28. // 设置定时器,定时时间为200ms
  29. SetTimer(1,200,NULL);
  30. return TRUE;  // return TRUE  unless you set the focus to a control
  31. }

6、修改CExample48Dlg::OnPaint()函数,如果窗口没有最小化就在指定的位置输出文本,即在OnPaint函数中if(IsIconic())对应的else大括号内添加相应代码。CExample48Dlg::OnPaint()函数修改如下:

C++代码
  1. void CExample48Dlg::OnPaint()
  2. {
  3. if (IsIconic())
  4. {
  5. CPaintDC dc(this); // device context for painting
  6. SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
  7. // Center icon in client rectangle
  8. int cxIcon = GetSystemMetrics(SM_CXICON);
  9. int cyIcon = GetSystemMetrics(SM_CYICON);
  10. CRect rect;
  11. GetClientRect(&rect);
  12. int x = (rect.Width() - cxIcon + 1) / 2;
  13. int y = (rect.Height() - cyIcon + 1) / 2;
  14. // Draw the icon
  15. dc.DrawIcon(x, y, m_hIcon);
  16. }
  17. else
  18. {
  19. CPaintDC dc(this); // device context for painting
  20. // 设置m_newFont对象的字体为当前字体,并将之前的字体指针保存到m_pOldFont
  21. m_pOldFont = (CFont*)dc.SelectObject(&m_newFont);
  22. // 设置
  23. dc.SetBkMode(TRANSPARENT); //设置背景为透明!
  24. // 设置文本颜色为红色
  25. dc.SetTextColor(RGB(255,0,0));
  26. // 在指定位置输出文本
  27. dc.TextOut(m_nTextX,10,_T("欢迎来到鸡啄米!"));
  28. // 设置文本颜色为绿色
  29. dc.SetTextColor(RGB(0,255,0));
  30. // 在指定位置输出文本
  31. dc.TextOut(10,m_nTextY,_T("谢谢关注www.jizhuomi.com"));
  32. // 恢复以前的字体
  33. dc.SelectObject(m_pOldFont);
  34. CDialogEx::OnPaint();
  35. }
  36. }

7、在Class View类视图中找到CExample48Dlg,右键点Properties,显示出其属性页,在属性页工具栏上点击Messages按钮,找到WM_TIMER消息,添加消息响应函数CExample48Dlg::OnTimer(UINT_PTR nIDEvent),并在此函数中修改两个文本输出的坐标位置。

C++代码
  1. void CExample48Dlg::OnTimer(UINT_PTR nIDEvent)
  2. {
  3. // TODO: Add your message handler code here and/or call default
  4. LOGFONT logFont;
  5. // 获取m_newFont字体的LOGFONT结构
  6. m_newFont.GetLogFont(&logFont);
  7. // 将m_nTextX的值减5
  8. m_nTextX -= 5;
  9. // 如果m_nTextX小于10,则文本“欢迎来到鸡啄米”回到起始位置
  10. if (m_nTextX < 10)
  11. m_nTextX = 260;
  12. // 将m_nTextY的值加一个字符高度
  13. m_nTextY += abs(logFont.lfHeight);
  14. // 如果m_nTextY大于260,则文本“谢谢关注www.jizhuomi.com”回到起始位置
  15. if (m_nTextY >260)
  16. m_nTextY = 10;
  17. // 使窗口客户区无效,之后就会重绘
  18. Invalidate();
  19. CDialogEx::OnTimer(nIDEvent);
  20. }

到这一步,两个文本就可以分别在水平和垂直方向滚动了。鸡啄米再简单解释下这个过程:程序刚启动时,会调用OnPaint函数,在初始位置绘出两个文本,然后每次到了定时器的定时时间后,会执行OnTimer函数,修改两个文本的坐标值,并通过Invalidate使窗口重绘,又会重新调用OnPaint函数绘制两个文本。这样通过定时修改坐标值就实现了两个文本的滚动效果。

8、运行程序,最终的效果如下图:

好了,本节就讲到这里了,最后的实例大家可以自己丰富下它的功能,看看效果。鸡啄米谢谢大家的支持。

转自:http://www.jizhuomi.com/software/181.html

VS2010/MFC编程入门之四十八(字体和文本输出:文本输出)的更多相关文章

  1. VS2010/MFC编程入门之四十七(字体和文本输出:CFont字体类)

    上一节中鸡啄米讲了MFC异常处理,本节的主要内容是字体CFont类. 字体简介 GDI(Graphics Device Interface),图形设备接口,是Windows提供的一些函数和结构,用于在 ...

  2. VS2010/MFC编程入门之三十八(状态栏的使用详解)

    上一节中鸡啄米讲了工具栏的创建.停靠与使用,本节来讲解状态栏的知识. 状态栏简介 状态栏相信大家在很多窗口中都能见到,它总是用来显示各种状态.状态栏实际上也是一个窗口,一般分为几个窗格,每个窗格分别用 ...

  3. VS2010/MFC编程入门之十八(对话框:字体对话框)

    鸡啄米在上一节为大家讲解了文件对话框的使用,本节则主要介绍字体对话框如何应用. 字体对话框的作用是用来选择字体.我们也经常能够见到.MFC使用CFontDialog类封装了字体对话框的所有操作.字体对 ...

  4. VS2010/MFC编程入门之四十六(MFC常用类:MFC异常处理)

    上一节中鸡啄米讲了CFile文件操作类,本节主要来说说MFC异常处理. 在鸡啄米C++编程入门系列的最后一节鸡啄米:C++编程入门系列之五十(异常处理)中,鸡啄米讲了C++标准异常的处理机制,如果你还 ...

  5. VS2010/MFC编程入门之四十九(图形图像:CDC类及其屏幕绘图函数)

    上一节中鸡啄米讲了文本输出的知识,本节的主要内容是CDC类及其屏幕绘图函数. CDC类简介 CDC类是一个设备上下文类. CDC类提供了用来处理显示器或打印机等设备上下文的成员函数,还有处理与窗口客户 ...

  6. VS2010/MFC编程入门之四十五(MFC常用类:CFile文件操作类)

    上一节中鸡啄米讲了定时器Timer的用法,本节介绍下文件操作类CFile类的使用. CFile类概述 如果你学过C语言,应该知道文件操作使用的是文件指针,通过文件指针实现对它指向的文件的各种操作.这些 ...

  7. VS2010/MFC编程入门之四十四(MFC常用类:定时器Timer)

    前面一节鸡啄米讲了CTime类和CTimeSpan类的使用,本节继续讲与时间有关的定时器.定时器并不是一个类,主要考虑到,提起时间的话就不能不说定时器,所以就把它放到CTime和CTimeSpan之后 ...

  8. VS2010/MFC编程入门之四十四:定时器Timer

    前面一节鸡啄米讲了CTime类和CTimeSpan类的使用,本节继续讲与时间有关的定时器.定时器并不是一个类,主要考虑到,提起时间的话就不能不说定时器,所以就把它放到CTime和CTimeSpan之后 ...

  9. VS2010/MFC编程入门之四十二(MFC常用类:CString类)

    上一节鸡啄米讲了分割窗口的有关知识,本节开始讲解MFC的一些常用类,先来说说CString类. CString类简介 CString类作为MFC的常用类,当之无愧.可以这样说,只要是从事MFC开发,基 ...

随机推荐

  1. 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十:PS/2模块④ — 普通鼠标

    实验十:PS/2模块④ - 普通鼠标 学习PS/2键盘以后,接下来就要学习 PS/2 鼠标.PS/2鼠标相较PS/2键盘,驱动难度稍微高了一点点,因为FPGA(从机)不仅仅是从PS/2鼠标哪里读取数据 ...

  2. IOS 7 更改导航栏文字到白色

    To hide status bar in any viewcontroller: -(BOOL) prefersStatusBarHidden { return YES; } To change t ...

  3. PCB 铺铜 转载

    所谓覆铜,就是将PCB上闲置的空间作为基准面,然后用固体铜填充,这些铜区又称为灌铜.敷铜的意义在于,减小地线阻抗,提高抗干扰能力:降低压降,提高电源效率:还有,与地线相连,减小环路面积.如果PCB的地 ...

  4. H3C系列之三层交换机文件管理

    笔者本篇文章所用h3c交换机的型号为三层交换机S3600-28TP-SI 对于文件的操作一般都在用户视图下操作,常见的有如下一些操作: 1.查看操作,常用的查看操作可以使用如下命令: <H3C& ...

  5. 【BZOJ3413】匹配 离线+后缀树+树状数组

    [BZOJ3413]匹配 Description Input 第一行包含一个整数n(≤100000). 第二行是长度为n的由0到9组成的字符串. 第三行是一个整数m. 接下来m≤5·10行,第i行是一 ...

  6. springmvc如何访问到静态的文件,如jpg,js,css

    如何你的DispatcherServlet拦截"*.do"这样的有后缀的URL,就不存在访问不到静态资源的问题. 如果你的DispatcherServlet拦截"/&qu ...

  7. windows下java开发资料汇总

    开发环境搭建:   (1) java开发环境配置    (2) maven环境快速搭建        项目部署:   (1) Eclipse中项目部署方法   (2) 使用Eclipse构建Maven ...

  8. vs附加调试 w3p进程没有名称

    解决: 把vs用管理员运行

  9. JNUOJ 1187 - 哨兵

    Time Limit: 10000ms Memory Limit: 262154KB 64-bit integer IO format: %lld      Java class name: Main ...

  10. TACOTRON:端到端的语音合成

    tacotron主要是将文本转化为语音,采用的结构为基于encoder-decoder的Seq2Seq的结构.其中还引入了注意机制(attention mechanism).在对模型的结构进行介绍之前 ...