一个比较常见的改进用户体验的方案是用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. 基于【CentOS-7+ Ambari 2.7.0 + HDP 3.0】搭建HAWQ数据仓库04 —— 安装HAWQ插件PXF3.3.0.0

    一. 安装PXF3.3.0.0,这里所安装的pxf的包文件都包含在apache-hawq-rpm-2.3.0.0-incubating.tar.gz里面下面步骤都是以root身份执行这里注意,pxf插 ...

  2. [3]java1.8线程池—ThreadPoolExecutor

    Wiki 上是这样解释的:Thread Pool 作用:利用线程池可以大大减少在创建和销毁线程上所花的时间以及系统资源的开销! 下面主要讲下线程池中最重要的一个类 ThreadPoolExecutor ...

  3. Python Django 配置QQ邮箱发送邮件

    一.实验环境 Python2.7 + Django1.10.0 二.获取QQ邮箱授权码 1.什么是授权码? 授权码是QQ邮箱推出的,用于登录第三方客户端的专用密码. 适用于登录以下服务:POP3/IM ...

  4. node 把文件封装一层文件夹

    把 pages 下面的单个js文件,封装上一个文件夹 var glob = require("glob"); const fs = require("fs-extra&q ...

  5. Vs code常用插件

    Vs code常用插件 1.View In Browser 由于 VSCode 没有提供直接在浏览器中打开文件的内置界面,所以此插件在快捷菜单中添加了在默认浏览器查看文件选项,以及在客户端(Firef ...

  6. css文字上下居中,一行文字居中,两行或多行文字同样居中

    附图: 1. 利用Flex布局实现 demo.html <div class="demo demo-flex"><span>孤云将野鹤,岂向人间住.莫买沃洲 ...

  7. oracle编码转换:AL32UTF8->ZHS16GBK

    --修改Oracle数据库字符集为utf-8: SQL>conn / as sysdba; SQL>shutdown immediate; SQL>startup mount; SQ ...

  8. 22.2、react生命周期与react脚手架(二)

    一.类:es6 <script type="text/babel"> class Person{ age = 10; constructor(name){ this.n ...

  9. 17.vue移动端项目二

    FilmList.vue 电影列表 <template> <div class="mz-film-list"> <!-- 正在热映 https://m ...

  10. 大臣的旅费---树的直径(dfs)

    很久以前,T王国空前繁荣.为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市. 为节省经费,T国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者 ...