需要用到文本编辑功能,当然是用现成的啦,自己写字符输入那还挺麻烦的。。。比如前面就没解决的问题,后续还有什么只能一行一行写,不能随意跳动行啥的,一系列问题,还有有现成的,不然真的为难死我了。。。菜鸡难过

MFC提供了两个类吧,一个CEditView,一个CRichEditView,虽然我只用了后面那个 ,前面那个没做什么了解。。。。后面这个虽然好用,问题也多。。。。。真的搞人耶,


新建项目:单文档->基类继承CRichEditView

★★★★这里千万注意,使用CRichEditView和RichEdit控件需要注意:MFC中调用DoModal()后窗体未弹出
其实你可以去.App类中去看,找到CTxt**App::InitInstance()函数,它也提示该怎么使用;

 只不过这行代码需要插入到CWinAppEx::InitInstance();上面,好像之前插错位置没正常显示,如下图:

现在运行这个程序,是可以直接在界面上进行文本输入的(实际上你还可以粘贴图片。。。不然为什么叫富文本,理论上我们只要写文本,应该是用CEditView的,不过用都用了,改是不可能改的。


 。。。发现编辑菜单下面的子菜单,比如什么剪贴复制粘贴都是可以直接用的,这可真是太贴心了,爱了爱了,目前主要就是实现文件菜单下的几个子菜单的功能,新建/打开/保存/另存为

。。。。这几个功能实际上也已经给你实现了一半了,比如点击保存它会给你弹出弹窗让你选择保存地址。。但是它又没有做完,毕竟他无法真的去保存。。。总而言之,自带的功能不够用,需要重写函数自己解决问题,不然要你👨‍💻干啥


 1.读写文件:

第一步当然是先搞定文件的读写,毕竟这些功能都需要建立在这个基础之上,不先写读写怎么验证你写的功能。。。。分别写一个函数用于保存文本至Txt,从Txt读取文本到成员变量(这里因为这个文本一直需要使用修改,所以让其作为一个成员变量),此处需要两个CString变量,一个保存文本,一个保存文件路径,下面这些都写在CTxt**View.h中:

1     CString TxtFileString;              // 存储文本文件的字符内容
2     CString TxtFileLoad;                // 该文本的保存路径
3 
4     bool SaveFileContents();            // 保存文件内容,存内容到指定的Txt文本
5     bool ReadFileContents();            // 读取文件内容,获取指定Txt文本内容

CTxt**View.cpp:具体实现读写函数,采用CFile类实现文件读写,

这里为了方便处理字符编码格式,固定了读写方式,均采用UTF-16-LE编码格式,即写入时,加入文件头部标志,用以表示这是一个UTF-16-LE编码的Txt文本,同样,读取的时候也可以通过检验这个标志来判断是否是UTF-16-LE编码的Txt文本

另外就是关于字符长度,以及字符的字节长度的处理需要搞清楚,往往会因为这个导致一些错误。UTF-16一个字符占两个字节(汉字/英文都是两个字节),在这里使用的CString也是宽字节,也是一个字符占两个字节,这里区别于普通的C/C++程序的字符表示方法(char,string),还是需要谨慎注意!

 1 /*
 2 function:保存文件内容,存内容到指定的Txt文本
 3 1.捕捉异常,
 4 2.返回值fasle,表明存在异常,错误;返回值true,表明程序正常处理,保存成功
 5 TxtFileString;        // 存储文本文件的字符内容
 6 TxtFileLoad;        // 该文本的保存路径
 7 */
 8 bool CTxt0721View::SaveFileContents()
 9 {
10     CFile save_file;
11     try
12     {
13         // 打开文件,第一参数文件路径,以写入模式打开,如果不存在则创建
14         save_file.Open(TxtFileLoad, CFile::modeWrite | CFile::modeCreate);
15         BYTE byUnicode[] = { 0xFF, 0xFE };
16         // 写入文件,第一个参数是写入内容,第二个参数是写入内容的长度
17         save_file.Write(byUnicode, sizeof(byUnicode));        // 文件开头写入byUnicode告知为Unicode编码,此后便可正常输入
18         save_file.Write((LPCTSTR)TxtFileString, wcslen((LPCTSTR)TxtFileString) * sizeof(WCHAR));
19         // 关闭文件
20         save_file.Close();
21         // 写入文件成功
22         return true;
23     }
24     catch(CFileException *e) {
25         CString save_errror_str;
26         save_errror_str.Format(L"保存数据失败的原因是:%d", e->m_cause);
27         MessageBox(save_errror_str);
28         save_file.Abort();
29         e->Delete();
30         // 写入文件失败
31         return false;
32     }
33 }
 1 /*
 2 function:读取文件内容,获取指定Txt文本内容
 3 1.捕捉异常,
 4 2.返回值fasle,表明存在异常,错误;返回值true,表明程序正常处理,读取Txt成功
 5 3.可以使用read一次全部读取,考虑会爆,循环多次读取(暂未处理)
 6 TxtFileString;        // 存储文本文件的字符内容
 7 TxtFileLoad;          // 该文本的保存路径
 8 */
 9 bool CTxt0721View::ReadFileContents()
10 {
11     CFile read_file;
12     try
13     {
14         // 只读模式打开文档
15         read_file.Open(TxtFileLoad, CFile::modeRead);
16         // 获取整个文件的字节长度--由于是UTF-16编码,一个字符占两个字节,文件字节长度是字符长的两倍--逻辑长度
17         DWORD file_length = read_file.GetLength();
18         // 文件指针定位,干掉前面两个字节--用于标记UTF-16的标记
19         read_file.Seek(2, CFile::begin);    // Skip 0xFF, 0xFE
20         // 创建字符串存储内容
21         wchar_t * file_contents = new wchar_t[file_length];
22         memset(file_contents, 0, sizeof(wchar_t)*file_length);
23         // 一次性读取整个文件
24         read_file.Read(file_contents, sizeof(char)*file_length);
25         // 关闭文件
26         read_file.Close();
27         // 将读取的文本存入核心变量 TxtFileString 中
28         TxtFileString = (CString)file_contents;
29         // 读取文件成功
30         return true;
31     }
32     catch (CFileException *e)
33     {
34         CString read_errror_str;
35         read_errror_str.Format(L"保存数据失败的原因是:%d", e->m_cause);
36         MessageBox(read_errror_str);
37         read_file.Abort();
38         e->Delete();
39         // 读取文件失败
40         return false;
41     }
42 }

在代码注释中也已经提及到了,使用Read函数一次性读完,当文件非常大的时候肯定会爆的,这还是需要控制的,可以设定指定大小空间,循环多次读取,也可使用CStdioFile类的ReadString,一行一行的读取,当然,这里文本比较小,(人为控制了),就不考虑那么多了,(懂的都懂,就是懒的写)

-------------------->>>>>>>>>>>>>> 千万记得Close()关闭打开的文件,


2.文件另存为:这里就要去菜单中添加事件处理程序了,消息响应(在此之前,还需要搞定如何读写RichEditView上的文本,通过程序向RichEditView写文本(缺少流的方式) )

这里考虑到应该设置一个变量用来监控文件是否修改过,然后才能知道这个文本是否需要保存,就和Txt一样,若文本没有改动,是不需要保存的,可以直接关闭,这里也是实现这一点,但是发现有一个函数可以实现这一点,CRichEditCtrl类(提供Rich Edit 控件功能,提供一些函数,在RichEdit上操作)的SetModify函数和GetModify函数,自己查资料吧,SetModify可以设置文本是否被修改过,GetModify能够获取这个状态,当然,除了你手动的通过SetModify设置状态,它也自动的在监测,只要修改了,它就能读取到----->>>>这表明,每一次保存都需要通过SetModify设置,表明文本已经保存,处于没有被修改状态(意思就是,已经存起来了,你关闭啥的随意,不必再次被保存,这个设计点的意义完全参考txt,

。。。这里先把CTxt**View.h放出来,不然中途临时涉及到的变量不好捋清楚

 1 // Txt0721View.h : CTxt0721View 类的接口
 2 //
 3 
 4 #pragma once
 5 
 6 class CTxt0721CntrItem;
 7 
 8 class CTxt0721View : public CRichEditView
 9 {
10 public:
11     
12     CRichEditCtrl *pRichEdit;            // = &GetRichEditCtrl();
13 
14     int  Number_QueryAccept;            // 实现文件拖拽,调用QueryAcceptData函数,因其拖到视图/放开鼠标响应两次,
15                                         // 穿过视图放到框架响应一次,故设定视图中偶数可响应特定函数,在框架中置0,保证偶数
16     bool IsFileNew;                        // 文件是否新建 -- 标记当前Txt文本是不是新建出来的文本
17 //  GetModify()函数取代了该 标记 功能
18 //    bool IsFileSave;                    // 文件是否保存 -- 标记文件刷新后是否保存(就是看文件内容改变后有没有保存
19     bool IsFileExit;                    // 是否按过菜单上的退出按键 -- 是否点过退出键
20 //    bool IsExit;                        // 是否退出程序 -- 是否退出程序(功能重复)
21     bool SaveButCancle;                    // 退出的时候要保存,弹出路径选择框,结果你又点了取消
22 // (在文件另存为,保存选择那里,选择路径时,你点了取消,取消当前操作,一般是退出操作或者保存操作
23     CString TxtFileString;                // 存储文本文件的字符内容
24     CString TxtFileLoad;                // 该文本的保存路径
25 
26 
27 
28     bool SaveFileContents();            // 保存文件内容,存内容到指定的Txt文本
29     bool ReadFileContents();            // 读取文件内容,获取指定Txt文本内容
30     void InitCTxt0721View();            // 初始化
31     // 询问是否保存文件 -- 然后根据选择调用函数,主要用于(新建)和(打开)功能
32     // ture,保存/不保存文件;false,退出当前功能
33     bool AskToSaveOrNot(CRichEditCtrl *pRichEdit);
34     // 实现文件拖拽时的打开Txt功能
35     bool DragAndDropOpenFile(CString FileLoad);        // 执行中退出事件--false,// 正常执行true;
36 
37 
38 
39 
40 
41 protected: // 仅从序列化创建
42     CTxt0721View();
43     DECLARE_DYNCREATE(CTxt0721View)
44 
45 // 特性
46 public:
47     CTxt0721Doc* GetDocument() const;
48 
49 // 操作
50 public:
51 
52 // 重写
53 public:
54     virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
55 protected:
56     virtual void OnInitialUpdate(); // 构造后第一次调用
57     virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
58 
59 // 实现
60 public:
61     virtual ~CTxt0721View();
62 #ifdef _DEBUG
63     virtual void AssertValid() const;
64     virtual void Dump(CDumpContext& dc) const;
65 #endif
66 
67 protected:
68 
69 // 生成的消息映射函数
70 protected:
71     afx_msg void OnDestroy();
72     afx_msg void OnFilePrintPreview();
73     afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
74     afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
75     DECLARE_MESSAGE_MAP()
76 public:
77     afx_msg void OnFileOpen();
78     afx_msg void OnFileSaveAs();
79     afx_msg void OnFileSave();
80     afx_msg void OnFileNew();
81     afx_msg void OnAppExit();
82 
83 
84     virtual HRESULT QueryAcceptData(LPDATAOBJECT lpdataobj, CLIPFORMAT* lpcfFormat, DWORD dwReco, BOOL bReally, HGLOBAL hMetaFile);
85 
86     bool IsUTF8(const void* pBuffer, long size);
87 };
88 
89 #ifndef _DEBUG  // Txt0721View.cpp 中的调试版本
90 inline CTxt0721Doc* CTxt0721View::GetDocument() const
91    { return reinterpret_cast<CTxt0721Doc*>(m_pDocument); }
92 #endif
View Code

 现在开始编写另存为事件处理程序:(点击菜单后,弹出另存为对话框,然后选择路径,保存)

1)文本类型处理,这里直接百度解决的,使用那个文件过滤器,就是确定一种后缀或者指定格式;

 wchar_t szFilter[] = _T("文本文件(*.txt)|*.txt|All Files(*.*)|*.*||");          ---->>> Txt,或者,所有类型

CFileDialog SaveAsFileDlg(false, NULL, NULL, 0, szFilter);   ----->>>参数false和true分别表示另存为和打开对话框,szFilter就是用来指定文件类型的,就是可惜没有一个字符编码的选项(我不知道有没有,我自己没搞清楚,我也还没查这个代码,就单纯抄了一下)

2)然后用SaveAsFileDlg.DoModal(),产生一个模态对话框(就是你必须先处理这个对话框,盖在你的原窗口上面,你不得不先处理),有确定和取消两个选项,分别对应这个函数的返回值,IDOK///IDCANCEL,可以用来检测用户到底选择了哪一个选项,

如果点击确定,表明,用户选择保存文件,这时就需要记录用户写入的文件路径,GetPathName()函数解决问题,然后就是关于文件后缀的问题,需要和szFilter[]数组结合起来判断,根据下标做判断就好了,具体实现看代码吧;          此处还有的一个问题就是是否存在同名文件,利用得到的文件路径去判断是否已经存在,然后询问用户是替换,还是就此结束,若替换,则直接保存到这个文件(直接替换),反之,则再次调用另存为函数,用来保存这个文件,直到用户保存了文件或者用户选择退出

如果点击了取消:表明另存为终止,这里是需要做出标记的,因为可能这是在退出过程中询问文件是否保存而引出的另存为事件,若这里用户选择取消,程序是不可以退出的,这里是一个细节的把控,需要注意,此处设计了一个变量SaveButCancle,用来专门标记这种情况,此外,设置SetModify(TRUE),表明该文件改动过,(因为每一次保存都会设置为文件没有被改动,所以保存失败需要重新设置为文件改动过)

 1 /*
 2 function:文件另存为
 3 1.弹出窗口,选择路径,然后->是,把文本内容保存到该路径,即复制当前窗口内容,
 4 然后根据文件路径建立一个txt文本文档,然后写入数据,关闭该文档,回到原窗口,结束
 5 2.检查输入的文件路径是否已经存在
 6 */
 7 void CTxt0721View::OnFileSaveAs()
 8 {
 9     // TODO: 在此添加命令处理程序代码
10 //    CRichEditCtrl *pRichEdit = &GetRichEditCtrl();
11     // 文件过滤,可以打开txt文本,目前设置只打开*txt文本。
12      wchar_t szFilter[] = _T("文本文件(*.txt)|*.txt|All Files(*.*)|*.*||");
13     // wchar_t szFilter[] = _T("文本文件(*.txt)|*.txt||");
14     // 用于文件另存为的选择对话框
15     CFileDialog SaveAsFileDlg(false, NULL, NULL, 0, szFilter);
16     // 判断另存为事件 点击了保存还是取消  保存 与 取消
17     if (IDOK == SaveAsFileDlg.DoModal())        // 选择保存文件
18     {
19         // 获取对话框返回的文件名------获取的是整个文件的路径
20         TxtFileLoad = SaveAsFileDlg.GetPathName();
21         // 判断有无后缀,先检查选用的哪一个文件保存类型   .txt/.* 
22         if (SaveAsFileDlg.m_ofn.nFilterIndex == 1)
23         {
24             // .txt 类型文件,判断是否有.txt后缀,没有就加上
25             if (TxtFileLoad.Right(4) != L".txt")
26                 TxtFileLoad += ".txt";
27         }
28         else if (SaveAsFileDlg.m_ofn.nFilterIndex == 2)
29         {
30             // All Files(*.*)|*.* 类型文件,检查是否有后缀(.),没有直接默认为  .txt
31             if (TxtFileLoad.Find('.') == -1)            // 无后缀
32                 TxtFileLoad += ".txt";
33         }
34         // 文件另存为,表明文件已经保存,有文件路径了--不是新建文件
35         IsFileNew = false;
36         // 然后调用文件保存函数,保存文件数据------需要调用保存函数 CTxt0721View::OnFileSave()
37         // 判断是否已有同名文件
38         if (PathFileExists(TxtFileLoad))        // 已经存在同名文件 -- 询问是否替换
39         {        // 文件已经存在
40             int ID_Message = MessageBox(L"该文本已存在,是否替换该文本?", L"提示", MB_YESNO);
41             if (ID_Message == IDYES)            // 直接替换该文本
42             {
43                 CTxt0721View::OnFileSave();
44             }
45             else
46             {                // 点否之后,没有路径
47                 IsFileNew = true;        
48                 CTxt0721View::OnFileSaveAs();        // 再次调用另存为函数,保存文件
49             }
50         }
51         else
52             CTxt0721View::OnFileSave();                // 不存在同名文件,直接替换
53     }
54     else
55     {
56         SaveButCancle = false;        // 退出的时候要保存,结果你又点了取消
57     // 此处即退出另存为函数,并且结束当前的功能,同时结束其父功能
58     // 例如,在 退出 功能中,调用另存为函数,然后在路径选择时,点了取消
59     // 则,此时不仅关闭 (另存为) 功能,同时需要关闭 (退出) 功能
60         IsFileNew = true;                // 点取消之后,没有路径
61         pRichEdit->SetModify(TRUE);        // 中途退出,文件没保存,
62     }    
63 }

3.文件保存:这里就简单多了,

首先用SetModify(FALSE),表式文本已经保存过,没被改动(这里也是为什么另存为函数CTxt0721View::OnFileSaveAs()中没有出现这一句代码的原因,因为另存为函数不会直接保存文本到Txt,它还是通过获取文件路径后调用的OnFileSave()函数来实现保存的,所以另存为函数不需要这一行代码)

其次就是获取视图上(CRichEditView)的文本,然后写入文件:根据目前有无文件路径做出判断,有文件路径--可以直接保存,调用写入文本函数CTxt0721View::SaveFileContents();若没有文件路径,则调用另存为函数,进行存储;        实际上是可以发现的,另存为函数主要是为了得到文件路径,文本保存工作主要还是通过保存函数OnFileSave()调用SaveFileContents()实现写入Txt;

 1 /*
 2 function:文件保存
 3 1.若文本是新建出来的,即IsFileNew = true,此文件没有保存路径,按保存需弹出另存为的功能。
 4 2.若文本已存在路径,则按照该路径对文本数据进行保存即可。
 5 */
 6 void CTxt0721View::OnFileSave()
 7 {
 8     // TODO: 在此添加命令处理程序代码
 9 //    CRichEditCtrl *pRichEdit = &GetRichEditCtrl();
10     // 用于记录文本是否修改过,FALSE未修改,每一次保存调用一次,表明未修改
11     pRichEdit->SetModify(FALSE);
12     // 获取需要保存的文本TxtFileString,即CRichEditView上的文本信息
13     // pEdit->GetSelText(); 获取选中区域的文本
14     pRichEdit->GetWindowText(TxtFileString);
15     // 调用文件保存函数,使数据保存到Txt文本之中
16     if (IsFileNew)        
17     {    // IsFileNew = true,文本是新建出来的,文件没有保存路径
18         // 弹出  另存为  的功能 ---- 用于获取  文件的保存路径
19         CTxt0721View::OnFileSaveAs();
20      }
21     else
22     {
23         // IsFileNew = false,文件有保存路径,直接保存
24         CTxt0721View::SaveFileContents();
25     }
26 }

4.新建:说实话,这个看的有点懵,因为孙鑫那本书讲了一堆,又没看出有什么用。。。(虽然我是写完了代码才看到有这段。。。。)我的解决方案就很简单了,直接询问是否保存当前界面上的数据,然后就直接清空界面,把若干数据全部都置为初始化状态,ok,问题解决。这不就是新建(真是个小机灵鬼)(Txt就是这个流程,虽然不知道它是怎么实现的)

AskToSaveOrNot(写在第六点  --  6.)用来询问用户是否保存,(若保存,就调用函数实现保存)

这个函数的返回值并不是表示文件保存与否,而是用来判断用户是否中途截断了运行,(比如点击新建菜单,内部会调用这个函数,询问是否保存当前文本,但是如果这时用户直接关闭了这个询问保存的对话框,就是截断了运行,此时,程序也只能结束它的父功能,即新建功能)(设计AskToSaveOrNot这个函数,主要还是多个地方会用到,都需要这样的一个功能)

后面几行代码比较简单,就是重置参数,和清空界面,可以查询CRichEditCtrl类,

 1 /*
 2 功能:新建
 3 1.保存现有文件(暂无法判断文件是否修改后保存了) -- 判断已可以实现
 4 2.清空当前数据,当作新文件 -- 造假新建
 5 */
 6 void CTxt0721View::OnFileNew()
 7 {
 8     // TODO: 在此添加命令处理程序代码
 9     // 保存文件
10     // CTxt0721View::OnFileSave();
11     // 询问是否保存
12 //    CRichEditCtrl *pRichEdit = &GetRichEditCtrl();
13     // 判断文件是否保存/不保存=true;   取消--退出当前功能=false;
14     if (CTxt0721View::AskToSaveOrNot(pRichEdit) == false)
15         return;
16     // pRichEdit->GetModify() = fasle    文件未修改 -- 文件已经保存 或者 新建文件没有动过
17 
18     // 执行到此处,当前 文件已经保存 或者 文件未保存,可以新建文档界面
19     // 新建文件 IsFileNew
20     CTxt0721View::InitCTxt0721View();                // 重置各种参数
21     // 清空数据信息   CRichEditView
22     // CRichEditView界面,文字全选,清空
23     pRichEdit->SetSel(0, -1);
24     pRichEdit->Clear();
25     // 用于记录文本是否修改过,FALSE未修改,新建出来的文档默认未修改
26     pRichEdit->SetModify(FALSE);
27 }

