Command(命令)——对象行为型模式(通过Command设计模式实现WinForm表单维护的撤销与重做功能)

意图

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

动机

有时必须向某个对象提交请求,但并不知道关于被请求的操作或请求的接受者的任何信息。

典型场景

Command模式的典型应用场景就是实现撤销与恢复功能。下图为实现普通界面的撤销与恢复功能的类
Command设计模式实现撤销与恢复功能

代码实现

ICommand接口,定义execute和undo操作

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Mesnac.Basic.Service
{
    /// <summary>
    /// 操作命令接口
    /// </summary>
    public interface ICommand
    {
        /// <summary>
        /// 命令执行方法,对应恢复操作
        /// </summary>
        void execute();
        /// <summary>
        /// 命令撤销方法
        /// </summary>
        void undo();
    }
}

OperationCommand实现ICommand接口,定义操作的具体实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;

namespace Mesnac.Basic.Service
{
    /// <summary>
    /// 操作命令类,用与进行撤销和恢复操作的封装类
    /// </summary>
    public class OperationCommand : ICommand
    {
        #region 字段定义

        private Control _ctrl;
        private object _newValue;
        private object _oldValue;
        private EventHandler _eventHandler;
        private DataGridViewCellEventHandler _dataGridViewCellEventHandler;

        #endregion

        #region 构造方法

        public OperationCommand (Control ctrl, object newValue, object oldValue)
        {
            this._ctrl = ctrl;
            this._newValue = newValue;
            this._oldValue = oldValue;
        }

        public OperationCommand(Control ctrl, object newValue, object oldValue, EventHandler eventHandler)
        {
            this._ctrl = ctrl;
            this._newValue = newValue; 
            this._oldValue = oldValue;
            this._eventHandler = eventHandler;
        }

        public OperationCommand(Control ctrl, object newValue, object oldValue, DataGridViewCellEventHandler dataGridViewCellEventHandler, int rowIndex, int columnIndex)
        {
            this._ctrl = ctrl;
            this._newValue = newValue;
            this._oldValue = oldValue;
            this._dataGridViewCellEventHandler = dataGridViewCellEventHandler;
        }

        #endregion

        #region ICommand接口成员实现

        #region 恢复操作实现

        /// <summary>
        /// 恢复操作实现
        /// </summary>
        public void execute()
        {
            if (this._ctrl is TextBox)
            {
                (this._ctrl as TextBox).TextChanged -= this._eventHandler;
                (this._ctrl as TextBox).Text = this._newValue == null ? String.Empty : this._newValue.ToString();
                (this._ctrl as TextBox).SelectionStart = (this._ctrl as TextBox).Text.Length;
                (this._ctrl as TextBox).TextChanged += this._eventHandler;
            }
            if (this._ctrl is CheckBox)
            {
                (this._ctrl as CheckBox).CheckedChanged -= this._eventHandler;
                bool newValue = false;
                if (this._newValue != null)
                {
                    bool.TryParse(this._newValue.ToString(), out newValue);
                }
                (this._ctrl as CheckBox).Checked = newValue;
                (this._ctrl as CheckBox).CheckedChanged += this._eventHandler;
            }
            if (this._ctrl is ComboBox)
            {
                (this._ctrl as ComboBox).SelectedIndexChanged -= this._eventHandler;
                (this._ctrl as ComboBox).SelectedItem = this._newValue;
                (this._ctrl as ComboBox).SelectedIndexChanged += this._eventHandler;
            }
            if (this._ctrl is DateTimePicker)
            {
                (this._ctrl as DateTimePicker).ValueChanged -= this._eventHandler;
                DateTime newValue = DateTime.Now;
                if (this._newValue != null)
                {
                    DateTime.TryParse(this._newValue.ToString(), out newValue);
                }
                (this._ctrl as DateTimePicker).Value = newValue;
                (this._ctrl as DateTimePicker).ValueChanged += this._eventHandler;
            }
            if (this._ctrl is DataGridView)
            {
                if (this._dataGridViewCellEventHandler != null)
                {
                    (this._ctrl as DataGridView).CellValueChanged -= this._dataGridViewCellEventHandler;
                }
                (this._ctrl as DataGridView).DataSource = this._newValue;
                Mesnac.Basic.DataProcessor.ClearSelectedStatus((this._ctrl as DataGridView));
                if (this._dataGridViewCellEventHandler != null)
                {
                    (this._ctrl as DataGridView).CellValueChanged += this._dataGridViewCellEventHandler;
                }
            }
        }

