question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Async Navigation Service

See original GitHub issue

I’ve written an async version of INavigationService and related classes which I’d like to contribute back to the project:

IAsyncNavigationService.cs

using System;
using System.Threading.Tasks;
using System.Windows.Navigation;

namespace MyApp.Mvvm.Navigation
{
    /// <summary>
    /// Temporary async version of <see cref="Caliburn.Micro.INavigationService"/>.
    /// </summary>
    public interface IAsyncNavigationService
    {
        #region Properties

        /// <summary>
        /// Indicates whether the navigator can navigate back.
        /// </summary>
        bool CanGoBack { get; }

        /// <summary>
        /// Indicates whether the navigator can navigate forward.
        /// </summary>
        bool CanGoForward { get; }

        /// <summary>
        /// The current content.
        /// </summary>
        object CurrentContent { get; }

        #endregion

        #region Events

        /// <summary>
        /// Raised after navigation.
        /// </summary>
        event NavigatedEventHandler Navigated;

        /// <summary>
        /// Raised prior to navigation.
        /// </summary>
        event NavigatingCancelEventHandler Navigating;

        /// <summary>
        /// Raised when navigation fails.
        /// </summary>
        event NavigationFailedEventHandler NavigationFailed;

        /// <summary>
        /// Raised when navigation is stopped.
        /// </summary>
        event NavigationStoppedEventHandler NavigationStopped;

        /// <summary>
        /// Raised when a fragment navigation occurs.
        /// </summary>
        event FragmentNavigationEventHandler FragmentNavigation;

        #endregion

        #region Methods

        /// <summary>
        /// Navigates to the view represented by the given view model.
        /// </summary>
        /// <param name="viewModel">The view model to navigate to.</param>
        /// <param name="extraData">Extra data to populate the view model with.</param>
        /// <returns>A <see cref="Task"/> that represents the asynchronous operation.</returns>
        Task NavigateToViewModelAsync(Type viewModel, object extraData = null);

        /// <summary>
        /// Navigates to the view represented by the given view model.
        /// </summary>
        /// <typeparam name="TViewModel">The view model to navigate to.</typeparam>
        /// <param name="extraData">Extra data to populate the view model with.</param>
        /// <returns>A <see cref="Task"/> that represents the asynchronous operation.</returns>
        Task NavigateToViewModelAsync<TViewModel>(object extraData = null);

        /// <summary>
        /// Stops the loading process.
        /// </summary>
        void StopLoading();

        /// <summary>
        /// Navigates back.
        /// </summary>
        /// <returns>A <see cref="Task"/> that represents the asynchronous operation.</returns>
        Task GoBackAsync();

        /// <summary>
        /// Navigates forward.
        /// </summary>
        /// <returns>A <see cref="Task"/> that represents the asynchronous operation.</returns>
        Task GoForwardAsync();

        /// <summary>
        /// Removes the most recent entry from the back stack.
        /// </summary>
        /// <returns> The entry that was removed. </returns>
        JournalEntry RemoveBackEntry();

        #endregion
    }
}

AsyncNavigationHelper.cs

using Caliburn.Micro;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace MyApp.Mvvm.Navigation
{
    /// <summary>
    /// Builds a Uri in a strongly typed fashion, based on a ViewModel.
    /// </summary>
    /// <typeparam name="TViewModel"></typeparam>
    public class NavigationHelper<TViewModel>
    {
        #region Fields

        readonly Dictionary<string, object> m_QueryString = new Dictionary<string, object>();
        IAsyncNavigationService m_NavigationService;

        #endregion

        #region Public Methods

        /// <summary>
        /// Adds a query string parameter to the Uri.
        /// </summary>
        /// <typeparam name="TValue">The type of the value.</typeparam>
        /// <param name="property">The property.</param>
        /// <param name="value">The property value.</param>
        /// <returns>Itself</returns>
        public NavigationHelper<TViewModel> WithParam<TValue>(Expression<Func<TViewModel, TValue>> property, TValue value)
        {
            if (value is ValueType || !ReferenceEquals(null, value))
            {
                m_QueryString[property.GetMemberInfo().Name] = value;
            }

            return this;
        }

        /// <summary>
        /// Attaches a navigation servies to this builder.
        /// </summary>
        /// <param name="navigationService">The navigation service.</param>
        /// <returns>Itself</returns>
        public NavigationHelper<TViewModel> AttachTo(IAsyncNavigationService navigationService)
        {
            m_NavigationService = navigationService;
            return this;
        }

        /// <summary>
        /// Navigates to the Uri represented by this builder.
        /// </summary>
        /// <returns>A <see cref="Task"/> that represents the asynchronous operation.</returns>
        public async Task NavigateAsync()
        {
            if (m_NavigationService == null)
            {
                throw new InvalidOperationException("Cannot navigate without attaching an IAsyncNavigationService. Call AttachTo first.");
            }

            await m_NavigationService.NavigateToViewModelAsync<TViewModel>(m_QueryString);
        }

        #endregion
    }
}

AsyncNavigationExtensions.cs

namespace MyApp.Mvvm.Navigation
{
    /// <summary>
    /// Extension methods related to navigation.
    /// </summary>
    public static class NavigationExtensions
    {
        #region Properties

        /// <summary>
        /// Creates a Uri builder based on a view model type.
        /// </summary>
        /// <typeparam name="TViewModel">The type of the view model.</typeparam>
        /// <param name="navigationService">The navigation service.</param>
        /// <returns>The builder.</returns>
        public static NavigationHelper<TViewModel> For<TViewModel>(this IAsyncNavigationService navigationService)
        {
            return new NavigationHelper<TViewModel>().AttachTo(navigationService);
        }

        #endregion
    }
}

Issue Analytics

  • State:open
  • Created 4 years ago
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
popcatalin81commented, Jun 25, 2019

@neilt6 Make it a method then. bool CanGoBackAsync()

2reactions
neilt6commented, Jun 25, 2019

@popcatalin81 I disagree with your suggestion, as I don’t believe properties should ever be async. To quote MSDN Magazine:

The term “asynchronous property” is actually an oxymoron. Property getters should execute immediately and retrieve current values, not kick off background operations. This is likely one of the reasons the async keyword can’t be used on a property getter. If you find your design asking for an asynchronous property, consider some alternatives first. In particular, should the property actually be a method (or a command)? If the property getter needs to kick off a new asynchronous operation each time it’s accessed, that’s not a property at all.

IMHO in a properly designed navigation service, CanGoBack and CanGoForward should just retrieve cached values that are updated in NavigateToViewModelAsync(), GoBackAsync(), and GoForwardAsync().

Read more comments on GitHub >

github_iconTop Results From Across the Web

c# - NavigationService.Navigate async behaviour
Created a window and a page which contains one button. Now on click of that button, i am navigating to another page using...
Read more >
NavigationService.Navigate Method (System.Windows. ...
Navigate asynchronously to source content located at a URI, pass an object containing navigation state for processing during navigation, and sandbox the content ......
Read more >
Up and running with React Navigation | by Daniel Merrill
When you tell the navigator to navigate to 'Home', this is how it'll know what to do. Initialize your navigator with the config...
Read more >
Create async versions of INavigateAsync.RequestNavigate
The implementation of navigation service will look for both interfaces and, if any of them is implemented, use it. For existing programs that ......
Read more >
Asynchronous Jobs | Adobe Experience Manager
You can view the status of asynchronous jobs from the Background Operations dashboard at Global Navigation -> Tools -> General -> Jobs.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found