孙鑫VC++深入详解(4):文本编程

本文介绍MFC编程中获取字体度量信息、创建插入符、加载字符串资源等实用技巧,并详细阐述了如何通过CDC类实现字符输入及文本平滑变色效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、获得设备描述表中当前字体的度量信息

CDC::GetTextMetrics(LPTEXTMETRIC lpMetrics)函数用来获得设备描述表中当前字体的度量信息,如字体的高度。字体度量信息TEXTMETRIC:

typedef struct tagTEXTMETRIC {
  LONG  tmHeight;//字体高度
  LONG  tmAscent;
  LONG  tmDescent;
  LONG  tmInternalLeading;
  LONG  tmExternalLeading;
  LONG  tmAveCharWidth;//字体平均宽度
  LONG  tmMaxCharWidth;//字体最大宽度
  LONG  tmWeight;
  LONG  tmOverhang;
  LONG  tmDigitizedAspectX;
  LONG  tmDigitizedAspectY;
  TCHAR tmFirstChar;
  TCHAR tmLastChar;
  TCHAR tmDefaultChar;
  TCHAR tmBreakChar;
  BYTE  tmItalic;
  BYTE  tmUnderlined;
  BYTE  tmStruckOut;
  BYTE  tmPitchAndFamily;
  BYTE  tmCharSet;
} TEXTMETRIC, *PTEXTMETRIC;

2、插入符

CreateSolidCaret(int nWidth, int nHeight)函数用来创建插入符,创建完后应使用ShowCaret()显示插入符。

eg:

int Ctest2View::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  在此添加您专用的创建代码
	CClientDC dc(this);
	TEXTMETRIC tm;
	dc.GetTextMetrics(&tm);

	CreateSolidCaret(tm.tmAveCharWidth/8, tm.tmHeight);
	ShowCaret();

	return 0;
}
CreateCaret(CBitmap* pBitmap)用来创建图形插入符:

int Ctest2View::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  在此添加您专用的创建代码
	static CBitmap bitmap;//位图对象应定义为成员变量或静态对象
	bitmap.LoadBitmapW(IDB_BITMAP1);

	CreateCaret(&bitmap);
	ShowCaret();

	return 0;
}
3、字符串资源

CString::LoadString(UINT nID)函数用来加载一个由nID标识的字符串资源,可以在资源视图下的String Table中定义字符串资源

4、获得字符串的宽度和高度

CDC::GetTextExtent(const CString& str) /GetTextExtent(LPCTSTR lpszString, int nCount)用来获得一个字符串在窗口中显示时所占的宽度和高度。返回CSize类型来表示宽度和高度。

5、路径层

创建路径层步骤:首先调用CDC::BeginPath()函数在设备描述表中打开一个路径层,然后利用GDI函数进行绘图操作,如绘制一些文本、点、线、矩形等,最后调用CDC::EndPath()函数关闭路径层。

CDC::SelectClipPath(int nMode)函数将路径层和设备描述表中裁剪区(绘图区域)按照指定的模式进行互操作,nMode取值意义:

RGN_DIFF:设备描述表中绘图区域为去除当前路径层的区域(向该设备描述表进行绘图将不会覆盖当前路径层的区域)

RGN_AND:设备描述表中绘图区域为与当前路径层的交集区域

RGN_COPY:设备描述表中绘图区域为当前路径层

RGN_OR:设备描述表中绘图区域为与当前路径层的并集区域

RGN_XOR:设备描述表中绘图区域为与当前路径曾的并集但不包括重叠的区域

6、字符输入功能的实现

实现用户敲击字符键后在窗口上显示用户输入的字符,以下为编程思路:

 用户敲击字符键盘会产生WM_CHAR消息,可以在这个消息响应函数中实现字符输出功能。我们把每次输入的字符都累加存储到一个字符串中,每次按下新的字符时,都在原始起点位置重新输出该字符串,因为人眼具有视觉残留效应,因此用户感觉不到这种重新输出的变化。故应为View类增加一个CPoint成员变量m_ptOrigin来保存原始起点位置,还要为View类增加一个CString类型成员变量m_strLine用来存储输入的字符串。

 程序另一个应实现的功能是当按下鼠标左键时插入符应该移到该位置,故在WM_LBUTTONDOWN消息响应函数中应调整插入符的位置,这可以利用CWnd类的静态成员函数static CWnd::SetCaretPos(POINT point)实现。而插入符移动到新位置后以前输入的字符不应从这个新位置再次输出,故应清空m_strLine,同时还要重新保存原始起点到m_ptOrigin

 程序在输出字符时,还应考虑回车键和退格键的处理。按下回车后插入符应移到下一行的位置,即插入符的横坐标不变,纵坐标加一个字体的高度,而当前设备描述表中字体高度可以用GetTextMetrics()函数获得。移动插入符可以调用函数SetCaretPos(POINT point)。因为插入符位置改变,故同时还应清空m_strLine。按下退格键即删除一个字符,一个取巧的方法是先把设备描述表中文本颜色变为背景色,将字符串输出一遍,然后再将这个字符删除,再恢复文本颜色,再把字符串输出一遍。在实现时可以利用CDC::GetBkColor()获得背景颜色,利用CDC::SetTextColor(COLORREF crColor)设置文本颜色,COLORREF用来描绘一个RGB颜色,通常使用宏RGB对其进行赋值。

