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

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

作者: UnSkyToo | 来源:发表于2017-09-19 11:18 被阅读0次

    谈到代码编辑器,基本功能的“撤销与重做”是必不可少的。
    刚好最近看了设计模式的“命令模式”,做这个倒是正好
    简单来说,就是把所以可以撤销的方法封装成类
    这里有个简单的测试例子,演示了用“命令模式”实现的“撤销”功能
    这里是两个基本接口

    // 命令接口,所有能被编辑器接受命令都从这里继承  
    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;
    

    就行了,试试效果吧

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/inrwsxtx.html