зеркало из https://github.com/DeGsoft/maui-linux.git
Include Startup class (#436)
* Implement IStartup * Clean up code * Move the static Current into MauiApp * Add missing SearchBar handler mapping * Don't need this anymore * Pass the handler along on iOS * Lots more perf fixes * Remove App.SetHandlerContext * Fix tests * Fix catalyst * revert * Fixed build errors * Fix the updated SearchBar * Fix net6 single project * Add IHostBuilderStartup Co-authored-by: Matthew Leibowitz <mattleibow@live.com>
This commit is contained in:
Родитель
4cfc221d5c
Коммит
d4a3de27fc
|
@ -313,6 +313,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android.AppCompat
|
|||
try
|
||||
{
|
||||
handler = Forms.MauiContext.Handlers.GetHandler(element.GetType());
|
||||
handler.SetMauiContext(Forms.MauiContext);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -340,10 +341,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android.AppCompat
|
|||
else if (handler is IVisualElementRenderer ver)
|
||||
renderer = ver;
|
||||
else if (handler is IAndroidViewHandler vh)
|
||||
{
|
||||
vh.SetContext(context);
|
||||
renderer = new HandlerToRendererShim(vh);
|
||||
}
|
||||
}
|
||||
|
||||
renderer.SetElement(element);
|
||||
|
|
|
@ -12,15 +12,15 @@ namespace Microsoft.Maui.Controls.Compatibility
|
|||
var defaultHandlers = new List<Type>
|
||||
{
|
||||
typeof(Button),
|
||||
typeof(ContentPage),
|
||||
typeof(Editor),
|
||||
typeof(Entry),
|
||||
typeof(ContentPage),
|
||||
typeof(Page),
|
||||
typeof(Label),
|
||||
typeof(Page),
|
||||
typeof(SearchBar),
|
||||
typeof(Slider),
|
||||
typeof(Stepper),
|
||||
typeof(Switch),
|
||||
typeof(SearchBar)
|
||||
};
|
||||
|
||||
Forms.RegisterCompatRenderers(
|
||||
|
|
|
@ -239,6 +239,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS
|
|||
try
|
||||
{
|
||||
handler = Forms.ActivationState.Context.Handlers.GetHandler(element.GetType());
|
||||
handler.SetMauiContext(Forms.ActivationState.Context);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
|
@ -6,7 +6,7 @@ using Microsoft.Maui;
|
|||
namespace Maui.Controls.Sample.Droid
|
||||
{
|
||||
[Application]
|
||||
public class MainApplication : MauiApplication<MyApp>
|
||||
public class MainApplication : MauiApplication<Startup>
|
||||
{
|
||||
public MainApplication(IntPtr handle, JniHandleOwnership ownerShip) : base(handle, ownerShip)
|
||||
{
|
||||
|
|
|
@ -11,7 +11,7 @@ using Maui.Controls.Sample;
|
|||
namespace Sample.MacCatalyst
|
||||
{
|
||||
[Register("AppDelegate")]
|
||||
public class AppDelegate : MauiUIApplicationDelegate<MyApp>
|
||||
public class AppDelegate : MauiUIApplicationDelegate<Startup>
|
||||
{
|
||||
}
|
||||
}
|
|
@ -6,6 +6,5 @@ namespace Maui.Controls.Sample.SingleProject
|
|||
[Activity(Theme = "@style/AppTheme.NoActionBar", MainLauncher = true)]
|
||||
public class MainActivity : MauiAppCompatActivity
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -6,9 +6,9 @@ using Microsoft.Maui;
|
|||
namespace Maui.Controls.Sample.SingleProject
|
||||
{
|
||||
[Application]
|
||||
public class MainApplication : MauiApplication<MyApp>
|
||||
public class MainApplication : MauiApplication<Startup>
|
||||
{
|
||||
public MainApplication(IntPtr handle, JniHandleOwnership ownerShip) : base(handle, ownerShip)
|
||||
public MainApplication(IntPtr handle, JniHandleOwnership ownership) : base(handle, ownership)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,7 @@ using Microsoft.Maui;
|
|||
namespace Maui.Controls.Sample.SingleProject
|
||||
{
|
||||
[Register("AppDelegate")]
|
||||
public class AppDelegate : MauiUIApplicationDelegate<MyApp>
|
||||
public class AppDelegate : MauiUIApplicationDelegate<Startup>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -16,4 +16,4 @@ namespace Maui.Controls.Sample.SingleProject
|
|||
|
||||
public IView View { get => (IView)Content; set => Content = (View)value; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +1,16 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Maui;
|
||||
using Microsoft.Maui;
|
||||
|
||||
namespace Maui.Controls.Sample.SingleProject
|
||||
{
|
||||
public class MainWindow : IWindow
|
||||
{
|
||||
public IPage Page { get; set; }
|
||||
public IMauiContext MauiContext { get; set; }
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
Page = App.Current.Services.GetService<MainPage>();
|
||||
Page = new MainPage();
|
||||
}
|
||||
|
||||
public IPage Page { get; set; }
|
||||
|
||||
public IMauiContext MauiContext { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +1,17 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Maui;
|
||||
using Microsoft.Maui.Hosting;
|
||||
using Microsoft.Maui;
|
||||
|
||||
namespace Maui.Controls.Sample.SingleProject
|
||||
{
|
||||
public class MyApp : MauiApp
|
||||
{
|
||||
public override IAppHostBuilder CreateBuilder() =>
|
||||
base.CreateBuilder().ConfigureServices((ctx, services) =>
|
||||
{
|
||||
services.AddTransient<MainPage>();
|
||||
services.AddTransient<IWindow, MainWindow>();
|
||||
});
|
||||
|
||||
public override IWindow CreateWindow(IActivationState state)
|
||||
{
|
||||
#if (ANDROID || IOS)
|
||||
|
||||
#if ANDROID || IOS
|
||||
// This will probably go into a compatibility app or window
|
||||
Microsoft.Maui.Controls.Compatibility.Forms.Init(state);
|
||||
#endif
|
||||
return Services.GetService<IWindow>();
|
||||
|
||||
return new MainWindow();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using Microsoft.Maui;
|
||||
using Microsoft.Maui.Controls.Compatibility;
|
||||
using Microsoft.Maui.Hosting;
|
||||
|
||||
namespace Maui.Controls.Sample.SingleProject
|
||||
{
|
||||
public class Startup : IStartup
|
||||
{
|
||||
public void Configure(IAppHostBuilder appBuilder)
|
||||
{
|
||||
appBuilder
|
||||
.RegisterCompatibilityRenderers()
|
||||
.UseMauiApp<MyApp>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,8 +4,7 @@ using Microsoft.Maui;
|
|||
namespace Maui.Controls.Sample.SingleProject
|
||||
{
|
||||
[Register("AppDelegate")]
|
||||
public class AppDelegate : MauiUIApplicationDelegate<MyApp>
|
||||
public class AppDelegate : MauiUIApplicationDelegate<Startup>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ using Maui.Controls.Sample;
|
|||
namespace Sample.iOS
|
||||
{
|
||||
[Register("AppDelegate")]
|
||||
public class AppDelegate : MauiUIApplicationDelegate<MyApp>
|
||||
public class AppDelegate : MauiUIApplicationDelegate<Startup>
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,20 +1,13 @@
|
|||
using Maui.Controls.Sample.Controls;
|
||||
using Maui.Controls.Sample.Pages;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Maui;
|
||||
using Microsoft.Maui.Controls;
|
||||
|
||||
namespace Maui.Controls.Sample
|
||||
{
|
||||
public class MainWindow : Window
|
||||
{
|
||||
public MainWindow() : this(App.Current.Services.GetRequiredService<IPage>())
|
||||
{
|
||||
}
|
||||
|
||||
public MainWindow(IPage page)
|
||||
{
|
||||
Page = page;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,65 +1,22 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Maui.Controls.Sample.Pages;
|
||||
using Maui.Controls.Sample.Services;
|
||||
using Maui.Controls.Sample.ViewModel;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Maui;
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Controls.Compatibility;
|
||||
using Microsoft.Maui.Hosting;
|
||||
|
||||
namespace Maui.Controls.Sample
|
||||
{
|
||||
public class MyApp : MauiApp
|
||||
{
|
||||
public readonly static bool UseXamlPage = false;
|
||||
public MyApp(ITextService textService)
|
||||
{
|
||||
Console.WriteLine($"The injected text service had a message: '{textService.GetText()}'");
|
||||
}
|
||||
|
||||
public override IAppHostBuilder CreateBuilder() =>
|
||||
base.CreateBuilder()
|
||||
.RegisterCompatibilityRenderers()
|
||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
config.AddInMemoryCollection(new Dictionary<string, string>
|
||||
{
|
||||
{ "MyKey", "Dictionary MyKey Value" },
|
||||
{ ":Title", "Dictionary_Title" },
|
||||
{ "Position:Name", "Dictionary_Name" },
|
||||
{ "Logging:LogLevel:Default", "Warning" }
|
||||
});
|
||||
})
|
||||
.ConfigureServices((hostingContext, services) =>
|
||||
{
|
||||
services.AddSingleton<ITextService, TextService>();
|
||||
services.AddTransient<MainPageViewModel>();
|
||||
if (UseXamlPage)
|
||||
services.AddTransient<IPage, XamlPage>();
|
||||
else
|
||||
services.AddTransient<IPage, MainPage>();
|
||||
services.AddTransient<IWindow, MainWindow>();
|
||||
})
|
||||
.ConfigureFonts((hostingContext, fonts) =>
|
||||
{
|
||||
fonts.AddFont("dokdo_regular.ttf", "Dokdo");
|
||||
});
|
||||
|
||||
//IAppState state
|
||||
public override IWindow CreateWindow(IActivationState state)
|
||||
{
|
||||
Forms.Init(state);
|
||||
return Services.GetRequiredService<IWindow>();
|
||||
}
|
||||
}
|
||||
|
||||
//to use DI ServiceCollection and not the MAUI one
|
||||
public class DIExtensionsServiceProviderFactory : IServiceProviderFactory<ServiceCollection>
|
||||
{
|
||||
public ServiceCollection CreateBuilder(IServiceCollection services)
|
||||
=> new ServiceCollection { services };
|
||||
|
||||
public IServiceProvider CreateServiceProvider(ServiceCollection containerBuilder)
|
||||
=> containerBuilder.BuildServiceProvider();
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Maui.Controls.Sample.ViewModel;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Maui;
|
||||
using Microsoft.Maui.Controls;
|
||||
|
||||
|
@ -11,14 +10,10 @@ namespace Maui.Controls.Sample.Pages
|
|||
{
|
||||
MainPageViewModel _viewModel;
|
||||
|
||||
public MainPage() : this(App.Current.Services.GetService<MainPageViewModel>())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public MainPage(MainPageViewModel viewModel)
|
||||
{
|
||||
BindingContext = _viewModel = viewModel;
|
||||
|
||||
SetupMauiLayout();
|
||||
//SetupCompatibilityLayout();
|
||||
}
|
||||
|
@ -98,8 +93,7 @@ namespace Maui.Controls.Sample.Pages
|
|||
};
|
||||
|
||||
verticalStack.Add(entry);
|
||||
|
||||
verticalStack.Add(new Entry { Text = "Entry", TextColor = Color.DarkRed });
|
||||
verticalStack.Add(new Entry { Text = "Entry", TextColor = Color.DarkRed, FontFamily = "Dokdo" });
|
||||
verticalStack.Add(new Entry { IsPassword = true, TextColor = Color.Black });
|
||||
verticalStack.Add(new Entry { IsTextPredictionEnabled = false });
|
||||
verticalStack.Add(new Entry { Placeholder = "This should be placeholder text" });
|
||||
|
@ -152,7 +146,10 @@ namespace Maui.Controls.Sample.Pages
|
|||
|
||||
verticalStack.Add(new Image() { Source = "dotnet_bot.png" });
|
||||
|
||||
Content = verticalStack;
|
||||
Content = new ScrollView
|
||||
{
|
||||
Content = verticalStack
|
||||
};
|
||||
}
|
||||
|
||||
void SetupCompatibilityLayout()
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Maui.Controls.Sample.Pages;
|
||||
using Maui.Controls.Sample.Services;
|
||||
using Maui.Controls.Sample.ViewModel;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Maui;
|
||||
using Microsoft.Maui.Controls.Compatibility;
|
||||
using Microsoft.Maui.Hosting;
|
||||
|
||||
namespace Maui.Controls.Sample
|
||||
{
|
||||
public class Startup : IStartup
|
||||
{
|
||||
public readonly static bool UseXamlPage = false;
|
||||
|
||||
public void Configure(IAppHostBuilder appBuilder)
|
||||
{
|
||||
appBuilder
|
||||
.RegisterCompatibilityRenderers()
|
||||
.UseMauiApp<MyApp>()
|
||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
config.AddInMemoryCollection(new Dictionary<string, string>
|
||||
{
|
||||
{"MyKey", "Dictionary MyKey Value"},
|
||||
{":Title", "Dictionary_Title"},
|
||||
{"Position:Name", "Dictionary_Name" },
|
||||
{"Logging:LogLevel:Default", "Warning"}
|
||||
});
|
||||
})
|
||||
.UseMauiServiceProviderFactory(true)
|
||||
//.UseServiceProviderFactory(new DIExtensionsServiceProviderFactory())
|
||||
.ConfigureServices((hostingContext, services) =>
|
||||
{
|
||||
services.AddSingleton<ITextService, TextService>();
|
||||
services.AddTransient<MainPageViewModel>();
|
||||
|
||||
if (UseXamlPage)
|
||||
services.AddTransient<IPage, XamlPage>();
|
||||
else
|
||||
services.AddTransient<IPage, MainPage>();
|
||||
|
||||
services.AddTransient<IWindow, MainWindow>();
|
||||
})
|
||||
.ConfigureFonts((hostingContext, fonts) =>
|
||||
{
|
||||
fonts.AddFont("dokdo_regular.ttf", "Dokdo");
|
||||
});
|
||||
}
|
||||
|
||||
// To use DI ServiceCollection and not the MAUI one
|
||||
public class DIExtensionsServiceProviderFactory : IServiceProviderFactory<ServiceCollection>
|
||||
{
|
||||
public ServiceCollection CreateBuilder(IServiceCollection services)
|
||||
=> new ServiceCollection { services };
|
||||
|
||||
public IServiceProvider CreateServiceProvider(ServiceCollection containerBuilder)
|
||||
=> containerBuilder.BuildServiceProvider();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,44 +1,29 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using Maui.Controls.Sample.Services;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Maui;
|
||||
|
||||
namespace Maui.Controls.Sample.ViewModel
|
||||
{
|
||||
public class MainPageViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IConfiguration Configuration;
|
||||
ITextService textService;
|
||||
|
||||
public MainPageViewModel() : this(new ITextService[] { App.Current.Services.GetService<ITextService>() })
|
||||
{
|
||||
}
|
||||
|
||||
public MainPageViewModel(IEnumerable<ITextService> textServices)
|
||||
{
|
||||
Configuration = App.Current.Services.GetService<IConfiguration>();
|
||||
|
||||
//var logger = App.Current.Services.GetService<ILogger<MainPageViewModel>>();
|
||||
|
||||
//logger.LogInformation("hello");
|
||||
|
||||
textService = textServices.FirstOrDefault();
|
||||
Text = textService.GetText();
|
||||
}
|
||||
|
||||
//public MainPageViewModel(ITextService textService)
|
||||
//{
|
||||
// Text = textService.GetText();
|
||||
//}
|
||||
|
||||
readonly IConfiguration _configuration;
|
||||
readonly ITextService _textService;
|
||||
string _text;
|
||||
|
||||
public MainPageViewModel(IConfiguration configuration, ITextService textService)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_textService = textService;
|
||||
|
||||
Console.WriteLine($"Value from config: {_configuration["MyKey"]}");
|
||||
|
||||
Text = _textService.GetText();
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get => _text;
|
||||
set => SetProperty(ref _text, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,17 +4,6 @@
|
|||
{
|
||||
Font? _font;
|
||||
|
||||
Font IText.Font
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_font == null)
|
||||
{
|
||||
_font = Font.OfSize(FontFamily, FontSize).WithAttributes(FontAttributes);
|
||||
}
|
||||
|
||||
return _font.Value;
|
||||
}
|
||||
}
|
||||
Font IText.Font => _font ??= Font.OfSize(FontFamily, FontSize).WithAttributes(FontAttributes);
|
||||
}
|
||||
}
|
|
@ -1,50 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Maui.Hosting;
|
||||
|
||||
namespace Microsoft.Maui
|
||||
{
|
||||
public abstract class App : IApp
|
||||
{
|
||||
IServiceProvider? _serviceProvider;
|
||||
IMauiContext? _context;
|
||||
|
||||
protected App()
|
||||
{
|
||||
if (Current != null)
|
||||
throw new InvalidOperationException($"Only one {nameof(App)} instance is allowed");
|
||||
Current = this;
|
||||
}
|
||||
|
||||
public static App? Current { get; internal set; }
|
||||
|
||||
public IServiceProvider? Services => _serviceProvider;
|
||||
|
||||
public IMauiContext? Context => _context;
|
||||
|
||||
//move to abstract
|
||||
public virtual IAppHostBuilder CreateBuilder() => CreateDefaultBuilder();
|
||||
public IServiceProvider? Services { get; private set; }
|
||||
|
||||
internal void SetServiceProvider(IServiceProvider provider)
|
||||
{
|
||||
_serviceProvider = provider;
|
||||
SetHandlerContext(provider.GetService<IMauiContext>());
|
||||
}
|
||||
|
||||
internal void SetHandlerContext(IMauiContext? context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public static IAppHostBuilder CreateDefaultBuilder()
|
||||
{
|
||||
var builder = new AppHostBuilder();
|
||||
|
||||
builder.UseMauiHandlers();
|
||||
builder.UseFonts();
|
||||
|
||||
return builder;
|
||||
Services = provider;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.Maui.Hosting;
|
||||
|
||||
namespace Microsoft.Maui
|
||||
{
|
||||
/// <summary>
|
||||
/// Startup allow to configure and instantiate application services.
|
||||
/// </summary>
|
||||
public interface IStartup
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures the application.
|
||||
/// Configure are called by the .NET MAUI Core runtime when the app starts.
|
||||
/// </summary>
|
||||
/// <param name="appBuilder">Defines a class that provides the mechanisms to configure an application's dependencies.</param>
|
||||
void Configure(IAppHostBuilder appBuilder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allow to create a custom IAppHostBuilder instance.
|
||||
/// </summary>
|
||||
public interface IHostBuilderStartup
|
||||
{
|
||||
/// <summary>
|
||||
/// Create and configure a builder object.
|
||||
/// </summary>
|
||||
/// <returns>The new instance of the IAppHostBuilder.</returns>
|
||||
public IAppHostBuilder CreateHostBuilder();
|
||||
}
|
||||
}
|
|
@ -55,9 +55,9 @@ namespace Microsoft.Maui.Handlers
|
|||
|
||||
public static void MapFont(ButtonHandler handler, IButton button)
|
||||
{
|
||||
var services = App.Current?.Services
|
||||
?? throw new InvalidOperationException($"Unable to find service provider, the App.Current.Services was null.");
|
||||
var fontManager = services.GetRequiredService<IFontManager>();
|
||||
_ = handler.Services ?? throw new InvalidOperationException($"{nameof(Services)} should have been set by base class.");
|
||||
|
||||
var fontManager = handler.Services.GetRequiredService<IFontManager>();
|
||||
|
||||
handler.TypedNativeView?.UpdateFont(button, fontManager);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Microsoft.Maui.Handlers
|
|||
|
||||
}
|
||||
|
||||
public ButtonHandler(PropertyMapper mapper) : base(mapper ?? ButtonMapper)
|
||||
public ButtonHandler(PropertyMapper? mapper = null) : base(mapper ?? ButtonMapper)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,9 +61,9 @@ namespace Microsoft.Maui.Handlers
|
|||
|
||||
public static void MapFont(ButtonHandler handler, IButton button)
|
||||
{
|
||||
var services = App.Current?.Services ??
|
||||
throw new InvalidOperationException($"Unable to find service provider, the App.Current.Services was null.");
|
||||
var fontManager = services.GetRequiredService<IFontManager>();
|
||||
_ = handler.Services ?? throw new InvalidOperationException($"{nameof(Services)} should have been set by base class.");
|
||||
|
||||
var fontManager = handler.Services.GetRequiredService<IFontManager>();
|
||||
|
||||
handler.TypedNativeView?.UpdateFont(button, fontManager);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
}
|
||||
|
||||
public EditorHandler(PropertyMapper mapper) : base(mapper ?? EditorMapper)
|
||||
public EditorHandler(PropertyMapper? mapper = null) : base(mapper ?? EditorMapper)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -67,9 +67,9 @@ namespace Microsoft.Maui.Handlers
|
|||
|
||||
public static void MapFont(EntryHandler handler, IEntry entry)
|
||||
{
|
||||
var services = App.Current?.Services
|
||||
?? throw new InvalidOperationException($"Unable to find service provider, the App.Current.Services was null.");
|
||||
var fontManager = services.GetRequiredService<IFontManager>();
|
||||
_ = handler.Services ?? throw new InvalidOperationException($"{nameof(Services)} should have been set by base class.");
|
||||
|
||||
var fontManager = handler.Services.GetRequiredService<IFontManager>();
|
||||
|
||||
handler.TypedNativeView?.UpdateFont(entry, fontManager);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
}
|
||||
|
||||
public EntryHandler(PropertyMapper mapper) : base(mapper ?? EntryMapper)
|
||||
public EntryHandler(PropertyMapper? mapper = null) : base(mapper ?? EntryMapper)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -103,9 +103,9 @@ namespace Microsoft.Maui.Handlers
|
|||
|
||||
public static void MapFont(EntryHandler handler, IEntry entry)
|
||||
{
|
||||
var services = App.Current?.Services
|
||||
?? throw new InvalidOperationException($"Unable to find service provider, the App.Current.Services was null.");
|
||||
var fontManager = services.GetRequiredService<IFontManager>();
|
||||
_ = handler.Services ?? throw new InvalidOperationException($"{nameof(Services)} should have been set by base class.");
|
||||
|
||||
var fontManager = handler.Services.GetRequiredService<IFontManager>();
|
||||
|
||||
handler.TypedNativeView?.UpdateFont(entry, fontManager);
|
||||
}
|
||||
|
|
|
@ -5,8 +5,6 @@ namespace Microsoft.Maui
|
|||
{
|
||||
public interface IAndroidViewHandler : IViewHandler
|
||||
{
|
||||
void SetContext(Context context);
|
||||
|
||||
AView? View { get; }
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
using System;
|
||||
using Microsoft.Maui;
|
||||
|
||||
namespace Microsoft.Maui
|
||||
{
|
||||
public interface IViewHandler
|
||||
{
|
||||
void SetMauiContext(IMauiContext mauiContext);
|
||||
void SetVirtualView(IView view);
|
||||
void UpdateValue(string property);
|
||||
void DisconnectHandler();
|
||||
|
|
|
@ -68,9 +68,9 @@ namespace Microsoft.Maui.Handlers
|
|||
|
||||
public static void MapFont(LabelHandler handler, ILabel label)
|
||||
{
|
||||
var services = App.Current?.Services
|
||||
?? throw new InvalidOperationException($"Unable to find service provider, the App.Current.Services was null.");
|
||||
var fontManager = services.GetRequiredService<IFontManager>();
|
||||
_ = handler.Services ?? throw new InvalidOperationException($"{nameof(Services)} should have been set by base class.");
|
||||
|
||||
var fontManager = handler.Services.GetRequiredService<IFontManager>();
|
||||
|
||||
handler.TypedNativeView?.UpdateFont(label, fontManager);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace Microsoft.Maui.Handlers
|
|||
|
||||
}
|
||||
|
||||
public LabelHandler(PropertyMapper mapper) : base(mapper ?? LabelMapper)
|
||||
public LabelHandler(PropertyMapper? mapper = null) : base(mapper ?? LabelMapper)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -53,9 +53,9 @@ namespace Microsoft.Maui.Handlers
|
|||
|
||||
public static void MapFont(LabelHandler handler, ILabel label)
|
||||
{
|
||||
var services = App.Current?.Services ??
|
||||
throw new InvalidOperationException($"Unable to find service provider, the App.Current.Services was null.");
|
||||
var fontManager = services.GetRequiredService<IFontManager>();
|
||||
_ = handler.Services ?? throw new InvalidOperationException($"{nameof(Services)} should have been set by base class.");
|
||||
|
||||
var fontManager = handler.Services.GetRequiredService<IFontManager>();
|
||||
|
||||
handler.TypedNativeView?.UpdateFont(label, fontManager);
|
||||
}
|
||||
|
|
|
@ -29,14 +29,14 @@ namespace Microsoft.Maui.Handlers
|
|||
|
||||
_ = TypedNativeView ?? throw new InvalidOperationException($"{nameof(TypedNativeView)} should have been set by base class.");
|
||||
_ = VirtualView ?? throw new InvalidOperationException($"{nameof(VirtualView)} should have been set by base class.");
|
||||
_ = MauiApp.Current?.Context ?? throw new InvalidOperationException($"The MauiApp.Current.Context can't be null.");
|
||||
_ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class.");
|
||||
|
||||
TypedNativeView.CrossPlatformMeasure = VirtualView.Measure;
|
||||
TypedNativeView.CrossPlatformArrange = VirtualView.Arrange;
|
||||
|
||||
foreach (var child in VirtualView.Children)
|
||||
{
|
||||
TypedNativeView.AddView(child.ToNative(MauiApp.Current.Context));
|
||||
TypedNativeView.AddView(child.ToNative(MauiContext));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,9 +44,9 @@ namespace Microsoft.Maui.Handlers
|
|||
{
|
||||
_ = TypedNativeView ?? throw new InvalidOperationException($"{nameof(TypedNativeView)} should have been set by base class.");
|
||||
_ = VirtualView ?? throw new InvalidOperationException($"{nameof(VirtualView)} should have been set by base class.");
|
||||
_ = MauiApp.Current?.Context ?? throw new InvalidOperationException($"The MauiApp.Current.Context can't be null.");
|
||||
_ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class.");
|
||||
|
||||
TypedNativeView.AddView(child.ToNative(MauiApp.Current.Context!), 0);
|
||||
TypedNativeView.AddView(child.ToNative(MauiContext), 0);
|
||||
}
|
||||
|
||||
public void Remove(IView child)
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Microsoft.Maui.Handlers
|
|||
|
||||
}
|
||||
|
||||
public LayoutHandler(PropertyMapper mapper) : base(mapper ?? LayoutMapper)
|
||||
public LayoutHandler(PropertyMapper? mapper = null) : base(mapper ?? LayoutMapper)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -32,14 +32,14 @@ namespace Microsoft.Maui.Handlers
|
|||
|
||||
_ = TypedNativeView ?? throw new InvalidOperationException($"{nameof(TypedNativeView)} should have been set by base class.");
|
||||
_ = VirtualView ?? throw new InvalidOperationException($"{nameof(VirtualView)} should have been set by base class.");
|
||||
_ = MauiApp.Current?.Context ?? throw new InvalidOperationException($"The MauiApp.Current.Context can't be null.");
|
||||
_ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class.");
|
||||
|
||||
TypedNativeView.CrossPlatformMeasure = VirtualView.Measure;
|
||||
TypedNativeView.CrossPlatformArrange = VirtualView.Arrange;
|
||||
|
||||
foreach (var child in VirtualView.Children)
|
||||
{
|
||||
TypedNativeView.AddSubview(child.ToNative(MauiApp.Current.Context));
|
||||
TypedNativeView.AddSubview(child.ToNative(MauiContext));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,9 +47,9 @@ namespace Microsoft.Maui.Handlers
|
|||
{
|
||||
_ = TypedNativeView ?? throw new InvalidOperationException($"{nameof(TypedNativeView)} should have been set by base class.");
|
||||
_ = VirtualView ?? throw new InvalidOperationException($"{nameof(VirtualView)} should have been set by base class.");
|
||||
_ = MauiApp.Current?.Context ?? throw new InvalidOperationException($"The MauiApp.Current.Context can't be null.");
|
||||
_ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class.");
|
||||
|
||||
TypedNativeView.AddSubview(child.ToNative(MauiApp.Current.Context));
|
||||
TypedNativeView.AddSubview(child.ToNative(MauiContext));
|
||||
TypedNativeView.SetNeedsLayout();
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
}
|
||||
|
||||
public ProgressBarHandler(PropertyMapper mapper) : base(mapper ?? ProgressMapper)
|
||||
public ProgressBarHandler(PropertyMapper? mapper = null) : base(mapper ?? ProgressMapper)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -32,9 +32,9 @@ namespace Microsoft.Maui.Handlers
|
|||
|
||||
public static void MapFont(SearchBarHandler handler, ISearchBar searchBar)
|
||||
{
|
||||
var services = App.Current?.Services
|
||||
?? throw new InvalidOperationException($"Unable to find service provider, the App.Current.Services was null.");
|
||||
var fontManager = services.GetRequiredService<IFontManager>();
|
||||
_ = handler.Services ?? throw new InvalidOperationException($"{nameof(Services)} should have been set by base class.");
|
||||
|
||||
var fontManager = handler.Services.GetRequiredService<IFontManager>();
|
||||
|
||||
handler.TypedNativeView?.UpdateFont(searchBar, fontManager, handler._editText);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
}
|
||||
|
||||
public SearchBarHandler(PropertyMapper mapper) : base(mapper ?? SearchBarMapper)
|
||||
public SearchBarHandler(PropertyMapper? mapper = null) : base(mapper ?? SearchBarMapper)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -31,9 +31,9 @@ namespace Microsoft.Maui.Handlers
|
|||
|
||||
public static void MapFont(SearchBarHandler handler, ISearchBar searchBar)
|
||||
{
|
||||
var services = App.Current?.Services ??
|
||||
throw new InvalidOperationException($"Unable to find service provider, the App.Current.Services was null.");
|
||||
var fontManager = services.GetRequiredService<IFontManager>();
|
||||
_ = handler.Services ?? throw new InvalidOperationException($"{nameof(Services)} should have been set by base class.");
|
||||
|
||||
var fontManager = handler.Services.GetRequiredService<IFontManager>();
|
||||
|
||||
handler.QueryEditor?.UpdateFont(searchBar, fontManager);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Microsoft.Maui.Handlers
|
|||
|
||||
}
|
||||
|
||||
public SliderHandler(PropertyMapper mapper) : base(mapper ?? SliderMapper)
|
||||
public SliderHandler(PropertyMapper? mapper = null) : base(mapper ?? SliderMapper)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace Microsoft.Maui.Handlers
|
|||
|
||||
}
|
||||
|
||||
public SwitchHandler(PropertyMapper mapper) : base(mapper ?? SwitchMapper)
|
||||
public SwitchHandler(PropertyMapper? mapper = null) : base(mapper ?? SwitchMapper)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -6,9 +6,7 @@ namespace Microsoft.Maui.Handlers
|
|||
{
|
||||
public partial class AbstractViewHandler<TVirtualView, TNativeView> : IAndroidViewHandler
|
||||
{
|
||||
public void SetContext(Context context) => Context = context;
|
||||
|
||||
public Context? Context { get; private set; }
|
||||
public Context? Context => MauiContext?.Context;
|
||||
|
||||
public void SetFrame(Rectangle frame)
|
||||
{
|
||||
|
|
|
@ -41,6 +41,12 @@ namespace Microsoft.Maui.Handlers
|
|||
|
||||
public object? NativeView => TypedNativeView;
|
||||
|
||||
public IServiceProvider? Services => MauiContext?.Services;
|
||||
|
||||
public IMauiContext? MauiContext { get; private set; }
|
||||
|
||||
public void SetMauiContext(IMauiContext mauiContext) => MauiContext = mauiContext;
|
||||
|
||||
public virtual void SetVirtualView(IView view)
|
||||
{
|
||||
_ = view ?? throw new ArgumentNullException(nameof(view));
|
||||
|
|
|
@ -18,7 +18,9 @@ namespace Microsoft.Maui.Hosting
|
|||
readonly List<Action<HostBuilderContext, IFontCollection>> _configureFontsActions = new List<Action<HostBuilderContext, IFontCollection>>();
|
||||
readonly List<IConfigureContainerAdapter> _configureContainerActions = new List<IConfigureContainerAdapter>();
|
||||
readonly Func<IServiceCollection> _serviceColectionFactory = new Func<IServiceCollection>(() => new MauiServiceCollection());
|
||||
IServiceFactoryAdapter _serviceProviderFactory = new ServiceFactoryAdapter<IServiceCollection>(new MauiServiceProviderFactory());
|
||||
|
||||
IServiceFactoryAdapter _serviceProviderFactory = new ServiceFactoryAdapter<IServiceCollection>(new MauiServiceProviderFactory(false));
|
||||
|
||||
bool _hostBuilt;
|
||||
HostBuilderContext? _hostBuilderContext;
|
||||
IHostEnvironment? _hostEnvironment;
|
||||
|
@ -26,7 +28,6 @@ namespace Microsoft.Maui.Hosting
|
|||
IServiceCollection? _services;
|
||||
IConfiguration? _hostConfiguration;
|
||||
IConfiguration? _appConfiguration;
|
||||
App? _app;
|
||||
|
||||
public AppHostBuilder()
|
||||
{
|
||||
|
@ -34,13 +35,17 @@ namespace Microsoft.Maui.Hosting
|
|||
}
|
||||
public IDictionary<object, object> Properties => new Dictionary<object, object>();
|
||||
|
||||
public IHost Build(IApp app)
|
||||
public static IAppHostBuilder CreateDefaultAppBuilder()
|
||||
{
|
||||
_app = app as App;
|
||||
return Build();
|
||||
var builder = new AppHostBuilder();
|
||||
|
||||
builder.UseMauiHandlers();
|
||||
builder.UseFonts();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public IHost Build()
|
||||
public IAppHost Build()
|
||||
{
|
||||
_services = _serviceColectionFactory();
|
||||
|
||||
|
@ -66,10 +71,6 @@ namespace Microsoft.Maui.Hosting
|
|||
|
||||
BuildFontRegistrar(_serviceProvider);
|
||||
|
||||
//we do this here because we can't inject the provider on the App ctor
|
||||
//before we register the user ConfigureServices should this live in IApp ?
|
||||
_app?.SetServiceProvider(_serviceProvider);
|
||||
|
||||
return new AppHost(_serviceProvider, null);
|
||||
}
|
||||
|
||||
|
@ -125,7 +126,6 @@ namespace Microsoft.Maui.Hosting
|
|||
}
|
||||
#pragma warning restore CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint
|
||||
|
||||
|
||||
void BuildHostConfiguration()
|
||||
{
|
||||
var configBuilder = new ConfigurationBuilder();
|
||||
|
@ -275,5 +275,10 @@ namespace Microsoft.Maui.Hosting
|
|||
{
|
||||
return ConfigureContainer<TContainerBuilder>(configureDelegate);
|
||||
}
|
||||
|
||||
IHost IHostBuilder.Build()
|
||||
{
|
||||
return Build();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Maui.Handlers;
|
||||
using Microsoft.Maui.Hosting.Internal;
|
||||
|
||||
namespace Microsoft.Maui.Hosting
|
||||
{
|
||||
|
@ -41,15 +42,15 @@ namespace Microsoft.Maui.Hosting
|
|||
{ typeof(ICheckBox), typeof(CheckBoxHandler) },
|
||||
{ typeof(IEditor), typeof(EditorHandler) },
|
||||
{ typeof(IEntry), typeof(EntryHandler) },
|
||||
{ typeof(ILayout), typeof(LayoutHandler) },
|
||||
{ typeof(ILabel), typeof(LabelHandler) },
|
||||
{ typeof(ILayout), typeof(LayoutHandler) },
|
||||
{ typeof(IPicker), typeof(PickerHandler) },
|
||||
{ typeof(IProgress), typeof(ProgressBarHandler) },
|
||||
{ typeof(ISearchBar), typeof(SearchBarHandler) },
|
||||
{ typeof(ISlider), typeof(SliderHandler) },
|
||||
{ typeof(IStepper), typeof(StepperHandler) },
|
||||
{ typeof(ISwitch), typeof(SwitchHandler) },
|
||||
{ typeof(ITimePicker), typeof(TimePickerHandler) }
|
||||
{ typeof(ITimePicker), typeof(TimePickerHandler) },
|
||||
});
|
||||
|
||||
return builder;
|
||||
|
@ -65,5 +66,31 @@ namespace Microsoft.Maui.Hosting
|
|||
});
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static IAppHostBuilder UseMauiApp<TApp>(this IAppHostBuilder builder)
|
||||
where TApp : MauiApp
|
||||
{
|
||||
builder.ConfigureServices((context, collection) =>
|
||||
{
|
||||
collection.AddSingleton<MauiApp, TApp>();
|
||||
});
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static IAppHostBuilder UseMauiApp<TApp>(this IAppHostBuilder builder, Func<IServiceProvider, TApp> implementationFactory)
|
||||
where TApp : MauiApp
|
||||
{
|
||||
builder.ConfigureServices((context, collection) =>
|
||||
{
|
||||
collection.AddSingleton<MauiApp>(implementationFactory);
|
||||
});
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static IAppHostBuilder UseMauiServiceProviderFactory(this IAppHostBuilder builder, bool constructorInjection)
|
||||
{
|
||||
builder.UseServiceProviderFactory(new MauiServiceProviderFactory(constructorInjection));
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
namespace Microsoft.Maui.Hosting
|
||||
{
|
||||
public static class AppHostExtensions
|
||||
{
|
||||
public static void SetServiceProvider(this IAppHost host, App app)
|
||||
{
|
||||
//we do this here because we can't inject the provider on the App ctor
|
||||
//before we register the user ConfigureServices should this live in IApp ?
|
||||
app?.SetServiceProvider(host.Services);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Microsoft.Maui.Hosting
|
||||
{
|
||||
public interface IAppHost : IHost
|
||||
{
|
||||
IMauiHandlersServiceProvider Handlers { get; }
|
||||
}
|
||||
}
|
|
@ -17,6 +17,6 @@ namespace Microsoft.Maui.Hosting
|
|||
new IAppHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory);
|
||||
new IAppHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory);
|
||||
#pragma warning restore CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint.
|
||||
IHost Build(IApp app);
|
||||
new IAppHost Build();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,23 +1,27 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.Maui.Hosting.Internal
|
||||
{
|
||||
internal class AppHost : IHost, IAsyncDisposable
|
||||
internal class AppHost : IAppHost, IAsyncDisposable
|
||||
{
|
||||
readonly ILogger<AppHost>? _logger;
|
||||
|
||||
public AppHost(IServiceProvider services, ILogger<AppHost>? logger)
|
||||
{
|
||||
Services = services ?? throw new ArgumentNullException(nameof(services));
|
||||
Handlers = Services.GetRequiredService<IMauiHandlersServiceProvider>();
|
||||
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public IServiceProvider Services { get; }
|
||||
|
||||
public IMauiHandlersServiceProvider Handlers { get; }
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
_logger?.Starting();
|
||||
|
@ -51,4 +55,4 @@ namespace Microsoft.Maui.Hosting.Internal
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,13 @@ namespace Microsoft.Maui.Hosting.Internal
|
|||
{
|
||||
internal class MauiServiceProviderFactory : IServiceProviderFactory<IServiceCollection>
|
||||
{
|
||||
readonly bool _constructorInjection;
|
||||
|
||||
public MauiServiceProviderFactory(bool constructorInjection)
|
||||
{
|
||||
_constructorInjection = constructorInjection;
|
||||
}
|
||||
|
||||
public IServiceCollection CreateBuilder(IServiceCollection services)
|
||||
{
|
||||
if (services is IMauiServiceCollection mauiServiceCollection)
|
||||
|
@ -21,10 +28,9 @@ namespace Microsoft.Maui.Hosting.Internal
|
|||
public IServiceProvider CreateServiceProvider(IServiceCollection containerBuilder)
|
||||
{
|
||||
if (containerBuilder is IMauiServiceCollection mauiServiceCollection)
|
||||
return mauiServiceCollection.BuildServiceProvider();
|
||||
return mauiServiceCollection.BuildServiceProvider(_constructorInjection);
|
||||
else
|
||||
throw new InvalidCastException($"{nameof(containerBuilder)} is not {nameof(IMauiServiceCollection)}");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,8 @@ namespace Microsoft.Maui.Hosting
|
|||
{
|
||||
class MauiHandlersServiceProvider : MauiServiceProvider, IMauiHandlersServiceProvider
|
||||
{
|
||||
public MauiHandlersServiceProvider(IMauiServiceCollection collection) : base(collection)
|
||||
public MauiHandlersServiceProvider(IMauiServiceCollection collection)
|
||||
: base(collection, false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -14,4 +15,4 @@ namespace Microsoft.Maui.Hosting
|
|||
public IViewHandler? GetHandler<T>() where T : IView
|
||||
=> GetHandler(typeof(T));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +1,23 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.Maui.Hosting
|
||||
{
|
||||
class MauiServiceProvider : IMauiServiceProvider
|
||||
{
|
||||
IMauiServiceCollection _collection;
|
||||
readonly IMauiServiceCollection _collection;
|
||||
readonly bool _constructorInjection;
|
||||
|
||||
// TODO: do this properly and support scopes
|
||||
IDictionary<ServiceDescriptor, object?> _singletons;
|
||||
readonly IDictionary<ServiceDescriptor, object?> _singletons;
|
||||
|
||||
public MauiServiceProvider(IMauiServiceCollection collection)
|
||||
public MauiServiceProvider(IMauiServiceCollection collection, bool constructorInjection)
|
||||
{
|
||||
_collection = collection ?? throw new ArgumentNullException(nameof(collection));
|
||||
_constructorInjection = constructorInjection;
|
||||
_singletons = new ConcurrentDictionary<ServiceDescriptor, object?>();
|
||||
}
|
||||
|
||||
|
@ -66,7 +69,12 @@ namespace Microsoft.Maui.Hosting
|
|||
object? CreateInstance(ServiceDescriptor item)
|
||||
{
|
||||
if (item.ImplementationType != null)
|
||||
return Activator.CreateInstance(item.ImplementationType);
|
||||
{
|
||||
if (_constructorInjection)
|
||||
return CreateInstance(item.ImplementationType);
|
||||
else
|
||||
return Activator.CreateInstance(item.ImplementationType);
|
||||
}
|
||||
|
||||
if (item.ImplementationInstance != null)
|
||||
return item.ImplementationInstance;
|
||||
|
@ -76,5 +84,47 @@ namespace Microsoft.Maui.Hosting
|
|||
|
||||
throw new InvalidOperationException($"You need to provide an {nameof(item.ImplementationType)}, an {nameof(item.ImplementationFactory)} or an {nameof(item.ImplementationInstance)}.");
|
||||
}
|
||||
|
||||
object? CreateInstance(Type implementationType)
|
||||
{
|
||||
(ConstructorInfo Constructor, ParameterInfo[] Parameters) match = default;
|
||||
var constructors = implementationType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
for (var i = 0; i < constructors.Length; i++)
|
||||
{
|
||||
var ctor = constructors[i];
|
||||
if (!ctor.IsFamily && !ctor.IsPrivate)
|
||||
{
|
||||
var parameters = ctor.GetParameters();
|
||||
if (match.Parameters == null || parameters.Length > match.Parameters.Length)
|
||||
match = (ctor, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
if (match.Constructor == null)
|
||||
throw new InvalidOperationException($"The type '{implementationType.Name}' did not have any public or internal constructors.");
|
||||
|
||||
var paramCount = match.Parameters!.Length;
|
||||
|
||||
if (paramCount == 0)
|
||||
return match.Constructor.Invoke(null);
|
||||
|
||||
var paramValues = new object?[paramCount];
|
||||
|
||||
for (var i = 0; i < paramCount; i++)
|
||||
{
|
||||
var param = match.Parameters[i];
|
||||
var value = GetService(param.ParameterType);
|
||||
if (value == null)
|
||||
{
|
||||
if (!param.HasDefaultValue)
|
||||
throw new InvalidOperationException($"No service for type '{param.ParameterType}' has been registered.");
|
||||
else
|
||||
value = param.DefaultValue;
|
||||
}
|
||||
paramValues[i] = value;
|
||||
}
|
||||
|
||||
return match.Constructor.Invoke(paramValues);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,8 +4,8 @@ namespace Microsoft.Maui.Hosting
|
|||
{
|
||||
public static class ServiceProviderExtensions
|
||||
{
|
||||
internal static IServiceProvider BuildServiceProvider(this IMauiServiceCollection serviceCollection)
|
||||
=> new MauiServiceProvider(serviceCollection);
|
||||
internal static IServiceProvider BuildServiceProvider(this IMauiServiceCollection serviceCollection, bool constructorInjection)
|
||||
=> new MauiServiceProvider(serviceCollection, constructorInjection);
|
||||
|
||||
internal static IMauiHandlersServiceProvider BuildHandlersServiceProvider(this IMauiServiceCollection serviceCollection)
|
||||
=> new MauiHandlersServiceProvider(serviceCollection);
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Maui
|
||||
{
|
||||
public abstract class MauiApp : App
|
||||
{
|
||||
public abstract IWindow CreateWindow(IActivationState state);
|
||||
|
||||
public MauiApp()
|
||||
protected MauiApp()
|
||||
{
|
||||
if (Current != null)
|
||||
throw new InvalidOperationException($"Only one {nameof(App)} instance is allowed.");
|
||||
|
||||
Current = this;
|
||||
}
|
||||
|
||||
public static MauiApp? Current { get; internal set; }
|
||||
|
||||
public abstract IWindow CreateWindow(IActivationState state);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,8 +21,7 @@ namespace Microsoft.Maui
|
|||
if (handler == null)
|
||||
throw new Exception($"Handler not found for view {view}");
|
||||
|
||||
if (handler is IAndroidViewHandler ahandler)
|
||||
ahandler.SetContext(context.Context);
|
||||
handler.SetMauiContext(context);
|
||||
|
||||
view.Handler = handler;
|
||||
}
|
||||
|
|
|
@ -21,10 +21,10 @@ namespace Microsoft.Maui
|
|||
{
|
||||
base.OnCreate(savedInstanceState);
|
||||
|
||||
if (App.Current as MauiApp == null)
|
||||
if (MauiApp.Current == null)
|
||||
throw new InvalidOperationException($"App is not {nameof(MauiApp)}");
|
||||
|
||||
var mauiApp = (MauiApp)App.Current;
|
||||
var mauiApp = MauiApp.Current;
|
||||
|
||||
if (mauiApp.Services == null)
|
||||
throw new InvalidOperationException("App was not initialized");
|
||||
|
@ -35,9 +35,6 @@ namespace Microsoft.Maui
|
|||
|
||||
window.MauiContext = mauiContext;
|
||||
|
||||
//Hack for now we set this on the App Static but this should be on IFrameworkElement
|
||||
App.Current.SetHandlerContext(window.MauiContext);
|
||||
|
||||
var content = (window.Page as IView) ??
|
||||
window.Page.View;
|
||||
|
||||
|
|
|
@ -2,30 +2,54 @@ using System;
|
|||
using Android.Runtime;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Maui.Hosting;
|
||||
|
||||
namespace Microsoft.Maui
|
||||
{
|
||||
public class MauiApplication<TApplication> : global::Android.App.Application where TApplication : MauiApp
|
||||
public class MauiApplication<TStartup> : Android.App.Application
|
||||
where TStartup : IStartup, new()
|
||||
{
|
||||
public MauiApplication(IntPtr handle, JniHandleOwnership ownerShip) : base(handle, ownerShip)
|
||||
{
|
||||
|
||||
}
|
||||
IHost? _host;
|
||||
|
||||
public override void OnCreate()
|
||||
{
|
||||
if (!(Activator.CreateInstance(typeof(TApplication)) is TApplication app))
|
||||
throw new InvalidOperationException($"We weren't able to create the App {typeof(TApplication)}");
|
||||
var startup = new TStartup();
|
||||
|
||||
_host = app.CreateBuilder().ConfigureServices(ConfigureNativeServices).Build(app);
|
||||
IAppHostBuilder appBuilder;
|
||||
|
||||
if (startup is IHostBuilderStartup hostBuilderStartup)
|
||||
{
|
||||
appBuilder = hostBuilderStartup
|
||||
.CreateHostBuilder();
|
||||
}
|
||||
else
|
||||
{
|
||||
appBuilder = AppHostBuilder
|
||||
.CreateDefaultAppBuilder();
|
||||
}
|
||||
|
||||
appBuilder.
|
||||
ConfigureServices(ConfigureNativeServices);
|
||||
|
||||
startup.Configure(appBuilder);
|
||||
|
||||
var host = appBuilder.Build();
|
||||
if (host.Services == null)
|
||||
throw new InvalidOperationException("App was not intialized");
|
||||
|
||||
var services = host.Services;
|
||||
|
||||
var app = services.GetRequiredService<MauiApp>();
|
||||
host.SetServiceProvider(app);
|
||||
|
||||
//_host.Start();
|
||||
base.OnCreate();
|
||||
}
|
||||
|
||||
//configure native services like HandlersContext, ImageSourceHandlers etc..
|
||||
// Configure native services like HandlersContext, ImageSourceHandlers etc..
|
||||
void ConfigureNativeServices(HostBuilderContext ctx, IServiceCollection services)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,8 @@ namespace Microsoft.Maui
|
|||
if (handler == null)
|
||||
throw new Exception($"Handler not found for view {view}");
|
||||
|
||||
handler.SetMauiContext(context);
|
||||
|
||||
view.Handler = handler;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Foundation;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
@ -9,7 +7,8 @@ using UIKit;
|
|||
|
||||
namespace Microsoft.Maui
|
||||
{
|
||||
public class MauiUIApplicationDelegate<TApplication> : UIApplicationDelegate, IUIApplicationDelegate where TApplication : MauiApp
|
||||
public class MauiUIApplicationDelegate<TStartup> : UIApplicationDelegate, IUIApplicationDelegate
|
||||
where TStartup : IStartup, new()
|
||||
{
|
||||
public override UIWindow? Window
|
||||
{
|
||||
|
@ -19,23 +18,40 @@ namespace Microsoft.Maui
|
|||
|
||||
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
|
||||
{
|
||||
if (!(Activator.CreateInstance(typeof(TApplication)) is TApplication app))
|
||||
throw new InvalidOperationException($"We weren't able to create the App {typeof(TApplication)}");
|
||||
var startup = new TStartup();
|
||||
|
||||
var host = app.CreateBuilder().ConfigureServices(ConfigureNativeServices).Build(app);
|
||||
IAppHostBuilder appBuilder;
|
||||
|
||||
if (MauiApp.Current == null || MauiApp.Current.Services == null)
|
||||
if (startup is IHostBuilderStartup hostBuilderStartup)
|
||||
{
|
||||
appBuilder = hostBuilderStartup
|
||||
.CreateHostBuilder();
|
||||
}
|
||||
else
|
||||
{
|
||||
appBuilder = AppHostBuilder
|
||||
.CreateDefaultAppBuilder();
|
||||
}
|
||||
|
||||
appBuilder.
|
||||
ConfigureServices(ConfigureNativeServices);
|
||||
|
||||
startup.Configure(appBuilder);
|
||||
|
||||
var host = appBuilder.Build();
|
||||
if (host.Services == null)
|
||||
throw new InvalidOperationException("App was not intialized");
|
||||
|
||||
var services = host.Services;
|
||||
|
||||
var mauiContext = new MauiContext(MauiApp.Current.Services);
|
||||
var app = services.GetRequiredService<MauiApp>();
|
||||
host.SetServiceProvider(app);
|
||||
|
||||
var mauiContext = new MauiContext(services);
|
||||
var window = app.CreateWindow(new ActivationState(mauiContext));
|
||||
|
||||
window.MauiContext = mauiContext;
|
||||
|
||||
//Hack for now we set this on the App Static but this should be on IFrameworkElement
|
||||
App.Current.SetHandlerContext(window.MauiContext);
|
||||
|
||||
var content = (window.Page as IView) ?? window.Page.View;
|
||||
|
||||
Window = new UIWindow
|
||||
|
@ -53,7 +69,6 @@ namespace Microsoft.Maui
|
|||
|
||||
void ConfigureNativeServices(HostBuilderContext ctx, IServiceCollection services)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ namespace Microsoft.Maui.Handlers.Benchmarks
|
|||
[MemoryDiagnoser]
|
||||
public class GetHandlersBenchmarker
|
||||
{
|
||||
MockApp _app;
|
||||
IAppHost _host;
|
||||
|
||||
Registrar<IFrameworkElement, IViewHandler> _registrar;
|
||||
|
||||
|
@ -16,10 +16,9 @@ namespace Microsoft.Maui.Handlers.Benchmarks
|
|||
[GlobalSetup(Target = nameof(GetHandlerUsingDI))]
|
||||
public void SetupForDI()
|
||||
{
|
||||
_app = new MockApp();
|
||||
_app.CreateBuilder()
|
||||
.RegisterHandler<IButton, ButtonHandler>()
|
||||
.Build(_app);
|
||||
_host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.Build();
|
||||
}
|
||||
|
||||
[GlobalSetup(Target = nameof(GetHandlerUsingRegistrar))]
|
||||
|
@ -34,7 +33,7 @@ namespace Microsoft.Maui.Handlers.Benchmarks
|
|||
{
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
_app.Context.Handlers.GetHandler<IButton>();
|
||||
_host.Handlers.GetHandler<IButton>();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,4 +46,4 @@ namespace Microsoft.Maui.Handlers.Benchmarks
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
using System;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Maui.Hosting;
|
||||
|
||||
namespace Microsoft.Maui.Handlers.Benchmarks
|
||||
{
|
||||
[MemoryDiagnoser]
|
||||
public class MauiServiceProviderBenchmarker
|
||||
{
|
||||
IAppHost _host;
|
||||
|
||||
[Params(100_000)]
|
||||
public int N { get; set; }
|
||||
|
||||
[IterationSetup(Target = nameof(DefaultBuilder))]
|
||||
public void SetupForDefaultBuilder()
|
||||
{
|
||||
_host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.ConfigureServices((ctx, svc) => svc.AddTransient<IFooService, FooService>())
|
||||
.Build();
|
||||
}
|
||||
|
||||
[IterationSetup(Target = nameof(DefaultBuilderWithConstructorInjection))]
|
||||
public void SetupForDefaultBuilderWithConstructorInjection()
|
||||
{
|
||||
_host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.UseMauiServiceProviderFactory(true)
|
||||
.ConfigureServices((ctx, svc) => svc.AddTransient<IFooService, FooService>())
|
||||
.Build();
|
||||
}
|
||||
|
||||
[IterationSetup(Target = nameof(OneConstructorParameter))]
|
||||
public void SetupForOneConstructorParameter()
|
||||
{
|
||||
_host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.UseMauiServiceProviderFactory(true)
|
||||
.ConfigureServices((ctx, svc) =>
|
||||
{
|
||||
svc.AddTransient<IFooService, FooService>();
|
||||
svc.AddTransient<IFooBarService, FooBarWithFooService>();
|
||||
})
|
||||
.Build();
|
||||
}
|
||||
|
||||
[IterationSetup(Target = nameof(TwoConstructorParameters))]
|
||||
public void SetupForTwoConstructorParameters()
|
||||
{
|
||||
_host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.UseMauiServiceProviderFactory(true)
|
||||
.ConfigureServices((ctx, svc) =>
|
||||
{
|
||||
svc.AddTransient<IFooService, FooService>();
|
||||
svc.AddTransient<IBarService, BarService>();
|
||||
svc.AddTransient<IFooBarService, FooBarWithFooAndBarService>();
|
||||
})
|
||||
.Build();
|
||||
}
|
||||
|
||||
[IterationSetup(Target = nameof(ExtensionsWithConstructorInjection))]
|
||||
public void SetupForExtensionsWithConstructorInjection()
|
||||
{
|
||||
_host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.UseServiceProviderFactory(new DIExtensionsServiceProviderFactory())
|
||||
.ConfigureServices((ctx, svc) => svc.AddTransient<IFooService, FooService>())
|
||||
.Build();
|
||||
}
|
||||
|
||||
[IterationSetup(Target = nameof(ExtensionsWithOneConstructorParameter))]
|
||||
public void SetupForExtensionsWithOneConstructorParameter()
|
||||
{
|
||||
_host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.UseServiceProviderFactory(new DIExtensionsServiceProviderFactory())
|
||||
.ConfigureServices((ctx, svc) =>
|
||||
{
|
||||
svc.AddTransient<IFooService, FooService>();
|
||||
svc.AddTransient<IFooBarService, FooBarWithFooService>();
|
||||
})
|
||||
.Build();
|
||||
}
|
||||
|
||||
[IterationSetup(Target = nameof(ExtensionsWithTwoConstructorParameters))]
|
||||
public void SetupForExtensionsWithTwoConstructorParameters()
|
||||
{
|
||||
_host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.UseServiceProviderFactory(new DIExtensionsServiceProviderFactory())
|
||||
.ConfigureServices((ctx, svc) =>
|
||||
{
|
||||
svc.AddTransient<IFooService, FooService>();
|
||||
svc.AddTransient<IBarService, BarService>();
|
||||
svc.AddTransient<IFooBarService, FooBarWithFooAndBarService>();
|
||||
})
|
||||
.Build();
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public void DefaultBuilder()
|
||||
{
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
_host.Services.GetService<IFooService>();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void DefaultBuilderWithConstructorInjection()
|
||||
{
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
_host.Services.GetService<IFooService>();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void OneConstructorParameter()
|
||||
{
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
_host.Services.GetService<IFooBarService>();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TwoConstructorParameters()
|
||||
{
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
_host.Services.GetService<IFooBarService>();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ExtensionsWithConstructorInjection()
|
||||
{
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
_host.Services.GetService<IFooService>();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ExtensionsWithOneConstructorParameter()
|
||||
{
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
_host.Services.GetService<IFooBarService>();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ExtensionsWithTwoConstructorParameters()
|
||||
{
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
_host.Services.GetService<IFooBarService>();
|
||||
}
|
||||
}
|
||||
|
||||
public class DIExtensionsServiceProviderFactory : IServiceProviderFactory<ServiceCollection>
|
||||
{
|
||||
public ServiceCollection CreateBuilder(IServiceCollection services)
|
||||
=> new ServiceCollection { services };
|
||||
|
||||
public IServiceProvider CreateServiceProvider(ServiceCollection containerBuilder)
|
||||
=> containerBuilder.BuildServiceProvider();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@
|
|||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -6,7 +6,8 @@ namespace Microsoft.Maui.Handlers.Benchmarks
|
|||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
//BenchmarkRunner.Run<MauiServiceProviderBenchmarker>();
|
||||
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).RunAllJoined();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +1,10 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Maui.Hosting;
|
||||
|
||||
namespace Microsoft.Maui.Handlers.Benchmarks
|
||||
{
|
||||
class MockApp : App
|
||||
class AppStub : MauiApp
|
||||
{
|
||||
public override IAppHostBuilder CreateBuilder()
|
||||
public override IWindow CreateWindow(IActivationState state)
|
||||
{
|
||||
return base.CreateBuilder()
|
||||
.ConfigureServices(ConfigureNativeServices);
|
||||
}
|
||||
|
||||
void ConfigureNativeServices(HostBuilderContext ctx, IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IMauiContext>(provider => new HandlersContextStub(provider));
|
||||
return new WindowStub();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
namespace Microsoft.Maui.Handlers.Benchmarks
|
||||
{
|
||||
interface IFooService
|
||||
{
|
||||
}
|
||||
|
||||
interface IBarService
|
||||
{
|
||||
}
|
||||
|
||||
interface IFooBarService
|
||||
{
|
||||
}
|
||||
|
||||
class FooService : IFooService
|
||||
{
|
||||
}
|
||||
|
||||
class BarService : IBarService
|
||||
{
|
||||
}
|
||||
|
||||
class FooBarWithFooService : IFooBarService
|
||||
{
|
||||
public FooBarWithFooService(IFooService foo)
|
||||
{
|
||||
Foo = foo;
|
||||
}
|
||||
|
||||
public IFooService Foo { get; }
|
||||
}
|
||||
|
||||
class FooBarWithFooAndBarService : IFooBarService
|
||||
{
|
||||
public FooBarWithFooAndBarService(IFooService foo, IBarService bar)
|
||||
{
|
||||
Foo = foo;
|
||||
Bar = bar;
|
||||
}
|
||||
|
||||
public IFooService Foo { get; }
|
||||
|
||||
public IBarService Bar { get; }
|
||||
}
|
||||
|
||||
class FooDualConstructor : IFooBarService
|
||||
{
|
||||
public FooDualConstructor(IFooService foo)
|
||||
{
|
||||
Foo = foo;
|
||||
}
|
||||
|
||||
public FooDualConstructor(IBarService bar)
|
||||
{
|
||||
Bar = bar;
|
||||
}
|
||||
|
||||
public IFooService Foo { get; }
|
||||
|
||||
public IBarService Bar { get; }
|
||||
}
|
||||
|
||||
class FooDefaultValueConstructor : IFooBarService
|
||||
{
|
||||
public FooDefaultValueConstructor(IBarService bar = null)
|
||||
{
|
||||
Bar = bar;
|
||||
}
|
||||
|
||||
public IBarService Bar { get; }
|
||||
}
|
||||
|
||||
class FooDefaultSystemValueConstructor : IFooBarService
|
||||
{
|
||||
public FooDefaultSystemValueConstructor(string text = "Default Value")
|
||||
{
|
||||
Text = text;
|
||||
}
|
||||
|
||||
public string Text { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.Maui.Handlers.Benchmarks
|
||||
{
|
||||
public class WindowStub : IWindow
|
||||
{
|
||||
public IMauiContext MauiContext { get; set; }
|
||||
public IPage Page { get; set; }
|
||||
}
|
||||
}
|
|
@ -8,10 +8,6 @@ namespace Microsoft.Maui.DeviceTests
|
|||
[Category(TestCategory.ActivityIndicator)]
|
||||
public partial class ActivityIndicatorHandlerTests : HandlerTestBase<ActivityIndicatorHandler, ActivityIndicatorStub>
|
||||
{
|
||||
public ActivityIndicatorHandlerTests(HandlerTestFixture fixture) : base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
[Theory(DisplayName = "IsRunning Initializes Correctly")]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
|
|
|
@ -9,10 +9,6 @@ namespace Microsoft.Maui.DeviceTests
|
|||
[Category(TestCategory.Button)]
|
||||
public partial class ButtonHandlerTests : HandlerTestBase<ButtonHandler, ButtonStub>
|
||||
{
|
||||
public ButtonHandlerTests(HandlerTestFixture fixture) : base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Text Initializes Correctly")]
|
||||
public async Task TextInitializesCorrectly()
|
||||
{
|
||||
|
|
|
@ -8,10 +8,6 @@ namespace Microsoft.Maui.DeviceTests
|
|||
[Category(TestCategory.CheckBox)]
|
||||
public partial class CheckBoxHandlerTests : HandlerTestBase<CheckBoxHandler, CheckBoxStub>
|
||||
{
|
||||
public CheckBoxHandlerTests(HandlerTestFixture fixture) : base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
[Theory(DisplayName = "IsChecked Initializes Correctly")]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
|
|
|
@ -9,10 +9,6 @@ namespace Microsoft.Maui.DeviceTests
|
|||
[Category(TestCategory.Editor)]
|
||||
public partial class EditorHandlerTests : HandlerTestBase<EditorHandler, EditorStub>
|
||||
{
|
||||
public EditorHandlerTests(HandlerTestFixture fixture) : base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Text Initializes Correctly")]
|
||||
public async Task TextInitializesCorrectly()
|
||||
{
|
||||
|
|
|
@ -9,10 +9,6 @@ namespace Microsoft.Maui.DeviceTests
|
|||
[Category(TestCategory.Entry)]
|
||||
public partial class EntryHandlerTests : HandlerTestBase<EntryHandler, EntryStub>
|
||||
{
|
||||
public EntryHandlerTests(HandlerTestFixture fixture) : base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Text Initializes Correctly")]
|
||||
public async Task TextInitializesCorrectly()
|
||||
{
|
||||
|
|
|
@ -8,8 +8,7 @@ namespace Microsoft.Maui.DeviceTests
|
|||
protected THandler CreateHandler(IView view)
|
||||
{
|
||||
var handler = Activator.CreateInstance<THandler>();
|
||||
if (handler is IAndroidViewHandler av)
|
||||
av.SetContext(DefaultContext);
|
||||
handler.SetMauiContext(MauiContext);
|
||||
|
||||
handler.SetVirtualView(view);
|
||||
view.Handler = handler;
|
||||
|
|
|
@ -2,23 +2,48 @@ using System;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.Maui.DeviceTests.Stubs;
|
||||
using Microsoft.Maui.Essentials;
|
||||
using Microsoft.Maui.Hosting;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Maui.DeviceTests
|
||||
{
|
||||
[Collection(TestCollections.Handlers)]
|
||||
public partial class HandlerTestBase<THandler, TStub> : TestBase
|
||||
public partial class HandlerTestBase<THandler, TStub> : TestBase, IDisposable
|
||||
where THandler : IViewHandler
|
||||
where TStub : StubBase, IView, new()
|
||||
{
|
||||
readonly HandlerTestFixture _fixture;
|
||||
AppStub _app;
|
||||
IAppHost _host;
|
||||
IMauiContext _context;
|
||||
|
||||
public HandlerTestBase(HandlerTestFixture fixture)
|
||||
public HandlerTestBase()
|
||||
{
|
||||
_fixture = fixture;
|
||||
var appBuilder = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.ConfigureFonts((ctx, fonts) =>
|
||||
{
|
||||
fonts.AddFont("dokdo_regular.ttf", "Dokdo");
|
||||
});
|
||||
|
||||
_host = appBuilder.Build();
|
||||
|
||||
_app = new AppStub();
|
||||
|
||||
_host.SetServiceProvider(_app);
|
||||
|
||||
_context = new ContextStub(_host.Services);
|
||||
}
|
||||
|
||||
public IApp App => _fixture.App;
|
||||
public void Dispose()
|
||||
{
|
||||
_host.Dispose();
|
||||
_host = null;
|
||||
_app = null;
|
||||
_context = null;
|
||||
}
|
||||
|
||||
public IApp App => _app;
|
||||
|
||||
public IMauiContext MauiContext => _context;
|
||||
|
||||
public Task<T> InvokeOnMainThreadAsync<T>(Func<T> func) =>
|
||||
MainThread.InvokeOnMainThreadAsync(func);
|
||||
|
|
|
@ -11,6 +11,8 @@ namespace Microsoft.Maui.DeviceTests
|
|||
protected THandler CreateHandler(IView view)
|
||||
{
|
||||
var handler = Activator.CreateInstance<THandler>();
|
||||
handler.SetMauiContext(MauiContext);
|
||||
|
||||
handler.SetVirtualView(view);
|
||||
view.Handler = handler;
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
using Xunit;
|
||||
|
||||
namespace Microsoft.Maui.DeviceTests
|
||||
{
|
||||
[CollectionDefinition(TestCollections.Handlers)]
|
||||
public class HandlerTestCollection : ICollectionFixture<HandlerTestFixture>
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Maui.DeviceTests.Stubs;
|
||||
using Microsoft.Maui.Hosting;
|
||||
|
||||
namespace Microsoft.Maui.DeviceTests
|
||||
{
|
||||
public class HandlerTestFixture : IDisposable
|
||||
{
|
||||
AppStub _app;
|
||||
IHost _host;
|
||||
IMauiContext _context;
|
||||
|
||||
public HandlerTestFixture()
|
||||
{
|
||||
_app = new AppStub();
|
||||
_context = new ContextStub(_app);
|
||||
_host = _app
|
||||
.CreateBuilder()
|
||||
.ConfigureFonts((ctx, fonts) =>
|
||||
{
|
||||
fonts.AddFont("dokdo_regular.ttf", "Dokdo");
|
||||
})
|
||||
.ConfigureServices((ctx, services) =>
|
||||
{
|
||||
services.AddSingleton(_context);
|
||||
})
|
||||
.Build(_app);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_host.Dispose();
|
||||
_host = null;
|
||||
|
||||
_app.Dispose();
|
||||
_app = null;
|
||||
|
||||
_context = null;
|
||||
}
|
||||
|
||||
public IApp App => _app;
|
||||
}
|
||||
}
|
|
@ -9,10 +9,6 @@ namespace Microsoft.Maui.DeviceTests
|
|||
[Category(TestCategory.Label)]
|
||||
public partial class LabelHandlerTests : HandlerTestBase<LabelHandler, LabelStub>
|
||||
{
|
||||
public LabelHandlerTests(HandlerTestFixture fixture) : base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Background Color Initializes Correctly")]
|
||||
public async Task BackgroundColorInitializesCorrectly()
|
||||
{
|
||||
|
|
|
@ -9,10 +9,6 @@ namespace Microsoft.Maui.DeviceTests.Handlers.Layout
|
|||
[Category(TestCategory.Layout)]
|
||||
public partial class LayoutHandlerTests : HandlerTestBase<LayoutHandler, LayoutStub>
|
||||
{
|
||||
public LayoutHandlerTests(HandlerTestFixture fixture) : base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Empty layout")]
|
||||
public async Task EmptyLayout()
|
||||
{
|
||||
|
|
|
@ -6,8 +6,5 @@ namespace Microsoft.Maui.DeviceTests
|
|||
[Category(TestCategory.Picker)]
|
||||
public partial class PickerHandlerTests : HandlerTestBase<PickerHandler, PickerStub>
|
||||
{
|
||||
public PickerHandlerTests(HandlerTestFixture fixture) : base(fixture)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,10 +9,6 @@ namespace Microsoft.Maui.DeviceTests
|
|||
[Category("ProgressBarHandler")]
|
||||
public partial class ProgressBarHandlerTests : HandlerTestBase<ProgressBarHandler, ProgressBarStub>
|
||||
{
|
||||
public ProgressBarHandlerTests(HandlerTestFixture fixture) : base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
[Theory(DisplayName = "Progress Initializes Correctly")]
|
||||
[InlineData(0.25)]
|
||||
[InlineData(0.5)]
|
||||
|
|
|
@ -8,10 +8,6 @@ namespace Microsoft.Maui.DeviceTests
|
|||
[Category(TestCategory.SearchBar)]
|
||||
public partial class SearchBarHandlerTests : HandlerTestBase<SearchBarHandler, SearchBarStub>
|
||||
{
|
||||
public SearchBarHandlerTests(HandlerTestFixture fixture) : base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Text Initializes Correctly")]
|
||||
public async Task TextInitializesCorrectly()
|
||||
{
|
||||
|
|
|
@ -9,10 +9,6 @@ namespace Microsoft.Maui.DeviceTests
|
|||
[Category(TestCategory.Slider)]
|
||||
public partial class SliderHandlerTests : HandlerTestBase<SliderHandler, SliderStub>
|
||||
{
|
||||
public SliderHandlerTests(HandlerTestFixture fixture) : base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
[Theory(DisplayName = "Percent Value Initializes Correctly")]
|
||||
[InlineData(0, 1, 0)]
|
||||
[InlineData(0, 1, 0.5)]
|
||||
|
|
|
@ -8,10 +8,6 @@ namespace Microsoft.Maui.DeviceTests
|
|||
[Category(TestCategory.Stepper)]
|
||||
public partial class StepperHandlerTests : HandlerTestBase<StepperHandler, StepperStub>
|
||||
{
|
||||
public StepperHandlerTests(HandlerTestFixture fixture) : base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Is Value Initializes Correctly")]
|
||||
public async Task ValueInitializesCorrectly()
|
||||
{
|
||||
|
|
|
@ -9,10 +9,6 @@ namespace Microsoft.Maui.DeviceTests
|
|||
[Category(TestCategory.Switch)]
|
||||
public partial class SwitchHandlerTests : HandlerTestBase<SwitchHandler, SwitchStub>
|
||||
{
|
||||
public SwitchHandlerTests(HandlerTestFixture fixture) : base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Is Toggled Initializes Correctly")]
|
||||
public async Task IsToggledInitializesCorrectly()
|
||||
{
|
||||
|
|
|
@ -8,10 +8,6 @@ namespace Microsoft.Maui.DeviceTests
|
|||
[Category(TestCategory.TimePicker)]
|
||||
public partial class TimePickerHandlerTests : HandlerTestBase<TimePickerHandler, TimePickerStub>
|
||||
{
|
||||
public TimePickerHandlerTests(HandlerTestFixture fixture) : base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Time Initializes Correctly")]
|
||||
public async Task IsToggledInitializesCorrectly()
|
||||
{
|
||||
|
@ -22,4 +18,4 @@ namespace Microsoft.Maui.DeviceTests
|
|||
await ValidateTime(timePickerStub, () => timePickerStub.Time = time);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +1,6 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.Maui.DeviceTests.Stubs
|
||||
{
|
||||
class AppStub : MauiApp, IDisposable
|
||||
class AppStub : App
|
||||
{
|
||||
public override IWindow CreateWindow(IActivationState state)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Current = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,29 +3,20 @@ using Microsoft.Extensions.DependencyInjection;
|
|||
|
||||
namespace Microsoft.Maui.DeviceTests.Stubs
|
||||
{
|
||||
class ContextStub : IMauiContext, IDisposable
|
||||
class ContextStub : IMauiContext
|
||||
{
|
||||
AppStub _app;
|
||||
|
||||
public ContextStub(AppStub app)
|
||||
public ContextStub(IServiceProvider services)
|
||||
{
|
||||
_app = app;
|
||||
Services = services;
|
||||
}
|
||||
|
||||
public IServiceProvider Services =>
|
||||
_app.Services;
|
||||
public IServiceProvider Services { get; }
|
||||
|
||||
public IMauiHandlersServiceProvider Handlers =>
|
||||
Services.GetRequiredService<IMauiHandlersServiceProvider>();
|
||||
|
||||
#if __ANDROID__
|
||||
public Android.Content.Context Context =>
|
||||
Android.App.Application.Context;
|
||||
public Android.Content.Context Context => Platform.DefaultContext;
|
||||
#endif
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_app = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Maui.Handlers;
|
||||
using Microsoft.Maui.Hosting;
|
||||
using Microsoft.Maui.Tests;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Maui.UnitTests
|
||||
{
|
||||
[Category(TestCategory.Core, TestCategory.Hosting)]
|
||||
public class HostBuilderHandlerTests
|
||||
{
|
||||
[Fact]
|
||||
public void CanBuildAHost()
|
||||
{
|
||||
var host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.Build();
|
||||
|
||||
Assert.NotNull(host);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanGetIMauiHandlersServiceProviderFromServices()
|
||||
{
|
||||
var host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.Build();
|
||||
|
||||
Assert.NotNull(host);
|
||||
Assert.NotNull(host.Services);
|
||||
Assert.NotNull(host.Handlers);
|
||||
Assert.IsType<MauiHandlersServiceProvider>(host.Handlers);
|
||||
Assert.Equal(host.Handlers, host.Services.GetService<IMauiHandlersServiceProvider>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanRegisterAndGetHandler()
|
||||
{
|
||||
var host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.RegisterHandler<IViewStub, ViewHandlerStub>()
|
||||
.Build();
|
||||
|
||||
var handler = host.Handlers.GetHandler(typeof(IViewStub));
|
||||
|
||||
Assert.NotNull(handler);
|
||||
Assert.IsType<ViewHandlerStub>(handler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanRegisterAndGetHandlerWithDictionary()
|
||||
{
|
||||
var host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.RegisterHandlers(new Dictionary<Type, Type>
|
||||
{
|
||||
{ typeof(IViewStub), typeof(ViewHandlerStub) }
|
||||
})
|
||||
.Build();
|
||||
|
||||
var handler = host.Handlers.GetHandler(typeof(IViewStub));
|
||||
|
||||
Assert.NotNull(handler);
|
||||
Assert.IsType<ViewHandlerStub>(handler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanRegisterAndGetHandlerForType()
|
||||
{
|
||||
var host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.RegisterHandler<IViewStub, ViewHandlerStub>()
|
||||
.Build();
|
||||
|
||||
var handler = host.Handlers.GetHandler(typeof(ViewStub));
|
||||
|
||||
Assert.NotNull(handler);
|
||||
Assert.IsType<ViewHandlerStub>(handler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultHandlersAreRegistered()
|
||||
{
|
||||
var host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.Build();
|
||||
|
||||
var handler = host.Handlers.GetHandler(typeof(IButton));
|
||||
|
||||
Assert.NotNull(handler);
|
||||
Assert.IsType<ButtonHandler>(handler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanSpecifyHandler()
|
||||
{
|
||||
var host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.RegisterHandler<ButtonStub, ButtonHandlerStub>()
|
||||
.Build();
|
||||
|
||||
var defaultHandler = host.Handlers.GetHandler(typeof(IButton));
|
||||
var specificHandler = host.Handlers.GetHandler(typeof(ButtonStub));
|
||||
|
||||
Assert.NotNull(defaultHandler);
|
||||
Assert.NotNull(specificHandler);
|
||||
Assert.IsType<ButtonHandler>(defaultHandler);
|
||||
Assert.IsType<ButtonHandlerStub>(specificHandler);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Maui.Hosting;
|
||||
using Microsoft.Maui.Tests;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Maui.UnitTests
|
||||
{
|
||||
[Category(TestCategory.Core, TestCategory.Hosting)]
|
||||
public class HostBuilderServicesTests
|
||||
{
|
||||
[Fact]
|
||||
public void CanGetServices()
|
||||
{
|
||||
var host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.Build();
|
||||
|
||||
Assert.NotNull(host);
|
||||
Assert.NotNull(host.Services);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetServiceThrowsWhenConstructorParamTypesWereNotRegistered()
|
||||
{
|
||||
var host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.UseMauiServiceProviderFactory(true)
|
||||
.ConfigureServices((ctx, services) => services.AddTransient<IFooBarService, FooBarService>())
|
||||
.Build();
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => host.Services.GetService<IFooBarService>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetServiceThrowsOnMultipleConstructors()
|
||||
{
|
||||
var host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.UseMauiServiceProviderFactory(true)
|
||||
.ConfigureServices((ctx, services) => services.AddTransient<IFooBarService, FooDualConstructor>())
|
||||
.Build();
|
||||
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => host.Services.GetService<IFooBarService>());
|
||||
|
||||
Assert.Contains("IFooService", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetServiceCanReturnTypesThatHaveConstructorParams()
|
||||
{
|
||||
var host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.UseMauiServiceProviderFactory(true)
|
||||
.ConfigureServices((ctx, services) =>
|
||||
{
|
||||
services.AddTransient<IFooService, FooService>();
|
||||
services.AddTransient<IBarService, BarService>();
|
||||
services.AddTransient<IFooBarService, FooBarService>();
|
||||
})
|
||||
.Build();
|
||||
|
||||
var foobar = host.Services.GetService<IFooBarService>();
|
||||
|
||||
Assert.NotNull(foobar);
|
||||
Assert.IsType<FooBarService>(foobar);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetServiceCanReturnTypesThatHaveUnregisteredConstructorParamsButHaveDefaultValues()
|
||||
{
|
||||
var host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.UseMauiServiceProviderFactory(true)
|
||||
.ConfigureServices((ctx, services) =>
|
||||
{
|
||||
services.AddTransient<IFooBarService, FooDefaultValueConstructor>();
|
||||
})
|
||||
.Build();
|
||||
|
||||
var foo = host.Services.GetService<IFooBarService>();
|
||||
|
||||
Assert.NotNull(foo);
|
||||
|
||||
var actual = Assert.IsType<FooDefaultValueConstructor>(foo);
|
||||
|
||||
Assert.Null(actual.Bar);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetServiceCanReturnTypesThatHaveRegisteredConstructorParamsAndHaveDefaultValues()
|
||||
{
|
||||
var host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.UseMauiServiceProviderFactory(true)
|
||||
.ConfigureServices((ctx, services) =>
|
||||
{
|
||||
services.AddTransient<IBarService, BarService>();
|
||||
services.AddTransient<IFooBarService, FooDefaultValueConstructor>();
|
||||
})
|
||||
.Build();
|
||||
|
||||
var foo = host.Services.GetService<IFooBarService>();
|
||||
|
||||
Assert.NotNull(foo);
|
||||
|
||||
var actual = Assert.IsType<FooDefaultValueConstructor>(foo);
|
||||
|
||||
Assert.NotNull(actual.Bar);
|
||||
Assert.IsType<BarService>(actual.Bar);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetServiceCanReturnTypesThatHaveSystemDefaultValues()
|
||||
{
|
||||
var host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.UseMauiServiceProviderFactory(true)
|
||||
.ConfigureServices((ctx, services) =>
|
||||
{
|
||||
services.AddTransient<IFooBarService, FooDefaultSystemValueConstructor>();
|
||||
})
|
||||
.Build();
|
||||
|
||||
var foo = host.Services.GetService<IFooBarService>();
|
||||
|
||||
Assert.NotNull(foo);
|
||||
|
||||
var actual = Assert.IsType<FooDefaultSystemValueConstructor>(foo);
|
||||
|
||||
Assert.Equal("Default Value", actual.Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WillRetrieveDifferentTransientServices()
|
||||
{
|
||||
var host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.ConfigureServices((ctx, services) => services.AddTransient<IFooService, FooService>())
|
||||
.Build();
|
||||
|
||||
AssertTransient<IFooService, FooService>(host.Services);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WillRetrieveSameSingletonServices()
|
||||
{
|
||||
var host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.ConfigureServices((ctx, services) => services.AddSingleton<IFooService, FooService>())
|
||||
.Build();
|
||||
|
||||
AssertSingleton<IFooService, FooService>(host.Services);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WillRetrieveMixedServices()
|
||||
{
|
||||
var host = AppHostBuilder
|
||||
.CreateDefaultAppBuilder()
|
||||
.ConfigureServices((ctx, services) =>
|
||||
{
|
||||
services.AddSingleton<IFooService, FooService>();
|
||||
services.AddTransient<IBarService, BarService>();
|
||||
})
|
||||
.Build();
|
||||
|
||||
AssertSingleton<IFooService, FooService>(host.Services);
|
||||
AssertTransient<IBarService, BarService>(host.Services);
|
||||
}
|
||||
|
||||
static void AssertTransient<TInterface, TConcrete>(IServiceProvider services)
|
||||
{
|
||||
var service1 = services.GetService<TInterface>();
|
||||
|
||||
Assert.NotNull(service1);
|
||||
Assert.IsType<TConcrete>(service1);
|
||||
|
||||
var service2 = services.GetService<TInterface>();
|
||||
|
||||
Assert.NotNull(service2);
|
||||
Assert.IsType<TConcrete>(service2);
|
||||
|
||||
Assert.NotEqual(service1, service2);
|
||||
}
|
||||
|
||||
static void AssertSingleton<TInterface, TConcrete>(IServiceProvider services)
|
||||
{
|
||||
var service1 = services.GetService<TInterface>();
|
||||
|
||||
Assert.NotNull(service1);
|
||||
Assert.IsType<TConcrete>(service1);
|
||||
|
||||
var service2 = services.GetService<TInterface>();
|
||||
|
||||
Assert.NotNull(service2);
|
||||
Assert.IsType<TConcrete>(service2);
|
||||
|
||||
Assert.Equal(service1, service2);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,232 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Maui.Handlers;
|
||||
using Microsoft.Maui.Hosting;
|
||||
using Microsoft.Maui.Tests;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Maui.UnitTests
|
||||
{
|
||||
[Category(TestCategory.Core, TestCategory.Hosting)]
|
||||
public partial class HostBuilderTests : IDisposable
|
||||
{
|
||||
[Fact]
|
||||
public void CanBuildAHost()
|
||||
{
|
||||
var host = App.CreateDefaultBuilder().Build();
|
||||
Assert.NotNull(host);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanGetStaticApp()
|
||||
{
|
||||
var app = new AppStub();
|
||||
app.CreateBuilder().Build(app);
|
||||
|
||||
Assert.NotNull(MauiApp.Current);
|
||||
Assert.Equal(MauiApp.Current, app);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldntCreateMultipleApp()
|
||||
{
|
||||
var app = new AppStub();
|
||||
Assert.Throws<InvalidOperationException>(() => new AppStub());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanGetServices()
|
||||
{
|
||||
var app = new AppStub();
|
||||
app.CreateBuilder().Build(app);
|
||||
|
||||
Assert.NotNull(app.Services);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanGetStaticServices()
|
||||
{
|
||||
var app = new AppStub();
|
||||
app.CreateBuilder().Build(app);
|
||||
|
||||
Assert.NotNull(MauiApp.Current.Services);
|
||||
Assert.Equal(app.Services, MauiApp.Current.Services);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlerContextNullBeforeBuild()
|
||||
{
|
||||
var app = new AppStub();
|
||||
app.CreateBuilder();
|
||||
|
||||
var handlerContext = MauiApp.Current.Context;
|
||||
|
||||
Assert.Null(handlerContext);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlerContextAfterBuild()
|
||||
{
|
||||
var app = new AppStub();
|
||||
app.CreateBuilder().Build(app);
|
||||
|
||||
var handlerContext = MauiApp.Current.Context;
|
||||
|
||||
Assert.NotNull(handlerContext);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanHandlerProviderContext()
|
||||
{
|
||||
var app = new AppStub();
|
||||
app.CreateBuilder().Build(app);
|
||||
|
||||
var handlerContext = MauiApp.Current.Context;
|
||||
|
||||
Assert.IsAssignableFrom<IMauiHandlersServiceProvider>(handlerContext.Handlers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanRegisterAndGetHandler()
|
||||
{
|
||||
var app = new AppStub();
|
||||
app.CreateBuilder()
|
||||
.RegisterHandler<IViewStub, ViewHandlerStub>()
|
||||
.Build(app);
|
||||
|
||||
var handler = MauiApp.Current.Context.Handlers.GetHandler(typeof(IViewStub));
|
||||
Assert.NotNull(handler);
|
||||
Assert.IsType<ViewHandlerStub>(handler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanRegisterAndGetHandlerWithDictionary()
|
||||
{
|
||||
var app = new AppStub();
|
||||
app.CreateBuilder()
|
||||
.RegisterHandlers(new Dictionary<Type, Type>
|
||||
{
|
||||
{ typeof(IViewStub), typeof(ViewHandlerStub) }
|
||||
})
|
||||
.Build(app);
|
||||
|
||||
var handler = MauiApp.Current.Context.Handlers.GetHandler(typeof(IViewStub));
|
||||
Assert.NotNull(handler);
|
||||
Assert.IsType<ViewHandlerStub>(handler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanRegisterAndGetHandlerForType()
|
||||
{
|
||||
var app = new AppStub();
|
||||
app.CreateBuilder()
|
||||
.RegisterHandler<IViewStub, ViewHandlerStub>()
|
||||
.Build(app);
|
||||
|
||||
var handler = MauiApp.Current.Context.Handlers.GetHandler(typeof(ViewStub));
|
||||
Assert.NotNull(handler);
|
||||
Assert.IsType<ViewHandlerStub>(handler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultHandlersAreRegistered()
|
||||
{
|
||||
var app = new AppStub();
|
||||
app.CreateBuilder().Build(app);
|
||||
|
||||
var handler = MauiApp.Current.Context.Handlers.GetHandler(typeof(IButton));
|
||||
Assert.NotNull(handler);
|
||||
Assert.IsType<ButtonHandler>(handler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanSpecifyHandler()
|
||||
{
|
||||
var app = new AppStub();
|
||||
app.CreateBuilder()
|
||||
.RegisterHandler<ButtonStub, ButtonHandlerStub>()
|
||||
.Build(app);
|
||||
|
||||
var defaultHandler = MauiApp.Current.Context.Handlers.GetHandler(typeof(IButton));
|
||||
var specificHandler = MauiApp.Current.Context.Handlers.GetHandler(typeof(ButtonStub));
|
||||
Assert.NotNull(defaultHandler);
|
||||
Assert.NotNull(specificHandler);
|
||||
Assert.IsType<ButtonHandler>(defaultHandler);
|
||||
Assert.IsType<ButtonHandlerStub>(specificHandler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WillRetrieveDifferentTransientServices()
|
||||
{
|
||||
var app = new AppStub();
|
||||
app.CreateBuilder()
|
||||
.ConfigureServices((ctx, services) => services.AddTransient<IFooService, FooService>())
|
||||
.Build(app);
|
||||
|
||||
AssertTransient<IFooService, FooService>(app);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WillRetrieveSameSingletonServices()
|
||||
{
|
||||
var app = new AppStub();
|
||||
app.CreateBuilder()
|
||||
.ConfigureServices((ctx, services) => services.AddSingleton<IFooService, FooService>())
|
||||
.Build(app);
|
||||
|
||||
AssertSingleton<IFooService, FooService>(app);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WillRetrieveMixedServices()
|
||||
{
|
||||
var app = new AppStub();
|
||||
app.CreateBuilder()
|
||||
.ConfigureServices((ctx, services) =>
|
||||
{
|
||||
services.AddSingleton<IFooService, FooService>();
|
||||
services.AddTransient<IBarService, BarService>();
|
||||
})
|
||||
.Build(app);
|
||||
|
||||
AssertSingleton<IFooService, FooService>(app);
|
||||
AssertTransient<IBarService, BarService>(app);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
(App.Current as AppStub)?.ClearApp();
|
||||
}
|
||||
|
||||
static void AssertTransient<TInterface, TConcrete>(AppStub app)
|
||||
{
|
||||
var service1 = app.Services.GetService<TInterface>();
|
||||
|
||||
Assert.NotNull(service1);
|
||||
Assert.IsType<TConcrete>(service1);
|
||||
|
||||
var service2 = app.Services.GetService<TInterface>();
|
||||
|
||||
Assert.NotNull(service2);
|
||||
Assert.IsType<TConcrete>(service2);
|
||||
|
||||
Assert.NotEqual(service1, service2);
|
||||
}
|
||||
|
||||
static void AssertSingleton<TInterface, TConcrete>(AppStub app)
|
||||
{
|
||||
var service1 = app.Services.GetService<TInterface>();
|
||||
|
||||
Assert.NotNull(service1);
|
||||
Assert.IsType<TConcrete>(service1);
|
||||
|
||||
var service2 = app.Services.GetService<TInterface>();
|
||||
|
||||
Assert.NotNull(service2);
|
||||
Assert.IsType<TConcrete>(service2);
|
||||
|
||||
Assert.Equal(service1, service2);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,32 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Maui.Hosting;
|
||||
using Microsoft.Maui.UnitTests.TestClasses;
|
||||
|
||||
namespace Microsoft.Maui.Tests
|
||||
{
|
||||
class AppStub : MauiApp
|
||||
class AppStub : App
|
||||
{
|
||||
public void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IMauiContext>(provider => new HandlersContextStub(provider));
|
||||
services.AddTransient<IButton, ButtonStub>();
|
||||
}
|
||||
|
||||
public override IAppHostBuilder CreateBuilder()
|
||||
{
|
||||
return base.CreateBuilder().ConfigureServices(ConfigureServices);
|
||||
}
|
||||
|
||||
public override IWindow CreateWindow(IActivationState state)
|
||||
{
|
||||
return new WindowStub();
|
||||
}
|
||||
|
||||
internal void ClearApp()
|
||||
{
|
||||
Current = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,10 @@
|
|||
{
|
||||
}
|
||||
|
||||
interface IFooBarService
|
||||
{
|
||||
}
|
||||
|
||||
class FooService : IFooService
|
||||
{
|
||||
}
|
||||
|
@ -15,4 +19,54 @@
|
|||
class BarService : IBarService
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class FooBarService : IFooBarService
|
||||
{
|
||||
public FooBarService(IFooService foo, IBarService bar)
|
||||
{
|
||||
Foo = foo;
|
||||
Bar = bar;
|
||||
}
|
||||
|
||||
public IFooService Foo { get; }
|
||||
|
||||
public IBarService Bar { get; }
|
||||
}
|
||||
|
||||
class FooDualConstructor : IFooBarService
|
||||
{
|
||||
public FooDualConstructor(IFooService foo)
|
||||
{
|
||||
Foo = foo;
|
||||
}
|
||||
|
||||
public FooDualConstructor(IBarService bar)
|
||||
{
|
||||
Bar = bar;
|
||||
}
|
||||
|
||||
public IFooService Foo { get; }
|
||||
|
||||
public IBarService Bar { get; }
|
||||
}
|
||||
|
||||
class FooDefaultValueConstructor : IFooBarService
|
||||
{
|
||||
public FooDefaultValueConstructor(IBarService bar = null)
|
||||
{
|
||||
Bar = bar;
|
||||
}
|
||||
|
||||
public IBarService Bar { get; }
|
||||
}
|
||||
|
||||
class FooDefaultSystemValueConstructor : IFooBarService
|
||||
{
|
||||
public FooDefaultSystemValueConstructor(string text = "Default Value")
|
||||
{
|
||||
Text = text;
|
||||
}
|
||||
|
||||
public string Text { get; }
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ namespace Microsoft.Maui.Tests
|
|||
|
||||
}
|
||||
|
||||
public ViewHandlerStub(PropertyMapper mapper) : base(mapper ?? MockViewMapper)
|
||||
public ViewHandlerStub(PropertyMapper mapper = null) : base(mapper ?? MockViewMapper)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Maui.UnitTests.TestClasses
|
||||
{
|
||||
class WindowStub : IWindow
|
||||
{
|
||||
public IPage Page { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
public IMauiContext MauiContext { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче