Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added new result awaiting and setting ViewModel navigation #4848

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions MvvmCross/Core/MvxSetup.cs
Expand Up @@ -12,6 +12,7 @@
using MvvmCross.Navigation;
using MvvmCross.Plugin;
using MvvmCross.ViewModels;
using MvvmCross.ViewModels.Result;
using MvvmCross.Views;

namespace MvvmCross.Core;
Expand Down Expand Up @@ -154,6 +155,8 @@ public virtual void InitializeSecondary()
var app = InitializeMvxApplication(_iocProvider);
SetupLog?.Log(LogLevel.Trace, "Setup: NavigationService");
InitializeNavigationService(_iocProvider);
SetupLog?.Log(LogLevel.Trace, "Setup: ResultViewModelManager");
InitializeResultViewModelManager(_iocProvider);
SetupLog?.Log(LogLevel.Trace, "Setup: ViewModelTypeFinder start");
InitializeViewModelTypeFinder(_iocProvider);
SetupLog?.Log(LogLevel.Trace, "Setup: ViewsContainer start");
Expand Down Expand Up @@ -345,6 +348,7 @@ protected virtual void RegisterDefaultSetupDependencies(IMvxIoCProvider iocProvi
iocProvider.LazyConstructAndRegisterSingleton<IMvxViewModelLoader, MvxViewModelLoader>();
iocProvider.LazyConstructAndRegisterSingleton<IMvxNavigationService, IMvxViewModelLoader, IMvxViewDispatcher, IMvxIoCProvider>(
(loader, dispatcher, p) => new MvxNavigationService(loader, dispatcher, p));
iocProvider.LazyConstructAndRegisterSingleton<IMvxResultViewModelManager, MvxResultViewModelManager>();
iocProvider.RegisterSingleton(() => new MvxViewModelByNameLookup());
iocProvider.LazyConstructAndRegisterSingleton<IMvxViewModelByNameLookup, MvxViewModelByNameLookup>(
nameLookup => nameLookup);
Expand Down Expand Up @@ -577,6 +581,20 @@ protected virtual IEnumerable<Assembly> GetBootstrapOwningAssemblies()
return GetViewAssemblies().Distinct();
}

protected virtual IMvxResultViewModelManager? InitializeResultViewModelManager(IMvxIoCProvider iocProvider)
{
ValidateArguments(iocProvider);

return CreateResultViewModelManager(iocProvider);
}

protected virtual IMvxResultViewModelManager? CreateResultViewModelManager(IMvxIoCProvider iocProvider)
{
ValidateArguments(iocProvider);

return iocProvider.Resolve<IMvxResultViewModelManager>();
}

protected abstract IMvxNameMapping CreateViewToViewModelNaming();

protected virtual IMvxViewModelByNameLookup? CreateViewModelByNameLookup(IMvxIoCProvider iocProvider)
Expand Down
72 changes: 72 additions & 0 deletions MvvmCross/Navigation/MvxNavigationExtensions.cs
Expand Up @@ -4,6 +4,7 @@
#nullable enable

using MvvmCross.ViewModels;
using MvvmCross.ViewModels.Result;

namespace MvvmCross.Navigation;

