这里只针对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

文字录入无限制Undo,Redo的实现的更多相关文章

  1. 从Undo,Redo谈命令模式

    一般的应用软件中,通常会提供Redo和Undo的操作,比如Paint.NET中的动作面板,Word中的撤销重做,一般我们按Ctrl-Z即可回退到上次操作. 要实现上面的这一功能,最直观的想法就是,我们 ...

  2. MySQL,MariaDB:Undo | Redo [转]

    本文是介绍MySQL数据库InnoDB存储引擎重做日志漫游 00 – Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版 ...

  3. iOS: 为画板App增加 Undo/Redo(撤销/重做)操作

    这个随笔的内容以上一个随笔为基础,(在iOS中实现一个简单的画板),上一个随笔实现了一个简单的画板:   今天我们要为这个画板增加Undo/Redo操作,当画错了一笔,可以撤销它,或者撤销之后后悔了, ...

  4. [转]MySQL日志——Undo | Redo

    本文是介绍MySQL数据库InnoDB存储引擎重做日志漫游 00 – Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版 ...

  5. Undo/Redo for Qt Tree Model

    Undo/Redo for Qt Tree Model eryar@163.com Abstract. Qt contains a set of item view classes that use ...

  6. 【转载】MySQL 日志 undo | redo

    本文是介绍MySQL数据库InnoDB存储引擎重做日志漫游 00 – Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版 ...

  7. MySQL InnoDB存储引擎undo redo解析

    本文介绍MySQL数据库InnoDB存储引擎重做日志漫游 00 – Undo Log Undo Log 为了实现事务原子,在MySQL数据库InnoDB存储引擎,还使用Undo Log(简称:MVCC ...

  8. MySQL日志Undo&Redo

    00 – Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版本并发控制(简称:MVCC). - 事务的原子性(Atomi ...

  9. MySql Undo Redo

    Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版本并发控制(简称:MVCC). - 事务的原子性(Atomicity) ...

随机推荐

  1. indeed 4.22 第一次网测

    1.第一题 没有看 2. 由于数据范围很小,所以每一层需要全排列,寻找最小的花费,然后所有层加起来就是最后的结果. #include<bits/stdc++.h> #define pb p ...

  2. SQLServer2008 将“单个用户”改为“多用户”

    一开始是要想要分离掉数据库,然后将其删除 不知道为什么一直分离不了,试了很多次,又尝试直接删除 结果数据库突然显示成了“单个用户” 尝试查看其属性,或者“新建查询”也都报错,提示已经有其他用户建立了连 ...

  3. 分布式文件管理系统MooseFS在centOS 7中的安装

    首先,MooseFS是做什么的在这边不做具体详述,这边主要记录一下我在自己部署MooseFS中遇到的问题和步骤(大部分参考的其他博客或者资料) 首先是准备资源,MooseFS的最新安装包可以去官网下载 ...

  4. 读书笔记「Python编程:从入门到实践」_5.if语句

    5.1 一个简单示例 cars = ['audi', 'bmw', 'subaru', 'toyota'] for car in cars: if car == 'bmw': print(car.up ...

  5. swift-UITextfield控件的基本属性设置

    //1.初始化UITextField let userName=UITextField(frame: CGRectMake(0, 100, 100, 100)); //2.将文本框userName添加 ...

  6. PY简易爬虫

    然而,实用性很差,仅仅是能用而已. 已知bug: 由于土啬的问题,经常会炸掉.网络不稳定导致各种Connection Aborted/SSLError: EOF occurred in violati ...

  7. C语言开发框架、printf(day02)

    C语言里包含以.c作为扩展名的文件,这种 文件叫源文件.C语言程序的绝大部分内容 应该记录在源文件里. C语言里还包括以.h作为扩展名的文件,这种 文件叫头文件. C语言程序里可以直接使用数字和加减乘 ...

  8. 51nod1289 大鱼吃小鱼

    有N条鱼每条鱼的位置及大小均不同,他们沿着X轴游动,有的向左,有的向右.游动的速度是一样的,两条鱼相遇大鱼会吃掉小鱼.从左到右给出每条鱼的大小和游动的方向(0表示向左,1表示向右).问足够长的时间之后 ...

  9. 51nod1183 编辑距离【动态规划】

    编辑距离,又称Levenshtein距离(也叫做Edit Distance),是指两个字串之间,由一个转成另一个所需的最少编辑操作次数.许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除 ...

  10. [USACO10DEC]宝箱Treasure Chest

    区间DP,但是卡空间. n2的就是f[i,j]=sum[i,j]-min(f[i+1][j],f[i][j-1])表示这个区间和减去对手取走的最多的. 但是空间是64MB,就很难受 发现一定是由大区间 ...