电子科技大学软件学院03级02班 周银辉
在文本编辑中经常使用到“撤销”操作(Ctrl-Z),以及一个与之相应的“重复”操作(Ctrl-Y),各种不同的编辑操作都可以使用这两个操作来撤销或重复,那么如此多的编辑操作是如何被设计到一起而使得一个“撤销”按钮可以撤销各种不同的操作呢?
关键在于两点:栈 和 多态。
如何设计:
很容易想到使用栈来保持那些已经被执行的操作,当要撤销上一步操作时,只需要取出栈顶元素并执行该元素所对应的操作便可。
接踵而至的一个问题是: 栈中元素类型是什么? 由于我们需要保存各种不同的文本编辑操作,要将它们统一地保存在一起,很自然地,我们应该让这些操作有着统一的父类, 我们栈中元素的类型是该父类类型就可以了.
我们这里设计了一个接口,所有的可撤销/重复的操作都应该继承该接口:
/// <summary>
/// 可撤销重复操作接口。
/// 所有的可重复可撤销的操作都应该继承这个接口。
/// </summary>
interface IUndoableOperate
{
void Undo();
void Redo();
void Execute();
}
比如我们有一个操作Operate1,它继承了IUndoableOperate接口
/// <summary>
/// 操作1
/// </summary>
class Operate1 : IUndoableOperate
{
#region IUndoableOperate 成员
/// <summary>
/// 撤销该操作时执行
/// </summary>
public void Undo()
{
Console.WriteLine("undo operate1");
}
/// <summary>
/// 重复该操作时执行
/// </summary>
public void Redo()
{
Console.WriteLine("do operate1");
}
/// <summary>
/// 执行操作1
/// </summary>
public void Execute()
{
this.Redo();
}
#endregion
}
其它任何与Operate1类似的操作都可以放到撤销栈中,以便以后撤销。
栈中元素都是IUndoableOperate类型,那么当我们取出栈顶元素并调用其Execute()函数时,其能被正确的执行吗?答案是肯定的,这利用了多态。
现在我们可以设计一个管理器来对栈进行管理,它将记录那些被执行或被撤销的操作,并提供方法允许你对已经执行过的操作进行撤销、已经撤销的操作进行重复。


/**//// <summary>
/// 撤销重复操作管理器
/// </summary>
class UndoStackManager
{
/**//// <summary>
/// 撤销栈
/// </summary>
Stack<IUndoableOperate> un_stack = new Stack<IUndoableOperate>();
/**//// <summary>
/// 重复栈
/// </summary>
Stack<IUndoableOperate> re_stack = new Stack<IUndoableOperate>();

public void ClearStack()
{
this.un_stack.Clear();
this.re_stack.Clear();
}

/**//// <summary>
/// 获取一个值,指示是否有可撤销的操作
/// </summary>
public bool CanUndo
{
get
{
return un_stack.Count != 0;
}
}

/**//// <summary>
/// 获取一个值,指示是否有可重复的操作
/// </summary>
public bool CanRedo
{
get
{
return re_stack.Count != 0;
}
}

/**//// <summary>
/// 撤销上一操作
/// </summary>
public void Undo()
{
if (this.CanUndo)
{
IUndoableOperate op = un_stack.Pop();
op.Undo();
re_stack.Push(op);
}
}

/**//// <summary>
/// 重复被撤销的操作
/// </summary>
public void Redo()
{
if (this.CanRedo)
{
IUndoableOperate op = re_stack.Pop();
op.Redo();
un_stack.Push(op);
}
}


/**//// <summary>
/// 将某一操作存放到撤销栈中
/// </summary>
/// <param name="op"></param>
public void PushToUndoStack(IUndoableOperate op)
{
this.un_stack.Push(op);
this.re_stack.Clear();
}
}以下是完整的示例代码:
1
using System;2
using System.Collections.Generic;3
using System.Text;4

5
namespace UndoRedo6


