采用设计模式中的“命令模式”实现 C1FlexGrid 的撤销还原功能,那就先从命令模式简单介绍开始吧。

一  命令模式

命令模式属于对象的行为型模式,将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队和记录请求日志,以及支持可撤销还原的操作。

采用命令模式,把发出命令的责任和执行命令的责任分隔开,委派给不同的对象。

ICommand 是命令的接口,指定所有命令必须实现两个方法 Execute(执行,还原)和 Undo(撤销);

ConcreteCommand 作为具体命令的实现,诸如增加/删除行/列命令,调整行/列命令,编辑命令等等;

Invoker 作为命令调用者,可以理解为命令集管理类,负责命令的调用以及命令集合管理等操作;

Receiver 是命令的接收者,是命令的真正执行者,在本文的实现里,可以理解为 C1FlexGrid

Client 则负责创建具体命令对象,并确定其接收者,这个 Client 可以是任何一个地方,只要那里需要执行某个命令;

命令模式具体讲解参考博客 savilleEdward_jie,楼主就不班门弄斧了。

理论毕竟是理论,还是要靠实践来检验,而且要根据实际情况灵活变通才是王道。于是楼主把它用到 C1FlexGrid 里玩玩看。

二  C1FlexGrid 撤销还原模块设计实现

模块设计图如下,楼主不擅长画什么 UML 活动图类图什么的,就画个大概意思吧。

先从一个单元格编辑命令 EditAction 开始吧。

IUndoableAction 相当于上面所讲的 ICommand 接口,这里楼主把里面的方法换成了 Undo 和 Redo,依次对应前面的 Undo 和 Execute,还有一个方法是 SaveNewState,是用于在命令第一次执行后,保存命令执行后的新状态,以便于还原;(说明一点的是,旧状态会在命令初始化时进行备份;而新状态则是通过调用 SaveNewState 方法来备份);

namespace Memento.SLFlexGrid.UndoStack
{
/// <summary>
/// 定义实现可撤销动作对象所需要的方法
/// </summary>
public interface IUndoableActio
{
/// <summary>
/// 撤销
/// </summary>
void Undo() /// <summary>
/// 还原
/// </summary>
void Redo() /// <summary>
/// 动作执行后保存状态
/// </summary>
bool SaveNewState()
}
}

IUndoableAction

FlexGridExtAction 算是在接口 ICommand 和具体命令实现类 ConcreteCommand 中间插入的一层,作为专门的 C1FlexGrid 命令的父类;

namespace Memento.SLFlexGrid.UndoStack
{
/// <summary>
/// SLFlexGridExt里撤销/还原动作的基类
/// </summary>
public abstract class FlexGridExtAction : IUndoableActio
{
#region 私有变量 protected SLFlexGridExt _flex;// 命令模式执行者 #endregion #region 构造函数 /// <summary>
/// 构造函数
/// </summary>
/// <param name="flex"></param>
public FlexGridExtAction(SLFlexGridExt flex)
{
_flex = flex
} #endregion #region 虚方法 /// <summary>
/// 撤销最后一个动作
/// </summary>
public abstract void Undo() /// <summary>
/// 还原最后一个动作
/// </summary>
public abstract void Redo() /// <summary>
/// 保存当前表格状态
/// </summary>
public abstract bool SaveNewState() #endregion
}
}

FlexGridExtAction

EditAction 是具体命令的实现类,相当于之前的 ConcreteCommand;

using C1.Silverlight.FlexGrid

namespace Memento.SLFlexGrid.UndoStack
{
/// <summary>
/// 单元格编辑动作
/// </summary>
public class EditAction : FlexGridExtActio
{
# region 私有变量 private CellRange _range
private object _oldValue
private object _newValue # endregion # region 构造函数 /// <summary>
/// 构造函数
/// </summary>
public EditAction(SLFlexGridExt flex)
: base(flex)
{
_range = flex.Selectio
_oldValue = GetValue()
} # endregion # region 其他方法 /// <summary>
/// 获取单元格的内容
/// </summary>
private object GetValue()
{
Row row = _flex.Rows[_range.Row]
Column col = _flex.Columns[_range.Column] return row[col]
} # endregion # region 接口IUndoableAction方法 /// <summary>
/// 撤销
/// </summary>
public override void Undo()
{
_flex[_range.Row, _range.Column] = _oldValue
_flex.Select(_range, true)
} /// <summary>
/// 还原
/// </summary>
public override void Redo()
{
_flex[_range.Row, _range.Column] = _newValue
_flex.Select(_range, true)
} /// <summary>
/// 动作执行后,保存新状态
/// </summary>
public override bool SaveNewState()
{
_newValue = GetValue()
// 默认null和空串等效,不做撤销还原
return !(object.Equals(_oldValue, _newValue) || (_oldValue == null && _newValue.Equals("")))
} # endregion
}
}

EditAction

UndoStack 是命令集堆栈管理类,相当于前面说道的 Invoker,这里的 UndoStack 还负责撤销还原状态的判断和通知;

using System
using System.Collections.Generic namespace Memento.SLFlexGrid.UndoStack
{
/// <summary>
/// 撤销还原栈基类
/// </summary>
public class UndoStack
{
#region 私有变量 private List<IUndoableAction> _stack = new List<IUndoableAction>()
private int _ptr = -1 private const int MAX_STACK_SIZE = 500 #endregion #region 构造函数 /// <summary>
/// 构造函数
/// </summary>
public UndoStack()
{
} #endregion #region 公开方法 /// <summary>
/// 清空撤销还原堆栈
/// </summary>
public virtual void Clear()
{
_stack.Clear()
_ptr = -1
OnStateChanged(EventArgs.Empty)
} /// <summary>
/// 获得一个值表示堆栈内是否有可撤销的动作
/// </summary>
public bool CanUndo
{
get
{
return _ptr > -1 && _ptr < _stack.Count
}
} /// <summary>
/// 获得一个值表示堆栈内是否有可还原的动作
/// </summary>
public bool CanRedo
{
get
{
return _ptr + 1 > -1 && _ptr + 1 < _stack.Count
}
} /// <summary>
/// 执行一个撤销命令
/// </summary>
public void Undo()
{
if (CanUndo)
{
IUndoableAction action = _stack[_ptr]
BeforeUndo(action)
action.Undo()
_ptr--
OnStateChanged(EventArgs.Empty)
}
} /// <summary>
/// 执行一个还原命令
/// </summary>
public void Redo()
{
if (CanRedo)
{
_ptr++
IUndoableAction action = _stack[_ptr]
BeforeRedo(action)
action.Redo()
OnStateChanged(EventArgs.Empty)
}
} /// <summary>
/// 添加动作到撤销还原堆栈
/// </summary>
public void AddAction(IUndoableAction action)
{
// 整理堆栈
while (_stack.Count > 0 && _stack.Count > _ptr + 1)
{
_stack.RemoveAt(_stack.Count - 1)
}
while (_stack.Count >= MAX_STACK_SIZE)
{
_stack.RemoveAt(0)
} // 更新指针并添加动作到堆栈中
_ptr = _stack.Count
_stack.Add(action) OnStateChanged(EventArgs.Empty)
} #endregion #region 委托事件 /// <summary>
/// 当堆栈状态改变时触发
/// </summary>
public event EventHandler StateChanged #endregion #region 虚方法 /// <summary>
/// 触发事件<see cref="StateChanged"/>
/// </summary>
/// <param name="e"><see cref="EventArgs"/>包含事件参数</param>
protected virtual void OnStateChanged(EventArgs e)
{
if (StateChanged != null)
{
StateChanged(this, e)
}
} /// <summary>
/// 在执行撤销动作之前调用
/// </summary>
protected virtual void BeforeUndo(IUndoableAction action)
{
} /// <summary>
/// 在执行还原动作之前调用
/// </summary>
protected virtual void BeforeRedo(IUndoableAction action)
{
} #endregion
}
}

UndoStack

ExcelUndoStack 则是继承 UndoStack,专门作为 C1FlexGrid 的命令集管理者;

namespace Memento.SLFlexGrid.UndoStack
{
public class FlexGridUndoStack : UndoStack
{
#region 私有变量 private SLFlexGridExt _flex
private IUndoableAction _pendingAction;// 当前挂起的动作
private string _oldCellValue = "";// 单元格编辑前的内容 #endregion #region 构造函数 /// <summary>
/// 构造函数
/// </summary>
public FlexGridUndoStack(SLFlexGridExt flex)
{
_flex = flex
flex.PrepareCellForEdit += flex_PrepareCellForEdit
flex.CellEditEnded += flex_CellEditEnded
} #endregion #region 重写方法 /// <summary>
/// 在执行撤销动作之前调用
/// </summary>
protected override void BeforeUndo(IUndoableAction action)
{
base.BeforeUndo(action)
} /// <summary>
/// 在执行还原动作之前调用
/// </summary>
protected override void BeforeRedo(IUndoableAction action)
{
base.BeforeRedo(action)
} #endregion #region 事件处理 // 单元格编辑
private void flex_PrepareCellForEdit(object sender, C1.Silverlight.FlexGrid.CellEditEventArgs e)
{
_pendingAction = new EditAction(_flex)
}
private void flex_CellEditEnded(object sender, C1.Silverlight.FlexGrid.CellEditEventArgs e)
{
if (!e.Cancel && _pendingAction is EditAction && _pendingAction.SaveNewState())
{
_flex.UndoStack.AddAction(_pendingAction)
}
_pendingAction = null
} #endregion
}
}

