一个比较常见的改进用户体验的方案是用Redo/Undo来取代确认对话框,由于这个功能比较常用,本文简单的给了一个在C#中通过Command模式实现Redo/Undo方案的例子,以供后续查询。

class Program
{
static void Main(string[] args)
{
var cmds = new CommandManager();
while (true)
{
var key = Console.ReadKey(true);
if (key.KeyChar >= '' && key.KeyChar <= '')
{
cmds.DoNewCommand(key.KeyChar.ToString(), () => Console.WriteLine("process " + key.KeyChar), () => Console.WriteLine("redo " + key.KeyChar));
}
else
{
if (key.Modifiers.HasFlag(ConsoleModifiers.Control) && (key.Key == ConsoleKey.Z))
cmds.UnDo();
else if (key.Modifiers.HasFlag(ConsoleModifiers.Control) && (key.Key == ConsoleKey.Y))
cmds.ReDo();
}
}
}
} class CommandManager
{
#region Command定义
public class Command
{
string name;
Action action;
Action unDoAction; internal Command(string name, Action action, Action unDoAction)
{
this.name = name;
this.action = action;
this.unDoAction = unDoAction;
} internal void Do() { action(); }
internal void UnDo() { unDoAction(); } public override string ToString()
{
return name.ToString();
}
}
#endregion public Stack<Command> ReDoActionStack { get; private set; }
public Stack<Command> UnDoActionStack { get; private set; } public CommandManager()
{
ReDoActionStack = new Stack<Command>();
UnDoActionStack = new Stack<Command>();
} public void DoNewCommand(string name, Action action, Action unDoAction)
{
var cmd = new Command(name, action, unDoAction);
UnDoActionStack.Push(cmd);
ReDoActionStack.Clear();
cmd.Do();
} public void UnDo()
{
if (!CanUnDo)
return; var cmd = UnDoActionStack.Pop();
ReDoActionStack.Push(cmd);
cmd.UnDo();
} public void ReDo()
{
if (!CanReDo)
return; var cmd = ReDoActionStack.Pop();
UnDoActionStack.Push(cmd);
cmd.Do();
} public bool CanUnDo { get { return UnDoActionStack.Count != ; } }
public bool CanReDo { get { return ReDoActionStack.Count != ; } }
//public IEnumerable<Command> Actions { get { return ReDoActionStack.Reverse().Concat(UnDoActionStack); } }
}

代码

原理很简单,通过Command模式把每一步操作封装成一个可undo的命令(包含do和redo两个操作)。并将每一步操作执行后用栈保存起来,undo的时候就以此将Command依次出栈,并执行undo操作。(从某种意义上来说,redo就是undo操作的undo)

上面的代码已经实现了基本的Undo/Redo功能,但实际使用的时候还是有一些细节需要考虑的:如undo或redo时失败(抛异常)的处理等。由于这些细节方面的处理方式不尽相同,本文只是实现一个基本框架,以备后续使用时参考,并不想把它弄的过于复杂。

这种方式比较简单,几乎每种语言都可以轻易的写出这种方式下的实现。但通过这种Command封装的方式实现的也有一些限制,使用的时候需要注意:

  1. 每一步操作都需要封装成command命令
  2. 每一步操作都是可逆的
  3. 当命令过多的时候需要考虑commandlist的内存占用和命令查询时的性能问题

