雞啄米在上一節(jié)中講了CFont字體類,本節(jié)主要講解文本輸出的方法和實(shí)例。

       文本輸出過程

       在文本輸出到設(shè)備以前,我們需要確定字體、字體顏色和輸出的文本內(nèi)容等信息。Windows窗口的客戶區(qū)由應(yīng)用程序管理,所以我們還要在應(yīng)用程序中控制輸出文本的格式,例如后續(xù)字符的位置、換行等格式。

       由此,文本的輸出過程大致包括確定字體信息、格式化文本和執(zhí)行輸出操作三個(gè)步驟。下面分別講解。

       1、確定字體信息

       文本在輸出以前應(yīng)該先確定字體信息,或者是當(dāng)前正在使用的字體,或者是自定義的字體,之后就可以根據(jù)確定的字體來顯示文本或者利用字體信息來設(shè)定文本的格式了,例如,我們可以根據(jù)當(dāng)前字體的字符高度來確定下一行字符在什么位置輸出。

       自定義字體可以通過CFont類的創(chuàng)建字體的幾個(gè)成員函數(shù)完成。獲取當(dāng)前選擇字體的信息可以使用API函數(shù)GetTextMetrics實(shí)現(xiàn),此函數(shù)的原型如下:

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

       參數(shù)hdc為設(shè)備上下文的句柄;參數(shù)lptm是指向TEXTMETRIC結(jié)構(gòu)體變量的指針,此結(jié)構(gòu)體變量用于接收字體信息。TEXTMETRIC結(jié)構(gòu)體的定義如下:

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; // 為輸出設(shè)備設(shè)計(jì)的x軸尺寸   
  12.   LONG  tmDigitizedAspectY; // 為輸出設(shè)備設(shè)計(jì)的y軸尺寸   
  13.   TCHAR tmFirstChar;     // 字體中第一個(gè)字符值   
  14.   TCHAR tmLastChar;      // 字體中最后一個(gè)字符值   
  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、格式化文本

       格式化文本一般包括兩種,一種是確定文本行中后續(xù)文本的位置,另一種是確定換行時(shí)下一行文本的位置。

       確定后續(xù)文本的位置

       一般我們可以先獲取當(dāng)前字符串的寬度,根據(jù)此寬度確定文本行中后續(xù)文本的位置。當(dāng)前字符串的寬度可以通過API函數(shù)GetTextExtentPoint32獲得。GetTextExtentPoint32函數(shù)的原型如下:

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

       參數(shù)hdc為設(shè)備上下文的句柄;參數(shù)lpString為指向文本字符串緩存的指針,此字符串不是必須以結(jié)束符結(jié)尾的,因?yàn)閰?shù)c指定了長(zhǎng)度;參數(shù)c為lpString指向的字符串的長(zhǎng)度;參數(shù)lpSize為指向SIZE結(jié)構(gòu)體變量的指針,此SIZE結(jié)構(gòu)體變量用于接收字符串的寬度和高度信息。SIZE結(jié)構(gòu)體定義如下:

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

       已知本字符串的起始水平坐標(biāo)和寬度,兩者相加即是后續(xù)文本的起始坐標(biāo)。

       確定換行時(shí)下一行文本的位置

       由GetTextMetrics函數(shù)獲取了當(dāng)前字體的信息并存入TEXTMETRIC結(jié)構(gòu)體后,通過計(jì)算當(dāng)前文本行的垂直坐標(biāo)、當(dāng)前字體的高度和行間距之和,就可以得到換行時(shí)下一行的垂直坐標(biāo)。

       3、執(zhí)行文本輸出操作

       最后,通過API函數(shù)TextOut執(zhí)行文本輸出操作。TextOut函數(shù)的原型如下:

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

       參數(shù)hdc為設(shè)備上下文的句柄;參數(shù)nXStart為起始點(diǎn)x坐標(biāo);參數(shù)nYStart為起始點(diǎn)y坐標(biāo);參數(shù)lpString為要輸出的文本字符串;參數(shù)cbString為字符串中要輸出的字符的數(shù)量。

       當(dāng)然也可以使用設(shè)備上下文類CDC的成員函數(shù)TextOut來輸出,CDC::TextOut函數(shù)的兩種重載形式如下:

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

       參數(shù)x指定文本起始點(diǎn)的x坐標(biāo);參數(shù)y指定文本起始點(diǎn)的y坐標(biāo);參數(shù)lpszString為要輸出的文本字符串;參數(shù)nCount指定字符串中的字節(jié)個(gè)數(shù);參數(shù)str為包含要輸出的字符的CString對(duì)象。

       字體和文本輸出的應(yīng)用實(shí)例

       雞啄米下面給大家演示一個(gè)簡(jiǎn)單的關(guān)于字體和文本輸出的實(shí)例。功能就是實(shí)現(xiàn)兩個(gè)字符串分別在水平方向和垂直方向上定時(shí)滾動(dòng)。實(shí)現(xiàn)步驟如下:

       1、創(chuàng)建一個(gè)基于對(duì)話框的MFC工程,名字設(shè)置為“Example48”。

       2、在自動(dòng)生成的對(duì)話框模板IDD_EXAMPLE48_DIALOG中,刪除“TODO: Place dialog controls here.”靜態(tài)文本框。

       3、在Example48Dlg.h文件中為CExample48類添加成員變量:

