文字录入无限制Undo,Redo的实现

这里只针对Edit的内容做一个简单的undo,redo功能;

原理就是,将新增字符和相关信息添加到undo列表,在undo动作时,取记录信息,并在edit中删除新增的字符,然后将此动作添加到redo列表,以便恢复。

 

本程序只对文本框文字的顺序增加做了处理,对于任意位置的删除,复制粘贴等没有进行处理,大家可以根据实际情况完善,增加辅助信息来完成对撤销和恢复的操作。

 

明白了原理,对于其他的操作都是这个道理,比如你画图什么的,保留每个图形的相关信息,然后撤销恢复重画,说的简单,做起来还是需要我们动脑子的^_^

 

为方便查看,将所有代码写到了一个单元。

 

Delphi代码

 

[delphi] view plaincopy
 
  1. unit Unit1;  
  2. interface  
  3. uses  
  4.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  
  5.   Dialogs, StdCtrls;  
  6. type  
  7.   TUndoInfo = record  
  8.     sText : String;    //每次增加的新字符串,不是整个edit的字符串  
  9.     iLastLen : Integer;//上一次edit内容的长度  
  10.   end;  
  11.   PUndoInfo = ^TUndoInfo;  
  12.   TForm1 = class(TForm)  
  13.     Edit1: TEdit;  
  14.     btn_undo: TButton;  
  15.     btn_redo: TButton;  
  16.     procedure FormCreate(Sender: TObject);  
  17.     procedure Edit1Change(Sender: TObject);  
  18.     procedure btn_undoClick(Sender: TObject);  
  19.     procedure btn_redoClick(Sender: TObject);  
  20.   private  
  21.     { Private declarations }  
  22.     FUnDoList : TList;      //undo列表  
  23.     FReDoList : TList;      //redo列表  
  24.     //添加到undo列表,s:新字符串,lastlen:上一次整个长度  
  25.     procedure AddUnDoAction(s:String;lastlen:Integer);  
  26.     //添加到redo列表  
  27.     procedure AddReDoAction(p:PUndoInfo);  
  28.   public  
  29.     { Public declarations }  
  30.   end;  
  31. var  
  32.   Form1: TForm1;  
  33. implementation  
  34. {$R *.dfm}  
  35. { TForm1 }  
  36. procedure TForm1.AddUnDoAction(s: String;lastlen:Integer);  
  37. var  
  38.   p:PUndoInfo;  
  39. begin  
  40.   New(p);  
  41.   p.sText := s;  
  42.   p.iLastLen := lastlen + Length(s);  
  43.   FUnDoList.Add(p);  
  44. end;  
  45. procedure TForm1.FormCreate(Sender: TObject);  
  46. begin  
  47.   FUnDoList := TList.Create;  
  48.   FReDoList := TList.Create;  
  49.   //添加初始值  
  50.   AddUnDoAction(Edit1.Text,0);  
  51. end;  
  52. 这里只简单的在OnChange事件中来添加 undo内容,实际应用中比这要复杂的多,这里只在 
  53. 这里说明一下简单应用 
  54. }  
  55. procedure TForm1.Edit1Change(Sender: TObject);  
  56. var  
  57.   lastlen:Integer;  
  58.   s:String;  
  59. begin  
  60.   //先取得上一次的最后长度  
  61.   lastlen := PUndoInfo(FUnDoList.Items[FUnDoList.Count - 1]).iLastLen;  
  62.   //本次新录入的字符串  
  63.   s := Copy(Edit1.Text,lastlen+1,Length(Edit1.Text)-lastlen);  
  64.   //添加到undo列表  
  65.   AddUnDoAction(s,lastlen);  
  66. end;  
  67. procedure TForm1.btn_undoClick(Sender: TObject);  
  68. var  
  69.   s,ts:string;  
  70.   lastlen:Integer;  
  71. begin  
  72.   //先取消OnChange事件,否则当修改edit的内容时,会重复触发OnChange事件,导致错误  
  73.   Edit1.OnChange := nil;  
  74.   //因为最后一个是原始值,所以这里判断是否大于1  
  75.   if FUnDoList.Count > then  
  76.   begin  
  77.     s := Edit1.Text;  
  78.     //取上次的值  
  79.     ts := PUndoInfo(FUnDoList.Items[FUnDoList.Count - 1]).sText;  
  80.     lastlen := PUndoInfo(FUnDoList.Items[FUnDoList.Count - 1]).iLastLen;  
  81.     //重新赋给edit内容  
  82.     Delete(s,lastlen,Length(ts));  
  83.     Edit1.Text := s;  
  84.     //添加到redo  
  85.     AddReDoAction(FUnDoList.Items[FUnDoList.Count - 1]);  
  86.     //将该项移出undo列表  
  87.     FUnDoList.Delete(FUnDoList.Count - 1);  
  88.   end;  
  89.   //回复OnChange事件  
  90.   Edit1.OnChange := Self.Edit1Change;  
  91. end;  
  92. procedure TForm1.AddReDoAction(p:PUndoInfo);  
  93. begin  
  94.   FReDoList.Add(p);  
  95. end;  
  96. //重复动作,原理同撤销动作  
  97. procedure TForm1.btn_redoClick(Sender: TObject);  
  98. var  
  99.   s,ts:string;  
  100.   lastlen:Integer;  
  101. begin  
  102.   Edit1.OnChange := nil;  
  103.   if FReDoList.Count > then  
  104.   begin  
  105.     ts := PUndoInfo(FReDoList.Items[FReDoList.Count - 1]).sText;  
  106.     lastlen := PUndoInfo(FReDoList.Items[FReDoList.Count - 1]).iLastLen;  
  107.     Edit1.Text := Edit1.Text + ts;  
  108.     FUnDoList.Add(PUndoInfo(FReDoList.Items[FReDoList.Count - 1]));  
  109.     FReDoList.Delete(FReDoList.Count - 1);  
  110.   end;  
  111.   Edit1.OnChange := Self.Edit1Change;  
  112. end;  
  113. end.  

 

 

C#代码

 

[c-sharp] view plaincopy
 
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.ComponentModel;  
  4. using System.Data;  
  5. using System.Drawing;  
  6. using System.Text;  
  7. using System.Windows.Forms;  
  8. namespace Test_CSharp_Win  
  9. {  
  10.     public partial class Form2 : Form  
  11.     {  
  12.         List<UnDoInfo> undoList;  
  13.         List<UnDoInfo> redoList;  
  14.         public Form2()  
  15.         {  
  16.             InitializeComponent();  
  17.             undoList = new List<UnDoInfo>();  
  18.             redoList = new List<UnDoInfo>();  
  19.             //添加初始值  
  20.             AddUnDoAction(textBox1.Text, 0);  
  21.         }  
  22.         /// <summary>  
  23.         /// //添加到undo列表  
  24.         /// </summary>  
  25.         /// <param name="s">新字符串</param>  
  26.         /// <param name="lastlen">上一次整个长度</param>  
  27.         private void AddUnDoAction(string s, int lastlen)  
  28.         {  
  29.             UnDoInfo info = new UnDoInfo();  
  30.             info.sText = s;  
  31.             info.iLastLen = lastlen + s.Length;  
  32.             undoList.Add(info);  
  33.         }  
  34.         /// <summary>  
  35.         /// 添加到redo列表  
  36.         /// </summary>  
  37.         /// <param name="info"></param>  
  38.         private void AddReDoAction(UnDoInfo info)  
  39.         {  
  40.             redoList.Add(info);  
  41.         }  
  42.         private void textBox1_TextChanged(object sender, EventArgs e)  
  43.         {  
  44.             string s = string.Empty;  
  45.             int lastlen = 0;  
  46.             //先取得上一次的最后长度  
  47.             lastlen = undoList[undoList.Count - 1].iLastLen;  
  48.             //本次新录入的字符串  
  49.             s = textBox1.Text.Substring(lastlen, textBox1.Text.Length - lastlen);  
  50.             //添加到undo列表  
  51.             AddUnDoAction(s, lastlen);  
  52.         }  
  53.         private void btn_undo_Click(object sender, EventArgs e)  
  54.         {  
  55.             string s = string.Empty;  
  56.             string ts = string.Empty;  
  57.             int lastlen = 0;  
  58.             //先取消TextChanged事件,否则当修改edit的内容时,会重复触发TextChanged事件,导致错误  
  59.             textBox1.TextChanged -= this.textBox1_TextChanged;  
  60.             //因为最后一个是原始值,所以这里判断是否大于1  
  61.             if (undoList.Count > 1)  
  62.             {  
  63.                 s = textBox1.Text;  
  64.                 ts = undoList[undoList.Count - 1].sText;  
  65.                 lastlen = undoList[undoList.Count - 1].iLastLen;  
  66.                 s = s.Remove(lastlen-1, ts.Length);  
  67.                 textBox1.Text = s;  
  68.                 //添加到redo  
  69.                 AddReDoAction(undoList[undoList.Count - 1]);  
  70.                 //将该项移出undo列表  
  71.                 undoList.RemoveAt(undoList.Count - 1);  
  72.             }  
  73.             //恢复TextChanged事件  
  74.             textBox1.TextChanged += this.textBox1_TextChanged;  
  75.         }  
  76.         /// <summary>  
  77.         /// 重复动作,原理同撤销动作  
  78.         /// </summary>  
  79.         /// <param name="sender"></param>  
  80.         /// <param name="e"></param>  
  81.         private void btn_redo_Click(object sender, EventArgs e)  
  82.         {  
  83.             string ts = string.Empty;  
  84.             int lastlen = 0;  
  85.             textBox1.TextChanged -= this.textBox1_TextChanged;  
  86.             if (redoList.Count > 0)  
  87.             {  
  88.                 ts = redoList[redoList.Count - 1].sText;  
  89.                 lastlen = redoList[redoList.Count - 1].iLastLen;  
  90.                 textBox1.Text = textBox1.Text + ts;  
  91.                 undoList.Add(redoList[redoList.Count - 1]);  
  92.                 redoList.RemoveAt(redoList.Count - 1);  
  93.             }  
  94.             textBox1.TextChanged += this.textBox1_TextChanged;  
  95.         }  
  96.     }  
  97.     struct UnDoInfo  
  98.     {  
  99.         //每次增加的新字符串,不是整个edit的字符串  
  100.         public string sText;  
  101.         //上一次edit内容的长度  
  102.         public int iLastLen;  
  103.     }  
  104. }  

 

 