5.打开文件:

同样的意思,也是先调用AskToSaveOrNot进行处理,询问是否保存当前已有的文本,然后重置参数(迎接新的文本),弹出对话框,选择待打开的文本,读取文件,显示文本到View,思路简单

 1 /*
 2 function:打开文件
 3 0.打开文件,首先需要保存当前现有文件,然后关闭当前文件,打开新文件
 4 1.根据文件选择对话框,选择需要打开的文件,获取其路径
 5 2.根据路径打开文件,读取数据信息,然后写入 CRichEditView
 6 */
 7 void CTxt0721View::OnFileOpen()
 8 {
 9     // TODO: 在此添加命令处理程序代码
10 //    CRichEditCtrl *pRichEdit = &GetRichEditCtrl();
11     // 判断文件是否保存/不保存=true;   取消--退出当前功能=false;
12     if (CTxt0721View::AskToSaveOrNot(pRichEdit) == false)
13         return;
14 
15     CTxt0721View::InitCTxt0721View();                // 重置各种参数
16 
17     // true参数打开文档,false参数则是另存为
18     // 文件过滤,可以打开txt文本,目前设置只打开*txt文本。
19      wchar_t szFilter[] = _T("文本文件(*.txt)|*.txt|All Files(*.*)|*.*||");
20     // wchar_t szFilter[] = _T("文本文件(*.txt)|*.txt||");
21     // 用于文件选择对话框
22     CFileDialog OpenFileDlg(true, NULL, NULL, 0, szFilter);
23     // 判断打开与否---判断点击了哪一个按钮,是打开,还是,取消
24     if (IDOK == OpenFileDlg.DoModal())
25     {
26         // 获取对话框返回的文件名------获取的是整个文件的路径
27         TxtFileLoad = OpenFileDlg.GetPathName();
28         // 打开文件,表明文件并非是新建的,已经有文件路径了
29         IsFileNew = false;
30         // 按照上述路径,读取文件内容,调用 ReadFileContents 函数
31         CTxt0721View::ReadFileContents();
32         // 写入 CRichEditView
33         // CRichEditCtrl *pRichEdit = &GetRichEditCtrl();
34         // 文本全选
35         pRichEdit->SetSel(0, -1);
36         // 使用下面两行的目的是为了替换的时候不至于出现 文字全选而出现的颜色,
37         pRichEdit->Clear();
38         pRichEdit->SetSel(0, -1);
39         // 替换选中的文本,即输入读取的文本信息
40         pRichEdit->ReplaceSel(TxtFileString);
41         // 用于记录文本是否修改过,FALSE未修改,新建出来的文档默认未修改
42         pRichEdit->SetModify(FALSE);
43     }
44 }

