談到代碼編輯器,基本功能的“撤銷與重做”是必不可少的骡和。
剛好最近看了設(shè)計(jì)模式的“命令模式”苗缩,做這個(gè)倒是正好
簡單來說,就是把所以可以撤銷的方法封裝成類
這里有個(gè)簡單的測試?yán)诱鳎菔玖擞谩懊钅J健睂?shí)現(xiàn)的“撤銷”功能
這里是兩個(gè)基本接口
// 命令接口脏款,所有能被編輯器接受命令都從這里繼承
public interface ICommand
{
void Execute();
}
// 可撤銷的命令借口,所有可撤銷的命令都從這里繼承
public interface IUndoCommand : ICommand
{
void Undo();
}
接下來是具體的命令
// 插入一個(gè)字符到編輯器的命令
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裤园,這個(gè)馬上說撤师,其他的就很簡單了,實(shí)現(xiàn)了接口的兩個(gè)函數(shù)Execute和Undo
而且這兩個(gè)實(shí)際上函數(shù)都是調(diào)用的CodeManager的函數(shù)拧揽,所以挺簡單了
// 管理文本的類剃盾,所有對(duì)文檔的操作都在這里實(shí)現(xiàn)
public class CodeManager
{
private Stack<IUndoCommand> undoCommands; // 保存執(zhí)行后腺占,可以撤銷的命令
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();
}
// 執(zhí)行命令痒谴,并且添加命令到堆棧中
public void Execute(ICommand cmd)
{
cmd.Execute();
redoCommands.Clear(); // 當(dāng)輸入一個(gè)新的命令后衰伯,要清除可重做的命令。因?yàn)橹刈雒顟?yīng)該只是在撤銷命令執(zhí)行后闰歪,才能使用的嚎研。具體可以看看其它編輯器,然后自己試試
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);
}
// 具體的對(duì)文本的操作函數(shù)
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];
}
}
這個(gè)類比較簡單库倘,代碼都很容易看懂
// 編輯器
public class Coder
{
public CodeManager codeManager;
public Coder()
{
codeManager = new CodeManager();
}
// 插入字符函數(shù)临扮,實(shí)例化具體的命令,并且讓CodeManager去執(zhí)行
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類也很簡單教翩,都是調(diào)用CodeManager的函數(shù)
到這里位置杆勇,撤銷與重做功能就完成了
新建一個(gè)窗口,拖一個(gè)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里實(shí)現(xiàn)一個(gè)函數(shù)彪笼,來間接的獲取Text钻注,但測試下就無所謂了
}
這個(gè)函數(shù)里,用0-9的數(shù)字鍵來模擬輸入字符配猫,用Z鍵模擬“撤銷”幅恋,用Y鍵模擬“重做”
好了,執(zhí)行試試泵肄,輸入捆交,撤銷,重做腐巢。
怎么樣品追,很簡單對(duì)吧(當(dāng)然,這里的命令都是很簡單的命令冯丙,如果涉及到命令組合肉瓦,要難很多)
本來到這里就結(jié)束的,但可能有人會(huì)問胃惜,怎么實(shí)現(xiàn)其它功能风宁。那這里我就在實(shí)現(xiàn)一個(gè)刪除字符的命令,剛好就與輸入命令相反
很簡單蛹疯,只要繼承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中加入函數(shù)
public void RemoveCharacter(int index)
{
RemoveCharacterCommand cmd = new RemoveCharacterCommand(codeManager, index);
codeManager.Execute(cmd);
}
public void SubtractCharacter()
{
RemoveCharacter(codeManager.Text.Length - 1);
}
沒做戒财,就這點(diǎn)代碼,沒了(可以看到捺弦,都是調(diào)用CodeManager里的函數(shù))
然后再switch(e.KeyCode)的分支中饮寞,加入一句
case Keys.Back:
coder.SubtractCharacter();
break;
就行了孝扛,試試效果吧