C++代碼
  1. int m_nTextX;   // 水平滾動(dòng)文本的起始點(diǎn)的x坐標(biāo)   
  2. int m_nTextY;   // 垂直滾動(dòng)文本的起始點(diǎn)的y坐標(biāo)   
  3. CFont m_newFont;   // 新字體   
  4. CFont *m_pOldFont; // 選擇新字體之前的字體  

       4、在CExample48Dlg類的構(gòu)造函數(shù)中,初始化新添加的成員變量:

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

       5、在CExample48Dlg對(duì)話框初始化函數(shù)中,創(chuàng)建新的字體,并開啟定時(shí)器

C++代碼
  1. BOOL CExample48Dlg::OnInitDialog()   
  2. {   
  3.     CDialogEx::OnInitDialog();   
  4.   
  5.     // Add "About..." menu item to system menu.   
  6.   
  7.     // IDM_ABOUTBOX must be in the system command range.   
  8.     ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);   
  9.     ASSERT(IDM_ABOUTBOX < 0xF000);   
  10.   
  11.     CMenu* pSysMenu = GetSystemMenu(FALSE);   
  12.     if (pSysMenu != NULL)   
  13.     {   
  14.         BOOL bNameValid;   
  15.         CString strAboutMenu;   
  16.         bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);   
  17.         ASSERT(bNameValid);   
  18.         if (!strAboutMenu.IsEmpty())   
  19.         {   
  20.             pSysMenu->AppendMenu(MF_SEPARATOR);   
  21.             pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);   
  22.         }   
  23.     }   
  24.   
  25.     // Set the icon for this dialog.  The framework does this automatically   
  26.     //  when the application's main window is not a dialog   
  27.     SetIcon(m_hIcon, TRUE);         // Set big icon   
  28.     SetIcon(m_hIcon, FALSE);        // Set small icon   
  29.   
  30.     // TODO: Add extra initialization here   
  31.     // 創(chuàng)建一種新的字體(18點(diǎn),隸書)   
  32.     m_newFont.CreatePointFont(180, _T("隸書"));   
  33.   
  34.     // 設(shè)置定時(shí)器,定時(shí)時(shí)間為200ms   
  35.     SetTimer(1,200,NULL);   
  36.   
  37.     return TRUE;  // return TRUE  unless you set the focus to a control   
  38. }  

       6、修改CExample48Dlg::OnPaint()函數(shù),如果窗口沒有最小化就在指定的位置輸出文本,即在OnPaint函數(shù)中if(IsIconic())對(duì)應(yīng)的else大括號(hào)內(nèi)添加相應(yīng)代碼。CExample48Dlg::OnPaint()函數(shù)修改如下:

