Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

More documentation, misc fixups

  • Loading branch information...
commit 0fdc1b178a45e207652697aa215fd01be7bda66a 1 parent 076451b
@paulcbetts paulcbetts authored
View
4 ReactiveUI.Routing.Tests/RoutingState.cs
@@ -14,7 +14,7 @@ public class TestViewModel : ReactiveObject, IRoutableViewModel
set { this.RaiseAndSetIfChanged(x => x.SomeProp, value); }
}
- public string FriendlyUrlName {
+ public string UrlPathSegment {
get { return "Test"; }
}
@@ -33,7 +33,7 @@ public void NavigationPushPopTest()
var fixture = new RoutingState();
Assert.False(fixture.NavigateBack.CanExecute(input));
- fixture.NavigateForward.Execute(input);
+ fixture.Navigate.Execute(input);
Assert.Equal(1, fixture.NavigationStack.Count);
Assert.True(fixture.NavigateBack.CanExecute(null));
View
33 ReactiveUI.Routing/Interfaces.cs
@@ -7,9 +7,21 @@
namespace ReactiveUI.Routing
{
+ /// <summary>
+ /// Implement this interface for ViewModels that can be navigated to.
+ /// </summary>
public interface IRoutableViewModel : IReactiveNotifyPropertyChanged
{
- string FriendlyUrlName { get; }
+ /// <summary>
+ /// A string token representing the current ViewModel, such as 'login' or 'user'
+ /// </summary>
+ string UrlPathSegment { get; }
+
+ /// <summary>
+ /// The IScreen that this ViewModel is currently being shown in. This
+ /// is usually passed into the ViewModel in the Constructor and saved
+ /// as a ReadOnly Property.
+ /// </summary>
IScreen HostScreen { get; }
}
@@ -18,19 +30,38 @@ public interface IViewForViewModel
object ViewModel { get; set; }
}
+ /// <summary>
+ /// Implement this interface on your Views.
+ /// </summary>
public interface IViewForViewModel<T> : IViewForViewModel
where T : IReactiveNotifyPropertyChanged
{
+ /// <summary>
+ /// The ViewModel corresponding to this specific View.
+ /// </summary>
T ViewModel { get; set; }
}
+ /// <summary>
+ /// IScreen represents any object that is hosting its own routing -
+ /// usually this object is your AppViewModel or MainWindow object.
+ /// </summary>
public interface IScreen
{
+ /// <summary>
+ /// The Router associated with this Screen.
+ /// </summary>
RoutingState Router { get; }
}
+ /// <summary>
+ /// Allows an additional string to make view resolution more specific than just a type.
+ /// </summary>
public class ViewContractAttribute : Attribute
{
+ /// <summary>
+ /// A unique string that will be used along with the type to resolve a View
+ /// </summary>
public string Contract { get; set; }
}
View
12 ReactiveUI.Routing/RoutedViewHost.cs
@@ -10,10 +10,18 @@
namespace ReactiveUI.Routing
{
+ /// <summary>
+ /// This control hosts the View associated with a Router, and will display
+ /// the View and wire up the ViewModel whenever a new ViewModel is
+ /// navigated to. Put this control as the only control in your Window.
+ /// </summary>
public class RoutedViewHost : ContentControl
{
IDisposable _inner = null;
+ /// <summary>
+ /// The Router associated with this View Host.
+ /// </summary>
public RoutingState Router {
get { return (RoutingState)GetValue(RouterProperty); }
set { SetValue(RouterProperty, value); }
@@ -21,6 +29,10 @@ public class RoutedViewHost : ContentControl
public static readonly DependencyProperty RouterProperty =
DependencyProperty.Register("Router", typeof(RoutingState), typeof(RoutedViewHost), new PropertyMetadata(null));
+ /// <summary>
+ /// This content is displayed whenever there is no page currently
+ /// routed.
+ /// </summary>
public object DefaultContent {
get { return (object)GetValue(DefaultContentProperty); }
set { SetValue(DefaultContentProperty, value); }
View
58 ReactiveUI.Routing/RoutingState.cs
@@ -8,26 +8,50 @@
namespace ReactiveUI.Routing
{
+ /// <summary>
+ /// RoutingState manages the ViewModel Stack and allows ViewModels to
+ /// navigate to other ViewModels.
+ /// </summary>
[DataContract]
public class RoutingState : ReactiveObject
{
[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 ReactiveCommand 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 ReactiveCommand NavigateForward { get; protected set; }
+ public ReactiveCommand 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 ReactiveCommand Navigate { get; protected set; }
+ public ReactiveCommand NavigateAndReset { get; protected set; }
+ /// <summary>
+ /// The currently visible ViewModel.
+ /// </summary>
[IgnoreDataMember]
public IObservable<IRoutableViewModel> CurrentViewModel { get; protected set; }
@@ -54,32 +78,38 @@ void setupRx()
NavigateBack.Subscribe(_ =>
NavigationStack.RemoveAt(NavigationStack.Count - 1));
- NavigateForward = new ReactiveCommand();
- NavigateForward.Subscribe(x =>
+ Navigate = new ReactiveCommand();
+ Navigate.Subscribe(x =>
NavigationStack.Insert(NavigationStack.Count, (IRoutableViewModel)x));
- Navigate = new ReactiveCommand();
- Navigate.Subscribe(x => {
+ NavigateAndReset = new ReactiveCommand();
+ NavigateAndReset.Subscribe(x => {
NavigationStack.Clear();
NavigationStack.Add((IRoutableViewModel) x);
});
- /*
- if (_AutoSaveContract != null) {
- Observable.Merge(Navigate, NavigateForward, NavigateBack)
- .Subscribe(_ => RxStorage.Engine.CreateSyncPoint(this, _AutoSaveContract));
- }
- */
-
CurrentViewModel = NavigationStack.CollectionCountChanged
.Select(_ => NavigationStack.LastOrDefault())
.Multicast(new ReplaySubject<IRoutableViewModel>(1)).PermaRef();
}
+ /// <summary>
+ /// Generates a routing Uri based on the current route state
+ /// </summary>
+ /// <returns></returns>
+ public string GetUrlForCurrentRoute()
+ {
+ return "app://" + String.Join("/", 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 T FindViewModelInStack<T>()
where T : IRoutableViewModel
{
return NavigationStack.Reverse().OfType<T>().FirstOrDefault();
}
}
-}
+}
View
11 ReactiveUI.Routing/RxRouting.cs
@@ -18,6 +18,14 @@ static RxRouting()
vm.Replace("ViewModel", "View");
}
+ /// <summary>
+ /// Returns the View associated with a ViewModel, deriving the name of
+ /// the Type via ViewModelToViewFunc, then discovering it via
+ /// ServiceLocator.
+ /// </summary>
+ /// <param name="viewModel">The ViewModel for which to find the
+ /// associated View.</param>
+ /// <returns>The View for the ViewModel.</returns>
public static IViewForViewModel ResolveView<T>(T viewModel)
where T : IReactiveNotifyPropertyChanged
{
@@ -36,6 +44,9 @@ public static IViewForViewModel ResolveView<T>(T viewModel)
public static class RoutableViewModelMixin
{
+ /// <summary>
+ /// This Observable fires whenever the current ViewModel is navigated to.
+ /// </summary>
public static IObservable<Unit> NavigatedToMe(this IRoutableViewModel This)
{
return Observable.Create<Unit>(subj => {
View
36 ReactiveUI.Routing/ViewModelViewHost.cs
@@ -1,12 +1,21 @@
using System;
+using System.Reactive.Linq;
using System.Windows;
using System.Windows.Controls;
using ReactiveUI.Xaml;
namespace ReactiveUI.Routing
{
+ /// <summary>
+ /// This content control will automatically load the View associated with
+ /// the ViewModel property and display it. This control is very useful
+ /// inside a DataTemplate to display the View associated with a ViewModel.
+ /// </summary>
public class ViewModelViewHost : ContentControl
{
+ /// <summary>
+ /// The ViewModel to display
+ /// </summary>
public IReactiveNotifyPropertyChanged ViewModel {
get { return (IReactiveNotifyPropertyChanged)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
@@ -14,6 +23,9 @@ public class ViewModelViewHost : ContentControl
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register("ViewModel", typeof(IReactiveNotifyPropertyChanged), typeof(ViewModelViewHost), new PropertyMetadata(null));
+ /// <summary>
+ /// If no ViewModel is displayed, this content (i.e. a control) will be displayed.
+ /// </summary>
public object DefaultContent {
get { return (object)GetValue(DefaultContentProperty); }
set { SetValue(DefaultContentProperty, value); }
@@ -23,17 +35,21 @@ public class ViewModelViewHost : ContentControl
public ViewModelViewHost()
{
- this.ObservableFromDP(x => x.ViewModel)
- .Subscribe(vm => {
- if (vm.Value == null) {
- Content = DefaultContent;
- return;
- }
+ var latestViewModel = Observable.CombineLatest(
+ this.ObservableFromDP(x => x.ViewModel).Select(x => x.Value).StartWith((IReactiveNotifyPropertyChanged)null),
+ this.ObservableFromDP(x => x.DataContext).Select(x => x.Value).OfType<IReactiveNotifyPropertyChanged>().StartWith((IReactiveNotifyPropertyChanged)null),
+ (vm, dc) => vm ?? dc);
- var view = RxRouting.ResolveView(vm.Value);
- view.ViewModel = vm.Value;
- Content = view;
- });
+ latestViewModel.Subscribe(vm => {
+ if (vm == null) {
+ Content = DefaultContent;
+ return;
+ }
+
+ var view = RxRouting.ResolveView(vm);
+ view.ViewModel = vm;
+ Content = view;
+ });
}
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.