Skip to content

Commit

Permalink
Merge pull request #2354 from PrismLibrary/di-navservice-fix
Browse files Browse the repository at this point in the history
Fixing DI NavigationService resolution
  • Loading branch information
dansiegel authored Feb 25, 2021
2 parents 9ab5752 + b9e600c commit 7b8b6c3
Show file tree
Hide file tree
Showing 15 changed files with 335 additions and 15 deletions.
5 changes: 5 additions & 0 deletions e2e/Forms/src/HelloWorld.Android/HelloWorld.Android.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,9 @@
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>
<UserProperties TriggeredFromHotReload="False" />
</VisualStudio>
</ProjectExtensions>
</Project>
2 changes: 1 addition & 1 deletion e2e/Forms/src/HelloWorld/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ protected override void OnInitialized()
{
InitializeComponent();

NavigationService.NavigateAsync($"MyMasterDetail/MyTabbedPage").OnNavigationError(OnNavigationError);
NavigationService.NavigateAsync($"MyTabbedPage").OnNavigationError(OnNavigationError);
}

private void OnNavigationError(Exception ex)
Expand Down
3 changes: 2 additions & 1 deletion e2e/Forms/src/ModuleA/ViewModels/ViewAViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ private void Save()
async void Navigate()
{
CanNavigate = false;
await _navigationService.SelectTabAsync("ViewC");
//await _navigationService.SelectTabAsync("ViewC");
await _navigationService.NavigateAsync("ViewC");
CanNavigate = true;
}

Expand Down
29 changes: 20 additions & 9 deletions src/Forms/Prism.Forms/Navigation/Xaml/Navigation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,7 @@ public static INavigationService GetNavigationService(Page page)
if (!currentScope.IsAttached)
page.SetValue(NavigationScopeProperty, currentScope);

currentScope.IsAttached = true;

navService = currentScope.Resolve<INavigationService>();
if (navService is IPageAware pa)
{
pa.Page = page;
}

page.SetValue(NavigationServiceProperty, navService);
navService = CreateNavigationService(currentScope, page);
}
else if(navService is IPageAware pa && pa.Page != page)
{
Expand All @@ -116,6 +108,25 @@ public static INavigationService GetNavigationService(Page page)
return navService;
}

private static INavigationService CreateNavigationService(IScopedProvider scope, Page page)
{
var navService = scope.Resolve<INavigationService>();
switch(navService)
{
case IPageAware pa when pa.Page is null:
pa.Page = page;
break;
case IPageAware pa1 when pa1.Page != page:
return CreateNavigationService(ContainerLocator.Container.CreateScope(), page);
}

page.SetValue(NavigationScopeProperty, scope);
scope.IsAttached = true;
page.SetValue(NavigationServiceProperty, navService);

return navService;
}

internal static Action GetRaiseCanExecuteChangedInternal(BindableObject view) => (Action)view.GetValue(RaiseCanExecuteChangedInternalProperty);

internal static void SetRaiseCanExecuteChangedInternal(BindableObject view, Action value) => view.SetValue(RaiseCanExecuteChangedInternalProperty, value);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
using Prism.Common;
using Prism.DI.Forms.Tests.Mocks.ViewModels;
using Prism.DI.Forms.Tests.Mocks.Views;
using Xamarin.Forms;
using Xunit;
using Xunit.Abstractions;

