Skip to content

Commit

Permalink
Merge pull request #3042 from PrismLibrary/dev/ds/cleanup
Browse files Browse the repository at this point in the history
Maui Cleanup
  • Loading branch information
dansiegel committed Jan 6, 2024
2 parents e7058ca + 23452b5 commit 992b188
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 38 deletions.
10 changes: 8 additions & 2 deletions src/Maui/Prism.Maui/Mvvm/ViewModelLocator.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Prism.Mvvm;

/// <summary>
/// This class defines the attached property and related change handler that calls the <see cref="Prism.Mvvm.ViewModelLocationProvider2"/>.
/// This class defines the attached property and related change handler that calls the <see cref="ViewModelLocationProvider"/>.
/// </summary>
public static class ViewModelLocator
{
Expand Down Expand Up @@ -60,9 +60,11 @@ internal static void Autowire(object view)
if (view is Element element &&
((ViewModelLocatorBehavior)element.GetValue(AutowireViewModelProperty) == ViewModelLocatorBehavior.Disabled
|| (element.BindingContext is not null && element.BindingContext != element.Parent)))
{
return;
}

else if(view is TabbedPage tabbed)
if (view is TabbedPage tabbed)
{
foreach (var child in tabbed.Children)
Autowire(child);
Expand All @@ -75,7 +77,9 @@ internal static void Autowire(object view)
ViewModelLocationProvider.AutoWireViewModelChanged(view, Bind);

if (view is BindableObject bindable && bindable.BindingContext is null)
{
bindable.BindingContext = new object();
}
}

/// <summary>
Expand All @@ -86,6 +90,8 @@ internal static void Autowire(object view)
private static void Bind(object view, object viewModel)
{
if (view is BindableObject element)
{
element.BindingContext = viewModel;
}
}
}
11 changes: 9 additions & 2 deletions src/Maui/Prism.Maui/Mvvm/ViewRegistryBase.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
using Prism.Ioc;
using Prism.Navigation.Xaml;
using Prism.Navigation.Xaml;

namespace Prism.Mvvm;

/// <summary>
/// The Base class for .NET Maui's ViewModel Registry
/// </summary>
public abstract class ViewRegistryBase : ViewRegistryBase<BindableObject>
{
/// <summary>
/// Initializes a new instance of the <see cref="ViewRegistryBase"/>
/// </summary>
/// <param name="registryType">The Registry Type</param>
/// <param name="registrations">The ViewRegistration collection</param>
protected ViewRegistryBase(ViewType registryType, IEnumerable<ViewRegistration> registrations)
: base(registryType, registrations)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Maui/Prism.Maui/Navigation/PrismWindowManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public Window CreateWindow(Application app, IActivationState activationState)
else if (app.Windows.OfType<PrismWindow>().Any())
return _initialWindow = app.Windows.OfType<PrismWindow>().First();

activationState.Context.Services.GetRequiredService<PrismAppBuilder>().OnAppStarted();
activationState.Context.Services.GetRequiredService<PrismAppBuilder>().OnCreateWindow();

return _initialWindow ?? throw new InvalidNavigationException("Expected Navigation Failed. No Root Window has been created.");
}
Expand Down
24 changes: 21 additions & 3 deletions src/Maui/Prism.Maui/Navigation/Xaml/Navigation.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.ComponentModel;
using Prism.Common;
using Prism.Ioc;
using Prism.Navigation.Internals;

