[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;-------- ...
随机推荐
- WebApi接口 - 如何在应用中调用webapi接口
很高兴能再次和大家分享webapi接口的相关文章,本篇将要讲解的是如何在应用中调用webapi接口:对于大部分做内部管理系统及类似系统的朋友来说很少会去调用别人的接口,因此可能在这方面存在一些困惑,希 ...
- Django
一.Django 简介 Django 是一个由 Python 写成的开放源代码的 Web 应用框架.它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的,即是 CMS(内容管理系统) ...
- Web安全相关(四):过多发布(Over Posting)
简介 过多发布的内容相对比较简单,因此,我只打算把原文中的一些关键信息翻译一下.原文链接如下: http://www.asp.net/mvc/overview/getting-started/gett ...
- jquery.cookie的使用
今天想到了要为自己的影像日记增加赞的功能,并且需要用到cookie. 记得原生的js操作cookie也不是很麻烦的,但似乎jquery更简单,不过相比原生js,需要额外引入2个文件,似乎又不是很好,但 ...
- Maven搭建SpringMVC+Hibernate项目详解 【转】
前言 今天复习一下SpringMVC+Hibernate的搭建,本来想着将Spring-Security权限控制框架也映入其中的,但是发现内容太多了,Spring-Security的就留在下一篇吧,这 ...
- 记录在Windows上安装和使用Oracle数据库过程中的坑
1.安装Oracle Oracle软件是免费的,可以去官网下载相应的安装包.但是如果用于商业用途需要购买License.官网上针对各种平台,32位和64位都有,如果在Windows一般会下载到两个文件 ...
- 如何在Open Live Writer(OLW)中使用precode代码高亮Syntax Highlighter
早先Microsotf的Windows Live Writer(WLW)现在已经开源了,并且更名为Open Live Writer,但是现在Windows Live Writer还是可以现在,Open ...
- RavenDB官网文档翻译系列第一
本系列文章主要翻译自RavenDB官方文档,有些地方做了删减,有些内容整合在一起.欢迎有需要的朋友阅读.毕竟还是中文读起来更亲切吗.下面进入正题. 起航 获取RavenDB RavenDB可以通过Nu ...
- 使用 SecurityManager 和 Policy File 管理 Java 程序的权限
参考资料 该文中的内容来源于 Oracle 的官方文档.Oracle 在 Java 方面的文档是非常完善的.对 Java 8 感兴趣的朋友,可以从这个总入口 Java SE 8 Documentati ...
- 使用tornado,我们可以做什么?
以下介绍都是建立在python2.x的基础上面,tornado使用任意版本皆可. 如果我们需要对外提供一个http server(web api)/websocket server时,我们都可以使用t ...