CSLA.NET 5.4.2
CSLA .NET is a software development framework that helps you build a reusable, maintainable object-oriented business layer for your app.
UndoableBase.cs
Go to the documentation of this file.
1//-----------------------------------------------------------------------
2// <copyright file="UndoableBase.cs" company="Marimer LLC">
3// Copyright (c) Marimer LLC. All rights reserved.
4// Website: https://www.lhotka.net/cslanet/
5// </copyright>
6// <summary>Implements n-level undo capabilities as</summary>
7//-----------------------------------------------------------------------
8using System;
9using System.Collections.Generic;
10using System.Reflection;
11using System.IO;
12using System.ComponentModel;
13using Csla.Properties;
14using Csla.Reflection;
17using System.Linq;
18
19namespace Csla.Core
20{
25 [Serializable()]
26 public abstract class UndoableBase : Csla.Core.BindableBase,
28 {
29 // keep a stack of object state values.
30 [NotUndoable()]
31 private Stack<byte[]> _stateStack = new Stack<byte[]>();
32 [NotUndoable]
33 private bool _bindingEdit;
34
38 protected UndoableBase()
39 {
40
41 }
42
48 [EditorBrowsable(EditorBrowsableState.Never)]
49 protected bool BindingEdit
50 {
51 get
52 {
53 return _bindingEdit;
54 }
55 set
56 {
57 _bindingEdit = value;
58 }
59 }
60
62 {
63 get { return EditLevel; }
64 }
65
69 [EditorBrowsable(EditorBrowsableState.Never)]
70 protected int EditLevel
71 {
72 get { return _stateStack.Count; }
73 }
74
75 void IUndoableObject.CopyState(int parentEditLevel, bool parentBindingEdit)
76 {
77 if (!parentBindingEdit)
78 CopyState(parentEditLevel);
79 }
80
81 void IUndoableObject.UndoChanges(int parentEditLevel, bool parentBindingEdit)
82 {
83 if (!parentBindingEdit)
84 UndoChanges(parentEditLevel);
85 }
86
87 void IUndoableObject.AcceptChanges(int parentEditLevel, bool parentBindingEdit)
88 {
89 if (!parentBindingEdit)
90 AcceptChanges(parentEditLevel);
91 }
92
97 [EditorBrowsable(EditorBrowsableState.Advanced)]
98 protected virtual void CopyingState()
99 {
100 }
101
106 [EditorBrowsable(EditorBrowsableState.Advanced)]
107 protected virtual void CopyStateComplete()
108 {
109 }
110
115 [EditorBrowsable(EditorBrowsableState.Never)]
116 protected internal void CopyState(int parentEditLevel)
117 {
118 CopyingState();
119
120 Type currentType = this.GetType();
121 var state = new MobileDictionary<string, object>();
122
123 if (this.EditLevel + 1 > parentEditLevel)
124 throw new UndoException(string.Format(Resources.EditLevelMismatchException, "CopyState"), this.GetType().Name, null, this.EditLevel, parentEditLevel - 1);
125
126 do
127 {
128 var currentTypeName = currentType.FullName;
129 // get the list of fields in this type
130 List<DynamicMemberHandle> handlers =
131 UndoableHandler.GetCachedFieldHandlers(currentType);
132 foreach (var h in handlers)
133 {
134 var value = h.DynamicMemberGet(this);
135 var fieldName = GetFieldName(currentTypeName, h.MemberName);
136
137 if (typeof(IUndoableObject).IsAssignableFrom(h.MemberType))
138 {
139 if (value == null)
140 {
141 // variable has no value - store that fact
142 state.Add(fieldName, null);
143 }
144 else
145 {
146 // this is a child object, cascade the call
147 ((IUndoableObject)value).CopyState(this.EditLevel + 1, BindingEdit);
148 }
149 }
150 else if (value is IMobileObject)
151 {
152 // this is a mobile object, store the serialized value
153 using (MemoryStream buffer = new MemoryStream())
154 {
155 var formatter = SerializationFormatterFactory.GetFormatter();
156 formatter.Serialize(buffer, value);
157 state.Add(fieldName, buffer.ToArray());
158 }
159 }
160 else
161 {
162 // this is a normal field, simply trap the value
163 state.Add(fieldName, value);
164 }
165 }
166
167 currentType = currentType.BaseType;
168 } while (currentType != typeof(UndoableBase));
169
170 // serialize the state and stack it
171 using (MemoryStream buffer = new MemoryStream())
172 {
173 var formatter = SerializationFormatterFactory.GetFormatter();
174 formatter.Serialize(buffer, state);
175 _stateStack.Push(buffer.ToArray());
176 }
178 }
179
184 [EditorBrowsable(EditorBrowsableState.Advanced)]
185 protected virtual void UndoChangesComplete()
186 {
187 }
188
193 [EditorBrowsable(EditorBrowsableState.Advanced)]
194 protected virtual void UndoingChanges()
195 {
196 }
197
208 [EditorBrowsable(EditorBrowsableState.Never)]
209 protected internal void UndoChanges(int parentEditLevel)
210 {
212
213 // if we are a child object we might be asked to
214 // undo below the level of stacked states,
215 // so just do nothing in that case
216 if (EditLevel > 0)
217 {
218 if (this.EditLevel - 1 != parentEditLevel)
219 throw new UndoException(string.Format(Resources.EditLevelMismatchException, "UndoChanges"), this.GetType().Name, null, this.EditLevel, parentEditLevel + 1);
220
222 using (MemoryStream buffer = new MemoryStream(_stateStack.Pop()))
223 {
224 buffer.Position = 0;
225 var formatter = SerializationFormatterFactory.GetFormatter();
226 state = (MobileDictionary<string, object>)formatter.Deserialize(buffer);
227 }
228
229 Type currentType = this.GetType();
230
231 do
232 {
233 var currentTypeName = currentType.FullName;
234
235 // get the list of fields in this type
236 List<DynamicMemberHandle> handlers = UndoableHandler.GetCachedFieldHandlers(currentType);
237 foreach (var h in handlers)
238 {
239 // the field is undoable, so restore its value
240 var value = h.DynamicMemberGet(this);
241 var fieldName = GetFieldName(currentTypeName, h.MemberName);
242
243 if (typeof(IUndoableObject).IsAssignableFrom(h.MemberType))
244 {
245 // this is a child object
246 // see if the previous value was empty
247 //if (state.Contains(h.MemberName))
248 if (state.Contains(fieldName))
249 {
250 // previous value was empty - restore to empty
251 h.DynamicMemberSet(this, null);
252 }
253 else
254 {
255 // make sure the variable has a value
256 if (value != null)
257 {
258 // this is a child object, cascade the call.
259 ((IUndoableObject)value).UndoChanges(this.EditLevel, BindingEdit);
260 }
261 }
262 }
263 else if (value is IMobileObject && state[fieldName] != null)
264 {
265 // this is a mobile object, deserialize the value
266 using (MemoryStream buffer = new MemoryStream((byte[])state[fieldName]))
267 {
268 buffer.Position = 0;
269 var formatter = SerializationFormatterFactory.GetFormatter();
270 var obj = formatter.Deserialize(buffer);
271 h.DynamicMemberSet(this, obj);
272 }
273 }
274 else
275 {
276 // this is a regular field, restore its value
277 h.DynamicMemberSet(this, state[fieldName]);
278 }
279 }
280
281 currentType = currentType.BaseType;
282 } while (currentType != typeof(UndoableBase));
283 }
285 }
286
291 [EditorBrowsable(EditorBrowsableState.Advanced)]
292 protected virtual void AcceptingChanges()
293 {
294 }
295
300 [EditorBrowsable(EditorBrowsableState.Advanced)]
301 protected virtual void AcceptChangesComplete()
302 {
303 }
304
314 [EditorBrowsable(EditorBrowsableState.Never)]
315 protected internal void AcceptChanges(int parentEditLevel)
316 {
318
319 if (this.EditLevel - 1 != parentEditLevel)
320 throw new UndoException(string.Format(Resources.EditLevelMismatchException, "AcceptChanges"), this.GetType().Name, null, this.EditLevel, parentEditLevel + 1);
321
322 if (EditLevel > 0)
323 {
324 _stateStack.Pop();
325 Type currentType = this.GetType();
326
327 do
328 {
329 // get the list of fields in this type
330 List<DynamicMemberHandle> handlers = UndoableHandler.GetCachedFieldHandlers(currentType);
331 foreach (var h in handlers)
332 {
333 // the field is undoable so see if it is a child object
334 if (typeof(Csla.Core.IUndoableObject).IsAssignableFrom(h.MemberType))
335 {
336 object value = h.DynamicMemberGet(this);
337 // make sure the variable has a value
338 if (value != null)
339 {
340 // it is a child object so cascade the call
341 ((Core.IUndoableObject)value).AcceptChanges(this.EditLevel, BindingEdit);
342 }
343 }
344 }
345
346 currentType = currentType.BaseType;
347 } while (currentType != typeof(UndoableBase));
348 }
350 }
351
358 private static string GetFieldName(string typeName, string memberName)
359 {
360 return typeName + "." + memberName;
361 }
362
363
364 #region Helper Functions
365
366 private static bool NotUndoableField(FieldInfo field)
367 {
368 return Attribute.IsDefined(field, typeof(NotUndoableAttribute));
369 }
370
371 private static string GetFieldName(FieldInfo field)
372 {
373 return field.DeclaringType.FullName + "!" + field.Name;
374 }
375
376 #endregion
377
378 #region Reset child edit level
379
380 internal static void ResetChildEditLevel(IUndoableObject child, int parentEditLevel, bool bindingEdit)
381 {
382 int targetLevel = parentEditLevel;
383 if (bindingEdit && targetLevel > 0 && !(child is FieldManager.FieldDataManager))
384 targetLevel--;
385 // if item's edit level is too high,
386 // reduce it to match list
387 while (child.EditLevel > targetLevel)
388 child.AcceptChanges(targetLevel, false);
389 // if item's edit level is too low,
390 // increase it to match list
391 while (child.EditLevel < targetLevel)
392 child.CopyState(targetLevel, false);
393 }
394
395 #endregion
396
397 #region MobileObject overrides
398
409 protected override void OnGetState(SerializationInfo info, StateMode mode)
410 {
411 if (mode != StateMode.Undo)
412 {
413 info.AddValue("_bindingEdit", _bindingEdit);
414 if (_stateStack.Count > 0)
415 {
416 var stackArray = _stateStack.ToArray();
417 info.AddValue("_stateStack", stackArray);
418 }
419 }
420 base.OnGetState(info, mode);
421 }
422
433 protected override void OnSetState(SerializationInfo info, StateMode mode)
434 {
435 if (mode != StateMode.Undo)
436 {
437 _bindingEdit = info.GetValue<bool>("_bindingEdit");
438 if (info.Values.ContainsKey("_stateStack"))
439 {
440 var stackArray = info.GetValue<byte[][]>("_stateStack");
441 _stateStack.Clear();
442 foreach (var item in stackArray.Reverse())
443 _stateStack.Push(item);
444 }
445 }
446 base.OnSetState(info, mode);
447 }
448
449 #endregion
450 }
451}
This class implements INotifyPropertyChanged and INotifyPropertyChanging in a serialization-safe mann...
Definition: BindableBase.cs:23
Defines a dictionary that can be serialized through the SerializationFormatterFactory....
bool Contains(K key)
Gets a value indicating whether the dictionary contains the specified key value.
Exception indicating a problem with the use of the n-level undo feature in CSLA .NET.
Implements n-level undo capabilities as described in Chapters 2 and 3.
Definition: UndoableBase.cs:28
bool BindingEdit
Gets or sets a value indicating whether n-level undo was invoked through IEditableObject.
Definition: UndoableBase.cs:50
virtual void CopyingState()
This method is invoked before the CopyState operation begins.
Definition: UndoableBase.cs:98
virtual void UndoingChanges()
This method is invoked after the UndoChanges operation is complete.
virtual void CopyStateComplete()
This method is invoked after the CopyState operation is complete.
virtual void AcceptChangesComplete()
This method is invoked after the AcceptChanges operation is complete.
override void OnGetState(SerializationInfo info, StateMode mode)
Override this method to insert your field values into the MobileFormatter serialzation stream.
UndoableBase()
Creates an instance of the object.
Definition: UndoableBase.cs:38
override void OnSetState(SerializationInfo info, StateMode mode)
Override this method to retrieve your field values from the MobileFormatter serialzation stream.
virtual void UndoChangesComplete()
This method is invoked before the UndoChanges operation begins.
virtual void AcceptingChanges()
This method is invoked before the AcceptChanges operation begins.
A strongly-typed resource class, for looking up localized strings, etc.
static string EditLevelMismatchException
Looks up a localized string similar to Edit level mismatch in {0}.
Object containing the serialization data for a specific object.
Dictionary< string, FieldData > Values
Dictionary containg field data.
void AddValue(string name, object value)
Adds a value to the serialization stream.
Defines the methods required to participate in n-level undo within the CSLA .NET framework.
void CopyState(int parentEditLevel, bool parentBindingEdit)
Copies the state of the object and places the copy onto the state stack.
int EditLevel
Gets the current edit level of the object.
Interface to be implemented by any object that supports serialization by the SerializationFormatterFa...
StateMode
Indicates the reason the MobileFormatter functionality has been invoked.
Definition: StateMode.cs:20
@ Serializable
Prevents updating or inserting until the transaction is complete.