FlexGridUndoStack

明显还差一个 Client 角色,既然是扩展 C1FlexGrid,实现其撤销还原模块,那就让它自己来负责吧,在 C1FlexGrid 的扩展类 FlexGridExt 中,添加 Invoker 对象,缓存命令集;添加 CanUndo 和 CanRedo 依赖属性,添加撤销还原方法的调用;下面是具体实现:

using System
using System.Collections.Generic
using System.Window
using System.Windows.Control
using System.Windows.Data
using System.Windows.Input
using System.Windows.Media using C1.Silverlight.FlexGrid using Memento.SLFlexGrid.UndoStack namespace Memento.SLFlexGrid
{
public class SLFlexGridExt : C1FlexGrid
{
#region 私有属性 private FlexGridUndoStack _undo;// 撤销还原堆栈 #endregion #region 公开属性 /// <summary>
/// 获得该<see cref="SLFlexGrid"/>的<see cref="UndoStack"/>
/// </summary>
public FlexGridUndoStack UndoStack
{
get
{
return _undo
}
} /// <summary>
/// 是否可以撤销
/// </summary>
public bool CanUndo
{
get
{
return (bool)GetValue(CanUndoProperty)
}
} /// <summary>
/// 是否可以还原
/// </summary>
public bool CanRedo
{
get
{
return (bool)GetValue(CanRedoProperty)
}
} #endregion #region 依赖属性 /// <summary>
/// 定义<see cref="CanUndo"/>依赖属性
/// </summary>
public static readonly DependencyProperty CanUndoProperty =
DependencyProperty.Register(
"CanUndo",
typeof(bool),
typeof(SLFlexGridExt),
new PropertyMetadata(false)) /// <summary>
/// 定义<see cref="CanRedo"/>依赖属性
/// </summary>
public static readonly DependencyProperty CanRedoProperty =
DependencyProperty.Register(
"CanRedo",
typeof(bool),
typeof(SLFlexGridExt),
new PropertyMetadata(false)) #endregion #region 构造函数 /// <summary>
/// 构造函数
/// </summary>
public SLFlexGridExt()
{
this.DefaultStyleKey = typeof(SLFlexGridExt) // 默认添加50行10列
for (int i = 0; i < 50; i++)
{
Rows.Add(new Row())
}
for (int c = 0; c < 10; c++)
{
Columns.Add(new Column())
}
} #endregion #region 重写方法 /// <summary>
/// 应用模版
/// </summary>
public override void OnApplyTemplate()
{
try
{
base.OnApplyTemplate() _undo = new FlexGridUndoStack(this)
_undo.StateChanged += (s, e) =>
{
SetValue(CanUndoProperty, _undo.CanUndo)
SetValue(CanRedoProperty, _undo.CanRedo)
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message)
}
} #endregion #region 公开方法 /// <summary>
/// 撤销
/// </summary>
public void Undo()
{
_undo.Undo()
} /// <summary>
/// 还原
/// </summary>
public void Redo()
{
_undo.Redo()
} #endregion
}
}

SLFlexGridExt

好了,万事俱备了,也不欠东风了,只需要在界面上加上一个“撤销”按钮和一个“还原”按钮,然后事件里依次执行 flex.Undo(); flex.Redo(); 即可,so easy 吧!

其他复杂的命令以后慢慢完善添加吧。欢迎指教!