namespace Prism.DI.Forms.Tests.Fixtures.Navigation
{
public class NavigationServiceFixture : FixtureBase
{
public NavigationServiceFixture(ITestOutputHelper testOutputHelper)
: base(testOutputHelper)
{
}

[Fact]
public void ContentPage_GetsCorrectNavService()
{
var app = CreateMockApplication();
app.NavigationService.NavigateAsync("XamlViewMockA");

var mainPage = app.MainPage;
var vm = mainPage.BindingContext as XamlViewMockAViewModel;

Assert.IsType<XamlViewMockA>(mainPage);
Assert.NotNull(vm);

var page = ((IPageAware)vm.NavigationService).Page;
Assert.IsType<XamlViewMockA>(mainPage);
Assert.Same(mainPage, page);
}

[Fact]
public void ContentPage_InNavigationPage_GetsCorrectNavService()
{
var app = CreateMockApplication();
app.NavigationService.NavigateAsync("NavigationPage/XamlViewMockA");

var mainPage = app.MainPage;
Assert.IsType<NavigationPage>(mainPage);

var view = mainPage.Navigation.NavigationStack[0] as XamlViewMockA;
Assert.NotNull(view);

var vm = view.BindingContext as XamlViewMockAViewModel;
Assert.NotNull(vm);

var page = ((IPageAware)vm.NavigationService).Page;
Assert.IsType<XamlViewMockA>(page);
Assert.Same(view, page);
}

[Fact]
public void TabbedPage_GetsCorrectNavService()
{
var app = CreateMockApplication();
app.NavigationService.NavigateAsync("XamlTabbedViewMock");

var tp = app.MainPage as TabbedPage;
Assert.NotNull(tp);

var tpVm = tp.BindingContext as XamlTabbedViewMockViewModel;
Assert.NotNull(tpVm);

var page = ((IPageAware)tpVm.NavigationService).Page;
Assert.IsType<XamlTabbedViewMock>(page);
Assert.Same(tp, page);
}

[Fact]
public void TabbedPage_ContentPage_NestedNavigationPage_GetsCorrectNavService()
{
var app = CreateMockApplication();
app.NavigationService.NavigateAsync("XamlTabbedViewMock");

var tp = app.MainPage as TabbedPage;
Assert.NotNull(tp);

var tab = tp.Children[0];
Assert.NotNull(tab);

var navPage = tab as NavigationPage;
Assert.NotNull(navPage);

var view = navPage.CurrentPage;
Assert.NotNull(view);

var vm = view.BindingContext as XamlViewMockAViewModel;
Assert.NotNull(vm);

var page = ((IPageAware)vm.NavigationService).Page;
Assert.IsType<XamlViewMockA>(page);
Assert.Same(view, page);
}

[Fact]
public void TabbedPage_ContentPage_GetsCorrectNavService()
{
var app = CreateMockApplication();
app.NavigationService.NavigateAsync("XamlTabbedViewMock");

var tp = app.MainPage as TabbedPage;
Assert.NotNull(tp);

var view = tp.Children[1];
Assert.NotNull(view);

var vm = view.BindingContext as XamlViewMockAViewModel;
Assert.NotNull(vm);

var page = ((IPageAware)vm.NavigationService).Page;
Assert.IsType<XamlViewMockA>(page);
Assert.Same(view, page);
}

[Fact]
public void TabbedPage_InNavigationPage_GetsCorrectNavService()
{
var app = CreateMockApplication();
app.NavigationService.NavigateAsync("NavigationPage/XamlTabbedViewMock");

var mainPage = app.MainPage;
Assert.IsType<NavigationPage>(mainPage);

var tp = mainPage.Navigation.NavigationStack[0] as TabbedPage;
Assert.NotNull(tp);

var tpVm = tp.BindingContext as XamlTabbedViewMockViewModel;
Assert.NotNull(tpVm);

var page = ((IPageAware)tpVm.NavigationService).Page;
Assert.IsType<XamlTabbedViewMock>(page);
Assert.Same(tp, page);
}

[Fact]
public void MasterDetail_GetsCorrectNavService()
{
var app = CreateMockApplication();
app.NavigationService.NavigateAsync("XamlMasterDetailViewMock");

var mainPage = app.MainPage;
Assert.IsType<XamlMasterDetailViewMock>(mainPage);

var vm = mainPage.BindingContext as XamlMasterDetailViewMockViewModel;
Assert.NotNull(vm);

var page = ((IPageAware)vm.NavigationService).Page;
Assert.IsType<XamlMasterDetailViewMock>(page);
Assert.Same(mainPage, page);
}

[Fact]
public void MasterDetail_Detail_GetsCorrectNavService()
{
var app = CreateMockApplication();
app.NavigationService.NavigateAsync("XamlMasterDetailViewMock");

var mainPage = app.MainPage as XamlMasterDetailViewMock;
Assert.NotNull(mainPage);

var detail = mainPage.Detail as NavigationPage;
Assert.NotNull(detail);

var view = detail.CurrentPage as XamlViewMockA;
Assert.NotNull(view);

var vm = view.BindingContext as XamlViewMockAViewModel;
Assert.NotNull(vm);

var page = ((IPageAware)vm.NavigationService).Page;
Assert.IsType<XamlViewMockA>(page);
Assert.Same(view, page);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ protected override void RegisterTypes(IContainerRegistry containerRegistry)
containerRegistry.RegisterForNavigation<XamlViewMock>();
containerRegistry.RegisterForNavigation<XamlViewMockB, XamlViewMockBViewModel>();
containerRegistry.RegisterForNavigation<XamlViewMockA, XamlViewMockAViewModel>();
containerRegistry.RegisterForNavigation<XamlTabbedViewMock, XamlTabbedViewMockViewModel>();
containerRegistry.RegisterForNavigation<XamlMasterDetailViewMock, XamlMasterDetailViewMockViewModel>();

ViewModelLocationProvider.Register<PartialView, PartialViewModel>();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Prism.Navigation;

namespace Prism.DI.Forms.Tests.Mocks.ViewModels
{
public class XamlMasterDetailViewMockViewModel
{
public INavigationService NavigationService { get; }

public XamlMasterDetailViewMockViewModel(INavigationService navigationService)
{
NavigationService = navigationService;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Prism.Mvvm;
using Prism.Navigation;

namespace Prism.DI.Forms.Tests.Mocks.ViewModels
{
public class XamlTabbedViewMockViewModel : BindableBase
{
public INavigationService NavigationService { get; }

public XamlTabbedViewMockViewModel(INavigationService navigationService)
{
NavigationService = navigationService;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ public string Test
set => SetProperty(ref _test, value);
}

public INavigationService NavigationService { get; set; }

public XamlViewMockAViewModel(INavigationService navigationService)
{
NavigationService = navigationService;
}

public void OnNavigatedFrom(INavigationParameters parameters)
{
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="http://prismlibrary.com"
xmlns:local="clr-namespace:Prism.DI.Forms.Tests.Mocks.Views"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="Prism.DI.Forms.Tests.Mocks.Views.XamlMasterDetailViewMock">

<MasterDetailPage.Master>
<ContentPage Title="Master">

</ContentPage>
</MasterDetailPage.Master>

<MasterDetailPage.Detail>
<NavigationPage>
<x:Arguments>
<local:XamlViewMockA Title="View A" />
</x:Arguments>
</NavigationPage>
</MasterDetailPage.Detail>

</MasterDetailPage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Xamarin.Forms;

namespace Prism.DI.Forms.Tests.Mocks.Views
{
public partial class XamlMasterDetailViewMock : MasterDetailPage
{
public XamlMasterDetailViewMock()
{
InitializeComponent();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="http://prismlibrary.com"
xmlns:local="clr-namespace:Prism.DI.Forms.Tests.Mocks.Views"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="Prism.DI.Forms.Tests.Mocks.Views.XamlTabbedViewMock">

<NavigationPage Title="View A">
<x:Arguments>
<local:XamlViewMockA Title="View A" />
</x:Arguments>
</NavigationPage>

<local:XamlViewMockA Title="View B" />

</TabbedPage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Xamarin.Forms;

namespace Prism.DI.Forms.Tests.Mocks.Views
{
public partial class XamlTabbedViewMock : TabbedPage
{
public XamlTabbedViewMock()
{
InitializeComponent();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<ContentPage
<ContentPage x:Class="Prism.DI.Forms.Tests.Mocks.Views.XamlViewMockA"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xaml="clr-namespace:Prism.Navigation.Xaml;assembly=Prism.Forms"
Title="{Binding Title}"
x:Class="Prism.DI.Forms.Tests.Mocks.Views.XamlViewMockA">
xmlns:prism="http://prismlibrary.com"
prism:ViewModelLocator.AutowireViewModel="True"
Title="{Binding Title}" >
<Button x:Name="testButton" Command="{xaml:NavigateTo 'XamlViewMockB'}">
<Button.CommandParameter>
<xaml:NavigationParameters>
<xaml:NavigationParameter Key="Foo" Value="Bar"/>
</xaml:NavigationParameters>
</Button.CommandParameter>
</Button>
</ContentPage>
</ContentPage>
Loading

0 comments on commit 7b8b6c3

Please sign in to comment.