无操作次数限制的 Undo/Redo 实现方案

一般的文档软件,图形设计工具,都会有Redo/Undo (即重做/撤消)功能,可是如何也在自己的应用程序当中实现这样的功能,而且是没有操作次数地Redo/Undo?
此时“软件设计模式”就显得很重要。
这里实现的Redo/Undo主要应用的“命令模式”与“备忘录模式”。
《C#设计模式》一书里讲到只用备忘录模式来实现,我看了之后觉得那种方法太烦琐了,我们可以利用

C#2.0的新特性来使之简化。
首先,如果要让我们的每一步操作都是可撤消、重做的,那么我们必须要将这些操作涉及的代码“封装

”到一个方法里去,以供以后调用。而且有撤消、重做,那么必须有两部分代码:
1.正向操作的代码 DoOperation
2.反向(撤消)的代码 UndoOperation.

然后,要实现无次数限制的撤消、重做,我们应该要用两个栈(Stack)来保存操作。

下面的UML 类图:


下面是实现代码:
1.ICommand.cs

    public interface ICommand
    
{
        ActionHandler DoOperation 
get;set;}
        ActionHandler UndoOperation 
get;set;}

        
void Do();
        
void Undo();
    }

2. 操作委托:

public delegate void ActionHandler();

3. Command.cs

    public class Command:ICommand 
    
{
        
private ActionHandler doOperation;

        
private ActionHandler undoOperation;

        
ICommand Members
    }

4. CommandManager.cs

    public static class CommandManager
    
{
        
private static Stack<ICommand> redoMementos;

        
private static Stack<ICommand> undoMementos;

        
static CommandManager()
        
{
            redoMementos 
= new Stack<ICommand>();
            undoMementos 
= new Stack<ICommand>();
        }


        
/// <summary>
        
/// 先清空Redo栈,将Command对象压入栈
        
/// </summary>
        
/// <param name="command">Command对象</param>

        public static void AddNewCommand(ICommand command)
        
{
            redoMementos.Clear();
            undoMementos.Push(command);
        }


        
/// <summary>
        
/// 从Redo栈中弹出一个Command对象,执行Do操作,并且将该Command对象压入Undo栈
        
/// </summary>

        public static void Redo()
        
{
            
try
            
{
                
if (redoMementos.Count > 0)
                
{
                    ICommand command 
= redoMementos.Pop();
                    
if (command != null)
                    
{
                        command.Do();
                        undoMementos.Push(command);
                    }

                }

            }

            
catch (InvalidOperationException invalidOperationException)
            
{
                Console.WriteLine(invalidOperationException.Message);
            }

        }


        
/// <summary>
        
/// 从Undo栈中弹出一个Command对象,执行Undo操作,并且将该Command对象压入Redo栈
        
/// </summary>

        public static void Undo()
        
{
            
try
            
{
                
if (undoMementos.Count > 0)
                
{
                    ICommand command 
= undoMementos.Pop();
                    
if (command != null)
                    
{
                        command.Undo();
                        redoMementos.Push(command);
                    }

                }

            }

            
catch (InvalidOperationException invalidOperationException)
            
{
                Console.WriteLine(invalidOperationException.Message);
            }

        }


        
/// <summary>
        
/// 清空Redo栈与Undo栈
        
/// </summary>

        public static void ClearAll()
        
{
            ClearRedoStack();
            ClearUndoStack();
        }


        
/// <summary>
        
/// 清空Redo栈
        
/// </summary>

        public static void ClearRedoStack()
        
{
            redoMementos.Clear();
        }


        
/// <summary>
        
/// 清空Undo栈
        
/// </summary>

        public static void ClearUndoStack()
        
{
            undoMementos.Clear();
        }


        
/// <summary>
        
/// 当前在Redo栈中能重做的步骤数
        
/// </summary>

        public static int RedoStepsCount
        
{
            
get
            
{
                
return redoMementos.Count;
            }

        }


        
/// <summary>
        
/// 当前在Undo栈中能撤消的步骤数
        
/// </summary>

        public static int UndoStepsCount
        
{
            
get
            
{
                
return undoMementos.Count;
            }

        }

    }


5. Redo/Undo测试UI程序(Form), 在Form上添加三个Button控件:btnNew, btnRedo, btnUndo, 逻辑代

码如下:

    public partial class Form1 : Form
    
{
        
private int x=10;
        
private int y=10;
        
public Form1()
        
{
            InitializeComponent();
        }


        
private void btnNew_Click(object sender, EventArgs e)
        
{
            btnRedo.Enabled 
= false;
            Command command 
= new Command();
            Label label 
= new Label();
            Random r 
= new Random();
            label.Text 
= ((char)r.Next(6599)).ToString();
            label.Font 
= new Font("Arial"16,FontStyle.Bold);
            label.AutoSize 
= true;
            label.Location 
= new Point(x, y);
            
/*
             * 使用匿名委托,更加简单,而且匿名委托方法里还可以使用外部变量。
             
*/

            command.DoOperation 
= delegate()
            
{
                
//Do或者Redo操作会执行到的代码
                x += label.Width + 10;
                y 
+= label.Height + 10;
                
this.Controls.Add(label);
            }
;
            command.UndoOperation 
= delegate()
            
{
                
//Undo操作会执行到的代码
                x -= label.Width + 10;
                y 
-= label.Height + 10;
                
this.Controls.Remove(label);
            }
;
            command.Do();
//执行DoOperation相应代码
            CommandManager.AddNewCommand(command);
        }


        
private void btnRedo_Click(object sender, EventArgs e)
        
{
            btnUndo.Enabled 
= true;
            CommandManager.Redo();
            
if (CommandManager.RedoStepsCount == 0)
            
{
                btnRedo.Enabled 
= false;
            }

        }


        
private void btnUndo_Click(object sender, EventArgs e)
        
{
            btnRedo.Enabled 
= true;
            CommandManager.Undo();
            
if (CommandManager.UndoStepsCount == 0)
            
{
                btnUndo.Enabled 
= false;
            }

        }

    }

这是我第一次在Blog上写关于设计模式的心得体会,难免有写得不对之处,希望与大家多交流,多指正不当之处。。。。
posted @ 2007-02-13 23:02  Stanley.Luo  阅读(8300)  评论(23编辑  收藏  举报