自己动手设计代码编辑器——(三)撤销与重做

来源:互联网 发布:怎么在淘宝买假学生证 编辑:程序博客网 时间:2024/05/16 09:31

谈到代码编辑器,基本功能的“撤销与重做”是必不可少的。


刚好最近看了设计模式的“命令模式”,做这个倒是正好


简单来说,就是把所以可以撤销的方法封装成类


这里有个简单的测试例子,演示了用“命令模式”实现的“撤销”功能


这里是两个基本接口

// 命令接口,所有能被编辑器接受命令都从这里继承public interface ICommand{void Execute();}

// 可撤销的命令借口,所有可撤销的命令都从这里继承public interface IUndoCommand : ICommand{void Undo();}

接下来是具体的命令

// 插入一个字符到编辑器的命令public class InsertCharacterCommand : IUndoCommand{private CodeManager codeManager;private int index;private char ch;public InsertCharacterCommand(CodeManager setCodeManager, int setIndex, char setCh){this.codeManager = setCodeManager;this.index = setIndex;this.ch = setCh;}public void Execute(){codeManager.InserCharacter(index, ch);}public void Undo(){codeManager.RemoveCharacter(index);}}

这里用到了CodeManager,这个马上说,其他的就很简单了,实现了借口的两个函数Execute和Undo

而且这两个实际上函数都是调用的CodeManager的函数,所以挺简单了


// 管理文本的类,所有对文档的操作都在这里实现public class CodeManager{private Stack<IUndoCommand> undoCommands; // 保存执行后,可以撤销的命令private Stack<IUndoCommand> redoCommands; // 保存撤销后,可以重做的命令private StringBuilder text; // 保存代码的地方public string Text{get{return text.ToString();}}public CodeManager(){undoCommands = new Stack<IUndoCommand>();redoCommands = new Stack<IUndoCommand>();text = new StringBuilder();}// 执行命令,并且添加命令到堆栈中public void Execute(ICommand cmd){cmd.Execute();redoCommands.Clear(); // 当输入一个新的命令后,要清除可重做的命令。因为重做命令应该只是在撤销命令执行后,才能使用的。具体可以看看其它编辑器,然后自己试试if( cmd is IUndoCommand ){undoCommands.Push(cmd as IUndoCommand);}else{undoCommands.Clear();}}// 撤销public void Undo(){if ( undoCommands.Count == 0 ){MessageBox.Show("不能撤销了");return;}IUndoCommand cmd = undoCommands.Pop();cmd.Undo();redoCommands.Push(cmd);}// 重做public void Redo(){if ( redoCommands.Count == 0 ){MessageBox.Show("不能重做了");return;}IUndoCommand cmd = redoCommands.Pop();cmd.Execute();undoCommands.Push(cmd);}// 具体的对文本的操作函数public void InserCharacter(int index, char ch){text.Insert(index, ch);}public void RemoveCharacter(int index){text.Remove(index, 1);}public char GetCharacter(int index){return text[index];}}

这个类比较简单,代码都很容易看懂


// 编辑器public class Coder{public CodeManager codeManager;public Coder(){codeManager = new CodeManager();}// 插入字符函数,实例化具体的命令,并且让CodeManager去执行public void InsertCharacter(int index, char ch){InsertCharacterCommand cmd = new InsertCharacterCommand(codeManager, index, ch);codeManager.Execute(cmd);}public void AppendCharacter(char ch){InsertCharacter(codeManager.Text.Length, ch);}public void Undo(){codeManager.Undo();}public void Redo(){codeManager.Redo();}}

Coder类就是具体和用户打交道的类

Coder类也很简单,都是调用CodeManager的函数


到这里位置,撤销与重做功能就完成了



新建一个窗口,拖一个Label控件,在窗口中输入如下代码,可以看看效果


void MainFormKeyDown(object sender, KeyEventArgs e){switch ( e.KeyCode ){case Keys.D0:case Keys.D1:case Keys.D2:case Keys.D3:case Keys.D4:case Keys.D5:case Keys.D6:case Keys.D7:case Keys.D8:case Keys.D9:coder.AppendCharacter(e.KeyCode.ToString()[1]);break;case Keys.Z:coder.Undo();break;case Keys.Y:coder.Redo();break;default:break;}label1.Text = coder.codeManager.Text; // 这里显然不该这样,正确的做法是在Coder里实现一个函数,来间接的获取Text,但测试下就无所谓了}

这个函数里,用0-9的数字键来模拟输入字符,用Z键模拟“撤销”,用Y键模拟“重做”


好了,执行试试,输入,撤销,重做。

怎么样,很简单对吧(当然,这里的命令都是很简单的命令,如果涉及到命令组合,要难很多)


本来到这里就结束的,但可能有人会问,怎么实现其它功能。那这里我就在实现一个删除字符的命令,刚好就与输入命令相反

很简单,只要继承IUndoCommand命令就行


// 删除字符命令public class RemoveCharacterCommand : IUndoCommand{private CodeManager codeManager;private int index;private char ch;public RemoveCharacterCommand(CodeManager setCodeManager, int setIndex){this.codeManager = setCodeManager;this.index = setIndex;this.ch = ' ';}public void Execute(){this.ch = codeManager.GetCharacter(index);codeManager.RemoveCharacter(index);}public void Undo(){codeManager.InserCharacter(index, ch);}}


然后再Coder中加入函数

public void RemoveCharacter(int index){RemoveCharacterCommand cmd = new RemoveCharacterCommand(codeManager, index);codeManager.Execute(cmd);}public void SubtractCharacter(){RemoveCharacter(codeManager.Text.Length - 1);}




没做,就这点代码,没了(可以看到,都是调用CodeManager里的函数)


然后再switch(e.KeyCode)的分支中,加入一句

case Keys.Back:

coder.SubtractCharacter();

break;


就行了,试试效果吧

原创粉丝点击