Reputation: 999
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...
Upvotes: 1
Views: 6710
Reputation: 7682
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
Upvotes: 0
Reputation: 556
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 :)
Upvotes: 1
Reputation: 556
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
Upvotes: 0