1
votes

My current set up:

Xamarin Forms, consisting of iOS, Android, WP app and shared PCL. Using MVVM Light to keep a nice separation of concerns.

Brief intro into what I want to achieve. I want to have a Base page that has a Cancel and Next button. On pressing the Next button Content is loaded dynamically within that base page.

Xaml View:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="LogInPresenterView">
  <ContentPage.ToolbarItems>
    <ToolbarItem Text="Cancel" Priority="0" Order="Primary" Command="{Binding Cancel}"></ToolbarItem>
    <ToolbarItem Text="Next" Priority="1" Order="Primary" Command="{Binding Next}"></ToolbarItem>
  </ContentPage.ToolbarItems>
</ContentPage>

ViewModel Code:

public class LogInPresenterViewModel : ViewModelBase
    {

        public LogInPresenterViewModel() {}

        private RelayCommand _next;
        public RelayCommand Next
        {
            get
            {
                return _next ?? (_next = new RelayCommand(async () => await DoNext()));
            }
        }

        private async Task DoNext()
        {
           // IN HERE I WOULD LIKE TO DYNCAMICALLY LOAD CONTENT / VIEWS
        }

    }

Usually you would have a StackLayout etc before the element. However, on clicking the Next Toolbar Item I want to dynamically load content (that has a viewmodel).

So maybe my ICommand for my next button checked to see what the current content type was, and depending on that I would load another bit of content.

The scenario would be, the base page would load along with the first bit of content - Enter Email and Password. User enters that then clicks on next, if all ok, the content is replaced with the option to enter a security code, keeping the base Close and Next buttons at the top.

Hopefully this makes sense. I know what I want to do in my head, I just don't know how to translate that into Xamarin Forms...

3

3 Answers

1
votes

Ok,

So first job is to create your region service in your PCL. This will look something like this:

using System;
using System.Collections.Generic;

namespace xxx
{
    public class RegionService : IRegionService
    {
        private Dictionary<string, object> _regionDictionary;

        public RegionService ()
        {
            _regionDictionary = new Dictionary<string, object> ();
        }

        #region IRegionService implementation

        public bool RegisterRegion (string regionName, object regionObject)
        {
            object region = null;

            _regionDictionary.TryGetValue (regionName, out region);
            if (region != null)
                _regionDictionary [regionName] = regionObject;
            else
                _regionDictionary.Add (regionName, regionObject);
            return true;
        }

        public object ResolveRegion (string regionName)
        {
            object region = null;

            _regionDictionary.TryGetValue (regionName, out region);

            if (region == null)
                throw new RegionServiceException ("Unable to resolve region with given name");

            return region;
        }

        #endregion
    }
}

This when you create your page with the dynamic content register your dynamic contentview in your code behind:

ContentView contentView = this.FindById<ContentView>("myContentView");
regionService.RegisterRegion("DynamicView", contentView);

You'll need to create an interface for your views and pages to use to indicate which region they wish to be presented in:

using System;

namespace xxx
{
    public interface IRegionView
    {
        string GetRegionName ();
    }
}

Then in your code behind for your view implement this interface to return the name of the region to display in.

You now need a custom presenter to use this region code. I use MVVMCross, so the details will vary for the MVVM implementation you are using, but essentially something like this is what you need:

public async static Task PresentPage(Page page)
{

    if (typeof(IRegionView).GetTypeInfo().IsAssignableFrom(page.GetType().GetTypeInfo()))
    {

        IRegionService regionService = Mvx.Resolve<IRegionService>();
        string regionName = (page as IRegionView).GetRegionName();
        Page region = regionService.ResolveRegion(regionName) as Page;

        if (typeof(IModalPage).GetTypeInfo().IsAssignableFrom(page.GetType().GetTypeInfo()))
            await region.Navigation.PushModalAsync(page);

        else if (typeof(IPopupPage).GetTypeInfo().IsAssignableFrom(page.GetType().GetTypeInfo()))
            region.PushOverlayPage(page);

        else if (typeof(NavigationPage).GetTypeInfo().IsAssignableFrom(region.GetType().GetTypeInfo()))
            await (region as NavigationPage).PushAsync(page);

    }
}

I hope this is useful for you :)

0
votes

So if this was me. I would create a region service where the contentview registers a unique region name.

Content would then be marked to use that region, and a custom presenter can be used to show the view model's content in the appropriate region.

I'm on my phone whilst travelling at the moment but I can post some code later on if that helps :)

Tristan

0
votes

You can dynamically load Xamarin Forms UI with XAML.

Old Answer: This can be achieved with the use of the LoadFromXaml method. It works in the same was as XamlReader.Load in Silverlight/WPF. It is a hidden method that can be only accessed through reflection. There is an article on how to do it here: http://www.cazzulino.com/dynamic-forms.html

But, I would like to ask to you go to this feature request at Xamarin and ask that the method be made public so that it becomes a fully supported feature: https://forums.xamarin.com/discussion/comment/252626