• [C1] 实现 C1FlexGrid 撤销还原功能


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

    一  命令模式

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

    clip_image002

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

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

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

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

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

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

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

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

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

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

    image

    先从一个单元格编辑命令 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 吧!

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

  • 相关阅读:
    一个好用的小图标库
    前端应用笔记
    HttpClient的使用今天遇到一个巨坑——HttpEntity内容取不出来
    HttpClient忽略SSL证书
    SpringBoot+Thyemelaf开发环境正常,打包jar发到服务器就报错Template might not exist or might not be accessible
    查询Mysql数据库所有数据库所占磁盘空间大小
    Docker安装并运行mysql5.6数据库
    Docker下载镜像太慢问题
    Docker安装Nginx
    vue-router导航守卫
  • 原文地址:https://www.cnblogs.com/memento/p/4415276.html
Copyright © 2020-2023  润新知