Expand Down Expand Up @@ -37,4 +38,75 @@ public static Task Navigate<TParameter>(this IMvxNavigationService navigationSer
{
return navigationService.Navigate(path.ToString(), param, presentationBundle, cancellationToken);
}

/// <summary>
/// Navigate from a Result Awaiting ViewModel to a Result Setting ViewModel determined by its type
/// </summary>
/// <param name="navigationService">Navigation service</param>
/// <param name="fromViewModel">Result Awaiting ViewModel</param>
/// <param name="presentationBundle">(optional) presentation bundle</param>
/// <param name="cancellationToken">(optional) CancellationToken to cancel the navigation</param>
/// <typeparam name="TViewModel">Type of <see cref="IMvxResultSettingViewModel{TResult}"/></typeparam>
/// <typeparam name="TResult">Result awaited by Result Awaiting ViewModel and set by Result Setting ViewModel</typeparam>
/// <returns>Boolean indicating successful navigation</returns>
public static async Task<bool> NavigateRegisteringToResult<TViewModel, TResult>(
this IMvxNavigationService navigationService,
IMvxResultAwaitingViewModel<TResult> fromViewModel,
IMvxBundle? presentationBundle = null,
CancellationToken cancellationToken = default)
where TViewModel : IMvxResultSettingViewModel<TResult>
{
bool navigated = await navigationService.Navigate(typeof(TViewModel), presentationBundle, cancellationToken);
if (navigated)
fromViewModel.RegisterToResult();
return navigated;
}

/// <summary>
/// Navigate from a Result Awaiting ViewModel to a Result Setting ViewModel determined by its type, with parameter
/// </summary>
/// <param name="navigationService">Navigation service</param>
/// <param name="fromViewModel">Result Awaiting ViewModel</param>
/// <param name="parameter">ViewModel parameter</param>
/// <param name="presentationBundle">(optional) presentation bundle</param>
/// <param name="cancellationToken">(optional) CancellationToken to cancel the navigation</param>
/// <typeparam name="TViewModel">Type of <see cref="IMvxResultSettingViewModel{TResult}"/> and <see cref="IMvxViewModel{TParameter}"/></typeparam>
/// <typeparam name="TResult">Result awaited by Result Awaiting ViewModel and set by Result Setting ViewModel</typeparam>
/// <returns>Boolean indicating successful navigation</returns>
public static async Task<bool> NavigateRegisteringToResult<TViewModel, TParameter, TResult>(
this IMvxNavigationService navigationService,
IMvxResultAwaitingViewModel<TResult> fromViewModel,
TParameter parameter,
IMvxBundle? presentationBundle = null,
CancellationToken cancellationToken = default)
where TViewModel : IMvxResultSettingViewModel<TResult>, IMvxViewModel<TParameter>
{
bool navigated = await navigationService.Navigate(typeof(TViewModel), parameter, presentationBundle, cancellationToken);
if (navigated)
fromViewModel.RegisterToResult();
return navigated;
}

/// <summary>
/// Closes the View attached to the Result Setting ViewModel, with result
/// </summary>
/// <param name="navigationService">Navigation service</param>
/// <param name="viewModel">Result Setting ViewModel to close</param>
/// <param name="result">Result set by Result Setting ViewModel</param>
/// <param name="cancellationToken">(optional) CancellationToken to cancel the closing</param>
/// <typeparam name="TViewModel">Type of <see cref="IMvxResultSettingViewModel{TResult}"/></typeparam>
/// <typeparam name="TResult">Result set by Result Setting ViewModel</typeparam>
/// <returns></returns>
public static async Task<bool> CloseSettingResult<TViewModel, TResult>(
this IMvxNavigationService navigationService,
TViewModel viewModel,
TResult result,
CancellationToken cancellationToken = default)
where TViewModel : IMvxResultSettingViewModel<TResult>
{
bool closed = await navigationService.Close(viewModel, cancellationToken);
if (closed)
viewModel.SetResult(result);
return closed;
}
}
75 changes: 72 additions & 3 deletions MvvmCross/ViewModels/MvxNavigationViewModel.cs
Expand Up @@ -2,9 +2,9 @@
// The .NET Foundation licenses this file to you under the MS-PL license.
// See the LICENSE file in the project root for more information.

using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using MvvmCross.Navigation;
using MvvmCross.ViewModels.Result;