[C1] 实现 C1FlexGrid 撤销还原功能的更多相关文章

  1. [C1] 分离 C1FlexGrid 滚动条

    一  场景介绍 Silverlight 5.0 的 C1FlexGrid 控件里自带的滚动条,是嵌入在 C1FlexGrid 宽度和高度的范围里的,效果如下图所示: (未隐藏自带滚动条) (隐藏自带的 ...

  2. [C1] 优化 C1FlexGrid 单元格边框

    一  优化理由 如下图所示,如果按照 C1FlexGrid 自带的单元格边框设置,即对每个单元格的 CellStyle 的 BorderThickness 进行设置,会得到如下图的效果: 其中,明显可 ...

  3. [C1] C1FlexGrid 行列增删&单元格合并拆分

    上一篇中实现了 C1FlexGrid的撤销还原功能,这篇是要仿 Excel 做一个行列删除以及单元格的自由合并拆分,楼主怕在原工程里复杂的说不清道不明,所以干脆提取出来做了一个 Demo 来说明实现过 ...

  4. C1FlexGrid小结(转自http://www.cnblogs.com/C1SupportTeam/archive/2012/12/11/2812316.html)

    C1FlexGrid控件来对一个表格格式中的数据进行显示,编辑,组和总结.该表格可以绑定到一个数据源,它可以对自己的数据进行管理. C1FlexGrid控件有一个包含以下元素的丰富的对象模型: 以下的 ...

  5. C#++c1FlexGrid+帮助文档09

    摘自: http://3y.uu456.com/bp-e2746s16s2d380eb62946d27-1.html C#:c1FlexGrid帮助文档:Value-MappedLists(值映射列表 ...

  6. 【Visual Studio 扩展工具】使用 ComponentOne迷你图控件,进行可视化数据趋势分析

    概述 迷你图 —— Sparklines是迷你的轻量级图表,有助于快速可视化数据. 它们是由数据可视化传奇人物Edward Tufte发明的,他将其描述为“数据密集,设计简单,字节大小的图形.”虽然迷 ...

  7. ORACLE常用数值函数、转换函数、字符串函数

    本文更多将会介绍三思在日常中经常会用到的,或者虽然很少用到,但是感觉挺有意思的一些函数.分二类介绍,分别是: 著名函数篇 -经常用到的函数 非著名函数篇-即虽然很少用到,但某些情况下却很实用 注:N表 ...

  8. oracle函数简析

    (一).数值型函数(Number Functions) 数值型函数输入数字型参数并返回数值型的值.多数该类函数的返回值支持38位小数点,诸如:COS, COSH, EXP, LN, LOG, SIN, ...

  9. Oracle---.oracle函数

    数值型函数: 绝对值: ABS(x) [功能]返回x的绝对值 [参数]x,数字型表达式 [返回]数字 [示例] select abs(100),abs(-100) from dual;-------- ...

随机推荐

  1. Linux上如何查看物理CPU个数,核数,线程数

    首先,看看什么是超线程概念 超线程技术就是利用特殊的硬件指令,把两个逻辑内核模拟成两个物理芯片,让单个处理器都能使用线程级并行计算,进而兼容多线程操作系统和软件,减少了CPU的闲置时间,提高的CPU的 ...

  2. 用FSM一键制作逐帧动画雪碧图 Vue2 + webpack

    因为工作需要要将五六十张逐帧图拼成雪碧图,网上想找到一件制作工具半天没有找到,就自己用canvas写了一个. 写成之后就再没有什么机会使用了,因此希望有人使用的时候如果遇到bug了能及时反馈给我. 最 ...

  3. iOS微信里打开app,Universal Links

    这两天在弄分享,从第三方应用或者浏览器打开自己app的东西 传统的方式是通过URL Scheme的方式,但是iOS9以后又出了新的更完美的方式Universal Links. 传统的URL Schem ...

  4. DevOps对于企业IT的价值

    其实从敏捷延展开的 DevOps 概念很早就已经被提出,不过由于配套的技术成熟度水平层次不齐, DevOps 的价值一直没有有效地发挥出来.现如今,随着容器技术的发展, DevOps 在企业中的实践难 ...

  5. SQL Server中SELECT会真的阻塞SELECT吗?

    在SQL Server中,我们知道一个SELECT语句执行过程中只会申请一些意向共享锁(IS) 与共享锁(S), 例如我使用SQL Profile跟踪会话86执行SELECT * FROM dbo.T ...

  6. Xamarin.Android通知详解

    一.发送通知的机制 在日常的app应用中经常需要使用通知,因为服务.广播后台活动如果有事件需要通知用户,则需要通过通知栏显示,而在Xamarin.Android下的通知需要获取Notification ...

  7. 【已解决】Https请求——基础连接已经关闭 发送时发生错误

    本人在做商用项目的推送消息功能时,借助第三方推送服务.这里避免有打广告的嫌疑,就不报名字了.由于是通过调用API接口,所以Post方法是自己写的,但是在开发环境是可以正常推送的,但是一上线就出各种问题 ...

  8. useful commands for docker beginner

    You may want to add my wechat public account or add my technical blog's RSS feed This list is meant ...

  9. ABP源码分析四十四:ZERO的配置

    ABP Zero模块中需要配置的地方主要集中在三块:配置静态的role,配置外部认证源,以及配置本地化语言和资源. UserManagementConfig/IUserManagementConfig ...

  10. Android PopupWindow Dialog 关于 is your activity running 崩溃详解

    Android PopupWindow Dialog 关于 is your activity running 崩溃详解 [TOC] 起因 对于 PopupWindow Dialog 需要 Activi ...