        #endregion

        #region 撤销操作实现

        /// <summary>
        /// 撤销操作实现
        /// </summary>
        public void undo()
        {
            if (this._ctrl is TextBox)
            {
                (this._ctrl as TextBox).TextChanged -= this._eventHandler;
                (this._ctrl as TextBox).Text = this._oldValue == null ? String.Empty : this._oldValue.ToString();
                (this._ctrl as TextBox).SelectionStart = (this._ctrl as TextBox).Text.Length;
                (this._ctrl as TextBox).TextChanged += this._eventHandler;
            }
            if (this._ctrl is CheckBox)
            {
                (this._ctrl as CheckBox).CheckedChanged -= this._eventHandler;
                bool oldValue = false;
                if (this._oldValue != null)
                {
                    bool.TryParse(this._oldValue.ToString(), out oldValue);
                }
                (this._ctrl as CheckBox).Checked = oldValue;
                (this._ctrl as CheckBox).CheckedChanged += this._eventHandler;
            }
            if (this._ctrl is ComboBox)
            {
                (this._ctrl as ComboBox).SelectedIndexChanged -= this._eventHandler;
                (this._ctrl as ComboBox).SelectedItem = this._oldValue;
                (this._ctrl as ComboBox).SelectedIndexChanged += this._eventHandler;
            }
            if (this._ctrl is DateTimePicker)
            {
                (this._ctrl as DateTimePicker).ValueChanged -= this._eventHandler;
                DateTime oldValue = DateTime.Now;
                if (this._oldValue != null)
                {
                    DateTime.TryParse(this._oldValue.ToString(), out oldValue);
                }
                (this._ctrl as DateTimePicker).Value = oldValue;
                (this._ctrl as DateTimePicker).ValueChanged += this._eventHandler;
            }
            if (this._ctrl is DataGridView)
            {
                if (this._dataGridViewCellEventHandler != null)
                {
                    (this._ctrl as DataGridView).CellValueChanged -= this._dataGridViewCellEventHandler;
                }
                
                (this._ctrl as DataGridView).DataSource = this._oldValue;
                Mesnac.Basic.DataProcessor.ClearSelectedStatus((this._ctrl as DataGridView));
                
                if (this._dataGridViewCellEventHandler != null)
                {
                    (this._ctrl as DataGridView).CellValueChanged += this._dataGridViewCellEventHandler;
                }
            }
        }

        #endregion

        #endregion
    }
}

