using System; using System.Collections.Generic; using System.Reflection; using System.Linq; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Platform; namespace Avalonia.Controls { /// /// Base class for initializing platform-specific services for an . /// /// The type of the AppBuilder class itself. public abstract class AppBuilderBase where TAppBuilder : AppBuilderBase, new() { private static bool s_setupWasAlreadyCalled; private Action _optionsInitializers; private Func _appFactory; private IApplicationLifetime _lifetime; /// /// Gets or sets the instance. /// public IRuntimePlatform RuntimePlatform { get; set; } /// /// Gets or sets a method to call the initialize the runtime platform services (e. g. AssetLoader) /// public Action RuntimePlatformServicesInitializer { get; private set; } /// /// Gets the instance being initialized. /// public Application Instance { get; private set; } /// /// Gets the type of the Instance (even if it's not created yet) /// public Type ApplicationType { get; private set; } /// /// Gets or sets a method to call the initialize the windowing subsystem. /// public Action WindowingSubsystemInitializer { get; private set; } /// /// Gets the name of the currently selected windowing subsystem. /// public string WindowingSubsystemName { get; private set; } /// /// Gets or sets a method to call the initialize the windowing subsystem. /// public Action RenderingSubsystemInitializer { get; private set; } /// /// Gets the name of the currently selected rendering subsystem. /// public string RenderingSubsystemName { get; private set; } /// /// Gets or sets a method to call after the is setup. /// public Action AfterSetupCallback { get; private set; } = builder => { }; public Action AfterPlatformServicesSetupCallback { get; private set; } = builder => { }; protected AppBuilderBase(IRuntimePlatform platform, Action platformServices) { RuntimePlatform = platform; RuntimePlatformServicesInitializer = () => platformServices((TAppBuilder)this); } /// /// Begin configuring an . /// /// The subclass of to configure. /// An instance. public static TAppBuilder Configure() where TApp : Application, new() { return new TAppBuilder() { ApplicationType = typeof(TApp), // Needed for CoreRT compatibility _appFactory = () => new TApp() }; } /// /// Begin configuring an . /// /// Factory function for . /// The subclass of to configure. /// is useful for passing of dependencies to . /// An instance. public static TAppBuilder Configure(Func appFactory) where TApp : Application { return new TAppBuilder() { ApplicationType = typeof(TApp), _appFactory = appFactory }; } protected TAppBuilder Self => (TAppBuilder)this; public TAppBuilder AfterSetup(Action callback) { AfterSetupCallback = (Action)Delegate.Combine(AfterSetupCallback, callback); return Self; } public TAppBuilder AfterPlatformServicesSetup(Action callback) { AfterPlatformServicesSetupCallback = (Action)Delegate.Combine(AfterPlatformServicesSetupCallback, callback); return Self; } /// /// Starts the application with an instance of . /// /// The window type. /// A delegate that will be called to create a data context for the window (optional). [Obsolete("Use either lifetimes or AppMain overload. See see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details")] public void Start(Func dataContextProvider = null) where TMainWindow : Window, new() { AfterSetup(builder => { var window = new TMainWindow(); if (dataContextProvider != null) window.DataContext = dataContextProvider(); ((IClassicDesktopStyleApplicationLifetime)builder.Instance.ApplicationLifetime) .MainWindow = window; }); // Copy-pasted because we can't call extension methods due to generic constraints var lifetime = new ClassicDesktopStyleApplicationLifetime() {ShutdownMode = ShutdownMode.OnMainWindowClose}; SetupWithLifetime(lifetime); lifetime.Start(Array.Empty()); } public delegate void AppMainDelegate(Application app, string[] args); [Obsolete("Use either lifetimes or AppMain overload. See see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details", true)] public void Start() { throw new NotSupportedException(); } public void Start(AppMainDelegate main, string[] args) { Setup(); main(Instance, args); } /// /// Sets up the platform-specific services for the application, but does not run it. /// /// public TAppBuilder SetupWithoutStarting() { Setup(); return Self; } /// /// Sets up the platform-specific services for the application and initialized it with a particular lifetime, but does not run it. /// /// /// public TAppBuilder SetupWithLifetime(IApplicationLifetime lifetime) { _lifetime = lifetime; Setup(); return Self; } /// /// Specifies a windowing subsystem to use. /// /// The method to call to initialize the windowing subsystem. /// The name of the windowing subsystem. /// An instance. public TAppBuilder UseWindowingSubsystem(Action initializer, string name = "") { WindowingSubsystemInitializer = initializer; WindowingSubsystemName = name; return Self; } /// /// Specifies a windowing subsystem to use. /// /// The dll in which to look for subsystem. /// An instance. public TAppBuilder UseWindowingSubsystem(string dll) => UseWindowingSubsystem(GetInitializer(dll), dll.Replace("Avalonia.", string.Empty)); /// /// Specifies a rendering subsystem to use. /// /// The method to call to initialize the rendering subsystem. /// The name of the rendering subsystem. /// An instance. public TAppBuilder UseRenderingSubsystem(Action initializer, string name = "") { RenderingSubsystemInitializer = initializer; RenderingSubsystemName = name; return Self; } /// /// Specifies a rendering subsystem to use. /// /// The dll in which to look for subsystem. /// An instance. public TAppBuilder UseRenderingSubsystem(string dll) => UseRenderingSubsystem(GetInitializer(dll)); static Action GetInitializer(string assemblyName) => () => { var assembly = Assembly.Load(new AssemblyName(assemblyName)); var platformClassName = assemblyName.Replace("Avalonia.", string.Empty) + "Platform"; var platformClassFullName = assemblyName + "." + platformClassName; var platformClass = assembly.GetType(platformClassFullName); var init = platformClass.GetRuntimeMethod("Initialize", Type.EmptyTypes); init.Invoke(null, null); }; public TAppBuilder UseAvaloniaModules() => AfterSetup(builder => SetupAvaloniaModules()); protected virtual bool CheckSetup => true; private void SetupAvaloniaModules() { var moduleInitializers = from assembly in AppDomain.CurrentDomain.GetAssemblies() from attribute in assembly.GetCustomAttributes() where attribute.ForWindowingSubsystem == "" || attribute.ForWindowingSubsystem == WindowingSubsystemName where attribute.ForRenderingSubsystem == "" || attribute.ForRenderingSubsystem == RenderingSubsystemName group attribute by attribute.Name into exports select (from export in exports orderby export.ForWindowingSubsystem.Length descending orderby export.ForRenderingSubsystem.Length descending select export).First().ModuleType into moduleType select (from constructor in moduleType.GetTypeInfo().DeclaredConstructors where constructor.GetParameters().Length == 0 && !constructor.IsStatic select constructor).Single() into constructor select (Action)(() => constructor.Invoke(Array.Empty())); Delegate.Combine(moduleInitializers.ToArray()).DynamicInvoke(); } /// /// Configures platform-specific options /// public TAppBuilder With(T options) { _optionsInitializers += () => { AvaloniaLocator.CurrentMutable.Bind().ToConstant(options); }; return Self; } /// /// Configures platform-specific options /// public TAppBuilder With(Func options) { _optionsInitializers += () => { AvaloniaLocator.CurrentMutable.Bind().ToFunc(options); }; return Self; } /// /// Sets up the platform-specific services for the . /// private void Setup() { if (RuntimePlatformServicesInitializer == null) { throw new InvalidOperationException("No runtime platform services configured."); } if (WindowingSubsystemInitializer == null) { throw new InvalidOperationException("No windowing system configured."); } if (RenderingSubsystemInitializer == null) { throw new InvalidOperationException("No rendering system configured."); } if (s_setupWasAlreadyCalled && CheckSetup) { throw new InvalidOperationException("Setup was already called on one of AppBuilder instances"); } s_setupWasAlreadyCalled = true; _optionsInitializers?.Invoke(); RuntimePlatformServicesInitializer(); WindowingSubsystemInitializer(); RenderingSubsystemInitializer(); AfterPlatformServicesSetupCallback(Self); Instance = _appFactory(); Instance.ApplicationLifetime = _lifetime; AvaloniaLocator.CurrentMutable.BindToSelf(Instance); Instance.RegisterServices(); Instance.Initialize(); AfterSetupCallback(Self); Instance.OnFrameworkInitializationCompleted(); } } }