[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;-------- ...
随机推荐
- 高频交易算法研发心得--MACD指标算法及应用
凤鸾宝帐景非常,尽是泥金巧样妆. 曲曲远山飞翠色:翩翩舞袖映霞裳. 梨花带雨争娇艳:芍药笼烟骋媚妆. 但得妖娆能举动,取回长乐侍君王. [摘自<封神演义>纣王在女娲宫上香时题的诗] 一首定 ...
- 设计模式之单例模式(Singleton)
设计模式之单例模式(Singleton) 设计模式是前辈的一些经验总结之后的精髓,学习设计模式可以针对不同的问题给出更加优雅的解答 单例模式可分为俩种:懒汉模式和饿汉模式.俩种模式分别有不同的优势和缺 ...
- 中文 iOS/Mac 开发博客列表
中文 iOS/Mac 开发博客列表 博客地址 RSS地址 OneV's Den http://onevcat.com/atom.xml 一只魔法师的工坊 http://blog.ibireme.com ...
- H3 BPM初次安装常见错误详解1-4
错误1: 首次安装完成无法访问,效果如下. 错误原因:没有配置IIS. 解决方法: 控制面板-程序-打开或关闭Windows功能,选择internet信息服务. 因为安装的时候没有没有iis,所以程序 ...
- 编译器开发系列--Ocelot语言1.抽象语法树
从今天开始研究开发自己的编程语言Ocelot,从<自制编译器>出发,然后再自己不断完善功能并优化. 编译器前端简单,就不深入研究了,直接用现成的一款工具叫JavaCC,它可以生成抽象语法树 ...
- 换个角度看微信小程序[推荐]
去年参加几次技术沙龙时,我注意到一个有意思的现象:与之前大家统一接受的换名片不同,有些人并不愿意被添加微信好友--"不好意思,不熟的人不加微信". 这个现象之所以有意思,是因为名片 ...
- MapReduce
2016-12-21 16:53:49 mapred-default.xml mapreduce.input.fileinputformat.split.minsize 0 The minimum ...
- 开始mono开发
使用mono框架开发android程序,第一步当然是构建开发环境,严格意义上说是使用 mono for android开发android程序. 参考Mono for Android安装配置破解 mo ...
- Centos、Ubuntu 安装 Mono、Jexus
Mono是.NET的跨平台实现 在众多关于语言的争论中,.NET一直被以不能跨平台而诟病,Mono改变了这一现状. 有人当心Mono会涉及版权啥的问题.高深的偶不懂,不过我觉得Unity3D都能用,为 ...
- 2000条你应知的WPF小姿势 基础篇<8-14>
在正文开始之前需要介绍一个人:Sean Sexton. 来自明尼苏达双城的软件工程师,对C#和WPF有着极深的热情.最为出色的是他维护了两个博客:2,000Things You Should Know ...