文字录入无限制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) ...
随机推荐
- python2.X现在不能安装Django了:Collecting django Using cached Django-2.0.tar.gz
使用pip安装django2: pip install django 报错: Collecting django Using cached Django-2.0.tar.gz Complete ...
- java.util.Arrays
package com.etc.Arrays; import java.util.Arrays; public class TestArraysClass { public static void m ...
- HTML+CSS(11)
n CSS背景属性 Background-color:背景色. Background-image:背景图片地址.如:background-image:url(images/bg.gif;) Back ...
- 最简单的多线程死锁案例代码(Java语言)
package com.thread.test; public class DeadLock { private static Object firstMonitor = new Object(); ...
- Deutsch lernen (14)
1. das Abseits, - 越位 Der Linienrichter winkte Abseits. winken - winkte - gewunken 示意 2. abs ...
- AI:恐怖谷理论的陷阱
科学人的小品:恐怖谷:娃娃为什么很可怕? 一.恐怖的来源 恐怖的来源:美学概念.思想对安全的认识,映射到美学领域,转化为美和丑.恐怖,是一种精心掩饰的丑陋. 二.桑尼与C3PO 桑尼更接近于人,为什么 ...
- semiautomatic annotated tools
在进行实验图像取样时,可能会用到大量的标签样本,拍摄大量图片进行手工标注要消耗大量时间,半自动化的标注工具可以节省一些时间. 原文链接:http://blog.sina.com.cn/s/blog_6 ...
- Android 性能测试初探(五)
书接上文 Android 性能测试初探之 GPU(四) 前文说了的一些性能测试项大家可能都听说,接下来我们聊聊大家不常关注的测试项- 功耗 . 功耗测试主要从以下几个方面入手进行测试 测试手机安装目标 ...
- UI Testing
UI Test能帮助我们去验证一些UI元素的属性和状态.Apple 在 Xcode 7 中新加入了一套 UI Testing 的工具,其目的就是解决自动化UI测试这个问题.新的 UI Testing ...
- [jzoj 5775]【NOIP2008模拟】农夫约的假期 (前缀和+递推)
传送门 Description 在某国有一个叫农夫约的人,他养了很多羊,其中有两头名叫mm和hh,他们的歌声十分好听,被当地人称为"魔音"······ 农夫约也有自己的假期呀!他要 ...