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

/ReactiveUI.Routing/RoutingState.cs

https://github.com/bsiegel/ReactiveUI
C# | 143 lines | 90 code | 18 blank | 35 comment | 3 complexity | df246dc4f7da5eb096b52439401a1ce0 MD5 | raw file
Possible License(s): Apache-2.0, CC-BY-SA-3.0, LGPL-2.0
  1. using System;
  2. using System.Linq;
  3. using System.Reactive;
  4. using System.Reactive.Concurrency;
  5. using System.Reactive.Linq;
  6. using System.Reactive.Subjects;
  7. using System.Runtime.Serialization;
  8. using System.Windows.Input;
  9. using ReactiveUI.Xaml;
  10. namespace ReactiveUI.Routing
  11. {
  12. /// <summary>
  13. /// RoutingState manages the ViewModel Stack and allows ViewModels to
  14. /// navigate to other ViewModels.
  15. /// </summary>
  16. [DataContract]
  17. public class RoutingState : ReactiveObject, IRoutingState
  18. {
  19. [field: IgnoreDataMember]
  20. bool rxObjectsSetup = false;
  21. [IgnoreDataMember] ReactiveCollection<IRoutableViewModel> _NavigationStack;
  22. /// <summary>
  23. /// Represents the current navigation stack, the last element in the
  24. /// collection being the currently visible ViewModel.
  25. /// </summary>
  26. [DataMember]
  27. public ReactiveCollection<IRoutableViewModel> NavigationStack {
  28. get { return _NavigationStack; }
  29. protected set { _NavigationStack = value; }
  30. }
  31. /// <summary>
  32. /// Navigates back to the previous element in the stack.
  33. /// </summary>
  34. [IgnoreDataMember]
  35. public IReactiveCommand NavigateBack { get; protected set; }
  36. /// <summary>
  37. /// Navigates to the a new element in the stack - the Execute parameter
  38. /// must be a ViewModel that implements IRoutableViewModel.
  39. /// </summary>
  40. [IgnoreDataMember]
  41. public INavigateCommand Navigate { get; protected set; }
  42. /// <summary>
  43. /// Navigates to a new element and resets the navigation stack (i.e. the
  44. /// new ViewModel will now be the only element in the stack) - the
  45. /// Execute parameter must be a ViewModel that implements
  46. /// IRoutableViewModel.
  47. /// </summary>
  48. [IgnoreDataMember]
  49. public INavigateCommand NavigateAndReset { get; protected set; }
  50. public RoutingState()
  51. {
  52. _NavigationStack = new ReactiveCollection<IRoutableViewModel>();
  53. setupRx();
  54. }
  55. [OnDeserialized]
  56. #if WP7
  57. public
  58. #endif
  59. void setupRx(StreamingContext sc) { setupRx(); }
  60. void setupRx()
  61. {
  62. if (rxObjectsSetup) return;
  63. NavigateBack = new ReactiveCommand(
  64. NavigationStack.CollectionCountChanged.StartWith(_NavigationStack.Count).Select(x => x > 0));
  65. NavigateBack.Subscribe(_ =>
  66. NavigationStack.RemoveAt(NavigationStack.Count - 1));
  67. Navigate = new NavigationReactiveCommand();
  68. Navigate.Subscribe(x => {
  69. var vm = x as IRoutableViewModel;
  70. if (vm == null) {
  71. throw new Exception("Navigate must be called on an IRoutableViewModel");
  72. }
  73. NavigationStack.Add(vm);
  74. });
  75. NavigateAndReset = new NavigationReactiveCommand();
  76. NavigateAndReset.Subscribe(x => {
  77. NavigationStack.Clear();
  78. Navigate.Execute(x);
  79. });
  80. rxObjectsSetup = true;
  81. }
  82. }
  83. class NavigationReactiveCommand : ReactiveCommand, INavigateCommand { }
  84. public static class RoutingStateMixins
  85. {
  86. /// <summary>
  87. /// Generates a routing Uri based on the current route state
  88. /// </summary>
  89. /// <returns></returns>
  90. public static string GetUrlForCurrentRoute(this IRoutingState This)
  91. {
  92. return "app://" + String.Join("/", This.NavigationStack.Select(x => x.UrlPathSegment));
  93. }
  94. /// <summary>
  95. /// Locate the first ViewModel in the stack that matches a certain Type.
  96. /// </summary>
  97. /// <returns>The matching ViewModel or null if none exists.</returns>
  98. public static T FindViewModelInStack<T>(this IRoutingState This)
  99. where T : IRoutableViewModel
  100. {
  101. return This.NavigationStack.Reverse().OfType<T>().FirstOrDefault();
  102. }
  103. /// <summary>
  104. /// Returns the currently visible ViewModel
  105. /// </summary>
  106. public static IRoutableViewModel GetCurrentViewModel(this IRoutingState This)
  107. {
  108. return This.NavigationStack.LastOrDefault();
  109. }
  110. /// <summary>
  111. /// Returns an Observable that signals ViewModel changes.
  112. /// </summary>
  113. public static IObservable<IRoutableViewModel> ViewModelObservable(this IRoutingState This)
  114. {
  115. return This.NavigationStack.CollectionCountChanged
  116. .Select(_ => This.GetCurrentViewModel())
  117. .StartWith(This.GetCurrentViewModel());
  118. }
  119. public static void Go<T>(this INavigateCommand This, string key = null)
  120. where T : IRoutableViewModel
  121. {
  122. This.Execute(RxApp.GetService<T>(key));
  123. }
  124. }
  125. }