6.两个函数,一个用来重置参数,CTxt0721View::InitCTxt0721View()
一个用来询问,当前文本是否保存,CTxt0721View::AskToSaveOrNot()
 1 /*
 2 功能:对CTxt0721View 对象进行初始化操作
 3 1.对应新建操作后的响应 -- 简而言之,初始化操作
 4 */
 5 void CTxt0721View::InitCTxt0721View()
 6 {
 7     IsFileNew = true;                // 默认是新建文档,即打开程序后的空白文档。
 8     IsFileExit = false;                // true表示已经经历过退出键了,OnClose不必再次退出 
 9     SaveButCancle = true;            // 退出的时候要保存,弹出路径选择框,结果你又点了取消
10 // (在文件另存为,保存选择那里,选择路径时,你点了取消,取消当前操作,一般是退出操作或者保存操作
11     TxtFileString.Empty();            // 初始文本内容为空
12     TxtFileLoad.Empty();            // 文本的保存路径
13 }
14 
15 
16 /*
17 功能:询问是否需要保存当前文件,并根据决策做出保存操作
18 1.实现询问是否保存以及保存功能
19 2.若是在保存中退出,则返回当前一个标识,提示结束当前功能
20 */
21 bool CTxt0721View::AskToSaveOrNot(CRichEditCtrl *pRichEdit)
22 {
23     // 判断当前文件是否修改,是否需要保存
24     if (pRichEdit->GetModify())        // 文件已修改 -- 需要保存 -- 选择直接 保存 或者 另存为
25     {
26         CString save_message = L"是否要将更改保存到";
27         if (IsFileNew)
28             save_message += L" 无标题 ";
29         else
30             save_message += TxtFileLoad;
31         // 弹出对话框,询问是否保存现有文件    MB_YESNOCANCEL -- Yes -- No -- Cancel
32         int ID_Message = MessageBox(save_message, L"Txt提示", MB_YESNOCANCEL);
33         if (ID_Message == IDYES)    // 保存文件
34         {
35             // 检测有无文件路径
36             if (!IsFileNew)        // 有文件路径 --- 调用保存函数
37             {
38                 CTxt0721View::OnFileSave();
39             }
40             else                // 无文件路径 --- 调用另存为函数
41             {
42                 CTxt0721View::OnFileSaveAs();
43             }
44             if (pRichEdit->GetModify())            // 确定保存,但是最后没有保存 -- 等效于取消当前事件
45                 return false;
46         }
47         else if (ID_Message == IDCANCEL)
48             return false;                // 取消 -- 直接退出 -- 同时需要退出 当前功能
49     }
50     return true;
51 }
View Code

