PageRenderTime 54ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/toolkit/Microsoft.Phone.Controls.Toolkit/ProgressBar/RelativeAnimatingContentControl.cs

https://bitbucket.org/jeremejevs/word-steps
C# | 466 lines | 213 code | 43 blank | 210 comment | 30 complexity | 221abdbd4d02b31a8bcea74a53ee43a4 MD5 | raw file
  1. // (c) Copyright Microsoft Corporation.
  2. // This source is subject to the Microsoft Public License (Ms-PL).
  3. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
  4. // All other rights reserved.
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Windows;
  8. using System.Windows.Controls;
  9. using System.Windows.Media;
  10. using System.Windows.Media.Animation;
  11. using System.Diagnostics.CodeAnalysis;
  12. // This is a very special primitive control that works around a limitation in
  13. // the core animation subsystem of Silverlight: there is no way to declare in
  14. // VSM states relative properties, such as animating from 0 to 33% the width of
  15. // the control, using double animations for translation.
  16. //
  17. // It's a tough problem to solve property, but this primitive, unsupported
  18. // control does offer a solution based on magic numbers that still allows a
  19. // designer to make alterations to their animation values to present their
  20. // vision for custom templates.
  21. //
  22. // This is instrumental in offering a Windows Phone ProgressBar implementation
  23. // that uses the render thread instead of animating UI thread-only properties.
  24. //
  25. // This control is not supported other than that it is used by the performance
  26. // progress bar control. It should not be used elsewhere.
  27. //
  28. namespace Microsoft.Phone.Controls.Primitives
  29. {
  30. /// <summary>
  31. /// A very specialized primitive control that works around a specific visual
  32. /// state manager issue. The platform does not support relative sized
  33. /// translation values, and this special control walks through visual state
  34. /// animation storyboards looking for magic numbers to use as percentages.
  35. /// This control is not supported, unofficial, and is a hack in many ways.
  36. /// It is used to enable a Windows Phone native platform-style progress bar
  37. /// experience in indeterminate mode that remains performant.
  38. /// </summary>
  39. public class RelativeAnimatingContentControl : ContentControl
  40. {
  41. /// <summary>
  42. /// A simple Epsilon-style value used for trying to determine the magic
  43. /// state, if any, of a double.
  44. /// </summary>
  45. private const double SimpleDoubleComparisonEpsilon = 0.000009;
  46. /// <summary>
  47. /// The last known width of the control.
  48. /// </summary>
  49. private double _knownWidth;
  50. /// <summary>
  51. /// The last known height of the control.
  52. /// </summary>
  53. private double _knownHeight;
  54. /// <summary>
  55. /// A set of custom animation adapters used to update the animation
  56. /// storyboards when the size of the control changes.
  57. /// </summary>
  58. private List<AnimationValueAdapter> _specialAnimations;
  59. /// <summary>
  60. /// Initializes a new instance of the RelativeAnimatingContentControl
  61. /// type.
  62. /// </summary>
  63. public RelativeAnimatingContentControl()
  64. {
  65. SizeChanged += OnSizeChanged;
  66. }
  67. /// <summary>
  68. /// Handles the size changed event.
  69. /// </summary>
  70. /// <param name="sender">The source object.</param>
  71. /// <param name="e">The event arguments.</param>
  72. private void OnSizeChanged(object sender, SizeChangedEventArgs e)
  73. {
  74. if (e != null && e.NewSize.Height > 0 && e.NewSize.Width > 0)
  75. {
  76. _knownWidth = e.NewSize.Width;
  77. _knownHeight = e.NewSize.Height;
  78. Clip = new RectangleGeometry { Rect = new Rect(0, 0, _knownWidth, _knownHeight), };
  79. UpdateAnyAnimationValues();
  80. }
  81. }
  82. /// <summary>
  83. /// Walks through the known storyboards in the control's template that
  84. /// may contain magic double animation values, storing them for future
  85. /// use and updates.
  86. /// </summary>
  87. private void UpdateAnyAnimationValues()
  88. {
  89. if (_knownHeight > 0 && _knownWidth > 0)
  90. {
  91. // Initially, before any special animations have been found,
  92. // the visual state groups of the control must be explored.
  93. // By definition they must be at the implementation root of the
  94. // control, and this is designed to not walk into any other
  95. // depth.
  96. if (_specialAnimations == null)
  97. {
  98. _specialAnimations = new List<AnimationValueAdapter>();
  99. foreach (VisualStateGroup group in VisualStateManager.GetVisualStateGroups(this))
  100. {
  101. if (group == null)
  102. {
  103. continue;
  104. }
  105. foreach (VisualState state in group.States)
  106. {
  107. if (state != null)
  108. {
  109. Storyboard sb = state.Storyboard;
  110. if (sb != null)
  111. {
  112. // Examine all children of the storyboards,
  113. // looking for either type of double
  114. // animation.
  115. foreach (Timeline timeline in sb.Children)
  116. {
  117. DoubleAnimation da = timeline as DoubleAnimation;
  118. DoubleAnimationUsingKeyFrames dakeys = timeline as DoubleAnimationUsingKeyFrames;
  119. if (da != null)
  120. {
  121. ProcessDoubleAnimation(da);
  122. }
  123. else if (dakeys != null)
  124. {
  125. ProcessDoubleAnimationWithKeys(dakeys);
  126. }
  127. }
  128. }
  129. }
  130. }
  131. }
  132. }
  133. // Update special animation values relative to the current size.
  134. UpdateKnownAnimations();
  135. }
  136. }
  137. /// <summary>
  138. /// Walks through all special animations, updating based on the current
  139. /// size of the control.
  140. /// </summary>
  141. private void UpdateKnownAnimations()
  142. {
  143. foreach (AnimationValueAdapter adapter in _specialAnimations)
  144. {
  145. adapter.UpdateWithNewDimension(_knownWidth, _knownHeight);
  146. }
  147. }
  148. /// <summary>
  149. /// Processes a double animation with keyframes, looking for known
  150. /// special values to store with an adapter.
  151. /// </summary>
  152. /// <param name="da">The double animation using key frames instance.</param>
  153. private void ProcessDoubleAnimationWithKeys(DoubleAnimationUsingKeyFrames da)
  154. {
  155. // Look through all keyframes in the instance.
  156. foreach (DoubleKeyFrame frame in da.KeyFrames)
  157. {
  158. var d = DoubleAnimationFrameAdapter.GetDimensionFromMagicNumber(frame.Value);
  159. if (d.HasValue)
  160. {
  161. _specialAnimations.Add(new DoubleAnimationFrameAdapter(d.Value, frame));
  162. }
  163. }
  164. }
  165. /// <summary>
  166. /// Processes a double animation looking for special values.
  167. /// </summary>
  168. /// <param name="da">The double animation instance.</param>
  169. private void ProcessDoubleAnimation(DoubleAnimation da)
  170. {
  171. // Look for a special value in the To property.
  172. if (da.To.HasValue)
  173. {
  174. var d = DoubleAnimationToAdapter.GetDimensionFromMagicNumber(da.To.Value);
  175. if (d.HasValue)
  176. {
  177. _specialAnimations.Add(new DoubleAnimationToAdapter(d.Value, da));
  178. }
  179. }
  180. // Look for a special value in the From property.
  181. if (da.From.HasValue)
  182. {
  183. var d = DoubleAnimationFromAdapter.GetDimensionFromMagicNumber(da.To.Value);
  184. if (d.HasValue)
  185. {
  186. _specialAnimations.Add(new DoubleAnimationFromAdapter(d.Value, da));
  187. }
  188. }
  189. }
  190. #region Private animation updating system
  191. /// <summary>
  192. /// A selection of dimensions of interest for updating an animation.
  193. /// </summary>
  194. private enum DoubleAnimationDimension
  195. {
  196. /// <summary>
  197. /// The width (horizontal) dimension.
  198. /// </summary>
  199. Width,
  200. /// <summary>
  201. /// The height (vertical) dimension.
  202. /// </summary>
  203. Height,
  204. }
  205. /// <summary>
  206. /// A simple class designed to store information about a specific
  207. /// animation instance and its properties. Able to update the values at
  208. /// runtime.
  209. /// </summary>
  210. private abstract class AnimationValueAdapter
  211. {
  212. /// <summary>
  213. /// Initializes a new instance of the AnimationValueAdapter type.
  214. /// </summary>
  215. /// <param name="dimension">The dimension of interest for updates.</param>
  216. public AnimationValueAdapter(DoubleAnimationDimension dimension)
  217. {
  218. Dimension = dimension;
  219. }
  220. /// <summary>
  221. /// Gets the dimension of interest for the control.
  222. /// </summary>
  223. public DoubleAnimationDimension Dimension { get; private set; }
  224. /// <summary>
  225. /// Updates the original instance based on new dimension information
  226. /// from the control. Takes both and allows the subclass to make the
  227. /// decision on which ratio, values, and dimension to use.
  228. /// </summary>
  229. /// <param name="width">The width of the control.</param>
  230. /// <param name="height">The height of the control.</param>
  231. public abstract void UpdateWithNewDimension(double width, double height);
  232. }
  233. private abstract class GeneralAnimationValueAdapter<T> : AnimationValueAdapter
  234. {
  235. /// <summary>
  236. /// Stores the animation instance.
  237. /// </summary>
  238. protected T Instance { get; set; }
  239. /// <summary>
  240. /// Gets the value of the underlying property of interest.
  241. /// </summary>
  242. /// <returns>Returns the value of the property.</returns>
  243. protected abstract double GetValue();
  244. /// <summary>
  245. /// Sets the value for the underlying property of interest.
  246. /// </summary>
  247. /// <param name="newValue">The new value for the property.</param>
  248. protected abstract void SetValue(double newValue);
  249. /// <summary>
  250. /// Gets the initial value (minus the magic number portion) that the
  251. /// designer stored within the visual state animation property.
  252. /// </summary>
  253. protected double InitialValue { get; private set; }
  254. /// <summary>
  255. /// The ratio based on the original magic value, used for computing
  256. /// the updated animation property of interest when the size of the
  257. /// control changes.
  258. /// </summary>
  259. private double _ratio;
  260. /// <summary>
  261. /// Initializes a new instance of the GeneralAnimationValueAdapter
  262. /// type.
  263. /// </summary>
  264. /// <param name="d">The dimension of interest.</param>
  265. /// <param name="instance">The animation type instance.</param>
  266. [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "Should not have any undesirable side-effects.")]
  267. public GeneralAnimationValueAdapter(DoubleAnimationDimension d, T instance)
  268. : base(d)
  269. {
  270. Instance = instance;
  271. InitialValue = StripMagicNumberOff(GetValue());
  272. _ratio = InitialValue / 100;
  273. }
  274. /// <summary>
  275. /// Approximately removes the magic number state from a value.
  276. /// </summary>
  277. /// <param name="number">The initial number.</param>
  278. /// <returns>Returns a double with an adjustment for the magic
  279. /// portion of the number.</returns>
  280. public double StripMagicNumberOff(double number)
  281. {
  282. return Dimension == DoubleAnimationDimension.Width ? number - .1 : number - .2;
  283. }
  284. /// <summary>
  285. /// Retrieves the dimension, if any, from the number. If the number
  286. /// is not magic, null is returned instead.
  287. /// </summary>
  288. /// <param name="number">The double value.</param>
  289. /// <returns>Returs a double animation dimension, if the number was
  290. /// partially magic; otherwise, returns null.</returns>
  291. public static DoubleAnimationDimension? GetDimensionFromMagicNumber(double number)
  292. {
  293. double floor = Math.Floor(number);
  294. double remainder = number - floor;
  295. if (remainder >= .1 - SimpleDoubleComparisonEpsilon && remainder <= .1 + SimpleDoubleComparisonEpsilon)
  296. {
  297. return DoubleAnimationDimension.Width;
  298. }
  299. if (remainder >= .2 - SimpleDoubleComparisonEpsilon && remainder <= .2 + SimpleDoubleComparisonEpsilon)
  300. {
  301. return DoubleAnimationDimension.Height;
  302. }
  303. return null;
  304. }
  305. /// <summary>
  306. /// Updates the animation instance based on the dimensions of the
  307. /// control.
  308. /// </summary>
  309. /// <param name="width">The width of the control.</param>
  310. /// <param name="height">The height of the control.</param>
  311. public override void UpdateWithNewDimension(double width, double height)
  312. {
  313. double size = Dimension == DoubleAnimationDimension.Width ? width : height;
  314. UpdateValue(size);
  315. }
  316. /// <summary>
  317. /// Updates the value of the property.
  318. /// </summary>
  319. /// <param name="sizeToUse">The size of interest to use with a ratio
  320. /// computation.</param>
  321. private void UpdateValue(double sizeToUse)
  322. {
  323. SetValue(sizeToUse * _ratio);
  324. }
  325. }
  326. /// <summary>
  327. /// Adapter for DoubleAnimation's To property.
  328. /// </summary>
  329. private class DoubleAnimationToAdapter : GeneralAnimationValueAdapter<DoubleAnimation>
  330. {
  331. /// <summary>
  332. /// Gets the value of the underlying property of interest.
  333. /// </summary>
  334. /// <returns>Returns the value of the property.</returns>
  335. protected override double GetValue()
  336. {
  337. return (double)Instance.To;
  338. }
  339. /// <summary>
  340. /// Sets the value for the underlying property of interest.
  341. /// </summary>
  342. /// <param name="newValue">The new value for the property.</param>
  343. protected override void SetValue(double newValue)
  344. {
  345. Instance.To = newValue;
  346. }
  347. /// <summary>
  348. /// Initializes a new instance of the DoubleAnimationToAdapter type.
  349. /// </summary>
  350. /// <param name="dimension">The dimension of interest.</param>
  351. /// <param name="instance">The instance of the animation type.</param>
  352. public DoubleAnimationToAdapter(DoubleAnimationDimension dimension, DoubleAnimation instance)
  353. : base(dimension, instance)
  354. {
  355. }
  356. }
  357. /// <summary>
  358. /// Adapter for DoubleAnimation's From property.
  359. /// </summary>
  360. private class DoubleAnimationFromAdapter : GeneralAnimationValueAdapter<DoubleAnimation>
  361. {
  362. /// <summary>
  363. /// Gets the value of the underlying property of interest.
  364. /// </summary>
  365. /// <returns>Returns the value of the property.</returns>
  366. protected override double GetValue()
  367. {
  368. return (double)Instance.From;
  369. }
  370. /// <summary>
  371. /// Sets the value for the underlying property of interest.
  372. /// </summary>
  373. /// <param name="newValue">The new value for the property.</param>
  374. protected override void SetValue(double newValue)
  375. {
  376. Instance.From = newValue;
  377. }
  378. /// <summary>
  379. /// Initializes a new instance of the DoubleAnimationFromAdapter
  380. /// type.
  381. /// </summary>
  382. /// <param name="dimension">The dimension of interest.</param>
  383. /// <param name="instance">The instance of the animation type.</param>
  384. public DoubleAnimationFromAdapter(DoubleAnimationDimension dimension, DoubleAnimation instance)
  385. : base(dimension, instance)
  386. {
  387. }
  388. }
  389. /// <summary>
  390. /// Adapter for double key frames.
  391. /// </summary>
  392. private class DoubleAnimationFrameAdapter : GeneralAnimationValueAdapter<DoubleKeyFrame>
  393. {
  394. /// <summary>
  395. /// Gets the value of the underlying property of interest.
  396. /// </summary>
  397. /// <returns>Returns the value of the property.</returns>
  398. protected override double GetValue()
  399. {
  400. return Instance.Value;
  401. }
  402. /// <summary>
  403. /// Sets the value for the underlying property of interest.
  404. /// </summary>
  405. /// <param name="newValue">The new value for the property.</param>
  406. protected override void SetValue(double newValue)
  407. {
  408. Instance.Value = newValue;
  409. }
  410. /// <summary>
  411. /// Initializes a new instance of the DoubleAnimationFrameAdapter
  412. /// type.
  413. /// </summary>
  414. /// <param name="dimension">The dimension of interest.</param>
  415. /// <param name="frame">The instance of the animation type.</param>
  416. public DoubleAnimationFrameAdapter(DoubleAnimationDimension dimension, DoubleKeyFrame frame)
  417. : base(dimension, frame)
  418. {
  419. }
  420. }
  421. #endregion
  422. }
  423. }