{7
class Program8

{9
static void Main(string[] args)10

{11
//声明一个管理器12
UndoStackManager manager = new UndoStackManager();13

14
//执行一些操作15
Operate1 op1 = new Operate1();16
op1.Execute();17
manager.PushToUndoStack(op1);18

19
Operate2 op2 = new Operate2();20
op2.Execute();21
manager.PushToUndoStack(op2);22

23
//撤销执行的操作24
manager.Undo();25
manager.Undo();26

27
//重复被撤销的操作28
manager.Redo();29
manager.Redo();30
}31
}32

33

34

/**//// <summary>35
/// 可撤销重复操作接口。36
/// 所有的可重复可撤销的操作都应该继承这个接口。37
/// </summary>38
interface IUndoableOperate39

{40
void Undo();41
void Redo();42
void Execute();43
}44

45

/**//// <summary>46
/// 操作147
/// </summary>48
class Operate1 : IUndoableOperate49

{50

IUndoableOperate 成员#region IUndoableOperate 成员51

52

/**//// <summary>53
/// 撤销该操作时执行54
/// </summary>55
public void Undo()56

{57
Console.WriteLine("undo operate1");58
}59

60

/**//// <summary>61
/// 重复该操作时执行62
/// </summary>63
public void Redo()64

{65
Console.WriteLine("do operate1");66
}67

68

/**//// <summary>69
/// 执行操作170
/// </summary>71
public void Execute()72

{73
this.Redo();74
}75

76
#endregion77

78
}79

80

81

/**//// <summary>82
/// 操作183
/// </summary>84
class Operate2 : IUndoableOperate85

{86

IUndoableOperate 成员#region IUndoableOperate 成员87

88

/**//// <summary>89
/// 撤销该操作时执行90
/// </summary>91
public void Undo()92

{93
Console.WriteLine("undo operate2");94
}95

96

/**//// <summary>97
/// 重复该操作时执行98
/// </summary>99
public void Redo()100

{101
Console.WriteLine("do operate2");102
}103

104

/**//// <summary>105
/// 执行操作1106
/// </summary>107
public void Execute()108

{109
this.Redo();110
}111

112
#endregion113
}114

115

/**//// <summary>116
/// 撤销重复操作管理器117
/// </summary>118
class UndoStackManager119

{120

/**//// <summary>121
/// 撤销栈122
/// </summary>123
Stack<IUndoableOperate> un_stack = new Stack<IUndoableOperate>();124

/**//// <summary>125
/// 重复栈126
/// </summary>127
Stack<IUndoableOperate> re_stack = new Stack<IUndoableOperate>();128

129

130
public void ClearStack()131

{132
this.un_stack.Clear();133
this.re_stack.Clear();134
}135

136

/**//// <summary>137
/// 获取一个值,指示是否有可撤销的操作138
/// </summary>139
public bool CanUndo140

{141
get142

{143
return un_stack.Count != 0;144
}145
}146

147

/**//// <summary>148
/// 获取一个值,指示是否有可重复的操作149
/// </summary>150
public bool CanRedo151

{152
get153

{154
return re_stack.Count != 0;155
}156
}157

158

/**//// <summary>159
/// 撤销上一操作160
/// </summary>161
public void Undo()162

{163
if (this.CanUndo)164

{165
IUndoableOperate op = un_stack.Pop();166
op.Undo();167
re_stack.Push(op);168
}169
}170

171

/**//// <summary>172
/// 重复被撤销的操作173
/// </summary>174
public void Redo()175

{176
if (this.CanRedo)177

{178
IUndoableOperate op = re_stack.Pop();179
op.Redo();180
un_stack.Push(op);181
}182
}183

184

185

/**//// <summary>186
/// 将某一操作存放到撤销栈中187
/// </summary>188
/// <param name="op"></param>189
public void PushToUndoStack(IUndoableOperate op)190

{191
this.un_stack.Push(op);192
this.re_stack.Clear();193
}194
}195
}196

示例代码的执行结果是:
do operate1
do operate2
undo operate2
undo operate1
do operate1
do operate2
本文介绍了一种在文本编辑器中实现撤销和重复功能的方法。通过定义一个接口,所有可撤销的操作都遵循该接口,利用栈来保存操作,实现了统一管理和撤销功能。
2436

被折叠的 条评论
为什么被折叠?



