Skip to content

Commit 7b8b6c3

Browse files
authored
Merge pull request #2354 from PrismLibrary/di-navservice-fix
Fixing DI NavigationService resolution
2 parents 9ab5752 + b9e600c commit 7b8b6c3

File tree

15 files changed

+335
-15
lines changed

15 files changed

+335
-15
lines changed

e2e/Forms/src/HelloWorld.Android/HelloWorld.Android.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,9 @@
105105
</ProjectReference>
106106
</ItemGroup>
107107
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
108+
<ProjectExtensions>
109+
<VisualStudio>
110+
<UserProperties TriggeredFromHotReload="False" />
111+
</VisualStudio>
112+
</ProjectExtensions>
108113
</Project>

e2e/Forms/src/HelloWorld/App.xaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ protected override void OnInitialized()
6262
{
6363
InitializeComponent();
6464

65-
NavigationService.NavigateAsync($"MyMasterDetail/MyTabbedPage").OnNavigationError(OnNavigationError);
65+
NavigationService.NavigateAsync($"MyTabbedPage").OnNavigationError(OnNavigationError);
6666
}
6767

6868
private void OnNavigationError(Exception ex)

e2e/Forms/src/ModuleA/ViewModels/ViewAViewModel.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ private void Save()
7979
async void Navigate()
8080
{
8181
CanNavigate = false;
82-
await _navigationService.SelectTabAsync("ViewC");
82+
//await _navigationService.SelectTabAsync("ViewC");
83+
await _navigationService.NavigateAsync("ViewC");
8384
CanNavigate = true;
8485
}
8586

src/Forms/Prism.Forms/Navigation/Xaml/Navigation.cs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,7 @@ public static INavigationService GetNavigationService(Page page)
9595
if (!currentScope.IsAttached)
9696
page.SetValue(NavigationScopeProperty, currentScope);
9797

98-
currentScope.IsAttached = true;
99-
100-
navService = currentScope.Resolve<INavigationService>();
101-
if (navService is IPageAware pa)
102-
{
103-
pa.Page = page;
104-
}
105-
106-
page.SetValue(NavigationServiceProperty, navService);
98+
navService = CreateNavigationService(currentScope, page);
10799
}
108100
else if(navService is IPageAware pa && pa.Page != page)
109101
{
@@ -116,6 +108,25 @@ public static INavigationService GetNavigationService(Page page)
116108
return navService;
117109
}
118110

111+
private static INavigationService CreateNavigationService(IScopedProvider scope, Page page)
112+
{
113+
var navService = scope.Resolve<INavigationService>();
114+
switch(navService)
115+
{
116+
case IPageAware pa when pa.Page is null:
117+
pa.Page = page;
118+
break;
119+
case IPageAware pa1 when pa1.Page != page:
120+
return CreateNavigationService(ContainerLocator.Container.CreateScope(), page);
121+
}
122+
123+
page.SetValue(NavigationScopeProperty, scope);
124+
scope.IsAttached = true;
125+
page.SetValue(NavigationServiceProperty, navService);
126+
127+
return navService;
128+
}
129+
119130
internal static Action GetRaiseCanExecuteChangedInternal(BindableObject view) => (Action)view.GetValue(RaiseCanExecuteChangedInternalProperty);
120131

