文字录入无限制Undo,Redo的实现
这里只针对Edit的内容做一个简单的undo,redo功能;
原理就是,将新增字符和相关信息添加到undo列表,在undo动作时,取记录信息,并在edit中删除新增的字符,然后将此动作添加到redo列表,以便恢复。
本程序只对文本框文字的顺序增加做了处理,对于任意位置的删除,复制粘贴等没有进行处理,大家可以根据实际情况完善,增加辅助信息来完成对撤销和恢复的操作。
明白了原理,对于其他的操作都是这个道理,比如你画图什么的,保留每个图形的相关信息,然后撤销恢复重画,说的简单,做起来还是需要我们动脑子的^_^
为方便查看,将所有代码写到了一个单元。
Delphi代码
- unit Unit1;
- interface
- uses
- Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
- Dialogs, StdCtrls;
- type
- TUndoInfo = record
- sText : String; //每次增加的新字符串,不是整个edit的字符串
- iLastLen : Integer;//上一次edit内容的长度
- end;
- PUndoInfo = ^TUndoInfo;
- TForm1 = class(TForm)
- Edit1: TEdit;
- btn_undo: TButton;
- btn_redo: TButton;
- procedure FormCreate(Sender: TObject);
- procedure Edit1Change(Sender: TObject);
- procedure btn_undoClick(Sender: TObject);
- procedure btn_redoClick(Sender: TObject);
- private
- { Private declarations }
- FUnDoList : TList; //undo列表
- FReDoList : TList; //redo列表
- //添加到undo列表,s:新字符串,lastlen:上一次整个长度
- procedure AddUnDoAction(s:String;lastlen:Integer);
- //添加到redo列表
- procedure AddReDoAction(p:PUndoInfo);
- public
- { Public declarations }
- end;
- var
- Form1: TForm1;
- implementation
- {$R *.dfm}
- { TForm1 }
- procedure TForm1.AddUnDoAction(s: String;lastlen:Integer);
- var
- p:PUndoInfo;
- begin
- New(p);
- p.sText := s;
- p.iLastLen := lastlen + Length(s);
- FUnDoList.Add(p);
- end;
- procedure TForm1.FormCreate(Sender: TObject);
- begin
- FUnDoList := TList.Create;
- FReDoList := TList.Create;
- //添加初始值
- AddUnDoAction(Edit1.Text,0);
- end;
- {
- 这里只简单的在OnChange事件中来添加 undo内容,实际应用中比这要复杂的多,这里只在
- 这里说明一下简单应用
- }
- procedure TForm1.Edit1Change(Sender: TObject);
- var
- lastlen:Integer;
- s:String;
- begin
- //先取得上一次的最后长度
- lastlen := PUndoInfo(FUnDoList.Items[FUnDoList.Count - 1]).iLastLen;
- //本次新录入的字符串
- s := Copy(Edit1.Text,lastlen+1,Length(Edit1.Text)-lastlen);
- //添加到undo列表
- AddUnDoAction(s,lastlen);
- end;
- procedure TForm1.btn_undoClick(Sender: TObject);
- var
- s,ts:string;
- lastlen:Integer;
- begin
- //先取消OnChange事件,否则当修改edit的内容时,会重复触发OnChange事件,导致错误
- Edit1.OnChange := nil;
- //因为最后一个是原始值,所以这里判断是否大于1
- if FUnDoList.Count > 1 then
- begin
- s := Edit1.Text;
- //取上次的值
- ts := PUndoInfo(FUnDoList.Items[FUnDoList.Count - 1]).sText;
- lastlen := PUndoInfo(FUnDoList.Items[FUnDoList.Count - 1]).iLastLen;
- //重新赋给edit内容
- Delete(s,lastlen,Length(ts));
- Edit1.Text := s;
- //添加到redo
- AddReDoAction(FUnDoList.Items[FUnDoList.Count - 1]);
- //将该项移出undo列表
- FUnDoList.Delete(FUnDoList.Count - 1);
- end;
- //回复OnChange事件
- Edit1.OnChange := Self.Edit1Change;
- end;
- procedure TForm1.AddReDoAction(p:PUndoInfo);
- begin
- FReDoList.Add(p);
- end;
- //重复动作,原理同撤销动作
- procedure TForm1.btn_redoClick(Sender: TObject);
- var
- s,ts:string;
- lastlen:Integer;
- begin
- Edit1.OnChange := nil;
- if FReDoList.Count > 0 then
- begin
- ts := PUndoInfo(FReDoList.Items[FReDoList.Count - 1]).sText;
- lastlen := PUndoInfo(FReDoList.Items[FReDoList.Count - 1]).iLastLen;
- Edit1.Text := Edit1.Text + ts;
- FUnDoList.Add(PUndoInfo(FReDoList.Items[FReDoList.Count - 1]));
- FReDoList.Delete(FReDoList.Count - 1);
- end;
- Edit1.OnChange := Self.Edit1Change;
- end;
- end.
C#代码
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Text;
- using System.Windows.Forms;
- namespace Test_CSharp_Win
- {
- public partial class Form2 : Form
- {
- List<UnDoInfo> undoList;
- List<UnDoInfo> redoList;
- public Form2()
- {
- InitializeComponent();
- undoList = new List<UnDoInfo>();
- redoList = new List<UnDoInfo>();
- //添加初始值
- AddUnDoAction(textBox1.Text, 0);
- }
- /// <summary>
- /// //添加到undo列表
- /// </summary>
- /// <param name="s">新字符串</param>
- /// <param name="lastlen">上一次整个长度</param>
- private void AddUnDoAction(string s, int lastlen)
- {
- UnDoInfo info = new UnDoInfo();
- info.sText = s;
- info.iLastLen = lastlen + s.Length;
- undoList.Add(info);
- }
- /// <summary>
- /// 添加到redo列表
- /// </summary>
- /// <param name="info"></param>
- private void AddReDoAction(UnDoInfo info)
- {
- redoList.Add(info);
- }
- private void textBox1_TextChanged(object sender, EventArgs e)
- {
- string s = string.Empty;
- int lastlen = 0;
- //先取得上一次的最后长度
- lastlen = undoList[undoList.Count - 1].iLastLen;
- //本次新录入的字符串
- s = textBox1.Text.Substring(lastlen, textBox1.Text.Length - lastlen);
- //添加到undo列表
- AddUnDoAction(s, lastlen);
- }
- private void btn_undo_Click(object sender, EventArgs e)
- {
- string s = string.Empty;
- string ts = string.Empty;
- int lastlen = 0;
- //先取消TextChanged事件,否则当修改edit的内容时,会重复触发TextChanged事件,导致错误
- textBox1.TextChanged -= this.textBox1_TextChanged;
- //因为最后一个是原始值,所以这里判断是否大于1
- if (undoList.Count > 1)
- {
- s = textBox1.Text;
- ts = undoList[undoList.Count - 1].sText;
- lastlen = undoList[undoList.Count - 1].iLastLen;
- s = s.Remove(lastlen-1, ts.Length);
- textBox1.Text = s;
- //添加到redo
- AddReDoAction(undoList[undoList.Count - 1]);
- //将该项移出undo列表
- undoList.RemoveAt(undoList.Count - 1);
- }
- //恢复TextChanged事件
- textBox1.TextChanged += this.textBox1_TextChanged;
- }
- /// <summary>
- /// 重复动作,原理同撤销动作
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void btn_redo_Click(object sender, EventArgs e)
- {
- string ts = string.Empty;
- int lastlen = 0;
- textBox1.TextChanged -= this.textBox1_TextChanged;
- if (redoList.Count > 0)
- {
- ts = redoList[redoList.Count - 1].sText;
- lastlen = redoList[redoList.Count - 1].iLastLen;
- textBox1.Text = textBox1.Text + ts;
- undoList.Add(redoList[redoList.Count - 1]);
- redoList.RemoveAt(redoList.Count - 1);
- }
- textBox1.TextChanged += this.textBox1_TextChanged;
- }
- }
- struct UnDoInfo
- {
- //每次增加的新字符串,不是整个edit的字符串
- public string sText;
- //上一次edit内容的长度
- public int iLastLen;
- }
- }
VC代码
- 头文件{DataDefined.h}
- struct UnDoInfo
- {
- CString sText;
- int iLastLen;
- };
- CArray<UnDoInfo*,UnDoInfo*&> undoList;
- CArray<UnDoInfo*,UnDoInfo*&> redoList;
- void AddUnDoAction(CString s,int lastlen);
- void AddReDoAction(UnDoInfo* info);
- 主cpp文件,其中要为CEdit指定内容变化响应事件{ON_EN_CHANGE(IDC_EDIT1, &CTest_c_MFCDlg::OnEnChangeEdit1)}
- #include "DataDefined.h"
- BOOL CTest_c_MFCDlg::OnInitDialog()
- {
- CDialog::OnInitDialog();
- //这里省略自动生成代码
- // TODO: 这里添加初始的信息
- CEdit* edit = (CEdit*)GetDlgItem(IDC_EDIT1);
- CString s;
- edit->GetWindowTextW(s);
- AddUnDoAction(s,0);
- return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
- }
- bool canchange = true; //这个控制是否触发CEdit的内容变化事件
- //添加到undo列表,s:新字符串,lastlen:上一次整个长度
- void AddUnDoAction(CString s,int lastlen)
- {
- UnDoInfo* info = new UnDoInfo;
- info->iLastLen = lastlen + s.GetLength();
- info->sText = s;
- undoList.Add(info);
- }
- //添加到redo列表
- void AddReDoAction(UnDoInfo* info)
- {
- redoList.Add(info);
- }
- //undo按钮点击事件
- void CTest_c_MFCDlg::OnBnClickedundo()
- {
- // TODO: 在此添加控件通知处理程序代码
- canchange = false;
- CString s,ts;
- int lastlen;
- CEdit* edit = (CEdit*)GetDlgItem(IDC_EDIT1);
- if (undoList.GetCount() > 1)
- {
- edit->GetWindowTextW(s);
- ts = undoList.GetAt(undoList.GetCount()-1)->sText;
- lastlen = undoList.GetAt(undoList.GetCount() - 1)->iLastLen;
- if (lastlen > 0)
- s.Delete(lastlen-1,ts.GetLength());
- else
- s = "";
- edit->SetWindowTextW(s);
- AddReDoAction(undoList.GetAt(undoList.GetCount() - 1));
- undoList.RemoveAt(undoList.GetCount() - 1);
- }
- canchange = true;
- }
- //redo按钮点击事件
- void CTest_c_MFCDlg::OnBnClickedredo()
- {
- // TODO: 在此添加控件通知处理程序代码
- canchange = false;
- CString s,ts;
- int lastlen;
- CEdit* edit = (CEdit*)GetDlgItem(IDC_EDIT1);
- if (redoList.GetCount() > 0)
- {
- edit->GetWindowTextW(s);
- ts = redoList.GetAt(redoList.GetCount() - 1)->sText;
- lastlen = redoList.GetAt(redoList.GetCount() - 1)->iLastLen;
- edit->SetWindowTextW(s + ts);
- undoList.Add(redoList.GetAt(redoList.GetCount() - 1));
- redoList.RemoveAt(redoList.GetCount() - 1);
- }
- canchange = true;
- }
- //CEdit的change处理代码
- void CTest_c_MFCDlg::OnEnChangeEdit1()
- {
- // TODO: 如果该控件是 RICHEDIT 控件,则它将不会
- // 发送该通知,除非重写 CDialog::OnInitDialog()
- // 函数并调用 CRichEditCtrl().SetEventMask(),
- // 同时将 ENM_CHANGE 标志“或”运算到掩码中。
- // TODO: 在此添加控件通知处理程序代码
- if (!canchange) return;
- CString s;
- int lastlen;
- lastlen = ((UnDoInfo*)undoList.GetAt(undoList.GetCount()-1))->iLastLen;
- CEdit* edit = (CEdit*)GetDlgItem(IDC_EDIT1);
- edit->GetWindowTextW(s);
- s = s.Mid(lastlen,s.GetLength()-lastlen);
- AddUnDoAction(s,lastlen);
- }
参考:
http://blog.csdn.net/bdmh/article/details/6426564
文字录入无限制Undo,Redo的实现的更多相关文章
- 从Undo,Redo谈命令模式
一般的应用软件中,通常会提供Redo和Undo的操作,比如Paint.NET中的动作面板,Word中的撤销重做,一般我们按Ctrl-Z即可回退到上次操作. 要实现上面的这一功能,最直观的想法就是,我们 ...
- MySQL,MariaDB:Undo | Redo [转]
本文是介绍MySQL数据库InnoDB存储引擎重做日志漫游 00 – Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版 ...
- iOS: 为画板App增加 Undo/Redo(撤销/重做)操作
这个随笔的内容以上一个随笔为基础,(在iOS中实现一个简单的画板),上一个随笔实现了一个简单的画板: 今天我们要为这个画板增加Undo/Redo操作,当画错了一笔,可以撤销它,或者撤销之后后悔了, ...
- [转]MySQL日志——Undo | Redo
本文是介绍MySQL数据库InnoDB存储引擎重做日志漫游 00 – Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版 ...
- 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 ...
- 【转载】MySQL 日志 undo | redo
本文是介绍MySQL数据库InnoDB存储引擎重做日志漫游 00 – Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版 ...
- MySQL InnoDB存储引擎undo redo解析
本文介绍MySQL数据库InnoDB存储引擎重做日志漫游 00 – Undo Log Undo Log 为了实现事务原子,在MySQL数据库InnoDB存储引擎,还使用Undo Log(简称:MVCC ...
- MySQL日志Undo&Redo
00 – Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版本并发控制(简称:MVCC). - 事务的原子性(Atomi ...
- MySql Undo Redo
Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版本并发控制(简称:MVCC). - 事务的原子性(Atomicity) ...
随机推荐
- 2015 多校赛 第五场 1010 (hdu 5352)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5352 看题看得心好累. 题目大意: 给出 n 个点,依次执行 m 次操作:输入“1 x”时,表示将与 ...
- MySQL命令学习之技巧(博主推荐)
关于,这篇博客呢,是
- [转载]Android平台第三方应用分享到微信开发
一.申请APPID 微信公共平台和微博分享一样,也需要申请一个ID,来作为调起微信.分享到微信的唯一标识. 申请微信APPID可以到微信平台http://open.weixin.qq.com/app/ ...
- (转)JavaScript深入之从原型到原型链
构造函数创建对象 我们先使用构造函数创建一个对象: function Person() { } var person = new Person(); person.name = 'Kevin'; co ...
- Mysql Event 自动分表
create table TempComments Like dycomments; 上述 SQL语句创建的新表带有原表的所有属性,主键,索引等. 自动分表怎么做呢? 使用上述语句自动创建分表. 那么 ...
- day34-3 类和对象小知识
目录 属性查找顺序 类与对象的绑定方法 类与数据类型 对象的高度整合 属性查找顺序 属性查找顺序:先从对象自身查找,对象没有就去类中查找,类中没有则报错 class Student: name = ' ...
- kvm之 virt-install工具命令详解
一.virt-install是一个命令行工具,它能够为KVM.Xen或其它支持libvrit API的hypervisor创建虚拟机并完成GuestOS安装:此外,它能够基于串行控制台.VNC或SDL ...
- 日记——OI历程
学OI也一年多了(2015.12-),一直没学出个像样的东西.相比dalao们,我还是弱爆了. ljj,qyf,yyf三位三区dalao. xxy,myj两位三区学长dalao. 稍微总结一下前一段时 ...
- 线程同步、信号量、system v IPC
一.线程同步 条件变量 什么是条件变量? 线程A等待某个条件成立,条件成立,线程A才继续向下执行.线程B的执行使条件成立,条件成立以后唤醒线程A,以继续执行.这个条件就是条件变量. pthread_c ...
- 洛谷P1101 单词方阵【DFS】
给一n \times nn×n的字母方阵,内可能蕴含多个"yizhong"单词.单词在方阵中是沿着同一方向连续摆放的.摆放可沿着 88 个方向的任一方向,同一单词摆放时不再改变方向 ...