question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Adopt possibility to resolve navigation Pages by type

See original GitHub issue

Description

Currently, the framework requires instantiation of a ContentPage object in order to navigate. This is quite obstructive when using constructor injection, especially when using MVU or lightweight patterns relying mostly on logic inside the code-behind. This has been addressed in this discussion and @matt-goldman actually provided a very lightweight and straightforward solution . I suggest considering on adopting it into the framework’s existing Navigation. Blazor fell back to property injection which might not be ideal but at least provided a feasible solution.

Public API Changes

Check the repository’s README for reference.

Intended Use-Case

Currently:

await Navigation.PushModalAsync(new MyPage(/*requires dependencies*/));

Goldman’s solution:

await Navigation.PushModalAsync<MyPage>(); // built-in container resolves dependencies

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:9
  • Comments:9

github_iconTop GitHub Comments

1reaction
marwalschcommented, Dec 7, 2021

This might be occasion to reconsider how Page instantiation through the framework works. Features like default navigations currently instantiate through the parameterless constructor of a page, which worked with the legacy(?) DependencyService but not with constructor injection.

Markup like <FlyoutPageItem TargetType="{x:Type page:SomePage}" /> currently not only makes this impossible, but also is counter-intuitive as the Type should suffice.

0reactions
bakerhillpinscommented, Jun 1, 2022

Yes it does: MauiApp.CreateBuilder().Services.AddSingleton<T>();

Calling CreateInstance explicitly to pass dependencies is actually an Anti-Pattern and forces you to have a Scoped/Transient lifetime.

@marwalsch I agree with your sentiment in general, however, I’d still like to see a way to pass in constructor parameters (dependencies) to the “Create” method. It’s useful for situations where you’re binding to collections of objects/data and you’re using a DataTemplateSelector to convert the collection bound items into Views with companion ViewModels. For example, any implementation of ItemsView.ItemsSource.

E.g. Without getting into specifics on the objects/data in a collection, consider the pattern where you’re using a CollectionView and binding it’s ItemSource property to that collection. When you specify a DataTemplateSelector implementation you can customize the view for each individual element of the collection based upon it’s type. When that Template is returned from DataTemplateSelector.OnSelectTemplate the object/data value that was provided to to OnSelectTemplate is applied to the newly created View’s BindingContext property. And there’s the issue; we want to have a ViewModel wrapped around that object/data value for our View to reference, not the raw value. If we have access to the DI Resolver which supports a specified constructor parameter we can use Binding (with a Converter and ConverterParameter specified) or create a custom IMarkupExetension to support the creation of the ViewModel using THE object/data value from the collection in its constructor.

This pattern allows dynamic generation of ViewModels for object/data values in hierarchical data patterns in bound collections by eliminating lots of extra code work in a top level ViewModel. Which would otherwise be responsible for creating all the ViewModels up front in separate collections, and also managing any/all updates to the VM collections when data in the source collection changes.

Some rudimentary code might help illustrate:

        <StackLayout Margin="10">
            <CollectionView ItemTemplate="{x:Static views:SettingDataTemplateSelector.Instance}"
                            ItemsSource="{Binding Settings}" />
        </StackLayout>

the DataTemplateSelector:

    internal class SettingDataTemplateSelector : DataTemplateSelector
    {
        public static readonly DataTemplateSelector Instance =
            new Lazy<DataTemplateSelector>( () => new SettingDataTemplateSelector() ).Value; 

        private static readonly DataTemplate SwitchTemplate =
            new Lazy<DataTemplate>( () => new DataTemplate( () => new SwitchView() ) ).Value;

        private static readonly DataTemplate EntryTemplate =
            new Lazy<DataTemplate>(() => new DataTemplate(() => new EntryView() ) ).Value;

        protected override DataTemplate OnSelectTemplate( object item, BindableObject container )
        {
            switch ( item  )
            {
                default:
                case SwitchSetting _:
                    return SettingDataTemplateSelector.SwitchTemplate;
                case ValueSetting<int> _:
                case ValueSetting<double> _:
                case ValueSetting<byte> _:
                case StringSetting _:
                    return SettingDataTemplateSelector.EntryTemplate;
            }
        }
    }

and the TemplatedView, where the IValueConverter.Convert method creates the ViewModel viewModels:DataSpecificViewModel by passing the actual value into the DI Resolver to be used in the constructor of the type.

<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Hypertherm.Settings.Views.EntryView">

    <!-- Placing the Binding on BindingContext at the StackPanel level causes the binding to be applied BEFORE the
         data is propagated to the children. Thus the children only ever see the ViewModel in their BindingContext -->
    <StackPanel Padding="10, 0"
          BindingContext="{Binding Mode=OneTime, Converter={x:Static uxConverters:ViewModelActivator.Instance}, ConverterParameter={ x:Type viewModels:DataSpecificViewModel}}">

        <!-- All children see the dynamically created ViewModel -->

    </StackPanel>

</ContentView>

I’ve had lots of success with this pattern using DryIoc with Prism. In fact, in the Prism implementation I can supply 0 - N constructor arguments (dependencies) and DriIoc will resolve the remaining unspecified ones, if I’ve not specified them all. Thus I can have the best of both worlds, I can specify the Object/Data dependency for the ViewModel, and let the Ioc Container look up any services that VM might also require.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Should You Use Navigation on Landing Pages?
This type of navigation makes it easy for visitors to access other sections of the website that are not part of the website...
Read more >
Navigation | Jetpack
It is now possible to extend the NavType class to create custom NavTypes. Custom types are supported only when building your navigation graph ......
Read more >
3 types of Navigation in SharePoint Online
How do you properly links sites in Modern SharePoint? This post explains latest developments on Navigation in SharePoint Online.
Read more >
Navigation
Use the Navigation component in Android Jetpack to implement navigation in your app. ... Drawable · Layout · Menu · String · Style...
Read more >
Bottom Navigation Pattern On Mobile Web Pages: A Better ...
Can we fix the mobile navigation of our websites to have a lower interaction cost? In this article, we'll find out.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found