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

Maui Cleanup #3042

Merged
merged 3 commits into from
Jan 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading