internal async Task <XF.Page> BuildPage(Type componentType) { var container = new RootContainerHandler(); var route = NavigationParameters[componentType]; #pragma warning disable CA2000 // Dispose objects before losing scope. Renderer is disposed when page is closed. var renderer = new MobileBlazorBindingsRenderer(_services, _services.GetRequiredService <ILoggerFactory>()); #pragma warning restore CA2000 // Dispose objects before losing scope var addComponentTask = renderer.AddComponent(componentType, container, route.Parameters); var elementAddedTask = container.WaitForElementAsync(); await Task.WhenAny(addComponentTask, elementAddedTask).ConfigureAwait(false); if (container.Elements.Count != 1) { throw new InvalidOperationException("The target component of a Shell navigation must have exactly one root element."); } var page = container.Elements.FirstOrDefault() as XF.Page ?? throw new InvalidOperationException("The target component of a Shell navigation must derive from the Page component."); DisposeRendererWhenPageIsClosed(renderer, page); return(page); }
//Slight tweak on the above method to make it async and allow use with a serviceProvider instead of a host, and with a type argument instead of generic argument so it can be called at run time //The above methos only needs host for it's services so it could be refactored. //The above method only needs <t> so it can be used as a type in an overload of the method it calls. //This version also allows an optional set of parameters //The only downside is you can't have design/compiletime type safety //There's a lot of duplicate code between the two, can probably refactor the core of the method into a separate method that they both call public static async Task <IComponent> AddComponent(this IServiceProvider services, XF.Element parent, Type type, System.Collections.Generic.Dictionary <string, object> parameters = null) { if (services is null) { throw new ArgumentNullException(nameof(services)); } if (parent is null) { throw new ArgumentNullException(nameof(parent)); } if (type is null) { throw new ArgumentNullException(nameof(type)); } if (!typeof(IComponent).IsAssignableFrom(type)) { throw new InvalidOperationException($"Cannot add {type.Name} to {parent.GetType().Name}. {type.Name} is not an IComponent. If you are trying to add a Xamarin.Forms type, try adding the Mobile Blazor Bindings equivalent instead."); } #pragma warning disable CA2000 // Dispose objects before losing scope var renderer = new MobileBlazorBindingsRenderer(services, services.GetRequiredService <ILoggerFactory>()); #pragma warning restore CA2000 // Dispose objects before losing scope return(await renderer.AddComponent(type, CreateHandler(parent, renderer), parameters).ConfigureAwait(false)); }
/// <summary> /// Creates a component of type <typeparamref name="TComponent"/> and adds it as a child of <paramref name="parent"/>. /// </summary> /// <typeparam name="TComponent"></typeparam> /// <param name="host"></param> /// <param name="parent"></param> public static void AddComponent <TComponent>(this IHost host, XF.Element parent) where TComponent : IComponent { if (host is null) { throw new ArgumentNullException(nameof(host)); } if (parent is null) { throw new ArgumentNullException(nameof(parent)); } var services = host.Services; var renderer = new MobileBlazorBindingsRenderer(services, services.GetRequiredService <ILoggerFactory>()); // TODO: This call is an async call, but is called as "fire-and-forget," which is not ideal. // We need to figure out how to get Xamarin.Forms to run this startup code asynchronously, which // is how this method should be called. renderer.AddComponent <TComponent>(new ElementHandler(renderer, parent)).ConfigureAwait(false); }