I\'m having a serious headache with this problem. I really dislike store apps but am forced to use it in this case. I\'ve only worked with XAML for a few weeks.
My q
This simply bothers me that no one has solved this at the architectural level. So this is the code for complete decoupling the views, viewmodels and the mapping between them with using the built-in Frame based navigation. The implementation uses Autofact as DI container, but can be easily ported to other IoC solutions.
Core VM logic (these should be in the same assembly):
// I would not get into how the ViewModel or property change notification is implemented
public abstract class PageViewModel : ViewModel
{
protected internal INavigationService Navigation { get; internal set; }
internal void NavigationCompleted()
{
OnNavigationCompleted();
}
protected virtual void OnNavigationCompleted()
{
}
}
public interface INavigationService
{
void Navigate() where TModel : PageViewModel;
}
public abstract class NavigationServiceBase : INavigationService
{
public abstract void Navigate() where TModel : PageViewModel;
protected void CompleteNavigation(PageViewModel model)
{
model.Navigation = this;
model.NavigationCompleted();
}
}
This code should be in a UWP class library or executable:
public interface INavigationMap
where TModel: PageViewModel
{
Type ViewType { get; }
}
internal class NavigationMap : INavigationMap
where TModel: PageViewModel
where TView: Page
{
public Type ViewType => typeof(TView);
}
public class NavigationService : NavigationServiceBase
{
private readonly Frame NavigationFrame;
private readonly ILifetimeScope Resolver;
public NavigationService(ILifetimeScope scope)
{
Resolver = scope;
NavigationFrame = Window.Current.Content as Frame;
NavigationFrame.Navigated += NavigationFrame_Navigated;
}
private void NavigationFrame_Navigated(object sender, Windows.UI.Xaml.Navigation.NavigationEventArgs e)
{
if(e.Content is FrameworkElement element)
{
element.DataContext = e.Parameter;
if(e.Parameter is PageViewModel page)
{
CompleteNavigation(page);
}
}
}
public override void Navigate()
{
var model = Resolver.Resolve();
var map = Resolver.Resolve>();
NavigationFrame.Navigate(map.ViewType, model);
}
}
The rest is just convenience code for registering in the DI and usage examples:
public static class NavigationMap
{
public static void RegisterNavigation(this ContainerBuilder builder)
where TModel : PageViewModel
where TView : Page
{
builder.RegisterInstance(new NavigationMap())
.As>()
.SingleInstance();
}
}
builder.RegisterNavigation();
public class UserAuthenticationModel : PageViewModel
{
protected override void OnNavigationCompleted()
{
// UI is visible and ready
// navigate to somewhere else
Navigation.Navigate();
}
}