文字录入无限制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) ...
随机推荐
- String,创建对象问题
String str=new String("aaa"); 这行代码究竟创建了几个String对象呢?答案是2个,而不是3个.由于new String("aaa" ...
- pinpoint 磁盘不足的坑
观察 pinpoint hbase 数据存储目录default中各个表的大小 TraceV2 15G ApplicationTraceIndex 15G major_compact的操作目的 合并文件 ...
- jquery插件之倒计时-团购秒杀
1.1 帮助文档关键字 倒计时 秒杀 timer 1.2. 使用场景 这样的倒计时在购物网站中会经常使用到,比如秒杀,限时抢购,确认收货倒计时. 这个功能并不难实现,就是利用js的定时执行,搜了 ...
- github添加公钥出现 github ssh key Key is invalid. Ensure you've copied the file correctly的解决办法
因为在公钥查看的时候可能是利用了vim明明查看,所以会有换行,导致这个错误,解决方法是用cat命令查看文件,或者其他方式查看,总之公钥不能有换行.
- 【Oracle】RedHat 6.5 安装 11gR2数据库
1. 挂载操作系统光盘 [root@drz ~]# mount /dev/cdrom /mnt mount: block device /dev/sr0 is write-protected, mou ...
- 小功能__tab实录
作为一个没有js基础的人来说,写一个小功能确实麻烦,也很累,从一个demo中发现details标签完美的实现菜单折叠功能,而不用费劲写好多li.div.js.发现html也是好厉害的.看来以后回家要多 ...
- Luogu P1365 WJMZBMR打osu! / Easy
概率期望专题首杀-- 毒瘤dp 首先根据数据范围推断出复杂度在O(n)左右 但不管怎么想都是n^2-- 晚上躺在床上吃东西的时候(误)想到之前有几道dp题是通过前缀和优化的 而期望的可加性又似乎为此创 ...
- 测试模式 windows2008 内部版本7601
win server 2008 r2 enterprise 64位系统. 最近手贱,对服务器进行了一下更新,结果傻叉了,这是什么鬼,明明显示已经激活的,但就是有这么一串碍眼的字幕. 电脑右下角居然出现 ...
- 【转载】Jmeter之Bean shell
一.什么是Bean Shell BeanShell是一种完全符合Java语法规范的脚本语言,并且又拥有自己的一些语法和方法; BeanShell是一种松散类型的脚本语言(这点和JS类似); BeanS ...
- 有关微信小程序
每个页面都要在app.json中配置 "pages": [ "pages/index/index", "pages/list/list", ...