MFC界面编程基础(19):文档视图结构应用程序例子(Editor)

上一篇:MFC界面编程基础(18):文档视图结构应用程序例子(Ex_DocView)下一篇:MFC界面编程基础(20):一个简单的文档序列化示例(Ex_SDIArchive)

创建单文档应用程序Editor

  • 第一步 用MFC AppWizard(exe)创建一个默认的单文档应用程序Editor,但在向导的【文档模板属性】页,设置文档视图结构的一些属性,如下图所示。
    在这里插入图片描述
    该对话框中包含以下几项:
    ①:文件扩展名:指定应用程序创建的文档所用的文件名后缀。输入后缀名.txt,表明Editor使用文本文件的后缀名.TXT;
    ②:文件类型ID:用于在Windows的注册数据库中标识应用程序的文档类型;
    ③:主框架标题:主框架窗口标题,默认情况下与项目名相一致;
    ④:文档类型名称:指定与一个从CDocument派生的文档类相关的文档类型名;
    ⑤:筛选器名称:用作“打开文件”、“保存文件”对话框中的过滤器。Visual Studio会自动根据输入的后缀名生成一个过滤器:Editor Files(.txt)。这样,当在Open File对话框中选择Editor Files(.txt)时,只有以.txt为后缀名的文件名显示在文件名列表中;
    ⑥:文件的新短名称:用于指定在new对话框中使用的文档名。当应用程序支持多种文档类型时,选择【Fi1e】→【New】命令会弹出一个对话框,列出应用程序所支持的所有文档类型,供用户选择。选择一种文档类型后,自动创建相应类型的文档。
    ⑦:文件类型长名称:用于指定当应用程序作为OLE Automation服务器时使用的文档类型名,使用默认值。

添加应用程序所需的数据

  • 第一步 打开项目工作区的文件视图,双击打开EditorDoc.h文件,如下图所示,并在该文件中,定义文档的数据成员,加入以下代码:
class CEditorDoc : public CDocument
{
……
public:
	CStringList lines;  // 链表CStringList来保存文本编辑器的数据
	int nLineNum;    // 用于指示当前编辑行行号
……
   }

在这里插入图片描述

  • 第二步 同上步,打开在EditorDoc.cpp文件,在CEditorDoc::OnNewDocument()成员函数加入初始化数据成员的代码:
BOOL EditorDoc::OnNewDocument()
{
	if (!CDocument::OnNewDocument())   
	return   FALSE;
	nLineNum=0;
	POSITION pos;		        // pos指向链表当前元素。
	pos=lines.GetHeadPosition();   // 返回链表头指针
	while(pos!=NULL)
	{
		((CString)lines.GetNext(pos)).Empty();
	}	
	lines.RemoveAll();           // 清除链表中的所有元素
	return TRUE;
}

说明:语句((CString)lines.GetNext(pos)).Empty()的作用是:以当前pos为参数,返回下一个元素指针,同时修改pos,使它指向下一个元素。使用强制类型转换将GetNext()函数返回的元素指针转化为CString类型,然后调用Cstring::Empty()方法清除该行中的所有字符。通过一个while循环,清除所有文本行的数据。
一般地,类的数据成员的初始化都是在构造函数中完成的,但由于文档对象创建后,需要反复刷新而不是反复创建,因此文档类的数据成员初始化工作放在OnNewDocument成员函数中完成而不在构造函数中做这件事情

  • 第三步 用MFC ClassWizard为CEditorDoc类添加虚函数DeleteContents(),如下图所示。同时,增加下述代码:
void CEditorDoc::DeleteContents() 
{
	nLineNum=0;
	POSITION pos;
	pos=lines.GetHeadPosition();
	while(pos!=NULL)
	{
		((CString)lines.GetNext(pos)).Empty();
	} 
	lines.RemoveAll();
	CDocument::DeleteContents();
}

在这里插入图片描述
说明:在使用【File】→【Open】命令打开一个文档或关闭应用程序时,都需要清理文档对象中的数据。文档的清理是在文档的CDocument::DeleteContents()虚函数中完成的。DeleteContents()成员函数需要反复调用,它的功能是删除文档的数据,并确信一个文档在使用前为空。有读者可能想到在析构函数中清理文档数据,但析构函数只在文档对象结束时调用,用于清除那些在对象生存期都将存在的数据项,显然析构函数不能满足重复调用的要求。

处理键盘输入

  • 第一步 在文档类的头文件EditorDoc.h中,定义视图类的数据成员,加入以下代码:
class CEditorView : public CView
{
……
	int lHeight;	
	int cWidth;
……
}
  • 第二步 用MFC ClassWizard为CEditorView类添加虚函数OnInitialUpdate(),并增加下列代码:
void CEditorView::OnInitialUpdate() 
{
	CDC *pDC=GetDC(); // 取得当前窗口的设备场境指针并存放在pDC中
	TEXTMETRIC tm;
	pDC->GetTextMetrics(&tm);
	lHeight=tm.tmHeight+tm.tmExternalLeading;
	cWidth=tm.tmAveCharWidth;
	CView::OnInitialUpdate();
}

说明:视图类一般在CView::OnInitialUpdate()成员函数来初始化视图类的数据成员。因为这时,视图窗口已经创建,马上开始更新,那么可能影响视图显示的数据一定要在这时初始化。
在以下情况下,应用程序将自动执行视图类的OnInitialUpdate()来初始化视图类数据成员:
①:调用CDocument::OnNewDocument时;
②:调用CDocument::OnOpenDocument时需要清除视图原有的显示内容。
TEXTMETRIC是一个数据结构,它包含字体的宽度、高度、字的前后空白等字段。调用CDC::GetTextMetrics()获取字体的TEXTMETRIC,从而取得字体的宽度和高度等信息。

  • 第三步 用MFC ClassWizard为类CEditorView添加WM_CHAR的消息处理函数OnChar(),如下图所示。打开CEditorView::OnChar()函数进行编辑。修改后的OnChar函数如下:
void CEditorView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	CEditorDoc* pDoc=GetDocument();
	CClientDC dc(this);
	CString line("");    // 存放编辑器当前行字符串
	POSITION pos=NULL;   // 字符串链表位置指示
	if(nChar=='\r')      // 若是回车,则增加一行
	{
		pDoc->nLineNum++;
	}
	else
	{
		// 按行号返回字符串链表中位置值
		pos=pDoc->lines.FindIndex(pDoc->nLineNum);
		if(!pos)
		{
		   // 没有找到该行号对应的行,因此它是一个空行,
		   // 把它加到字符串链表中
			line+=(char)nChar;
			pDoc->lines.AddTail(CString(line));
		}
		else
{
		   // 当前文本行还没有换行结束,因此将文本加入到行末
			line=pDoc->lines.GetAt(pos);
			line+=(char)nChar;
			pDoc->lines.SetAt(pos,line);
		 }
		 TEXTMETRIC tm;
		 dc.GetTextMetrics(&tm);
		 dc.TextOut(0, (int)pDoc->nLineNum*tm.tmHeight,line,line.GetLength());
	}
	CView::OnChar(nChar, nRepCnt, nFlags);
}

在这里插入图片描述

说明:编辑器要不断接收用户的键盘输入,就必须处理键盘消息。每按下一个字符,窗口就会接收到一个消息WM_CHAR。WM_CHAR消息是在视图类中处理的,对该消息的处理过程大致包括:读取用户输入的字符,如果输入是一个回车,则将总行数nLineNum加1,否则将输入字符加到当前行行末。最后调用TextOut函数输出当前编辑中的文本行。

  • 第四步 修改CEditorView类的OnDraw()函数,编辑后该函数的代码如下:
void CEditorView::OnDraw(CDC* pDC)
{
	CEditorDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	TEXTMETRIC tm;
	pDC->GetTextMetrics(&tm);
	lHeight=tm.tmHeight+tm.tmExternalLeading;
	cWidth=tm.tmAveCharWidth;
	int y=0;
	POSITION pos;
	CString line;
	if(!(pos=pDoc->lines.GetHeadPosition()))
	{
		return;
	}
	while(pos!=NULL)
	{
		line=pDoc->lines.GetNext(pos);
		pDC->TextOut(0, y, line, line.GetLength());
		y+=lHeight;
	}
}

在OnDraw()函数中,首先调用GetDocument()函数,取得指向当前视图所对应的文档的指针。通过这个指针,来访问文档中的数据。以后在视图中修改文档中的数据,也是通过GetDocument()来取得文档指针,再通过该文档指针修改文档中的数据。

  • 第五步 编译运行,结果如下图所示。
    在这里插入图片描述
上一篇:MFC界面编程基础(18):文档视图结构应用程序例子(Ex_DocView)下一篇:MFC界面编程基础(20):一个简单的文档序列化示例(Ex_SDIArchive)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值