MFC中利用序列化技术实现撤销功能

来源:互联网 发布:linux显示文件内容命令 编辑:程序博客网 时间:2024/05/29 03:43
一、 CUndo类接口说明
1.1 virtual void Serialize(CArchive& ar)=0;

作用:纯虚函数,序列化从CUndo继承的子类,主要作用是对该子类各个成员变量进行序列化,程序中完成一步操作时,保存各个成员变量的值。

1.2 virtual void DeleteContents() = 0;
作用:纯虚函数,由从CUndo继承的子类进行实现,主要作用是在撤销操作时,在加载上一步资源之前,处理子类中的相关资源,如将成员变量恢复到原始状态等等。

上面两个接口,由CUndo的子类进行实现,在程序中不会被直接调用。

1.3 void CheckPoint();
作用:设置检查点,程序中每进行一步操作,设置一个检查点,记录该操作,用于后面的撤销操作使用。

伪代码如下:

CheckPoint() {if (m_chkpt <= 0) {CMemFile* file = new CMemFile(m_growsize);//动态创建了一片内存用于序列化对象Store(file);//存储操作,主要作用就是序列化存储该对象{              file->SeekToBegin();     CArchive ar(file, CArchive::store);     Serialize(ar);      ar.Close();}AddUndo(file);{      ……                                     m_undolist.AddHead(file);//将该存储该对象属性的内存地址放进撤销操作列表中}ClearRedoList();//将反撤销操作的列表清空(正常进行操作时,反撤销操作是不能用的){      ……     m_redolist.RemoveAll();}}}

1.4 void Undo();
作用:程序进行撤销操作时,调用该接口。

伪代码如下:
Undo() {……CMemFile *pFile = (CMemFile *) m_undolist.GetHead();//获取撤销操作列表当前头结点m_undolist.RemoveHead();//移除该头结点AddRedo(pFile);//将头结点增加到反撤销操作列表{     m_redolist.AddHead(file);}pFile = (CMemFile *)m_undolist.GetHead();//获取当前撤销操作的头结点Load(pFile);//加载存储的对象的成员变量的值{      DeleteContents();//在恢复上一步操作之前,调用DeleteContents接口      file->SeekToBegin();      CArchive ar(file, CArchive::load);      Serialize(ar); //序列化加载上一步操作中对象各个成员变量的值      ar.Close();}}

1.5 void Redo();
作用:程序进行反撤销操作时,调用该接口

伪代码如下:
Redo() {CMemFile *pFile = (CMemFile *) m_redolist.GetHead() ;m_redolist.RemoveHead();//从反撤销操作中移除该头结点AddUndo(pFile);//将节点加到撤销操作列表中{                     m_undolist.AddHead(file);}Load(pFile);{      DeleteContents();//在恢复下一步操作之前,调用DeleteContents接口      file->SeekToBegin();      CArchive ar(file, CArchive::load);      Serialize(ar); //序列化加载下一步操作中对象各个成员变量的值      ar.Close();}}

1.6 void SetUndolevels(int undoLevels){m_undoLevels = undoLevels; }
作用:设置撤销操作的深度



1.7 ~CUndo()
作用:析构函数,释放资源

伪代码如下:
~CUndo() {      Clear();     {          // Clear undo list         ClearUndoList();         // Clear redo list         ClearRedoList();     }}

二、利用序列化技术实现撤销功能的实例

以下实现一个在对话框的客户区可以绘制直线的程序,每次通过鼠标左键按下记录绘制起点,然后鼠标移动,左键弹起时记录绘制终点,在起点和终点之间绘制一条直线。每次只能显示一条直线,程序有撤销和反撤销的功能。
2.1 创建从CUndo派生的绘制直线的类CDrawline
该类主要包含CPoint类型的两个成员变量,分别代表绘制直线的起点和终点,接口SetStartPoint和SetEndPoint分别设置起点和终点。
class CDrawLine :public CUndo{public:CDrawLine();virtual ~CDrawLine();private:CPoint m_cpStart;CPoint m_cpEnd;public:void SetStartPoint(const CPoint& point){m_cpStart=point;}void SetEndPoint(const CPoint& point){m_cpEnd=point;}void OnLButtonUp();void Draw(CDC* dc,CWnd* pWnd);virtual void Serialize(CArchive& ar);virtual void DeleteContents();};

2.1.1 实现Serialize

void CDrawLine::Serialize(CArchive& ar){if(ar.IsStoring()){ar<<m_cpStart;ar<<m_cpEnd;}else{ar>>m_cpStart;ar>>m_cpEnd;}}

2.1.2 实现DeleteContents
在这里只是为了说明,DeleteContents需要在CUndo的派生类中自己实现,其实在本类中该函数进行的操作没有特别大的意义。
void CDrawLine::DeleteContents(){m_cpStart=m_cpEnd=CPoint();}2.1.3 实现Draw函数,用来绘制直线。void CDrawLine::Draw(CDC* pDC,CWnd* pWnd){if(m_cpEnd==m_cpStart) return;CRect client;pWnd->GetClientRect(&client);CPen pen(PS_SOLID,2,RGB(255,0,0));pDC->SelectObject(&pen);pDC->MoveTo(m_cpStart);pDC->LineTo(m_cpEnd);pDC->DeleteDC();}

2.1.4 实现OnLButtonUp函数,用来记录检查点

void CDrawLine::OnLButtonUp(){CheckPoint();}

2.2 实现对话框程序
2.2.1 新建对话框工程UndoTest,在相应类中增加一个CDrawLine类型的成员变量 m_DrawLine;
为对话框增加撤销和前进的菜单,以及对应的快捷键Ctrl+Z,Ctrl+Y,具体过程,这里不再详述。
class CUndoTestDlg : public CDialogEx{       …       CDrawLine m_DrawLine;}

界面如下图:


2.2.2 增加并实现Draw函数用来绘制直线。

void CUndoTestDlg::Draw(void){HDC hdc=::GetDC(m_hWnd);CDC dc;dc.Attach(hdc);m_DrawLine.Draw(&dc,this);}2.2.3 在OnPaint中调用上述Draw函数void CUndoTestDlg::OnPaint(){……Draw();}

2.2.4 增加并实现鼠标左键按下的消息处理函数,记录绘制起始点

void CUndoTestDlg::OnLButtonDown(UINT nFlags, CPoint point){m_DrawLine.SetStartPoint(point);CDialogEx::OnLButtonDown(nFlags, point);}
2.2.5 增加并实现鼠标左键抬起的消息处理函数,记录绘制终点、重绘界面以及记录撤销操作的检查点。

void CUndoTestDlg::OnLButtonUp(UINT nFlags, CPoint point){m_DrawLine.SetEndPoint(point);Invalidate(TRUE);m_DrawLine.OnLButtonUp();CDialogEx::OnLButtonUp(nFlags, point);}

2.2.6 增加并实现撤销菜单的事件处理函数,实现撤销功能

void CUndoTestDlg::OnUndo(){m_DrawLine.Undo();this->Invalidate(TRUE);}

2.2.7 增加并实现前进菜单的事件处理函数,实现反撤销功能

void CUndoTestDlg::OnMenuRedo(){m_DrawLine.Redo();this->Invalidate(TRUE);}


附undo.h:

// undo.h implementation// Author - Keith Rule (keithr@europa.com)//// A description of this code can be found in May 1997 - Windows Tech Journal.// Back issues can be ordered from either (800) 241-4320 or (918) 831-9557.//// An extended version of this class is part of the MFC Class of the week// (http://www.weeklymfc.com/).//// Modified by Jiang Feng , 1998.10//#ifndef _UNDO_H_#define _UNDO_H_//------------------------------------------------------------//  Undo/Redo for MFC By Keith Ruleclass CUndo {private:CObListm_undolist;// Stores undo statesCObListm_redolist;// Stores redo stateslongm_growsize;// Adjust for faster saveslongm_undoLevels;// Requested Undolevels longm_chkpt;//added by yangguichun, 2008/06/25BOOLm_bUndo;BOOLm_bRedo;//end added by yangguichun, 2008/06/25void AddUndo(CMemFile*);void AddRedo(CMemFile *pFile); void Load(CMemFile*);void Store(CMemFile*);void ClearRedoList();void ClearUndoList();public:// Here are the hooks into the CDocument classvirtual void Serialize(CArchive& ar) = 0;virtual void DeleteContents() = 0;// User accessable functionsCUndo(long undolevels = 8, long = 32768);// Constructor~CUndo();// DestructorBOOL CanUndo();// Returns TRUE if can UndoBOOL CanRedo();// Returns TRUE if can Redovoid Undo();// Restore next Undo statevoid Redo();// Restore next Redo statevoid CheckPoint();// Save current state void EnableCheckPoint();void DisableCheckPoint();void SetUndolevels(int undoLevels){m_undoLevels = undoLevels; }BOOL IsUndo(){ return m_bUndo; };BOOL IsRedo() { return m_bRedo; };/// <summary>///清除undo和redo的列表/// </summary>void Clear(){// Clear undo listClearUndoList();// Clear redo listClearRedoList();};};// Constructorinline CUndo::CUndo(long undolevels, long growsize) : m_growsize(growsize), m_undoLevels(undolevels),m_chkpt(0){m_undoLevels = 10;//added by yangguichun, 2008/06/25m_bUndo = FALSE;m_bRedo = FALSE;//end added by yangguichun, 2008/06/25} // Remove contents of the redo listinline void CUndo::ClearRedoList(){// Clear redo listPOSITION pos = m_redolist.GetHeadPosition(); CMemFile* nextFile = NULL;while(pos) {nextFile = (CMemFile *) m_redolist.GetNext(pos);delete nextFile;}m_redolist.RemoveAll();}inline void CUndo::ClearUndoList(){POSITION pos = m_undolist.GetHeadPosition(); CMemFile  *nextFile = NULL;while(pos) {nextFile = (CMemFile *) m_undolist.GetNext(pos);delete nextFile;}m_undolist.RemoveAll();}// Destructorinline CUndo::~CUndo() {Clear();}// Checks undo availability, may be used to enable menusinline BOOL CUndo::CanUndo() {return (m_undolist.GetCount() > 1);}// Checks redo availability, may be used to enable menusinline BOOL CUndo::CanRedo() {return (m_redolist.GetCount() > 0);}// Adds state to the beginning of undo listinline void CUndo::AddUndo(CMemFile* file) {// Remove old state if there are more than max allowedif (m_undolist.GetCount() > m_undoLevels) {CMemFile* pFile = (CMemFile *) m_undolist.RemoveTail();delete pFile;}// Add new state to head of undo listm_undolist.AddHead(file);}// Saves current object into CMemFile instanceinline void CUndo::Store(CMemFile* file) {file->SeekToBegin();CArchive ar(file, CArchive::store);Serialize(ar); ar.Close();}// Loads CMemfile instance to current objectinline void CUndo::Load(CMemFile* file) {DeleteContents(); file->SeekToBegin();CArchive ar(file, CArchive::load);Serialize(ar); ar.Close();}// Save current object state to Undo listinline void CUndo::CheckPoint() {if (m_chkpt <= 0) {CMemFile* file = new CMemFile(m_growsize);Store(file);AddUndo(file);ClearRedoList();}}inline void CUndo::EnableCheckPoint(){if (m_chkpt > 0) {m_chkpt--;}}inline void CUndo::DisableCheckPoint(){m_chkpt++;}// Place CMemFile instnace on Redo listinline void CUndo::AddRedo(CMemFile *file) {// Move state to head of redo listm_redolist.AddHead(file);}// Perform an Undo commandinline void CUndo::Undo() {if (CanUndo()) {// Remember that the head of the undo list// is the current state. So we just move that// to the Redo list and load then previous state.//added by yangguichun, 2008/06/25m_bUndo = TRUE;//end added by yangguichun, 2008/06/25CMemFile *pFile = (CMemFile *) m_undolist.GetHead();m_undolist.RemoveHead();AddRedo(pFile);pFile = (CMemFile *)m_undolist.GetHead();Load(pFile);//added by yangguichun, 2008/06/25m_bUndo = FALSE;//end added by yangguichun, 2008/06/25}}//Perform a Redo Commandinline void CUndo::Redo() {if (CanRedo()) {//added by yangguichun, 2008/06/25m_bRedo = TRUE;//end added by yangguichun, 2008/06/25CMemFile *pFile = (CMemFile *) m_redolist.GetHead() ;m_redolist.RemoveHead();AddUndo(pFile);Load(pFile);//added by yangguichun, 2008/06/25m_bRedo = FALSE;//end added by yangguichun, 2008/06/25}}#endif


0 0