namespace Prism.Navigation.Xaml;
Expand Down Expand Up @@ -79,13 +78,19 @@ private static void OnNavigationScopeChanged(BindableObject bindable, object old
/// <param name="value">The Can Navigate value</param>
public static void SetCanNavigate(BindableObject view, bool value) => view.SetValue(CanNavigateProperty, value);

/// <summary>
/// Gets the Child Regions for a given <see cref="Page"/>
/// </summary>
/// <param name="page">The <see cref="Page"/> host.</param>
/// <param name="setIfNull">Initializes the <see cref="ChildRegionCollection"/> if it has not been set.</param>
/// <returns>The <see cref="ChildRegionCollection"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Never)]
public static ChildRegionCollection GetChildRegions(this Page page, bool setIfNull = false)
{
var value = page.GetValue(ChildMvvmViewsProperty) as ChildRegionCollection;
if (value is null && setIfNull)
{
value = new ChildRegionCollection();
value = [];
page.SetValue(ChildMvvmViewsProperty, value);
}

Expand All @@ -111,18 +116,31 @@ internal static void ClearChildRegions(this Page page)
[EditorBrowsable(EditorBrowsableState.Never)]
public static INavigationService GetNavigationService(Page page)
{
if (page == null) throw new ArgumentNullException(nameof(page));
ArgumentNullException.ThrowIfNull(page);

var container = page.GetContainerProvider();
return container.Resolve<INavigationService>();
}

/// <summary>
/// Sets the <see cref="IContainerProvider"/> for the given <see cref="BindableObject"/>
/// </summary>
/// <param name="bindable">The <see cref="BindableObject"/>.</param>
/// <param name="container">The <see cref="IContainerProvider"/>.</param>
[EditorBrowsable(EditorBrowsableState.Never)]
public static void SetContainerProvider(this BindableObject bindable, IContainerProvider container)
{
bindable.SetValue(NavigationScopeProperty, container);
}

/// <summary>
/// Gets the Container for the given View
/// </summary>
/// <param name="bindable">The View</param>
/// <returns>The <see cref="IContainerProvider"/>.</returns>
/// <remarks>
/// Will initialize a new Container Scope if the <see cref="Mvvm.ViewModelLocatorBehavior"/> is Forced.
/// </remarks>
[EditorBrowsable(EditorBrowsableState.Never)]
public static IContainerProvider GetContainerProvider(this BindableObject bindable)
{
Expand Down
24 changes: 14 additions & 10 deletions src/Maui/Prism.Maui/PrismAppBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public sealed class PrismAppBuilder
private List<Action<IContainerRegistry>> _registrations { get; }
private List<Action<IContainerProvider>> _initializations { get; }
private IContainerProvider _container { get; }
private Func<IContainerProvider, INavigationService, Task> _onAppStarted;
private Func<IContainerProvider, INavigationService, Task> _createWindow;
private Action<RegionAdapterMappings> _configureAdapters;
private Action<IRegionBehaviorFactory> _configureBehaviors;

Expand Down Expand Up @@ -106,12 +106,16 @@ internal static object DefaultViewModelLocator(object view, Type viewModelType)
{
try
{
if (view is not BindableObject bindable)
if (view is not BindableObject bindable || bindable.BindingContext is not null)
return null;

var container = bindable.GetContainerProvider();

return container.Resolve(viewModelType);
return container.Resolve(viewModelType, (typeof(IDispatcher), bindable.Dispatcher));
}
catch (ViewModelCreationException)
{
throw;
}
catch (Exception ex)
{
Expand Down Expand Up @@ -177,26 +181,26 @@ internal void OnInitialized()
}
}

internal void OnAppStarted()
internal void OnCreateWindow()
{
if (_onAppStarted is null)
throw new ArgumentException("You must call OnAppStart on the PrismAppBuilder.");
if (_createWindow is null)
throw new ArgumentException("You must call CreateWindow on the PrismAppBuilder.");

// Ensure that this is executed before we navigate.
OnInitialized();
var onStart = _onAppStarted(_container, _container.Resolve<INavigationService>());
var onStart = _createWindow(_container, _container.Resolve<INavigationService>());
onStart.Wait();
}

/// <summary>
/// When the <see cref="Application"/> is started and the native platform calls <see cref="IApplication.CreateWindow(IActivationState?)"/>
/// this delegate will be invoked to do your initial Navigation.
/// </summary>
/// <param name="onAppStarted">The Navigation Delegate.</param>
/// <param name="createWindow">The Navigation Delegate.</param>
/// <returns>The <see cref="PrismAppBuilder"/>.</returns>
public PrismAppBuilder CreateWindow(Func<IContainerProvider, INavigationService, Task> onAppStarted)
public PrismAppBuilder CreateWindow(Func<IContainerProvider, INavigationService, Task> createWindow)
{
_onAppStarted = onAppStarted;
_createWindow = createWindow;
return this;
}

Expand Down
95 changes: 87 additions & 8 deletions src/Maui/Prism.Maui/PrismAppBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,33 @@

namespace Prism;

/// <summary>
/// Common extensions and overloads for the <see cref="PrismAppBuilder"/>
/// </summary>
public static class PrismAppBuilderExtensions
{
private static bool s_didRegisterModules = false;

/// <summary>
/// Configures the <see cref="MauiAppBuilder"/> to use Prism with a callback for the <see cref="PrismAppBuilder"/>
/// </summary>
/// <param name="builder">The <see cref="MauiAppBuilder"/>.</param>
/// <param name="containerExtension">The instance of the <see cref="IContainerExtension"/> Prism should use.</param>
/// <param name="configurePrism">A delegate callback for the <see cref="PrismAppBuilder"/></param>
/// <returns>The <see cref="MauiAppBuilder"/>.</returns>
public static MauiAppBuilder UsePrism(this MauiAppBuilder builder, IContainerExtension containerExtension, Action<PrismAppBuilder> configurePrism)
{
var prismBuilder = new PrismAppBuilder(containerExtension, builder);
configurePrism(prismBuilder);
return builder;
}

/// <summary>
/// Provides a Delegate to invoke when the App is initialized.
/// </summary>
/// <param name="builder">The <see cref="PrismAppBuilder"/>.</param>
/// <param name="action">The delegate to invoke.</param>
/// <returns>The <see cref="PrismAppBuilder"/>.</returns>
public static PrismAppBuilder OnInitialized(this PrismAppBuilder builder, Action action)
{
return builder.OnInitialized(_ => action());
Expand Down Expand Up @@ -45,9 +61,24 @@ public static PrismAppBuilder ConfigureModuleCatalog(this PrismAppBuilder builde
});
}

/// <summary>
/// When the <see cref="Application"/> is started and the native platform calls <see cref="IApplication.CreateWindow(IActivationState?)"/>
/// this delegate will be invoked to do your initial Navigation.
/// </summary>
/// <param name="builder">The <see cref="PrismAppBuilder"/>.</param>
/// <param name="uri">The initial Navigation Uri.</param>
/// <returns>The <see cref="PrismAppBuilder"/>.</returns>
public static PrismAppBuilder CreateWindow(this PrismAppBuilder builder, string uri) =>
builder.CreateWindow(navigation => navigation.NavigateAsync(uri));

/// <summary>
/// When the <see cref="Application"/> is started and the native platform calls <see cref="IApplication.CreateWindow(IActivationState?)"/>
/// this delegate will be invoked to do your initial Navigation.
/// </summary>
/// <param name="builder">The <see cref="PrismAppBuilder"/>.</param>
/// <param name="uri">The intial Navigation Uri.</param>
/// <param name="onError">A delegate callback if the navigation fails.</param>
/// <returns>The <see cref="PrismAppBuilder"/>.</returns>
public static PrismAppBuilder CreateWindow(this PrismAppBuilder builder, string uri, Action<Exception> onError) =>
builder.CreateWindow(async navigation =>
{
Expand All @@ -56,30 +87,78 @@ public static PrismAppBuilder CreateWindow(this PrismAppBuilder builder, string
onError(result.Exception);
});

public static PrismAppBuilder CreateWindow(this PrismAppBuilder builder, Func<IContainerProvider, INavigationService, Task> CreateWindowed) =>
builder.CreateWindow((c, n) => CreateWindowed(c, n));
/// <summary>
/// When the <see cref="Application"/> is started and the native platform calls <see cref="IApplication.CreateWindow(IActivationState?)"/>
/// this delegate will be invoked to do your initial Navigation.
/// </summary>
/// <param name="builder">The <see cref="PrismAppBuilder"/>.</param>
/// <param name="createWindow">The Navigation Delegate.</param>
/// <returns>The <see cref="PrismAppBuilder"/>.</returns>
public static PrismAppBuilder CreateWindow(this PrismAppBuilder builder, Func<IContainerProvider, INavigationService, Task> createWindow) =>
builder.CreateWindow((c, n) => createWindow(c, n));

public static PrismAppBuilder CreateWindow(this PrismAppBuilder builder, Func<INavigationService, Task> CreateWindowed) =>
builder.CreateWindow((_, n) => CreateWindowed(n));
/// <summary>
/// When the <see cref="Application"/> is started and the native platform calls <see cref="IApplication.CreateWindow(IActivationState?)"/>
/// this delegate will be invoked to do your initial Navigation.
/// </summary>
/// <param name="builder">The <see cref="PrismAppBuilder"/>.</param>
/// <param name="createWindow">The Navigation Delegate.</param>
/// <returns>The <see cref="PrismAppBuilder"/>.</returns>
public static PrismAppBuilder CreateWindow(this PrismAppBuilder builder, Func<INavigationService, Task> createWindow) =>
builder.CreateWindow((_, n) => createWindow(n));

public static PrismAppBuilder CreateWindow(this PrismAppBuilder builder, Func<INavigationService, INavigationBuilder> CreateWindowed) =>
builder.CreateWindow(n => CreateWindowed(n).NavigateAsync());
/// <summary>
/// When the <see cref="Application"/> is started and the native platform calls <see cref="IApplication.CreateWindow(IActivationState?)"/>
/// this delegate will be invoked to do your initial Navigation.
/// </summary>
/// <param name="builder">The <see cref="PrismAppBuilder"/>.</param>
/// <param name="createWindow">The Navigation Delegate.</param>
/// <returns>The <see cref="PrismAppBuilder"/>.</returns>
public static PrismAppBuilder CreateWindow(this PrismAppBuilder builder, Func<INavigationService, INavigationBuilder> createWindow) =>
builder.CreateWindow(n => createWindow(n).NavigateAsync());

/// <summary>
/// When the <see cref="Application"/> is started and the native platform calls <see cref="IApplication.CreateWindow(IActivationState?)"/>
/// this delegate will be invoked to do your initial Navigation.
/// </summary>
/// <param name="builder">The <see cref="PrismAppBuilder"/>.</param>
/// <param name="createWindow">The Navigation Delegate.</param>
/// <returns>The <see cref="PrismAppBuilder"/>.</returns>
public static PrismAppBuilder CreateWindow(this PrismAppBuilder builder, Func<IContainerProvider, INavigationService, INavigationBuilder> createWindow) =>
builder.CreateWindow((c, n) => createWindow(c, n).NavigateAsync());

public static PrismAppBuilder CreateWindow(this PrismAppBuilder builder, Func<IContainerProvider, INavigationService, INavigationBuilder> CreateWindowed) =>
builder.CreateWindow((c, n) => CreateWindowed(c, n).NavigateAsync());

/// <summary>
/// Provides a configuration delegate to add services to the <see cref="MauiAppBuilder.Services"/>
/// </summary>
/// <param name="builder">The <see cref="PrismAppBuilder"/>.</param>
/// <param name="configureServices">Configuration Delegate</param>
/// <returns>The <see cref="PrismAppBuilder"/>.</returns>
public static PrismAppBuilder ConfigureServices(this PrismAppBuilder builder, Action<IServiceCollection> configureServices)
{
configureServices(builder.MauiBuilder.Services);
return builder;
}

/// <summary>
/// Provides a delegate to configure Logging within the Maui application
/// </summary>
/// <param name="builder">The <see cref="PrismAppBuilder"/>.</param>
/// <param name="configureLogging"></param>
/// <returns>The <see cref="PrismAppBuilder"/>.</returns>
public static PrismAppBuilder ConfigureLogging(this PrismAppBuilder builder, Action<ILoggingBuilder> configureLogging)
{
configureLogging(builder.MauiBuilder.Logging);
return builder;
}

/// <summary>
/// Provides a configuration Delegate to the <see cref="ViewModelLocationProvider"/> to set the
/// DefaultViewTypeToViewModelTypeResolver.
/// </summary>
/// <param name="builder">The <see cref="PrismAppBuilder"/>.</param>
/// <param name="viewModelTypeResolver">The Configuration Delegate for the Default ViewType to ViewModelType Resolver.</param>
/// <returns>The <see cref="PrismAppBuilder"/>.</returns>
public static PrismAppBuilder ConfigureViewTypeToViewModelTypeResolver(this PrismAppBuilder builder, Func<Type, Type> viewModelTypeResolver)
{
ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver(viewModelTypeResolver);
Expand Down
8 changes: 1 addition & 7 deletions src/Prism.Core/Mvvm/ViewRegistryBase{TBaseView}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,15 @@ public object CreateView(IContainerProvider container, string name)
{
try
{
var registration = GetRegistration(name);
if (registration is null)
throw new KeyNotFoundException($"No view with the name '{name}' has been registered");

var registration = GetRegistration(name) ?? throw new KeyNotFoundException($"No view with the name '{name}' has been registered");
var view = container.Resolve(registration.View) as TBaseView;
SetNavigationNameProperty(view, registration.Name);
//;

//;
SetContainerProvider(view, container);
ConfigureView(view, container);

if (registration.ViewModel is not null)
SetViewModelProperty(view, registration.ViewModel);
//

Autowire(view);

Expand Down
Loading

0 comments on commit 992b188

Please sign in to comment.