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

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

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

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

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

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

Delphi代码

  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. {
  53. 这里只简单的在OnChange事件中来添加 undo内容,实际应用中比这要复杂的多,这里只在
  54. 这里说明一下简单应用
  55. }
  56. procedure TForm1.Edit1Change(Sender: TObject);
  57. var
  58. lastlen:Integer;
  59. s:String;
  60. begin
  61. //先取得上一次的最后长度
  62. lastlen := PUndoInfo(FUnDoList.Items[FUnDoList.Count - 1]).iLastLen;
  63. //本次新录入的字符串
  64. s := Copy(Edit1.Text,lastlen+1,Length(Edit1.Text)-lastlen);
  65. //添加到undo列表
  66. AddUnDoAction(s,lastlen);
  67. end;
  68. procedure TForm1.btn_undoClick(Sender: TObject);
  69. var
  70. s,ts:string;
  71. lastlen:Integer;
  72. begin
  73. //先取消OnChange事件,否则当修改edit的内容时,会重复触发OnChange事件,导致错误
  74. Edit1.OnChange := nil;
  75. //因为最后一个是原始值,所以这里判断是否大于1
  76. if FUnDoList.Count > 1 then
  77. begin
  78. s := Edit1.Text;
  79. //取上次的值
  80. ts := PUndoInfo(FUnDoList.Items[FUnDoList.Count - 1]).sText;
  81. lastlen := PUndoInfo(FUnDoList.Items[FUnDoList.Count - 1]).iLastLen;
  82. //重新赋给edit内容
  83. Delete(s,lastlen,Length(ts));
  84. Edit1.Text := s;
  85. //添加到redo
  86. AddReDoAction(FUnDoList.Items[FUnDoList.Count - 1]);
  87. //将该项移出undo列表
  88. FUnDoList.Delete(FUnDoList.Count - 1);
  89. end;
  90. //回复OnChange事件
  91. Edit1.OnChange := Self.Edit1Change;
  92. end;
  93. procedure TForm1.AddReDoAction(p:PUndoInfo);
  94. begin
  95. FReDoList.Add(p);
  96. end;
  97. //重复动作,原理同撤销动作
  98. procedure TForm1.btn_redoClick(Sender: TObject);
  99. var
  100. s,ts:string;
  101. lastlen:Integer;
  102. begin
  103. Edit1.OnChange := nil;
  104. if FReDoList.Count > 0 then
  105. begin
  106. ts := PUndoInfo(FReDoList.Items[FReDoList.Count - 1]).sText;
  107. lastlen := PUndoInfo(FReDoList.Items[FReDoList.Count - 1]).iLastLen;
  108. Edit1.Text := Edit1.Text + ts;
  109. FUnDoList.Add(PUndoInfo(FReDoList.Items[FReDoList.Count - 1]));
  110. FReDoList.Delete(FReDoList.Count - 1);
  111. end;
  112. Edit1.OnChange := Self.Edit1Change;
  113. end;
  114. end.

C#代码

  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代码

  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

上一篇:Flask生成SECRET_KEY(密钥)的一种简单方法


下一篇:Oracle core05_事务和一致性