文字录入无限制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) ...
随机推荐
- indeed 4.22 第一次网测
1.第一题 没有看 2. 由于数据范围很小,所以每一层需要全排列,寻找最小的花费,然后所有层加起来就是最后的结果. #include<bits/stdc++.h> #define pb p ...
- SQLServer2008 将“单个用户”改为“多用户”
一开始是要想要分离掉数据库,然后将其删除 不知道为什么一直分离不了,试了很多次,又尝试直接删除 结果数据库突然显示成了“单个用户” 尝试查看其属性,或者“新建查询”也都报错,提示已经有其他用户建立了连 ...
- 分布式文件管理系统MooseFS在centOS 7中的安装
首先,MooseFS是做什么的在这边不做具体详述,这边主要记录一下我在自己部署MooseFS中遇到的问题和步骤(大部分参考的其他博客或者资料) 首先是准备资源,MooseFS的最新安装包可以去官网下载 ...
- 读书笔记「Python编程:从入门到实践」_5.if语句
5.1 一个简单示例 cars = ['audi', 'bmw', 'subaru', 'toyota'] for car in cars: if car == 'bmw': print(car.up ...
- swift-UITextfield控件的基本属性设置
//1.初始化UITextField let userName=UITextField(frame: CGRectMake(0, 100, 100, 100)); //2.将文本框userName添加 ...
- PY简易爬虫
然而,实用性很差,仅仅是能用而已. 已知bug: 由于土啬的问题,经常会炸掉.网络不稳定导致各种Connection Aborted/SSLError: EOF occurred in violati ...
- C语言开发框架、printf(day02)
C语言里包含以.c作为扩展名的文件,这种 文件叫源文件.C语言程序的绝大部分内容 应该记录在源文件里. C语言里还包括以.h作为扩展名的文件,这种 文件叫头文件. C语言程序里可以直接使用数字和加减乘 ...
- 51nod1289 大鱼吃小鱼
有N条鱼每条鱼的位置及大小均不同,他们沿着X轴游动,有的向左,有的向右.游动的速度是一样的,两条鱼相遇大鱼会吃掉小鱼.从左到右给出每条鱼的大小和游动的方向(0表示向左,1表示向右).问足够长的时间之后 ...
- 51nod1183 编辑距离【动态规划】
编辑距离,又称Levenshtein距离(也叫做Edit Distance),是指两个字串之间,由一个转成另一个所需的最少编辑操作次数.许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除 ...
- [USACO10DEC]宝箱Treasure Chest
区间DP,但是卡空间. n2的就是f[i,j]=sum[i,j]-min(f[i+1][j],f[i][j-1])表示这个区间和减去对手取走的最多的. 但是空间是64MB,就很难受 发现一定是由大区间 ...