我们平时所用的文本编辑器如记事本,VS等,都有一个一闪一闪的竖线,我们称之为插入符
通常我们输入的信息都是显示在插入符之后的,而我们建立的基本MFC程序,里面是没有插入符的
所以我们首先先创建一个插入符
int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: 在此添加您专用的创建代码
CreateSolidCaret(20,100);
ShowCaret();
return 0;
}
这个时候我们发现插入符大小,即高度和宽度不对,而我们常用的编辑器中插入符应该是随我们使用的字体变化的,所以我们又没有办法获取到当前窗口的字体尺寸呢?
GetTextMetrics()函数:
GetTextMetrics函数需要一个TEXTMETRIC的指针,我们看下这个结构体:
我们只需要使用高度和宽度就可以了,这里简单介绍下升序(Ascent)高度和降序(Descent)高度:
我们使用平均高度/8(除以8是因为8比较合适,大家也可以尝试下别的):
int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::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 创建一个对象,放在 view 类的头文件里:
private:
CBitmap bitmap;
完成函数:
int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: 在此添加您专用的创建代码
CClientDC dc(this);
TEXTMETRIC tm;
dc.GetTextMetrics(&tm);
//CreateSolidCaret(tm.tmAveCharWidth/8,tm.tmHeight);
bitmap.LoadBitmap(IDB_BITMAP1);
CreateCaret(&bitmap);
ShowCaret();
return 0;
}
由于窗口尺寸发生变化的时候,窗口会进行重绘,所以我们直接输出会被覆盖掉
可以利用 view 类中提供的 OnPaint() 函数输出;利用 CString类创建一个字符对象
void CChildView::OnPaint()
{
CPaintDC dc(this); // 用于绘制的设备上下文
// TODO: 在此处添加消息处理程序代码
CString str("琛小獭");
dc.TextOutW(50,50,str);
// 不要为绘制消息而调用 CWnd::OnPaint()
}
我们还可以利用 LoadStringW() 函数,在资源视图中找到 StringTable,双击在最下面的空格添加一个:
void CChildView::OnPaint()
{
CPaintDC dc(this); // 用于绘制的设备上下文
// TODO: 在此处添加消息处理程序代码
CString str("琛小獭");
dc.TextOutW(50,50,str);
str.LoadStringW(IDS_CHEN);//填101也可
dc.TextOutW(0,200,str);
// 不要为绘制消息而调用 CWnd::OnPaint()
}
void CChildView::OnPaint()
{
CPaintDC dc(this); // 用于绘制的设备上下文
// TODO: 在此处添加消息处理程序代码
CString str("琛小獭");
dc.TextOutW(50,50,str);
str.LoadStringW(IDS_CHEN);//填101也可
dc.TextOutW(0,200,str);
// 不要为绘制消息而调用 CWnd::OnPaint()
dc.BeginPath();
dc.Rectangle(50,50,50+sz.cx,50+sz.cy);
dc.EndPath();
//dc.SelectClipPath(RGN_DIFF);
for (int i = 0; i < 300; i += 10)//网格
{
dc.MoveTo(0, i);
dc.LineTo(300,i);
dc.MoveTo(i,0);
dc.LineTo(i, 300);
}
}
void CChildView::OnPaint()
{
CPaintDC dc(this); // 用于绘制的设备上下文
// TODO: 在此处添加消息处理程序代码
CString str("琛小獭");
dc.TextOutW(50,50,str);
str.LoadStringW(IDS_CHEN);//填101也可
dc.TextOutW(0,200,str);
// 不要为绘制消息而调用 CWnd::OnPaint()
dc.BeginPath();
dc.Rectangle(50,50,50+sz.cx,50+sz.cy);
dc.EndPath();
dc.SelectClipPath(RGN_DIFF);
for (int i = 0; i < 300; i += 10)//网格
{
dc.MoveTo(0, i);
dc.LineTo(300,i);
dc.MoveTo(i,0);
dc.LineTo(i, 300);
}
}
首先我们添加一个消息响应函数 WM_CHAR,让我们输入的字符能被获取,所以需要添加一个字符串m_srtLine 用来存储输入的字符,字符输出需要一个点作为起点;当我们鼠标点击,插入符会随着点击移动,所以添加一个消息响应WM_LButtonDown,此时改变输出起点:
private:
CPoint m_ptOrigin;
private:
CString m_strLine;
用SetCaretPos函数改变插入符位置,鼠标点击后字符应该清零,改变输出起点
void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
SetCaretPos(point);
m_strLine.Empty();
m_ptOrigin = point;
CWnd::OnLButtonDown(nFlags, point);
}
如果我们输入 回车,表示换行;输入 退格,表示删除一个字符;
插入符也要随之移动
void CChildView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CClientDC dc(this);
TEXTMETRIC tm;
dc.GetTextMetrics(&tm);
//判断是否回车,回车清空str,并且改变插入符位置******************
if (0x0d == nChar)
{
m_strLine.Empty();
m_ptOrigin.y += tm.tmHeight;
}
//**************************************************************
//判断是否是退格,退格则先将字符颜色设置为背景色****************
//然后删除字符串中的最后一个字符,再将颜色改回来*******************
else if (0x08 == nChar)
{
COLORREF clr = dc.SetTextColor(dc.GetBkColor());
dc.TextOut(m_ptOrigin.x, m_ptOrigin.y,m_strLine);
m_strLine = m_strLine.Left(m_strLine.GetLength() - 1);
dc.SetTextColor(clr);
}
//*************************************************************
//都不是则作为字符输出
else
{
m_strLine += (char)nChar;
}
//移动插入符***************************************************
CSize sz = dc.GetTextExtent(m_strLine);
CPoint pt;
pt.x = m_ptOrigin.x+sz.cx;
pt.y = m_ptOrigin.y;
SetCaretPos(pt);
//*************************************************************
//输出
dc.TextOut(m_ptOrigin.x, m_ptOrigin.y, m_strLine);
CWnd::OnChar(nChar, nRepCnt, nFlags);
}
void CChildView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CClientDC dc(this);
CFont ft;//改变字体
ft.CreatePointFont(300,"楷体",NULL);//关联字体
CFont *pft = dc.SelectObject(&ft);//选择字体
TEXTMETRIC tm;
dc.GetTextMetrics(&tm);
//判断是否回车,回车清空str,并且改变插入符位置******************
if (0x0d == nChar)
{
m_strLine.Empty();
m_ptOrigin.y += tm.tmHeight;
}
//**************************************************************
//判断是否是退格,退格则先将字符颜色设置为背景色****************
//再删除字符串中的最后一个字符,再将颜色改回来*******************
else if (0x08 == nChar)
{
COLORREF clr = dc.SetTextColor(dc.GetBkColor());
dc.TextOut(m_ptOrigin.x, m_ptOrigin.y,m_strLine);
m_strLine = m_strLine.Left(m_strLine.GetLength() - 1);
dc.SetTextColor(clr);
}
//*************************************************************
//都不是则输出
else
{
m_strLine += (char)nChar;
}
//移动插入符***************************************************
CSize sz = dc.GetTextExtent(m_strLine);
CPoint pt;
pt.x = m_ptOrigin.x+sz.cx;
pt.y = m_ptOrigin.y;
SetCaretPos(pt);
//*************************************************************
dc.TextOut(m_ptOrigin.x, m_ptOrigin.y, m_strLine);
dc.SelectObject(pft);//选择回来
CWnd::OnChar(nChar, nRepCnt, nFlags);
}
添加消息响应函数 WM_TIMER,每间隔一段时间让颜色渐变的范围变大
利用DrawText函数改变颜色
首先在 OnCreate中加入Timer:
SetTimer(1, 100, NULL);
然后完成:
void CChildView::OnTimer(UINT_PTR nIDEvent)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_nWidth += 5;
CClientDC dc(this);
TEXTMETRIC tm;
dc.GetTextMetrics(&tm);
CRect rt;
rt.left = 0;
rt.top = 200;
rt.right = m_nWidth;
rt.bottom = rt.top + tm.tmHeight;
dc.SetTextColor(RGB(255,0,0));
CString str;
str.LoadString(IDS_CHEN);
dc.DrawText(str,rt,DT_LEFT);
CSize sz = dc.GetTextExtent(str);
if (m_nWidth > sz.cx)
{
m_nWidth = 0;
dc.SetTextColor(RGB(0,255,0));
dc.TextOut(0,200,str);
}
CWnd::OnTimer(nIDEvent);
}