上一篇: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) |
---|