COLORREF RGB(
  BYTE byRed,
  BYTE byGreen,
  BYTE byBlue
);
  另一个问题是每输入一个字符或者按一次退格键,插入符应该移动到新的位置,移动插入符可以调用函数SetCaretPos(POINT point),新位置的纵坐标不会变,横坐标为原点m_ptOrgin横坐标加上字符串m_strLine的宽度。CDC::GetTextExtent()函数可以获得字符串的宽度。

实现代码:

void Ctest2View::OnLButtonDown(UINT nFlags, CPoint point)
{
	SetCaretPos(point);
	m_strLine.Empty();
	m_ptOrgin = point;
	
	CView::OnLButtonDown(nFlags, point);
}

void Ctest2View::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	CClientDC dc(this);

	if(nChar == 0x0d)//回车
	{
		
		TEXTMETRIC tm;
		dc.GetTextMetrics(&tm);

		m_ptOrgin.y += tm.tmHeight;
		SetCaretPos(m_ptOrgin);//插入符换行
		m_strLine.Empty();//清空字符串
	}
	else if(nChar == 0x08)//退格
	{
		COLORREF BkColor = dc.GetBkColor();
		COLORREF TextColor = dc.SetTextColor(BkColor);

		dc.TextOutW(m_ptOrgin.x, m_ptOrgin.y, m_strLine);
		m_strLine = m_strLine.Left(m_strLine.GetLength()-1);
		dc.SetTextColor(TextColor);

		CSize sz = dc.GetTextExtent(m_strLine);
		CPoint ptCaret;
		ptCaret.x = m_ptOrgin.x + sz.cx;
		ptCaret.y = m_ptOrgin.y;
		SetCaretPos(ptCaret);//移动插入符

		dc.TextOutW(m_ptOrgin.x, m_ptOrgin.y, m_strLine);//去除最后一个字符后再次输出文本
	}
	else
	{
		char ch = nChar;
		m_strLine += ch;

		CSize sz = dc.GetTextExtent(m_strLine);
		CPoint ptCaret;
		ptCaret.x = m_ptOrgin.x + sz.cx;
		ptCaret.y = m_ptOrgin.y;
		SetCaretPos(ptCaret);//移动插入符

		dc.TextOutW(m_ptOrgin.x, m_ptOrgin.y, m_strLine);//输出文本
	}
	
	CView::OnChar(nChar, nRepCnt, nFlags);
}

7、MFC的CEditView和CRichEditView类可以实现字处理程序的一些基本功能,如输出字符、编辑等。

8、文本平滑变色功能的实现

为了实现一行文本平滑变色的效果可以使用DrawText()函数来输出文本,DrawText()是在指定的矩形范围内输出文字,没有特殊设定的情况下,若DrawText()要显示的文本太多,以至于超过设定的矩形区域时,则超出部分不会显示出来。利用函数的这个特点我们可以先设置一个100毫秒的定时器,再设置一个新的字体颜色,然后在OnTimer()函数中,在窗口中文本原来的位置重新输出一遍该文本,初始输出时先把矩形宽度设置成一个很小的值,然后不断的加大矩形的宽度,这样就可以不断地增加显示新颜色文本的内容,从而实现文字平滑变色的效果。

SetTimer()函数用来设置定时器:每过一段指定的时间就会产生WM_TIMER消息,函数调用成功会返回定时器的标识,用来区分不同的定时器(OnTimer中参数也为定时器标识)。

UINT_PTR SetTimer(
   UINT_PTR nIDEvent, //定时器标识
   UINT nElapse, //时间间隔,毫秒
   void (CALLBACK* lpfnTimer)(HWND,UINT,UINT_PTR,DWORD)//函数指针,并且要求是一个回调函数:定时器消息产生时系统自动调用.为NULL则WM_TIMER消息被放到消息队列中
);

还要为View类增加一个int型变量m_nWidth,用来保存DrawText()矩形区域的宽度,并使其不断增大,而矩形区域的高度应为一个字符的高度,可以通过CDC::GetTextMetrics()函数获得。

实现代码:

int Ctest2View::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  在此添加您专用的创建代码
	CClientDC dc(this);
	TEXTMETRIC tm;
	dc.GetTextMetrics(&tm);

	CreateSolidCaret(tm.tmAveCharWidth/8, tm.tmHeight);
	ShowCaret();

	SetTimer(1, 100, NULL);

	return 0;
}

void Ctest2View::OnDraw(CDC* pDC)
{
	Ctest2Doc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// TODO: 在此处为本机数据添加绘制代码
	
	CString str;
	str.LoadStringW(IDS_STRING101);//加载字符串资源
	pDC->TextOutW(0, 200, str);//输出原文本
}

void Ctest2View::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	CClientDC dc(this);
	COLORREF colOld = dc.SetTextColor(RGB(255,0,0));
	
	m_nWidth += 5;//矩形区域宽度

	TEXTMETRIC tm;
	dc.GetTextMetrics(&tm);
	CRect rect(0, 200, m_nWidth, 200+tm.tmHeight);//矩形区域坐标

	CString str;
	str.LoadStringW(IDS_STRING101);
	dc.DrawText(str, &rect, DT_LEFT);//输出新颜色的文本

	CSize sz = dc.GetTextExtent(str);
	if(m_nWidth > sz.cx)//矩形区域宽度超过原文宽度则将文本改变为原来颜色
	{
		m_nWidth = 0;
		dc.SetTextColor(colOld);
		dc.TextOutW(0, 200, str);
	}

	CView::OnTimer(nIDEvent);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值