// Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; using System.Reflection; using System.Linq; 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() { /// /// 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 or sets the instance being initialized. /// public Application Instance { get; protected 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 => { }; /// /// Gets or sets a method to call before is called on the /// . /// public Action BeforeStartCallback { get; private set; } = builder => { }; protected AppBuilderBase(IRuntimePlatform platform, Action platformSevices) { RuntimePlatform = platform; RuntimePlatformServicesInitializer = platformSevices; } /// /// Begin configuring an . /// /// The subclass of to configure. /// An instance. public static TAppBuilder Configure() where TApp : Application, new() { return Configure(new TApp()); } /// /// Begin configuring an . /// /// An instance. public static TAppBuilder Configure(Application app) { AvaloniaLocator.CurrentMutable.BindToSelf(app); return new TAppBuilder() { Instance = app, }; } protected TAppBuilder Self => (TAppBuilder) this; /// /// Registers a callback to call before is called on the /// . /// /// The callback. /// An instance. public TAppBuilder BeforeStarting(Action callback) { BeforeStartCallback = (Action)Delegate.Combine(BeforeStartCallback, callback); return Self; } public TAppBuilder AfterSetup(Action callback) { AfterSetupCallback = (Action)Delegate.Combine(AfterSetupCallback, callback); return Self; } /// /// Starts the application with an instance of . /// /// The window type. public void Start() where TMainWindow : Window, new() { Setup(); BeforeStartCallback(Self); var window = new TMainWindow(); window.Show(); Instance.Run(window); } /// /// Sets up the platform-specific services for the application, but does not run it. /// /// public TAppBuilder SetupWithoutStarting() { 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", new Type[0]); init.Invoke(null, null); }; public TAppBuilder UseAvaloniaModules() => AfterSetup(builder => SetupAvaloniaModules()); private void SetupAvaloniaModules() { var moduleInitializers = from assembly in AvaloniaLocator.Current.GetService().GetLoadedAssemblies() 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(new object[0])); Delegate.Combine(moduleInitializers.ToArray()).DynamicInvoke(); } /// /// Sets up the platform-speciic services for the . /// private void Setup() { if (Instance == null) { throw new InvalidOperationException("No App instance configured."); } 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."); } Instance.RegisterServices(); RuntimePlatformServicesInitializer(); WindowingSubsystemInitializer(); RenderingSubsystemInitializer(); Instance.Initialize(); AfterSetupCallback(Self); } } }