/ReactiveUI.Routing/RoutingState.cs
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
- using System;
- using System.Linq;
- using System.Reactive;
- using System.Reactive.Concurrency;
- using System.Reactive.Linq;
- using System.Reactive.Subjects;
- using System.Runtime.Serialization;
- using System.Windows.Input;
- using ReactiveUI.Xaml;
- namespace ReactiveUI.Routing
- {
- /// <summary>
- /// RoutingState manages the ViewModel Stack and allows ViewModels to
- /// navigate to other ViewModels.
- /// </summary>
- [DataContract]
- public class RoutingState : ReactiveObject, IRoutingState
- {
- [field: IgnoreDataMember]
- bool rxObjectsSetup = false;
- [IgnoreDataMember] ReactiveCollection<IRoutableViewModel> _NavigationStack;
- /// <summary>
- /// Represents the current navigation stack, the last element in the
- /// collection being the currently visible ViewModel.
- /// </summary>
- [DataMember]
- public ReactiveCollection<IRoutableViewModel> NavigationStack {
- get { return _NavigationStack; }
- protected set { _NavigationStack = value; }
- }
- /// <summary>
- /// Navigates back to the previous element in the stack.
- /// </summary>
- [IgnoreDataMember]
- public IReactiveCommand NavigateBack { get; protected set; }
- /// <summary>
- /// Navigates to the a new element in the stack - the Execute parameter
- /// must be a ViewModel that implements IRoutableViewModel.
- /// </summary>
- [IgnoreDataMember]
- public INavigateCommand Navigate { get; protected set; }
- /// <summary>
- /// Navigates to a new element and resets the navigation stack (i.e. the
- /// new ViewModel will now be the only element in the stack) - the
- /// Execute parameter must be a ViewModel that implements
- /// IRoutableViewModel.
- /// </summary>
- [IgnoreDataMember]
- public INavigateCommand NavigateAndReset { get; protected set; }
- public RoutingState()
- {
- _NavigationStack = new ReactiveCollection<IRoutableViewModel>();
- setupRx();
- }
- [OnDeserialized]
- #if WP7
- public
- #endif
- void setupRx(StreamingContext sc) { setupRx(); }
- void setupRx()
- {
- if (rxObjectsSetup) return;
- NavigateBack = new ReactiveCommand(
- NavigationStack.CollectionCountChanged.StartWith(_NavigationStack.Count).Select(x => x > 0));
- NavigateBack.Subscribe(_ =>
- NavigationStack.RemoveAt(NavigationStack.Count - 1));
- Navigate = new NavigationReactiveCommand();
- Navigate.Subscribe(x => {
- var vm = x as IRoutableViewModel;
- if (vm == null) {
- throw new Exception("Navigate must be called on an IRoutableViewModel");
- }
- NavigationStack.Add(vm);
- });
- NavigateAndReset = new NavigationReactiveCommand();
- NavigateAndReset.Subscribe(x => {
- NavigationStack.Clear();
- Navigate.Execute(x);
- });
- rxObjectsSetup = true;
- }
- }
- class NavigationReactiveCommand : ReactiveCommand, INavigateCommand { }
- public static class RoutingStateMixins
- {
- /// <summary>
- /// Generates a routing Uri based on the current route state
- /// </summary>
- /// <returns></returns>
- public static string GetUrlForCurrentRoute(this IRoutingState This)
- {
- return "app://" + String.Join("/", This.NavigationStack.Select(x => x.UrlPathSegment));
- }
- /// <summary>
- /// Locate the first ViewModel in the stack that matches a certain Type.
- /// </summary>
- /// <returns>The matching ViewModel or null if none exists.</returns>
- public static T FindViewModelInStack<T>(this IRoutingState This)
- where T : IRoutableViewModel
- {
- return This.NavigationStack.Reverse().OfType<T>().FirstOrDefault();
- }
- /// <summary>
- /// Returns the currently visible ViewModel
- /// </summary>
- public static IRoutableViewModel GetCurrentViewModel(this IRoutingState This)
- {
- return This.NavigationStack.LastOrDefault();
- }
- /// <summary>
- /// Returns an Observable that signals ViewModel changes.
- /// </summary>
- public static IObservable<IRoutableViewModel> ViewModelObservable(this IRoutingState This)
- {
- return This.NavigationStack.CollectionCountChanged
- .Select(_ => This.GetCurrentViewModel())
- .StartWith(This.GetCurrentViewModel());
- }
- public static void Go<T>(this INavigateCommand This, string key = null)
- where T : IRoutableViewModel
- {
- This.Execute(RxApp.GetService<T>(key));
- }
- }
- }