7.最后一个功能  --  退出:

退出嘛,不就是退出三部曲,WM_CLOSE,WM_DESTORY,WM_QUIT,在退出函数CTxt0721View::OnAppExit()中发送消息WM_CLOSE不就得了。。。这个世界总是无情棒喝单纯的人。。

先不管,照常写退出函数,按照Txt功能,退出前首先是要询问是否保存当前文本的(前提是当前文本改动过没保存),这是老操作了,不难,只要在合适位置发送WM_CLOSE消息就行了,问题解决。。(解决个屁

 1 /*
 2 功能:退出 -- 此处主要是实现菜单部分的退出按键
 3 1.若有文件路径已保存,则可以直接退出,反之需要询问是否保存
 4 2.弹出窗口,询问是否保存(保存,不保存,取消)
 5 */
 6 void CTxt0721View::OnAppExit()
 7 {
 8     // TODO: 在此添加命令处理程序代码
 9     SaveButCancle = true;    // 退出的时候询问是否保存,点了保存后,选择路径时,又点了取消
10     // 结果你又点了取消,取消退出,置fasle,-- 正常情况下可以直接退出,SaveButCancle = true
11     IsFileExit = true;        // 默认按过退出菜单,置true
12 
13 //    CRichEditCtrl *pRichEdit = &GetRichEditCtrl();
14     // pEdit->GetModify()  -> 判断文件是否已经保存
15     if (pRichEdit->GetModify())            // 文件已修改,未保存 -- 提示用户是否保存
16     {
17         // 提示消息罢了
18         CString save_message = L"是否要将更改保存到";
19         if (IsFileNew)
20             save_message += L" 无标题 ";
21         else
22             save_message += TxtFileLoad;
23         // 退出时,弹出对话框,询问是否保存    MB_YESNOCANCEL -- Yes -- No -- Cancel
24         int ID_Message = MessageBox(save_message, L"Txt退出", MB_YESNOCANCEL);
25         if (ID_Message == IDYES)            // YES按键 -- 保存文件,然后退出
26         {
27             // 检测有无文件路径
28             if (!IsFileNew)        // 有文件路径 --- 调用保存函数
29             {
30                 CTxt0721View::OnFileSave();    
31             }
32             else                // 无文件路径 --- 调用另存为函数
33             {
34                 CTxt0721View::OnFileSaveAs();
35                 // 检查 另存为 功能是否完整实现
36                 if (SaveButCancle == false)        // 另存为功能中途退出
37                 {
38                     IsFileExit = false;            // 退出菜单无效,退出该功能,置false
39                     return;                        // 直接退出--退出功能
40                 }    
41             }    
42             AfxGetMainWnd()->PostMessage(WM_CLOSE);            // 发送WM_CLOSE消息,退出
43         }
44         else if (ID_Message == IDNO)    // 文件不保存,直接退出即可
45         {
46             AfxGetMainWnd()->PostMessage(WM_CLOSE);            // 发送WM_CLOSE消息,退出
47         }
48         else if (ID_Message == IDCANCEL)    // 取消--无操作
49         {
50             IsFileExit = false;            // 退出菜单无效,退出该功能,置false
51             return;                        // 直接退出--退出功能
52         }
53     }
54     else                // 文件未修改,即文件(已经保存过 或者 刚新建没动直接 , 退出即可
55     {
56         AfxGetMainWnd()->PostMessage(WM_CLOSE);                // 发送WM_CLOSE消息,退出
57     }
58 }
View Code

 问题在哪儿?你会发现,当你点击退出菜单之后,会弹出一个对话框询问你是否保存,你点击之后发现还会弹出一个对话框。。。。

那么为什么会出现这个问题。。。。。

回到系统自动为我们生成的代码,当你运行时,你编辑文本后,点击退出,发现一样会跳出对话框询问你是否保存,

那现在就能够理解了,弹出的两个对话框一个是OnAppExit()中我们自己写的,还有一个是系统自己给你提供的。。。。是不是突然感觉自带功能也不是那么友好。。。。

那到底它在哪里控制这个弹窗的出现呢?

寻寻觅觅:发现:肯定是和ID_FILE_CLOSE消息有关的,经过我一番查找(我也不记得是在哪找到的了,反正就是一番查找)(老老实实翻文档呗)(关闭对话框 介绍对话框的创建与关闭)

看到了吧,意思就是说,SaveModified()函数中判断文本是否修改,修改了就给你弹出对话框问你保不保存,那现在问题就简单了,我去Doc中拦截这个处理;

SaveModified()是一个虚函数,类向导,阿巴阿巴,当然就是干掉自带的那一句return CRichEditDoc::SaveModified();    然后我们自己来写return就好了,它的返回值类型是BOOL,。。。真的不喜欢这样写,搞得我总以为是bool,其实是int,。。。 这里返回return false;(将表示不希望窗口关闭,相当于对关闭消息不作处理,你发任你发),返回值return true;理论上也就是返回1,这表明将关闭窗口,也就是后续会调用OnCloseDocument,销毁文档(如果这里有什么我理解错了,希望能够指点我一下,这里仅个人对这个资料的理解,刚学,怕有理解错误

OK,问题解决,这样就好办了,去SaveModified()中做调整就好了;

。。。。。。问题又来了,菜单上的退出确实没问题,可以达到目的,但是这里不是还有一个退出。。。

当我点击这个叉叉时,由于我重写的SaveModified(),他不会给我提供对话框询问保存与否,。。。所以这里实际上是需要我们做一个调控的,用一个变量IsFileExit来记录是否有执行过OnAppExit()函数(也就是询问到底有没有弹出对话框来询问是否保存)(特别说一点,这里弹出对话框询问是否保存都是针对文本已修改未保存的情况,特别注意哦),没有执行就去在执行一次,这次问题算是真的解决了。

 1 BOOL CTxt0721Doc::SaveModified()
 2 {
 3     // TODO: 在此添加专用代码和/或调用基类
 4 
 5 //    return CRichEditDoc::SaveModified();
 6     // 如果继续并关闭文档,则为非零值;如果文档不应关闭,则为0。
 7     CMainFrame *pMain = (CMainFrame *)AfxGetApp()->m_pMainWnd;
 8     CTxt0721View *pView = (CTxt0721View *)pMain->GetActiveView();
 9     
10     // 退出发问,有没有按过退出(只执行一次 退出 功能)---没有那手动给你按,按过,滚蛋
11     // 执行过IsFileExit = true;      没有执行过IsFileExit = fasle;
12     if (pView->IsFileExit == false)            
13         pView->OnAppExit();                // 手动执行 退出 功能
14             
15     // 1.关闭窗口需要保证执行过(退出)功能,pView->IsFileExit判断是否执行过
16     // 2.关闭窗口需要保证,整个(退出)功能完整执行,pView->SaveButCancle判断是否中途退出
17     if (pView->IsFileExit && pView->SaveButCancle)    
18         return true;
19     else                // 窗口不关闭
20         return false;
21 }

至此,差不多告一段落了,基本功能都实现了吧,

下一篇03:MFC实现Txt03之CRichEditView实现文本拖拽功能


2022-09-14:回来挨打,这里的××该怎么拦截呢?当然是使用函数OnSysCommand(UINT nID, LPARAM lParam),专门拦截控制栏信息,nID就是控制栏的各种消息ID,如果想拦截××,只需要做判断if (nID == SC_CLOSE),然后在if里面做出相应处理即可


 这个保存了代码,有时间搞上来,。。很懒

2022-08-17(0720)

posted on 2022-08-17 00:33  夜_归_人  阅读(416)  评论(0编辑  收藏  举报