[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;-------- ...
随机推荐
- Linux上如何查看物理CPU个数,核数,线程数
首先,看看什么是超线程概念 超线程技术就是利用特殊的硬件指令,把两个逻辑内核模拟成两个物理芯片,让单个处理器都能使用线程级并行计算,进而兼容多线程操作系统和软件,减少了CPU的闲置时间,提高的CPU的 ...
- 用FSM一键制作逐帧动画雪碧图 Vue2 + webpack
因为工作需要要将五六十张逐帧图拼成雪碧图,网上想找到一件制作工具半天没有找到,就自己用canvas写了一个. 写成之后就再没有什么机会使用了,因此希望有人使用的时候如果遇到bug了能及时反馈给我. 最 ...
- iOS微信里打开app,Universal Links
这两天在弄分享,从第三方应用或者浏览器打开自己app的东西 传统的方式是通过URL Scheme的方式,但是iOS9以后又出了新的更完美的方式Universal Links. 传统的URL Schem ...
- DevOps对于企业IT的价值
其实从敏捷延展开的 DevOps 概念很早就已经被提出,不过由于配套的技术成熟度水平层次不齐, DevOps 的价值一直没有有效地发挥出来.现如今,随着容器技术的发展, DevOps 在企业中的实践难 ...
- SQL Server中SELECT会真的阻塞SELECT吗?
在SQL Server中,我们知道一个SELECT语句执行过程中只会申请一些意向共享锁(IS) 与共享锁(S), 例如我使用SQL Profile跟踪会话86执行SELECT * FROM dbo.T ...
- Xamarin.Android通知详解
一.发送通知的机制 在日常的app应用中经常需要使用通知,因为服务.广播后台活动如果有事件需要通知用户,则需要通过通知栏显示,而在Xamarin.Android下的通知需要获取Notification ...
- 【已解决】Https请求——基础连接已经关闭 发送时发生错误
本人在做商用项目的推送消息功能时,借助第三方推送服务.这里避免有打广告的嫌疑,就不报名字了.由于是通过调用API接口,所以Post方法是自己写的,但是在开发环境是可以正常推送的,但是一上线就出各种问题 ...
- 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 ...
- ABP源码分析四十四:ZERO的配置
ABP Zero模块中需要配置的地方主要集中在三块:配置静态的role,配置外部认证源,以及配置本地化语言和资源. UserManagementConfig/IUserManagementConfig ...
- Android PopupWindow Dialog 关于 is your activity running 崩溃详解
Android PopupWindow Dialog 关于 is your activity running 崩溃详解 [TOC] 起因 对于 PopupWindow Dialog 需要 Activi ...