[C1] 实现 C1FlexGrid 撤销还原功能
采用设计模式中的“命令模式”实现 C1FlexGrid 的撤销还原功能,那就先从命令模式简单介绍开始吧。
一 命令模式
命令模式属于对象的行为型模式,将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队和记录请求日志,以及支持可撤销还原的操作。
采用命令模式,把发出命令的责任和执行命令的责任分隔开,委派给不同的对象。
ICommand 是命令的接口,指定所有命令必须实现两个方法 Execute(执行,还原)和 Undo(撤销);
ConcreteCommand 作为具体命令的实现,诸如增加/删除行/列命令,调整行/列命令,编辑命令等等;
Invoker 作为命令调用者,可以理解为命令集管理类,负责命令的调用以及命令集合管理等操作;
Receiver 是命令的接收者,是命令的真正执行者,在本文的实现里,可以理解为 C1FlexGrid;
Client 则负责创建具体命令对象,并确定其接收者,这个 Client 可以是任何一个地方,只要那里需要执行某个命令;
命令模式具体讲解参考博客 saville 和 Edward_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 撤销还原功能的更多相关文章
- [C1] 分离 C1FlexGrid 滚动条
一 场景介绍 Silverlight 5.0 的 C1FlexGrid 控件里自带的滚动条,是嵌入在 C1FlexGrid 宽度和高度的范围里的,效果如下图所示: (未隐藏自带滚动条) (隐藏自带的 ...
- [C1] 优化 C1FlexGrid 单元格边框
一 优化理由 如下图所示,如果按照 C1FlexGrid 自带的单元格边框设置,即对每个单元格的 CellStyle 的 BorderThickness 进行设置,会得到如下图的效果: 其中,明显可 ...
- [C1] C1FlexGrid 行列增删&单元格合并拆分
上一篇中实现了 C1FlexGrid的撤销还原功能,这篇是要仿 Excel 做一个行列删除以及单元格的自由合并拆分,楼主怕在原工程里复杂的说不清道不明,所以干脆提取出来做了一个 Demo 来说明实现过 ...
- C1FlexGrid小结(转自http://www.cnblogs.com/C1SupportTeam/archive/2012/12/11/2812316.html)
C1FlexGrid控件来对一个表格格式中的数据进行显示,编辑,组和总结.该表格可以绑定到一个数据源,它可以对自己的数据进行管理. C1FlexGrid控件有一个包含以下元素的丰富的对象模型: 以下的 ...
- C#++c1FlexGrid+帮助文档09
摘自: http://3y.uu456.com/bp-e2746s16s2d380eb62946d27-1.html C#:c1FlexGrid帮助文档:Value-MappedLists(值映射列表 ...
- 【Visual Studio 扩展工具】使用 ComponentOne迷你图控件,进行可视化数据趋势分析
概述 迷你图 —— Sparklines是迷你的轻量级图表,有助于快速可视化数据. 它们是由数据可视化传奇人物Edward Tufte发明的,他将其描述为“数据密集,设计简单,字节大小的图形.”虽然迷 ...
- ORACLE常用数值函数、转换函数、字符串函数
本文更多将会介绍三思在日常中经常会用到的,或者虽然很少用到,但是感觉挺有意思的一些函数.分二类介绍,分别是: 著名函数篇 -经常用到的函数 非著名函数篇-即虽然很少用到,但某些情况下却很实用 注:N表 ...
- oracle函数简析
(一).数值型函数(Number Functions) 数值型函数输入数字型参数并返回数值型的值.多数该类函数的返回值支持38位小数点,诸如:COS, COSH, EXP, LN, LOG, SIN, ...
- Oracle---.oracle函数
数值型函数: 绝对值: ABS(x) [功能]返回x的绝对值 [参数]x,数字型表达式 [返回]数字 [示例] select abs(100),abs(-100) from dual;-------- ...
随机推荐
- Autofac - MVC/WebApi中的应用
Autofac前面写了那么多篇, 其实就是为了今天这一篇, Autofac在MVC和WebApi中的应用. 一.目录结构 先看一下我的目录结构吧, 搭了个非常简单的架构, IOC(web), IBLL ...
- GJM : C#设计模式汇总整理——导航 【原创】
感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...
- Windows Server 2008 R2 下配置TLS1.2,添加自签名证书
前言 2017年1月1日起App Store上的所有App应用将强制开启ATS功能. 苹果的ATS(App Transport Security)对服务器硬性3点要求: ① ATS要求TLS1.2或者 ...
- windows 部署 git 服务器报 Please make sure you have the correct access rights and the repository exists.错误
这两天在阿里云上弄windows 服务器,顺便部署了一个git服务.根据网上教程一步步操作下来,最后在 remote远程仓库的时候提示 fatal: 'yourpath/test.git' does ...
- jQuery.data() 使用方法
data() 方法向被选元素附加数据,或者从被选元素获取数据.在实际开发中,可以用来记录上一步操作某一对象的值,来给下一步操作做一些判断 $("#btn1").click(func ...
- 机器指令翻译成 JavaScript —— No.5 指令变化
上一篇,我们通过内置解释器的方案,解决任意跳转的问题.同时,也提到另一个问题:如果指令发生变化,又该如何应对. 指令自改 如果指令加载到 RAM 中,那就和普通数据一样,也是可以随意修改的.然而,对应 ...
- 一缕阳光:DDD(领域驱动设计)应对具体业务场景,如何聚焦 Domain Model(领域模型)?
写在前面 阅读目录: 问题根源是什么? <领域驱动设计-软件核心复杂性应对之道>分层概念 Repository(仓储)职责所在? Domain Model(领域模型)重新设计 Domain ...
- 同步与异步 & 阻塞与非阻塞
在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式: 一.同步 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用 ...
- HTML5移动开发学习笔记之Canvas基础
1.第一个Canvas程序 看的是HTML5移动开发即学即用这本书,首先学习Canvas基础,废话不多说,直接看第一个例子. 效果图为: 代码如下: <!DOCTYPE html> < ...
- 【.net 深呼吸】使用二进制格式来压缩XML文档
在相当多的情况下,咱们写入XML文件默认是使用文本格式来写入的,如果XML内容是通过网络传输,或者希望节省空间,特别是对于XML文档较大的情况,是得考虑尽可能地压缩XML文件的大小. XmlDicti ...