EduXavier
EduXavier

Reputation: 515

Xamarin forms 4 shell navigation with complex parameters

I´m migratting a xamarin forms 3.x app with Prism to forms 4 with shell navigation.

Do I have to create my custom solution to pass complex parameters to the new page or Xamarin has some buildin feature to receive other than string parameters?

Thanks.

Upvotes: 3

Views: 7989

Answers (5)

Phuluso Ramulifho
Phuluso Ramulifho

Reputation: 498

You can user stored preferences to store complex data like:

private async void OnItemSelected(Item item)
{
    if (item == null)
        return;

    var jsonstr = JsonConvert.SerializeObject(item);

    //Clear the shared preferences in case there is any
    Preferences.Clear();

    //Store your complex json on a shared preference
    Preferences.Set("Data", jsonstr);
    await Shell.Current.GoToAsync(nameof(DetailsPage));
   
   
}

Retrieve it on the details page like:

        bool hasKey = Preferences.ContainsKey("Data");
        var content = Preferences.Get("Data", string.Empty);

        Details details = hasKey ? JsonConvert.DeserializeObject<Model>(content) : null;

Upvotes: 0

Chris A
Chris A

Reputation: 164

You can always serialize the model to a JSON string and un-serializes it on the other side?

    async void Handle_ItemTapped(object sender, ItemTappedEventArgs e)
    {
        if (e.Item == null)
            return;

        DailyPnL PnLClicked = (DailyPnL)e.Item;
        string jason = await Task.Run(() => JsonConvert.SerializeObject(PnLClicked));

        await Shell.Current.GoToAsync($"viewdailypnl?pnlmodel={jason}");

        //Deselect Item
        ((ListView)sender).SelectedItem = null;
    }

Then in your code behind:

    public string pnlmodel
    {
        set
        {
            string derulo = Uri.UnescapeDataString(value);
            viewModel.PnL =  Task.Run(() => JsonConvert.DeserializeObject<DailyPnL>(derulo)).Result;

        }
    }

Upvotes: 5

Mike Humphreys
Mike Humphreys

Reputation: 111

I've run some tests which seems to be working. I'm relatively new to Xamarin, hence recommend caution and welcome any feedback for any potential issues I may be overlooking.

I wrote an extension to Shell to accept a data object parameter 'navigationData' as follows:-

await Shell.Current.GoToAsync(state, navigationData, animate);

The extension ...

namespace Xamarin.Forms
{
    public static class ShellExtensions
    {
        public static async Task GoToAsync(this Shell shell, ShellNavigationState state, object navigationData, bool animate=false)
        {
            shell.Navigated += async (sender, e) =>
            {
                if ((Shell.Current?.CurrentItem?.CurrentItem as IShellSectionController)?.PresentedPage is MyContentPage
                    p) await p.InitializeAsync(navigationData).ConfigureAwait(false);
            };
            await shell.GoToAsync(state, animate);
        }
    }
}

As shown above the extension:-

  1. hooks to the Shell 'Navigated' event,
  2. retrieves the 'current view (page)' as 'MyContentPage' i.e. subclassed ContentPage,
  3. calls an InitializeAsync method on the view passing in the navigationData parameter
  4. the view then calls an InitializeAsync method on the binding context (view model) passing the navigationData parameter onto the viewModel.

In the extension method above, 'MyContentPage' is a custom abstract subclass of ContentPage with an InitializeAsync(navigationData) method that simply calls a similar method on the viewModel (binding context of the view). Similarily, ViewModels subclass a custom ViewModelBase class that has a virtual InitializeAsync(navigationData). This can be overridden in the viewModel with the desired implementation and handling of the navigation data.

Simplified sample of Views, ViewModels and related base classes shown below

using System.Threading.Tasks;
using MyXamarinApp.ViewModels;
using Xamarin.Forms;

namespace MyXamarinApp.Views
{
    public ItemDetailPage : MyContent<ItemDetailViewModel>{}

    public ItemPage : MyContentPage<ItemViewModel>{}

    public abstract class MyContentPage<T> : MyContentPage where T : ViewModelBase
    {
        protected T Vm;

        protected override ViewModelBase VmBase => Vm as ViewModelBase;

        protected MyContentPage()
        {
            BindingContext = Vm = ViewModelLocator.Resolve<T>();
        }

        private Comand _showDetailCommand;
        public Command ShowDetailCommand
        {
            get { return _showDetailCommand ??= new Command(async () => 
                await Shell.Current.GoToAsync("itemDetail", new NavigationDataObject())); }
        }
    }


    public abstract class MyContentPage : ContentPage
    {
        protected abstract ViewModelBase VmBase { get; }

        public virtual async Task InitializeAsync(object navigationData)
        {
            await VmBase.InitializeAsync(navigationData);
        }
    }
}

public class NavigationDataObject
{
    'Properties' etc.
}

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace MyXamarinApp.ViewModels
{
    public ItemViewModel : ViewModelBase{}

    public ItemDetailViewModel : ViewModelBase
    {
        private NavigationDataObject _navData;
        public override async Task InitializeAsync(object navigationData)
        {
            if (navigationData is NavigationDataObject navData)
            {
                _navData = navData;
            }
            await base.InitializeAsync(navigationData);
        }
    }

    public abstract class ViewModelBase
    {

        public virtual Task InitializeAsync(object navigationData)
        {
            return Task.FromResult(false);
        }
    }
}

Upvotes: 6

prasadsunny1
prasadsunny1

Reputation: 830

There is a framework called Xamarin.Zero https://github.com/markjackmilian/Xam.Zero It lets you use shell while giving you convenient ViewModel to ViewModel navigation, IOC.

Upvotes: 1

Bruno Caceiro
Bruno Caceiro

Reputation: 7179

As far as I know, and, reading the docs, the only samples regard passing simple data, like string when navigating.

However, I was able to find an Issue (and Pull Request), for passing objects/ Models, for the next version (I assume this is the case you are referring to).

You can track it here.

Upvotes: 5

Related Questions