121132
internal static void SetRaiseCanExecuteChangedInternal(BindableObject view, Action value) => view.SetValue(RaiseCanExecuteChangedInternalProperty, value);
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
using Prism.Common;
2+
using Prism.DI.Forms.Tests.Mocks.ViewModels;
3+
using Prism.DI.Forms.Tests.Mocks.Views;
4+
using Xamarin.Forms;
5+
using Xunit;
6+
using Xunit.Abstractions;
7+
8+
namespace Prism.DI.Forms.Tests.Fixtures.Navigation
9+
{
10+
public class NavigationServiceFixture : FixtureBase
11+
{
12+
public NavigationServiceFixture(ITestOutputHelper testOutputHelper)
13+
: base(testOutputHelper)
14+
{
15+
}
16+
17+
[Fact]
18+
public void ContentPage_GetsCorrectNavService()
19+
{
20+
var app = CreateMockApplication();
21+
app.NavigationService.NavigateAsync("XamlViewMockA");
22+
23+
var mainPage = app.MainPage;
24+
var vm = mainPage.BindingContext as XamlViewMockAViewModel;
25+
26+
Assert.IsType<XamlViewMockA>(mainPage);
27+
Assert.NotNull(vm);
28+
29+
var page = ((IPageAware)vm.NavigationService).Page;
30+
Assert.IsType<XamlViewMockA>(mainPage);
31+
Assert.Same(mainPage, page);
32+
}
33+
34+
[Fact]
35+
public void ContentPage_InNavigationPage_GetsCorrectNavService()
36+
{
37+
var app = CreateMockApplication();
38+
app.NavigationService.NavigateAsync("NavigationPage/XamlViewMockA");
39+
40+
var mainPage = app.MainPage;
41+
Assert.IsType<NavigationPage>(mainPage);
42+
43+
var view = mainPage.Navigation.NavigationStack[0] as XamlViewMockA;
44+
Assert.NotNull(view);
45+
46+
var vm = view.BindingContext as XamlViewMockAViewModel;
47+
Assert.NotNull(vm);
48+
49+
var page = ((IPageAware)vm.NavigationService).Page;
50+
Assert.IsType<XamlViewMockA>(page);
51+
Assert.Same(view, page);
52+
}
53+
54+
[Fact]
55+
public void TabbedPage_GetsCorrectNavService()
56+
{
57+
var app = CreateMockApplication();
58+
app.NavigationService.NavigateAsync("XamlTabbedViewMock");
59+
60+
var tp = app.MainPage as TabbedPage;
61+
Assert.NotNull(tp);
62+
63+
var tpVm = tp.BindingContext as XamlTabbedViewMockViewModel;
64+
Assert.NotNull(tpVm);
65+
66+
var page = ((IPageAware)tpVm.NavigationService).Page;
67+
Assert.IsType<XamlTabbedViewMock>(page);
68+
Assert.Same(tp, page);
69+
}
70+
71+
[Fact]
72+
public void TabbedPage_ContentPage_NestedNavigationPage_GetsCorrectNavService()
73+
{
74+
var app = CreateMockApplication();
75+
app.NavigationService.NavigateAsync("XamlTabbedViewMock");
76+
77+
var tp = app.MainPage as TabbedPage;
78+
Assert.NotNull(tp);
79+
80+
var tab = tp.Children[0];
81+
Assert.NotNull(tab);
82+
83+
var navPage = tab as NavigationPage;
84+
Assert.NotNull(navPage);
85+
86+
var view = navPage.CurrentPage;
87+
Assert.NotNull(view);
88+
89+
var vm = view.BindingContext as XamlViewMockAViewModel;
90+
Assert.NotNull(vm);
91+
92+
var page = ((IPageAware)vm.NavigationService).Page;
93+
Assert.IsType<XamlViewMockA>(page);
94+
Assert.Same(view, page);
95+
}
96+
97+
[Fact]
98+
public void TabbedPage_ContentPage_GetsCorrectNavService()
99+
{
100+
var app = CreateMockApplication();
101+
app.NavigationService.NavigateAsync("XamlTabbedViewMock");
102+
103+
var tp = app.MainPage as TabbedPage;
104+
Assert.NotNull(tp);
105+
106+
var view = tp.Children[1];
107+
Assert.NotNull(view);
108+
109+
var vm = view.BindingContext as XamlViewMockAViewModel;
110+
Assert.NotNull(vm);
111+
112+
var page = ((IPageAware)vm.NavigationService).Page;
113+
Assert.IsType<XamlViewMockA>(page);
114+
Assert.Same(view, page);
115+
}
116+
117+
[Fact]
118+
public void TabbedPage_InNavigationPage_GetsCorrectNavService()
119+
{
120+
var app = CreateMockApplication();
121+
app.NavigationService.NavigateAsync("NavigationPage/XamlTabbedViewMock");
122+
123+
var mainPage = app.MainPage;
124+
Assert.IsType<NavigationPage>(mainPage);
125+
126+
var tp = mainPage.Navigation.NavigationStack[0] as TabbedPage;
127+
Assert.NotNull(tp);
128+
129+
var tpVm = tp.BindingContext as XamlTabbedViewMockViewModel;
130+
Assert.NotNull(tpVm);
131+
132+
var page = ((IPageAware)tpVm.NavigationService).Page;
133+
Assert.IsType<XamlTabbedViewMock>(page);
134+
Assert.Same(tp, page);
135+
}
136+
137+
[Fact]
138+
public void MasterDetail_GetsCorrectNavService()
139+
{
140+
var app = CreateMockApplication();
141+
app.NavigationService.NavigateAsync("XamlMasterDetailViewMock");
142+
143+
var mainPage = app.MainPage;
144+
Assert.IsType<XamlMasterDetailViewMock>(mainPage);
145+
146+
var vm = mainPage.BindingContext as XamlMasterDetailViewMockViewModel;
147+
Assert.NotNull(vm);
148+
149+
var page = ((IPageAware)vm.NavigationService).Page;
150+
Assert.IsType<XamlMasterDetailViewMock>(page);
151+
Assert.Same(mainPage, page);
152+
}
153+
154+
[Fact]
155+
public void MasterDetail_Detail_GetsCorrectNavService()
156+
{
157+
var app = CreateMockApplication();
158+
app.NavigationService.NavigateAsync("XamlMasterDetailViewMock");
159+
160+
var mainPage = app.MainPage as XamlMasterDetailViewMock;
161+
Assert.NotNull(mainPage);
162+
163+
var detail = mainPage.Detail as NavigationPage;
164+
Assert.NotNull(detail);
165+
166+
var view = detail.CurrentPage as XamlViewMockA;
167+
Assert.NotNull(view);
168+
169+
var vm = view.BindingContext as XamlViewMockAViewModel;
170+
Assert.NotNull(vm);
171+
172+
var page = ((IPageAware)vm.NavigationService).Page;
173+
Assert.IsType<XamlViewMockA>(page);
174+
Assert.Same(view, page);
175+
}
176+
}
177+
}

