天天看點

Visual Studio/MFC程式設計入門之字型和文本輸出:文本輸出

MFC界面開發必備庫:

  • Xtreme Toolkit Pro:是屢獲殊榮的VC界面庫,是MFC開發中最全面界面控件套包,它提供了Windows開發所需要的11種主流的Visual C++ MFC控件,包括Command Bars、Controls、Chart Pro、Calendar、Docking Pane、Property Grid、Report Control、Shortcut Bar、Syntax Edit、Skin Framework 和Task Panel。
  • BCGControlBar:庫擁有500多個經過全面設計、測試和充分記錄的MFC擴充類。 我們的元件可以輕松地內建到您的應用程式中,并為您節省數百個開發和調試時間。

在上一節中講了CFont字型類,本節主要講解文本輸出的方法和執行個體。

文本輸出過程

在文本輸出到裝置以前,我們需要确定字型、字型顔色和輸出的文本内容等資訊。Windows視窗的客戶區由應用程式管理,是以我們還要在應用程式中控制輸出文本的格式,例如後續字元的位置、換行等格式。

由此,文本的輸出過程大緻包括确定字型資訊、格式化文本和執行輸出操作三個步驟。下面分别講解。

1、确定字型資訊

文本在輸出以前應該先确定字型資訊,或者是目前正在使用的字型,或者是自定義的字型,之後就可以根據确定的字型來顯示文本或者利用字型資訊來設定文本的格式了,例如,我們可以根據目前字型的字元高度來确定下一行字元在什麼位置輸出。

自定義字型可以通過CFont類的建立字型的幾個成員函數完成。擷取目前選擇字型的資訊可以使用API函數GetTextMetrics實作,此函數的原型如下:

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

參數hdc為裝置上下文的句柄;參數lptm是指向TEXTMETRIC結構體變量的指針,此結構體變量用于接收字型資訊。TEXTMETRIC結構體的定義如下:

C++代碼

typedef struct tagTEXTMETRIC {
LONG  tmHeight;        // 字元高度
LONG  tmAscent;        // 字元基線以上的高度
LONG  tmDescent;       // 字元基線以下的高度
LONG  tmInternalLeading; // 由tmHeight成員指定的字元高度頂部的空間
LONG  tmExternalLeading; // 行間距
LONG  tmAveCharWidth;  // 字元的平均寬度
LONG  tmMaxCharWidth;  // 字元的最大寬度
LONG  tmWeight;        // 字元的粗度
LONG  tmOverhang;      // 合成字型間附加的寬度
LONG  tmDigitizedAspectX; // 為輸出裝置設計的x軸尺寸
LONG  tmDigitizedAspectY; // 為輸出裝置設計的y軸尺寸
TCHAR tmFirstChar;     // 字型中第一個字元值
TCHAR tmLastChar;      // 字型中最後一個字元值
TCHAR tmDefaultChar;   // 替換字型中沒有的字元
TCHAR tmBreakChar;     // 作為分隔符的字元
BYTE  tmItalic;        // 非0則表示字型為斜體
BYTE  tmUnderlined;    // 非0則表示字型有下劃線
BYTE  tmStruckOut;     // 非0則表示字元帶有删除線
BYTE  tmPitchAndFamily;// 字型間距和字型族
BYTE  tmCharSet;       // 字元集
} 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++代碼

typedef struct tagSIZE {
LONG cx;   // 寬度
LONG cy;   // 高度
} 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++代碼

int m_nTextX;   // 水準滾動文本的起始點的x坐标
int m_nTextY;   // 垂直滾動文本的起始點的y坐标
CFont m_newFont;   // 新字型
CFont *m_pOldFont; // 選擇新字型之前的字型
      

4、在CExample48Dlg類的構造函數中,初始化新添加的成員變量:

C++代碼

CExample48Dlg::CExample48Dlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CExample48Dlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_nTextX = 260;
m_nTextY = 10;
m_pOldFont = NULL;
}
      

5、在CExample48Dlg對話框初始化函數中,建立新的字型,并開啟定時器:

C++代碼

BOOL CExample48Dlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog.  The framework does this automatically
//  when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE);         // Set big icon
SetIcon(m_hIcon, FALSE);        // Set small icon
// TODO: Add extra initialization here
// 建立一種新的字型(18點,隸書)
m_newFont.CreatePointFont(180, _T("隸書"));
// 設定定時器,定時時間為200ms
SetTimer(1,200,NULL);
return TRUE;  // return TRUE  unless you set the focus to a control
}
      

6、修改CExample48Dlg::OnPaint()函數,如果視窗沒有最小化就在指定的位置輸出文本,即在OnPaint函數中if(IsIconic())對應的else大括号内添加相應代碼。CExample48Dlg::OnPaint()函數修改如下:

C++代碼

void CExample48Dlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CPaintDC dc(this); // device context for painting
// 設定m_newFont對象的字型為目前字型,并将之前的字型指針儲存到m_pOldFont
m_pOldFont = (CFont*)dc.SelectObject(&m_newFont);
// 設定
dc.SetBkMode(TRANSPARENT); //設定背景為透明!
// 設定文本顔色為紅色
dc.SetTextColor(RGB(255,0,0));
// 在指定位置輸出文本
dc.TextOut(m_nTextX,10,_T("歡迎來到雞啄米!"));
// 設定文本顔色為綠色
dc.SetTextColor(RGB(0,255,0));
// 在指定位置輸出文本
dc.TextOut(10,m_nTextY,_T("謝謝關注www.jizhuomi.com"));
// 恢複以前的字型
dc.SelectObject(m_pOldFont);
CDialogEx::OnPaint();
}
}
      

7、在Class View類視圖中找到CExample48Dlg,右鍵點Properties,顯示出其屬性頁,在屬性頁工具欄上點選Messages按鈕,找到WM_TIMER消息,添加消息響應函數CExample48Dlg::OnTimer(UINT_PTR nIDEvent),并在此函數中修改兩個文本輸出的坐标位置。

C++代碼

void CExample48Dlg::OnTimer(UINT_PTR nIDEvent)
{
// TODO: Add your message handler code here and/or call default
LOGFONT logFont;
// 擷取m_newFont字型的LOGFONT結構
m_newFont.GetLogFont(&logFont);
// 将m_nTextX的值減5
m_nTextX -= 5;
// 如果m_nTextX小于10,則文本“歡迎來到雞啄米”回到起始位置
if (m_nTextX < 10)
m_nTextX = 260;
// 将m_nTextY的值加一個字元高度
m_nTextY += abs(logFont.lfHeight);
// 如果m_nTextY大于260,則文本“謝謝關注www.jizhuomi.com”回到起始位置
if (m_nTextY >260)
m_nTextY = 10;
// 使視窗客戶區無效,之後就會重繪
Invalidate();
CDialogEx::OnTimer(nIDEvent);
}
      

到這一步,兩個文本就可以分别在水準和垂直方向滾動了。再簡單解釋下這個過程:程式剛啟動時,會調用OnPaint函數,在初始位置繪出兩個文本,然後每次到了定時器的定時時間後,會執行OnTimer函數,修改兩個文本的坐标值,并通過Invalidate使視窗重繪,又會重新調用OnPaint函數繪制兩個文本。這樣通過定時修改坐标值就實作了兩個文本的滾動效果。

8、運作程式,最終的效果如下圖:

Visual Studio/MFC程式設計入門之字型和文本輸出:文本輸出

好了,本節就講到這裡了,最後的執行個體大家可以自己豐富下它的功能,看看效果。

繼續閱讀