Command(命令)——物件行為型模式(通過Command設計模式實現WinForm表單維護的撤銷與重做功能)
阿新 • • 發佈:2018-11-11
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<Con