EventHandlerProcess对常规WinForm控件的事件进行处理把对控件的操作封装为ICommand对象

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Mesnac.Basic.Service
{
    /// <summary>
    /// 事件处理器
    /// </summary>
    public class EventHandlerProcessor
    {
        #region 字段定义

        private object _oldValue;           //保存事件源原始值

        #endregion

        #region 构造方法

        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="oldValue">传递事件源原始值</param>
        public EventHandlerProcessor(object oldValue)
        {
            this._oldValue = oldValue;
        }

        #endregion

        #region 事件处理

        #region 文本框事件处理

        /// <summary>
        /// 文本框事件处理
        /// </summary>
        /// <param name="sender">事件源</param>
        /// <param name="e">事件参数</param>
        public void TextBox_LostFocus(object sender, EventArgs e)
        {
            TextBox textBox = sender as TextBox;
            string oldStr = String.IsNullOrEmpty(this._oldValue as string) ? String.Empty : this._oldValue.ToString();
            if (textBox.Text.Equals(oldStr))
            {
                return;
            }
            OperationCommand cmd = new OperationCommand(textBox, textBox.Text, oldStr, this.TextBox_LostFocus);
            UndoRedoService.UndoStack.Push(cmd);
            this._oldValue = textBox.Text;
        }

        #endregion

        #region 复选框事件处理

        /// <summary>
        /// 复选框事件处理
        /// </summary>
        /// <param name="sender">事件源</param>
        /// <param name="e">事件参数</param>
        public void CheckBox_CheckedChanged(object sender, EventArgs e)
        {
            CheckBox checkBox = sender as CheckBox;
            bool oldValue = false;
            if (this._oldValue != null)
            {
                bool.TryParse(this._oldValue.ToString(), out oldValue);
            }
            if (checkBox.Checked == oldValue)
            {
                return;
            }
            OperationCommand cmd = new OperationCommand(checkBox, checkBox.Checked, oldValue, this.CheckBox_CheckedChanged);
            UndoRedoService.UndoStack.Push(cmd);
            this._oldValue = checkBox.Checked;
        }

        #endregion

        #region 组合框事件处理

        /// <summary>
        /// 组合框事件处理
        /// </summary>
        /// <param name="sender">事件源</param>
        /// <param name="e">事件参数</param>
        public void ComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            ComboBox comboBox = sender as ComboBox;
            if (comboBox.SelectedItem == this._oldValue)
            {
                return;
            }
            OperationCommand cmd = new OperationCommand(comboBox, comboBox.SelectedItem, this._oldValue, this.ComboBox_SelectedIndexChanged);
            UndoRedoService.UndoStack.Push(cmd);
            this._oldValue = comboBox.SelectedItem;
        }

        #endregion

        #region 日历事件处理

        /// <summary>
        /// 日历控件事件处理
        /// </summary>
        /// <param name="sender">事件源</param>
        /// <param name="e">事件参数</param>
        public void DateTimePicker_ValueChanged(object sender, EventArgs e)
        {
            DateTimePicker dateTimePicker = sender as DateTimePicker;
            DateTime oldValue = DateTime.Now;
            if (this._oldValue != null)
            {
                DateTime.TryParse(this._oldValue.ToString(), out oldValue);
            }
            if (String.Format("{0:yyyyMMddHHmmss}", dateTimePicker.Value).Equals(String.Format("{0:yyyyMMddHHmmss}", oldValue)))
            {
                return;
            }
            OperationCommand cmd = new OperationCommand(dateTimePicker, dateTimePicker.Value, this._oldValue, this.DateTimePicker_ValueChanged);
            UndoRedoService.UndoStack.Push(cmd);
            this._oldValue = dateTimePicker.Value;
        }

        #endregion

        #region DataGridView事件处理

        /// <summary>
        /// DataGridView事件处理
        /// </summary>
        /// <param name="sender">事件源</param>
        /// <param name="e">事件参数</param>
        public void DataGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            DataGridView dataGridView = sender as DataGridView;
            DataTable dtNew = Mesnac.Basic.DataProcessor.GetDataTableFromGridView(dataGridView);
            OperationCommand cmd = new OperationCommand(dataGridView, dtNew, this._oldValue, this.DataGridView_CellValueChanged, e.RowIndex, e.ColumnIndex);
            UndoRedoService.UndoStack.Push(cmd);
            this._oldValue = Mesnac.Basic.DataProcessor.GetDataTableFromGridView(dataGridView);
        }

        #endregion

        #endregion
    }
}

UndoRedoService定义撤销恢复服务类,作为调用入口

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Mesnac.Basic.Service
{
    /// <summary>
    /// UndoRedo服务类
    /// </summary>
    public class UndoRedoService
    {
        #region 字段定义

        private static Dictionary<Control, EventHandlerProcessor> _dicControlEventHandler = new Dictionary<Control, EventHandlerProcessor>();

        #endregion

        #region 属性定义

        /// <summary>
        /// Undo操作栈
        /// </summary>
        public static Stack<ICommand> UndoStack = new Stack<ICommand>();

        /// <summary>
        /// Redo操作栈
        /// </summary>
        public static Stack<ICommand> RedoStack = new Stack<ICommand>();

        /// <summary>
        /// 控件事件处理绑定集合
        /// </summary>
        public static Dictionary<Control, EventHandlerProcessor> DicControlEventHandler
        {
            get
            {
                return _dicControlEventHandler;
            }
        }

        #endregion

        #region Undo Redo服务初始化

        /// <summary>
        /// Undo Redo服务初始化
        /// </summary>
        /// <param name="controls">要注册UndoRedo服务的控件集合</param>
        public static void Init(List<Control> controls)
        {
            Clear();

            EventHandlerProcessor ehp = null;

            foreach (Control ctl in controls)
            {
                if (ctl is TextBox)
                {
                    ehp = new EventHandlerProcessor((ctl as TextBox).Text);
                    _dicControlEventHandler.Add(ctl, ehp);
                    (ctl as TextBox).LostFocus -= ehp.TextBox_LostFocus;
                    (ctl as TextBox).LostFocus += ehp.TextBox_LostFocus;
                }
                if (ctl is CheckBox)
                {
                    ehp = new EventHandlerProcessor((ctl as CheckBox).Checked);
                    _dicControlEventHandler.Add(ctl, ehp);
                    (ctl as CheckBox).CheckedChanged -= ehp.CheckBox_CheckedChanged;
                    (ctl as CheckBox).CheckedChanged += ehp.CheckBox_CheckedChanged;
                }
                if (ctl is ComboBox)
                {
                    ehp = new EventHandlerProcessor((ctl as ComboBox).SelectedItem);
                    _dicControlEventHandler.Add(ctl, ehp);
                    (ctl as ComboBox).SelectedIndexChanged -= ehp.ComboBox_SelectedIndexChanged;
                    (ctl as ComboBox).SelectedIndexChanged += ehp.ComboBox_SelectedIndexChanged;
                }
                if (ctl is DateTimePicker)
                {
                    ehp = new EventHandlerProcessor((ctl as DateTimePicker).Value);
                    _dicControlEventHandler.Add(ctl, ehp);
                    (ctl as DateTimePicker).ValueChanged -= ehp.DateTimePicker_ValueChanged;
                    (ctl as DateTimePicker).ValueChanged += ehp.DateTimePicker_ValueChanged;
                }
                if (ctl is DataGridView)
                {
                    DataTable dtOld = Mesnac.Basic.DataProcessor.GetDataTableFromGridView((ctl as DataGridView));
                    ehp = new EventHandlerProcessor(dtOld);
                    _dicControlEventHandler.Add(ctl, ehp);
                    (ctl as DataGridView).CellValueChanged -= ehp.DataGridView_CellValueChanged;
                    (ctl as DataGridView).CellValueChanged += ehp.DataGridView_CellValueChanged;
                }
            }
        }

        #endregion

        #region 清除操作栈

        /// <summary>
        /// 清除操作栈
        /// </summary>
        public static void Clear()
        {
            foreach(Control ctl in _dicControlEventHandler.Keys)
            {
                if (ctl is TextBox)
                {
                    (ctl as TextBox).LostFocus -= _dicControlEventHandler[ctl].TextBox_LostFocus;
                }
                if (ctl is CheckBox)
                {
                    (ctl as CheckBox).CheckedChanged -= _dicControlEventHandler[ctl].CheckBox_CheckedChanged;
                }
                if (ctl is ComboBox)
                {
                    (ctl as ComboBox).SelectedIndexChanged -= _dicControlEventHandler[ctl].ComboBox_SelectedIndexChanged;
                }
                if (ctl is DateTimePicker)
                {
                    (ctl as DateTimePicker).ValueChanged -= _dicControlEventHandler[ctl].DateTimePicker_ValueChanged;
                }
                if (ctl is DataGridView)
                {
                    (ctl as DataGridView).CellValueChanged -= _dicControlEventHandler[ctl].DataGridView_CellValueChanged;
                }
            }
            
            _dicControlEventHandler.Clear();

            UndoStack.Clear();
            RedoStack.Clear();
        }

        #endregion
    }
}

调用入口为UndoRedoService.Init

在需要有撤销与恢复功能的界面初始化的地方调用UndoRedoService.Init方法,把界面控件列表传入Init方法即可。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值