[Silverlight] 实现C1FlexGrid撤销还原功能

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

一  命令模式

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

clip_image002

采用命令模式,把发出命令的责任和执行命令的责任分隔开,委派给不同的对象。ICommand是命令的接口,指定所有命令必须实现两个方法Execute(执行,还原)和Undo(撤销);ConcreteCommand作为具体命令的实现,诸如增加/删除行/列命令,调整行/列命令,编辑命令等等;Invoker作为命令调用者,可以理解为命令集管理类,负责命令的调用以及命令集合管理等操作;Receiver是命令的接收者,是命令的真正执行者,在本文的实现里,可以理解为C1FlexGridClient则负责创建具体命令对象,并确定其接收者,这个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吧!

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

你可能感兴趣的:(silverlight)