VC代码

 

[cpp] view plaincopy
 
  1. 头文件{DataDefined.h}  
  2. struct UnDoInfo  
  3. {  
  4.     CString sText;  
  5.     int iLastLen;  
  6. };  
  7. CArray<UnDoInfo*,UnDoInfo*&> undoList;  
  8. CArray<UnDoInfo*,UnDoInfo*&> redoList;  
  9. void AddUnDoAction(CString s,int lastlen);  
  10. void AddReDoAction(UnDoInfo* info);  
  11. 主cpp文件,其中要为CEdit指定内容变化响应事件{ON_EN_CHANGE(IDC_EDIT1, &CTest_c_MFCDlg::OnEnChangeEdit1)}  
  12. #include "DataDefined.h"  
  13. BOOL CTest_c_MFCDlg::OnInitDialog()  
  14. {  
  15.     CDialog::OnInitDialog();  
  16.     //这里省略自动生成代码  
  17.     // TODO: 这里添加初始的信息  
  18.     CEdit* edit = (CEdit*)GetDlgItem(IDC_EDIT1);  
  19.     CString s;  
  20.     edit->GetWindowTextW(s);  
  21.     AddUnDoAction(s,0);  
  22.     return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE  
  23. }  
  24. bool canchange = true; //这个控制是否触发CEdit的内容变化事件  
  25. //添加到undo列表,s:新字符串,lastlen:上一次整个长度  
  26. void AddUnDoAction(CString s,int lastlen)  
  27. {  
  28.     UnDoInfo* info = new UnDoInfo;  
  29.     info->iLastLen = lastlen + s.GetLength();  
  30.     info->sText = s;  
  31.     undoList.Add(info);  
  32. }  
  33. //添加到redo列表  
  34. void AddReDoAction(UnDoInfo* info)  
  35. {  
  36.     redoList.Add(info);  
  37. }  
  38. //undo按钮点击事件  
  39. void CTest_c_MFCDlg::OnBnClickedundo()  
  40. {  
  41.     // TODO: 在此添加控件通知处理程序代码  
  42.     canchange = false;  
  43.     CString s,ts;  
  44.     int lastlen;  
  45.     CEdit* edit = (CEdit*)GetDlgItem(IDC_EDIT1);  
  46.     if (undoList.GetCount() > 1)  
  47.     {  
  48.         edit->GetWindowTextW(s);  
  49.         ts = undoList.GetAt(undoList.GetCount()-1)->sText;  
  50.         lastlen = undoList.GetAt(undoList.GetCount() - 1)->iLastLen;  
  51.         if (lastlen > 0)  
  52.             s.Delete(lastlen-1,ts.GetLength());  
  53.         else  
  54.             s = "";  
  55.         edit->SetWindowTextW(s);  
  56.         AddReDoAction(undoList.GetAt(undoList.GetCount() - 1));  
  57.         undoList.RemoveAt(undoList.GetCount() - 1);  
  58.     }  
  59.     canchange = true;  
  60. }  
  61. //redo按钮点击事件  
  62. void CTest_c_MFCDlg::OnBnClickedredo()  
  63. {  
  64.     // TODO: 在此添加控件通知处理程序代码  
  65.     canchange = false;  
  66.     CString s,ts;  
  67.     int lastlen;  
  68.     CEdit* edit = (CEdit*)GetDlgItem(IDC_EDIT1);  
  69.     if (redoList.GetCount() > 0)  
  70.     {  
  71.         edit->GetWindowTextW(s);  
  72.         ts = redoList.GetAt(redoList.GetCount() - 1)->sText;  
  73.         lastlen = redoList.GetAt(redoList.GetCount() - 1)->iLastLen;  
  74.         edit->SetWindowTextW(s + ts);  
  75.         undoList.Add(redoList.GetAt(redoList.GetCount() - 1));  
  76.         redoList.RemoveAt(redoList.GetCount() - 1);  
  77.     }  
  78.     canchange = true;  
  79. }  
  80. //CEdit的change处理代码  
  81. void CTest_c_MFCDlg::OnEnChangeEdit1()  
  82. {  
  83.     // TODO:  如果该控件是 RICHEDIT 控件,则它将不会  
  84.     // 发送该通知,除非重写 CDialog::OnInitDialog()  
  85.     // 函数并调用 CRichEditCtrl().SetEventMask(),  
  86.     // 同时将 ENM_CHANGE 标志“或”运算到掩码中。  
  87.     // TODO:  在此添加控件通知处理程序代码  
  88.     if (!canchange) return;  
  89.     CString s;  
  90.     int lastlen;  
  91.     lastlen = ((UnDoInfo*)undoList.GetAt(undoList.GetCount()-1))->iLastLen;  
  92.     CEdit* edit = (CEdit*)GetDlgItem(IDC_EDIT1);      
  93.     edit->GetWindowTextW(s);  
  94.     s = s.Mid(lastlen,s.GetLength()-lastlen);  
  95.     AddUnDoAction(s,lastlen);  
  96. }  

 参考:

http://blog.csdn.net/bdmh/article/details/6426564

posted @ 2014-09-18 21:27  findumars  Views(861)  Comments(0Edit  收藏  举报