namespace MvvmCross.ViewModels
{
Expand All @@ -27,8 +27,8 @@ protected MvxNavigationViewModel(ILoggerFactory logFactory, IMvxNavigationServic
protected virtual ILogger Log => _log ??= LoggerFactory.CreateLogger(GetType().Name);
}

public abstract class MvxNavigationViewModel<TParameter> : MvxNavigationViewModel, IMvxViewModel<TParameter>
where TParameter : class
public abstract class MvxNavigationViewModel<TParameter>
: MvxNavigationViewModel, IMvxViewModel<TParameter>
{
protected MvxNavigationViewModel(ILoggerFactory logFactory, IMvxNavigationService navigationService)
: base(logFactory, navigationService)
Expand All @@ -37,5 +37,74 @@ protected MvxNavigationViewModel(ILoggerFactory logFactory, IMvxNavigationServic

public abstract void Prepare(TParameter parameter);
}

public abstract class MvxNavigationResultAwaitingViewModel<TResult>
: MvxNavigationViewModel, IMvxResultAwaitingViewModel<TResult>
{
protected MvxNavigationResultAwaitingViewModel(ILoggerFactory logFactory, IMvxNavigationService navigationService)
: base(logFactory, navigationService)
{
}

protected override void ReloadFromBundle(IMvxBundle state)
{
base.ReloadFromBundle(state);
this.ReloadAndRegisterToResult(state);
}

protected override void SaveStateToBundle(IMvxBundle bundle)
{
base.SaveStateToBundle(bundle);
this.SaveRegisterToResult(bundle);
}

public override void ViewDestroy(bool viewFinishing = true)
{
base.ViewDestroy(viewFinishing);

if (viewFinishing)
{
this.UnregisterToResult();
}
}

public abstract bool ResultSet(IMvxResultSettingViewModel<TResult> viewModel, TResult result);
}

public abstract class MvxNavigationResultAwaitingViewModel<TParameter, TResult>
: MvxNavigationResultAwaitingViewModel<TResult>, IMvxViewModel<TParameter>
{
protected MvxNavigationResultAwaitingViewModel(ILoggerFactory logFactory, IMvxNavigationService navigationService)
: base(logFactory, navigationService)
{
}

public abstract void Prepare(TParameter parameter);
}

public abstract class MvxNavigationResultSettingViewModel<TResult>
: MvxNavigationViewModel, IMvxResultSettingViewModel<TResult>
{
protected MvxNavigationResultSettingViewModel(ILoggerFactory logFactory, IMvxNavigationService navigationService)
: base(logFactory, navigationService)
{
}

public virtual void SetResult(TResult result)
{
this.SetResult<TResult>(result);
}
}

public abstract class MvxNavigationResultSettingViewModel<TParameter, TResult>
: MvxNavigationResultSettingViewModel<TResult>, IMvxViewModel<TParameter>
{
protected MvxNavigationResultSettingViewModel(ILoggerFactory logFactory, IMvxNavigationService navigationService)
: base(logFactory, navigationService)
{
}

public abstract void Prepare(TParameter parameter);
}
#nullable restore
}
32 changes: 32 additions & 0 deletions MvvmCross/ViewModels/Result/IMvxResultAwaitingViewModel.cs
@@ -0,0 +1,32 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MS-PL license.
// See the LICENSE file in the project root for more information.

namespace MvvmCross.ViewModels.Result;

/// <summary>
/// Implementing this interface requires to manually register and unregister ViewModel to result:
/// <code>
/// protected override void ReloadFromBundle(IMvxBundle state)
/// {
/// base.ReloadFromBundle(state);
/// this.ReloadAndRegisterToResult&lt;<typeparamref name="TResult"/>&gt;(state);
/// }
/// protected override void SaveStateToBundle(IMvxBundle bundle)
/// {
/// base.SaveStateToBundle(bundle);
/// this.SaveRegisterToResult&lt;<typeparamref name="TResult"/>&gt;(bundle);
/// }
/// public override ViewDestroy(bool viewFinishing = true)
/// {
/// base.ViewDestroy();
/// if (viewFinishing)
/// this.UnregisterToResult&lt;<typeparamref name="TResult"/>&gt;();
/// }
/// </code>
/// </summary>
/// <typeparam name="TResult"></typeparam>
public interface IMvxResultAwaitingViewModel<TResult> : IMvxViewModel
{
bool ResultSet(IMvxResultSettingViewModel<TResult> viewModel, TResult result);
}
10 changes: 10 additions & 0 deletions MvvmCross/ViewModels/Result/IMvxResultSettingViewModel.cs
@@ -0,0 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MS-PL license.
// See the LICENSE file in the project root for more information.

namespace MvvmCross.ViewModels.Result;

public interface IMvxResultSettingViewModel<in TResult> : IMvxViewModel
{
void SetResult(TResult result);
}
16 changes: 16 additions & 0 deletions MvvmCross/ViewModels/Result/IMvxResultViewModelManager.cs
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MS-PL license.
// See the LICENSE file in the project root for more information.

namespace MvvmCross.ViewModels.Result;

public interface IMvxResultViewModelManager
{
void RegisterToResult<TResult>(IMvxResultAwaitingViewModel<TResult> viewModel);

bool UnregisterToResult<TResult>(IMvxResultAwaitingViewModel<TResult> viewModel);

bool IsRegistered<TResult>(IMvxResultAwaitingViewModel<TResult> viewModel);

void SetResult<TResult>(IMvxResultSettingViewModel<TResult> viewModel, TResult result);
}