tests/Forms/Prism.DI.Forms.Tests/Mocks/PrismApplicationMock.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ protected override void RegisterTypes(IContainerRegistry containerRegistry)
5757
containerRegistry.RegisterForNavigation<XamlViewMock>();
5858
containerRegistry.RegisterForNavigation<XamlViewMockB, XamlViewMockBViewModel>();
5959
containerRegistry.RegisterForNavigation<XamlViewMockA, XamlViewMockAViewModel>();
60+
containerRegistry.RegisterForNavigation<XamlTabbedViewMock, XamlTabbedViewMockViewModel>();
61+
containerRegistry.RegisterForNavigation<XamlMasterDetailViewMock, XamlMasterDetailViewMockViewModel>();
6062

6163
ViewModelLocationProvider.Register<PartialView, PartialViewModel>();
6264
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using Prism.Navigation;
2+
3+
namespace Prism.DI.Forms.Tests.Mocks.ViewModels
4+
{
5+
public class XamlMasterDetailViewMockViewModel
6+
{
7+
public INavigationService NavigationService { get; }
8+
9+
public XamlMasterDetailViewMockViewModel(INavigationService navigationService)
10+
{
11+
NavigationService = navigationService;
12+
}
13+
}
14+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Prism.Mvvm;
2+
using Prism.Navigation;
3+
4+
namespace Prism.DI.Forms.Tests.Mocks.ViewModels
5+
{
6+
public class XamlTabbedViewMockViewModel : BindableBase
7+
{
8+
public INavigationService NavigationService { get; }
9+
10+
public XamlTabbedViewMockViewModel(INavigationService navigationService)
11+
{
12+
NavigationService = navigationService;
13+
}
14+
}
15+
}

tests/Forms/Prism.DI.Forms.Tests/Mocks/ViewModels/XamlViewMockAViewModel.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ public string Test
2020
set => SetProperty(ref _test, value);
2121
}
2222

23+
public INavigationService NavigationService { get; set; }
24+
25+
public XamlViewMockAViewModel(INavigationService navigationService)
26+
{
27+
NavigationService = navigationService;
28+
}
29+
2330
public void OnNavigatedFrom(INavigationParameters parameters)
2431
{
2532
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
4+
xmlns:prism="http://prismlibrary.com"
5+
xmlns:local="clr-namespace:Prism.DI.Forms.Tests.Mocks.Views"
6+
prism:ViewModelLocator.AutowireViewModel="True"
7+
x:Class="Prism.DI.Forms.Tests.Mocks.Views.XamlMasterDetailViewMock">
8+
9+
<MasterDetailPage.Master>
10+
<ContentPage Title="Master">
11+
12+
</ContentPage>
13+
</MasterDetailPage.Master>
14+
15+
<MasterDetailPage.Detail>
16+
<NavigationPage>
17+
<x:Arguments>
18+
<local:XamlViewMockA Title="View A" />
19+
</x:Arguments>
20+
</NavigationPage>
21+
</MasterDetailPage.Detail>
22+
23+
</MasterDetailPage>

0 commit comments

Comments
 (0)