C++代碼
  1. void CExample48Dlg::OnPaint()   
  2. {   
  3.     if (IsIconic())   
  4.     {   
  5.         CPaintDC dc(this); // device context for painting   
  6.   
  7.         SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);   
  8.   
  9.         // Center icon in client rectangle   
  10.         int cxIcon = GetSystemMetrics(SM_CXICON);   
  11.         int cyIcon = GetSystemMetrics(SM_CYICON);   
  12.         CRect rect;   
  13.         GetClientRect(&rect);   
  14.         int x = (rect.Width() - cxIcon + 1) / 2;   
  15.         int y = (rect.Height() - cyIcon + 1) / 2;   
  16.   
  17.         // Draw the icon   
  18.         dc.DrawIcon(x, y, m_hIcon);   
  19.     }   
  20.     else  
  21.     {   
  22.         CPaintDC dc(this); // device context for painting   
  23.         // 設(shè)置m_newFont對(duì)象的字體為當(dāng)前字體,并將之前的字體指針保存到m_pOldFont   
  24.         m_pOldFont = (CFont*)dc.SelectObject(&m_newFont);   
  25.         // 設(shè)置   
  26.         dc.SetBkMode(TRANSPARENT); //設(shè)置背景為透明!   
  27.         // 設(shè)置文本顏色為紅色   
  28.         dc.SetTextColor(RGB(255,0,0));   
  29.         // 在指定位置輸出文本   
  30.         dc.TextOut(m_nTextX,10,_T("歡迎來到雞啄米!"));   
  31.         // 設(shè)置文本顏色為綠色   
  32.         dc.SetTextColor(RGB(0,255,0));   
  33.         // 在指定位置輸出文本   
  34.         dc.TextOut(10,m_nTextY,_T("謝謝關(guān)注www.jizhuomi.com"));   
  35.         // 恢復(fù)以前的字體   
  36.         dc.SelectObject(m_pOldFont);   
  37.   
  38.         CDialogEx::OnPaint();   
  39.     }   
  40. }  

       7、在Class View類視圖中找到CExample48Dlg,右鍵點(diǎn)Properties,顯示出其屬性頁(yè),在屬性頁(yè)工具欄上點(diǎn)擊Messages按鈕,找到WM_TIMER消息,添加消息響應(yīng)函數(shù)CExample48Dlg::OnTimer(UINT_PTR nIDEvent),并在此函數(shù)中修改兩個(gè)文本輸出的坐標(biāo)位置。

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結(jié)構(gòu)   
  6.     m_newFont.GetLogFont(&logFont);   
  7.   
  8.     // 將m_nTextX的值減5   
  9.     m_nTextX -= 5;   
  10.     // 如果m_nTextX小于10,則文本“歡迎來到雞啄米”回到起始位置   
  11.     if (m_nTextX < 10)   
  12.         m_nTextX = 260;   
  13.   
  14.     // 將m_nTextY的值加一個(gè)字符高度   
  15.     m_nTextY += abs(logFont.lfHeight);   
  16.     // 如果m_nTextY大于260,則文本“謝謝關(guān)注www.jizhuomi.com”回到起始位置   
  17.     if (m_nTextY >260)   
  18.         m_nTextY = 10;   
  19.   
  20.     // 使窗口客戶區(qū)無效,之后就會(huì)重繪   
  21.     Invalidate();   
  22.   
  23.     CDialogEx::OnTimer(nIDEvent);   
  24. }  

       到這一步,兩個(gè)文本就可以分別在水平和垂直方向滾動(dòng)了。雞啄米再簡(jiǎn)單解釋下這個(gè)過程:程序剛啟動(dòng)時(shí),會(huì)調(diào)用OnPaint函數(shù),在初始位置繪出兩個(gè)文本,然后每次到了定時(shí)器的定時(shí)時(shí)間后,會(huì)執(zhí)行OnTimer函數(shù),修改兩個(gè)文本的坐標(biāo)值,并通過Invalidate使窗口重繪,又會(huì)重新調(diào)用OnPaint函數(shù)繪制兩個(gè)文本。這樣通過定時(shí)修改坐標(biāo)值就實(shí)現(xiàn)了兩個(gè)文本的滾動(dòng)效果。

       8、運(yùn)行程序,最終的效果如下圖:

       好了,本節(jié)就講到這里了,最后的實(shí)例大家可以自己豐富下它的功能,看看效果。雞啄米謝謝大家的支持。

除非特別注明,雞啄米文章均為原創(chuàng)
轉(zhuǎn)載請(qǐng)標(biāo)明本文地址:http://www.jizhuomi.com/software/241.html
2012年9月22日
作者:雞啄米 分類:軟件開發(fā) 瀏覽: 評(píng)論:5