Many applications requires history support (undo/redo). I found class ChangPropertyCommand very helpful for such tasks, because it allows you to specify any property of the object to be changed, so you don’t need to write many classes.
public class ChangePropertyCommand : Command { private var Target_; private var NewValue_; private var OldValue_; private string PropertyName_; public ChangePropertyCommand(object Target, string PropertyName, object Value) { Target_ = Target; NewValue_ = Value; PropertyName_ = PropertyName; OldValue_ = GetValue(); } public override void Execute() { base.Execute(); SetValue(NewValue_); } public override void UnExecute() { base.UnExecute(); SetValue(OldValue_); } private void SetValue(object Value) { object tmp = Target_; var type = Target_.GetType(); string[] paths = PropertyName_.Split("."); PropertyInfo prop = type.GetProperty(paths(0)); for (int i = 0; i <= paths.Length - 2; i++) { prop = type.GetProperty(paths(i)); tmp = prop.GetValue(tmp, null); type = tmp.GetType; } prop = type.GetProperty(paths(paths.Length - 1)); prop.SetValue(tmp, Value, null); } private object GetValue() { object ret = Target_; var type = Target_.GetType(); string[] paths = PropertyName_.Split("."); foreach (string path in paths) { var prop = type.GetProperty(path); ret = prop.GetValue(ret, null); if (ret != null) { type = ret.GetType; } } return ret; } }
Imports System.Reflection Public Class ChangePropertyCommand Inherits Command Private Target_ Private NewValue_ Private OldValue_ Private PropertyName_ As String Sub New(ByVal Target As Object, ByVal PropertyName As String, ByVal Value As Object) Target_ = Target NewValue_ = Value PropertyName_ = PropertyName OldValue_ = GetValue() End Sub Public Overrides Sub Execute() MyBase.Execute() SetValue(NewValue_) End Sub Public Overrides Sub UnExecute() MyBase.UnExecute() SetValue(OldValue_) End Sub Private Sub SetValue(ByVal Value As Object) Dim tmp As Object = Target_ Dim type = Target_.GetType() Dim paths As String() = PropertyName_.Split(".") Dim prop As PropertyInfo = type.GetProperty(paths(0)) For i As Integer = 0 To paths.Length - 2 prop = type.GetProperty(paths(i)) tmp = prop.GetValue(tmp, Nothing) type = tmp.GetType Next prop = type.GetProperty(paths(paths.Length - 1)) prop.SetValue(tmp, Value, Nothing) End Sub Private Function GetValue() As Object Dim ret As Object = Target_ Dim type = Target_.GetType() Dim paths As String() = PropertyName_.Split(".") For Each path As String In paths Dim prop = type.GetProperty(path) ret = prop.GetValue(ret, Nothing) If ret IsNot Nothing Then type = ret.GetType End If Next Return ret End Function End Class
Here some example of using this class:
ChangePropertyCommand width = new ChangePropertyCommand(CurrentElement, "Width", Current.Width); ChangePropertyCommand height = new ChangePropertyCommand(CurrentElement, "Height", Current.Height);
Dim width As New ChangePropertyCommand(CurrentElement, "Width", Current.Width) Dim height As New ChangePropertyCommand(CurrentElement, "Height", Current.Height)
As you can see, this code creates two commands, they will change properties Width and Height of the CurrentElement object, by values from Current object.
Often, Execute and Unexecute methods are not called directly, but called by History class.