转:C#中Undo/Redo的一个简易实现的更多相关文章

  1. 说说MySQL中的Redo log Undo log都在干啥

        在数据库系统中,既有存放数据的文件,也有存放日志的文件.日志在内存中也是有缓存Log buffer,也有磁盘文件log file,本文主要描述存放日志的文件.     MySQL中的日志文件, ...

  2. 【转】说说MySQL中的Redo log Undo log都在干啥

    阅读目录(Content) 1 undo 1.1 undo是啥 1.2 undo参数 1.3 undo空间管理 2 redo 2.1 redo是啥 2.2 redo 参数 2.3 redo 空间管理 ...

  3. Oracle 中UNDO与REDO的差别具体解释

    一 为了更清楚的看出2者差别,请看下表:                                               UNDO                             ...

  4. MySQL中的redo log和undo log

    MySQL中的redo log和undo log MySQL日志系统中最重要的日志为重做日志redo log和归档日志bin log,后者为MySQL Server层的日志,前者为InnoDB存储引擎 ...

  5. 使用 history 对象和 location 对象中的属性和方法制作一个简易的网页浏览工具

    查看本章节 查看作业目录 需求说明: 使用 history 对象和 location 对象中的属性和方法制作一个简易的网页浏览工具 实现思路: 使用history对象中的 forward() 方法和 ...

  6. MySQL,MariaDB:Undo | Redo [转]

    本文是介绍MySQL数据库InnoDB存储引擎重做日志漫游 00 – Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版 ...

  7. iOS: 为画板App增加 Undo/Redo(撤销/重做)操作

    这个随笔的内容以上一个随笔为基础,(在iOS中实现一个简单的画板),上一个随笔实现了一个简单的画板:   今天我们要为这个画板增加Undo/Redo操作,当画错了一笔,可以撤销它,或者撤销之后后悔了, ...

  8. [转]MySQL日志——Undo | Redo

    本文是介绍MySQL数据库InnoDB存储引擎重做日志漫游 00 – Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版 ...

  9. 从Undo,Redo谈命令模式

    一般的应用软件中,通常会提供Redo和Undo的操作,比如Paint.NET中的动作面板,Word中的撤销重做,一般我们按Ctrl-Z即可回退到上次操作. 要实现上面的这一功能,最直观的想法就是,我们 ...

随机推荐

  1. 【Tensorflow】tensorboard

    tbCallBack = tf.keras.callbacks.TensorBoard(log_dir='./log' , histogram_freq=0, write_graph=True, wr ...

  2. Fiddler抓包手机代理配置

    参考链接:https://i.wanz.im/2013/04/30/debugging_http_request_with_fiddler/ http://www.hangge.com/blog/ca ...

  3. 关于Kafka java consumer管理TCP连接的讨论

    本篇是<关于Kafka producer管理TCP连接的讨论>的续篇,主要讨论Kafka java consumer是如何管理TCP连接.实际上,这两篇大部分的内容是相同的,即consum ...

  4. 使用RecyclerView设置自定义分割线

    在安卓开发中,RecyclerView控件来做一些列表是非常方便的,如何使用在网上很多文章,这次着重来说一下怎么设置自定义分割线: 首先,我们来看一下怎么设置默认的分割线 RecyclerView m ...

  5. MFMailComposeViewController发送邮件的实例

    本文转载至 http://blog.csdn.net/liufeng520/article/details/7585140   iPhone API已经提供了系统写邮件界面的接口,使用MFMailCo ...

  6. Webservice学习之WSDL详解

    1. <definitions/> 这部分在基础篇里已经介绍,主要说明引用了哪些schema以及schema的位置等,可以看下基础篇的介绍,SayHello的Demo这部分内容如下: &l ...

  7. hibernate06--参数的绑定

    创建Dept实体类 以及 对应的 Dept.hbm.xml文件 /** * @author 小豆腐 * *部门的实体类 */ public class Dept { private Integer d ...

  8. js 日期排序(sort)

    按创建时间日期排序 例如 eg 1.升序 2.降序 返回的结果: 注: 支持IE和Chrome其他的浏览器可自行测试

  9. java的Io流学习

    Java中io流的学习(一)File:https://blog.csdn.net/qq_41061437/article/details/81672859 Java中io流的学习(二)FileInpu ...

  10. 《JAVA编程思想》第四版 PDF

    感谢,参考:https://www.cnblogs.com/buwuliao/p/8073211.html 一.链接: 中文版: https://pan.baidu.com/s/1d07Kp4 密码: ...