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