PageRenderTime 42ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/BE2012/Common/SuspensionManager.cs

https://bitbucket.org/damirarh/bleedingedge2012
C# | 257 lines | 155 code | 21 blank | 81 comment | 14 complexity | 4443614c6cb2af53c35c17e75a19858c MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Runtime.Serialization;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. using Windows.ApplicationModel;
  9. using Windows.Storage;
  10. using Windows.Storage.Streams;
  11. using Windows.UI.Xaml;
  12. using Windows.UI.Xaml.Controls;
  13. namespace BE2012.Common
  14. {
  15. /// <summary>
  16. /// SuspensionManager captures global session state to simplify process lifetime management
  17. /// for an application. Note that session state will be automatically cleared under a variety
  18. /// of conditions and should only be used to store information that would be convenient to
  19. /// carry across sessions, but that should be discarded when an application crashes or is
  20. /// upgraded.
  21. /// </summary>
  22. internal sealed class SuspensionManager
  23. {
  24. private static Dictionary<string, object> _sessionState = new Dictionary<string, object>();
  25. private static List<Type> _knownTypes = new List<Type>();
  26. private const string sessionStateFilename = "_sessionState.xml";
  27. /// <summary>
  28. /// Provides access to global session state for the current session. This state is
  29. /// serialized by <see cref="SaveAsync"/> and restored by
  30. /// <see cref="RestoreAsync"/>, so values must be serializable by
  31. /// <see cref="DataContractSerializer"/> and should be as compact as possible. Strings
  32. /// and other self-contained data types are strongly recommended.
  33. /// </summary>
  34. public static Dictionary<string, object> SessionState
  35. {
  36. get { return _sessionState; }
  37. }
  38. /// <summary>
  39. /// List of custom types provided to the <see cref="DataContractSerializer"/> when
  40. /// reading and writing session state. Initially empty, additional types may be
  41. /// added to customize the serialization process.
  42. /// </summary>
  43. public static List<Type> KnownTypes
  44. {
  45. get { return _knownTypes; }
  46. }
  47. /// <summary>
  48. /// Save the current <see cref="SessionState"/>. Any <see cref="Frame"/> instances
  49. /// registered with <see cref="RegisterFrame"/> will also preserve their current
  50. /// navigation stack, which in turn gives their active <see cref="Page"/> an opportunity
  51. /// to save its state.
  52. /// </summary>
  53. /// <returns>An asynchronous task that reflects when session state has been saved.</returns>
  54. public static async Task SaveAsync()
  55. {
  56. try
  57. {
  58. // Save the navigation state for all registered frames
  59. foreach (var weakFrameReference in _registeredFrames)
  60. {
  61. Frame frame;
  62. if (weakFrameReference.TryGetTarget(out frame))
  63. {
  64. SaveFrameNavigationState(frame);
  65. }
  66. }
  67. // Serialize the session state synchronously to avoid asynchronous access to shared
  68. // state
  69. MemoryStream sessionData = new MemoryStream();
  70. DataContractSerializer serializer = new DataContractSerializer(typeof(Dictionary<string, object>), _knownTypes);
  71. serializer.WriteObject(sessionData, _sessionState);
  72. // Get an output stream for the SessionState file and write the state asynchronously
  73. StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(sessionStateFilename, CreationCollisionOption.ReplaceExisting);
  74. using (Stream fileStream = await file.OpenStreamForWriteAsync())
  75. {
  76. sessionData.Seek(0, SeekOrigin.Begin);
  77. await sessionData.CopyToAsync(fileStream);
  78. await fileStream.FlushAsync();
  79. }
  80. }
  81. catch (Exception e)
  82. {
  83. throw new SuspensionManagerException(e);
  84. }
  85. }
  86. /// <summary>
  87. /// Restores previously saved <see cref="SessionState"/>. Any <see cref="Frame"/> instances
  88. /// registered with <see cref="RegisterFrame"/> will also restore their prior navigation
  89. /// state, which in turn gives their active <see cref="Page"/> an opportunity restore its
  90. /// state.
  91. /// </summary>
  92. /// <returns>An asynchronous task that reflects when session state has been read. The
  93. /// content of <see cref="SessionState"/> should not be relied upon until this task
  94. /// completes.</returns>
  95. public static async Task RestoreAsync()
  96. {
  97. _sessionState = new Dictionary<String, Object>();
  98. try
  99. {
  100. // Get the input stream for the SessionState file
  101. StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync(sessionStateFilename);
  102. using (IInputStream inStream = await file.OpenSequentialReadAsync())
  103. {
  104. // Deserialize the Session State
  105. DataContractSerializer serializer = new DataContractSerializer(typeof(Dictionary<string, object>), _knownTypes);
  106. _sessionState = (Dictionary<string, object>)serializer.ReadObject(inStream.AsStreamForRead());
  107. }
  108. // Restore any registered frames to their saved state
  109. foreach (var weakFrameReference in _registeredFrames)
  110. {
  111. Frame frame;
  112. if (weakFrameReference.TryGetTarget(out frame))
  113. {
  114. frame.ClearValue(FrameSessionStateProperty);
  115. RestoreFrameNavigationState(frame);
  116. }
  117. }
  118. }
  119. catch (Exception e)
  120. {
  121. throw new SuspensionManagerException(e);
  122. }
  123. }
  124. private static DependencyProperty FrameSessionStateKeyProperty =
  125. DependencyProperty.RegisterAttached("_FrameSessionStateKey", typeof(String), typeof(SuspensionManager), null);
  126. private static DependencyProperty FrameSessionStateProperty =
  127. DependencyProperty.RegisterAttached("_FrameSessionState", typeof(Dictionary<String, Object>), typeof(SuspensionManager), null);
  128. private static List<WeakReference<Frame>> _registeredFrames = new List<WeakReference<Frame>>();
  129. /// <summary>
  130. /// Registers a <see cref="Frame"/> instance to allow its navigation history to be saved to
  131. /// and restored from <see cref="SessionState"/>. Frames should be registered once
  132. /// immediately after creation if they will participate in session state management. Upon
  133. /// registration if state has already been restored for the specified key
  134. /// the navigation history will immediately be restored. Subsequent invocations of
  135. /// <see cref="RestoreAsync"/> will also restore navigation history.
  136. /// </summary>
  137. /// <param name="frame">An instance whose navigation history should be managed by
  138. /// <see cref="SuspensionManager"/></param>
  139. /// <param name="sessionStateKey">A unique key into <see cref="SessionState"/> used to
  140. /// store navigation-related information.</param>
  141. public static void RegisterFrame(Frame frame, String sessionStateKey)
  142. {
  143. if (frame.GetValue(FrameSessionStateKeyProperty) != null)
  144. {
  145. throw new InvalidOperationException("Frames can only be registered to one session state key");
  146. }
  147. if (frame.GetValue(FrameSessionStateProperty) != null)
  148. {
  149. throw new InvalidOperationException("Frames must be either be registered before accessing frame session state, or not registered at all");
  150. }
  151. // Use a dependency property to associate the session key with a frame, and keep a list of frames whose
  152. // navigation state should be managed
  153. frame.SetValue(FrameSessionStateKeyProperty, sessionStateKey);
  154. _registeredFrames.Add(new WeakReference<Frame>(frame));
  155. // Check to see if navigation state can be restored
  156. RestoreFrameNavigationState(frame);
  157. }
  158. /// <summary>
  159. /// Disassociates a <see cref="Frame"/> previously registered by <see cref="RegisterFrame"/>
  160. /// from <see cref="SessionState"/>. Any navigation state previously captured will be
  161. /// removed.
  162. /// </summary>
  163. /// <param name="frame">An instance whose navigation history should no longer be
  164. /// managed.</param>
  165. public static void UnregisterFrame(Frame frame)
  166. {
  167. // Remove session state and remove the frame from the list of frames whose navigation
  168. // state will be saved (along with any weak references that are no longer reachable)
  169. SessionState.Remove((String)frame.GetValue(FrameSessionStateKeyProperty));
  170. _registeredFrames.RemoveAll((weakFrameReference) =>
  171. {
  172. Frame testFrame;
  173. return !weakFrameReference.TryGetTarget(out testFrame) || testFrame == frame;
  174. });
  175. }
  176. /// <summary>
  177. /// Provides storage for session state associated with the specified <see cref="Frame"/>.
  178. /// Frames that have been previously registered with <see cref="RegisterFrame"/> have
  179. /// their session state saved and restored automatically as a part of the global
  180. /// <see cref="SessionState"/>. Frames that are not registered have transient state
  181. /// that can still be useful when restoring pages that have been discarded from the
  182. /// navigation cache.
  183. /// </summary>
  184. /// <remarks>Apps may choose to rely on <see cref="LayoutAwarePage"/> to manage
  185. /// page-specific state instead of working with frame session state directly.</remarks>
  186. /// <param name="frame">The instance for which session state is desired.</param>
  187. /// <returns>A collection of state subject to the same serialization mechanism as
  188. /// <see cref="SessionState"/>.</returns>
  189. public static Dictionary<String, Object> SessionStateForFrame(Frame frame)
  190. {
  191. var frameState = (Dictionary<String, Object>)frame.GetValue(FrameSessionStateProperty);
  192. if (frameState == null)
  193. {
  194. var frameSessionKey = (String)frame.GetValue(FrameSessionStateKeyProperty);
  195. if (frameSessionKey != null)
  196. {
  197. // Registered frames reflect the corresponding session state
  198. if (!_sessionState.ContainsKey(frameSessionKey))
  199. {
  200. _sessionState[frameSessionKey] = new Dictionary<String, Object>();
  201. }
  202. frameState = (Dictionary<String, Object>)_sessionState[frameSessionKey];
  203. }
  204. else
  205. {
  206. // Frames that aren't registered have transient state
  207. frameState = new Dictionary<String, Object>();
  208. }
  209. frame.SetValue(FrameSessionStateProperty, frameState);
  210. }
  211. return frameState;
  212. }
  213. private static void RestoreFrameNavigationState(Frame frame)
  214. {
  215. var frameState = SessionStateForFrame(frame);
  216. if (frameState.ContainsKey("Navigation"))
  217. {
  218. frame.SetNavigationState((String)frameState["Navigation"]);
  219. }
  220. }
  221. private static void SaveFrameNavigationState(Frame frame)
  222. {
  223. var frameState = SessionStateForFrame(frame);
  224. frameState["Navigation"] = frame.GetNavigationState();
  225. }
  226. }
  227. public class SuspensionManagerException : Exception
  228. {
  229. public SuspensionManagerException()
  230. {
  231. }
  232. public SuspensionManagerException(Exception e) : base("SuspensionManager failed", e)
  233. {
  234. }
  235. }
  236. }