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));
        }
예제 #3
0
        /// <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);
        }