Command(命令)——物件行為型模式(通過Command設計模式實現WinForm表單維護的撤銷與重做功能)
Command(命令)——物件行為型模式(通過Command設計模式實現WinForm表單維護的撤銷與重做功能)
意圖
將一個請求封裝為一個物件,從而使你可用不同的請求對客戶進行引數化;對請求排隊或記錄請求日誌,以及支援可撤銷的操作。
動機
有時必須向某個物件提交請求,但並不知道關於被請求的操作或請求的接受者的任何資訊。
典型場景
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方法即可。
相關文章
- 設計模式--命令模式Command(行為型)設計模式
- 設計模式——命令模式實現撤銷設計模式
- 設計模式-命令模式(Command)設計模式
- 設計模式——18命令模式(Command)設計模式
- Java設計模式(22)命令模式(Command模式)Java設計模式
- 命令模式與go-redis command設計模式GoRedis
- 設計模式的征途—19.命令(Command)模式設計模式
- C++設計模式——命令模式(command pattern)C++設計模式
- C#設計模式系列:命令模式(Command)C#設計模式
- JAVA設計模式之 命令模式【Command Pattern】Java設計模式
- 設計模式:命令模式(Command Pattern)及例項設計模式
- 4/24 設計模式之命令設計模式 Command Pattern設計模式
- 使用C# (.NET Core) 實現命令設計模式 (Command Pattern)C#設計模式
- 【圖解設計模式系列】The Command Pattern: 命令列模式圖解設計模式命令列
- 命令模式(Command Pattern)。模式
- 撤銷功能的實現——備忘錄模式(五)模式
- 撤銷功能的實現——備忘錄模式(四)模式
- 撤銷功能的實現——備忘錄模式(三)模式
- 撤銷功能的實現——備忘錄模式(二)模式
- 撤銷功能的實現——備忘錄模式(一)模式
- JavaScript設計模式--行為型別--命令模式JavaScript設計模式型別
- 設計模式-- 觀察者模式Observer(物件行為型)設計模式Server物件
- 命令模式 :Command(轉自LoveCherry)模式
- fastadmin命令列模式--commandAST命令列模式
- command模式模式
- 物件導向-設計模式-行為型物件設計模式
- AUTOCAD——命令重複、撤銷與重做
- Windows Phone 7 MVVM模式通訊方式之實現CommandWindowsMVVM模式
- Qt中的撤銷/重做功能QT
- Spartacus 使用 Command 設計模式之後對以前 Connector 實現的重用設計模式
- 設計模式-行為型設計模式
- 行為型設計模式設計模式
- [.net 物件導向程式設計深入](26)實戰設計模式——策略模式 Strategy (行為型)物件程式設計設計模式
- 設計模式--策略模式Strategy(行為型)設計模式
- 行為型命令模式模式
- Command 模式 Step by Step模式
- 設計模式(十九)----行為型模式之命令模式設計模式
- (Java)設計模式:行為型Java設計模式