Skip to content

Commit 14efed1

Browse files
David BritchDavid Britch
authored andcommitted
ViewModelLocator is now responsible for connecting view models to views.
Uses an auto-wiring convention-based approach.
1 parent a7f9d2c commit 14efed1

27 files changed

Lines changed: 137 additions & 159 deletions

src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using eShopOnContainers.Core.Helpers;
22
using eShopOnContainers.Services;
3-
using eShopOnContainers.ViewModels.Base;
3+
using eShopOnContainers.Core.ViewModels.Base;
44
using System.Threading.Tasks;
55
using Xamarin.Forms;
66
using Xamarin.Forms.Xaml;
@@ -28,12 +28,13 @@ private void InitApp()
2828
{
2929
UseMockServices = Settings.UseMocks;
3030

31-
ViewModelLocator.Instance.UpdateDependencies(UseMockServices);
31+
ViewModelLocator.Initialize();
32+
ViewModelLocator.UpdateDependencies(UseMockServices);
3233
}
3334

3435
private Task InitNavigation()
3536
{
36-
var navigationService = ViewModelLocator.Instance.Resolve<INavigationService>();
37+
var navigationService = ViewModelLocator.Resolve<INavigationService>();
3738
return navigationService.InitializeAsync();
3839
}
3940

src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/ServicesHelper.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using eShopOnContainers.Core.Models.Basket;
22
using eShopOnContainers.Core.Models.Catalog;
3-
using eShopOnContainers.ViewModels.Base;
3+
using eShopOnContainers.Core.ViewModels.Base;
44
using System;
55
using System.Collections.Generic;
66
using System.Diagnostics;
@@ -21,7 +21,7 @@ public static void FixCatalogItemPictureUri(IEnumerable<CatalogItem> catalogItem
2121

2222
try
2323
{
24-
if (!ViewModelLocator.Instance.UseMockService
24+
if (!ViewModelLocator.UseMockService
2525
&& Settings.UrlBase != GlobalSetting.DefaultEndpoint)
2626
{
2727
foreach (var catalogItem in catalogItems)
@@ -54,7 +54,7 @@ public static void FixBasketItemPictureUri(IEnumerable<BasketItem> basketItems)
5454

5555
try
5656
{
57-
if (!ViewModelLocator.Instance.UseMockService
57+
if (!ViewModelLocator.UseMockService
5858
&& Settings.UrlBase != GlobalSetting.DefaultEndpoint)
5959
{
6060
foreach (var basketItem in basketItems)

src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/Settings.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using eShopOnContainers.ViewModels.Base;
1+
using eShopOnContainers.Core.ViewModels.Base;
22
using Plugin.Settings;
33
using Plugin.Settings.Abstractions;
44

@@ -27,7 +27,7 @@ private static ISettings AppSettings
2727
private const string IdUrlBase = "url_base";
2828
private static readonly string AccessTokenDefault = string.Empty;
2929
private static readonly string IdTokenDefault = string.Empty;
30-
private static readonly bool UseMocksDefault = ViewModelLocator.Instance.UseMockService;
30+
private static readonly bool UseMocksDefault = ViewModelLocator.UseMockService;
3131
private static readonly string UrlBaseDefault = GlobalSetting.Instance.BaseEndpoint;
3232

3333
#endregion

src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Navigation/INavigationService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using eShopOnContainers.ViewModels.Base;
1+
using eShopOnContainers.Core.ViewModels.Base;
22
using System;
33
using System.Threading.Tasks;
44

src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Navigation/NavigationService.cs

Lines changed: 12 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,25 @@
11
using eShopOnContainers.Core.Helpers;
22
using eShopOnContainers.Core.ViewModels;
33
using eShopOnContainers.Core.Views;
4-
using eShopOnContainers.ViewModels.Base;
4+
using eShopOnContainers.Core.ViewModels.Base;
55
using System;
6-
using System.Collections.Generic;
6+
using System.Globalization;
7+
using System.Reflection;
78
using System.Threading.Tasks;
89
using Xamarin.Forms;
910

1011
namespace eShopOnContainers.Services
1112
{
1213
public class NavigationService : INavigationService
1314
{
14-
protected readonly Dictionary<Type, Type> _mappings;
15-
16-
protected Application CurrentApplication
15+
protected Application CurrentApplication
1716
{
1817
get
1918
{
2019
return Application.Current;
2120
}
2221
}
2322

24-
public NavigationService()
25-
{
26-
_mappings = new Dictionary<Type, Type>();
27-
28-
CreatePageViewModelMappings();
29-
}
30-
3123
public Task InitializeAsync()
3224
{
3325
if(string.IsNullOrEmpty(Settings.AuthAccessToken))
@@ -100,7 +92,7 @@ public virtual Task RemoveBackStackAsync()
10092

10193
protected virtual async Task InternalNavigateToAsync(Type viewModelType, object parameter)
10294
{
103-
Page page = CreateAndBindPage(viewModelType, parameter);
95+
Page page = CreatePage(viewModelType, parameter);
10496

10597
if (page is LoginView)
10698
{
@@ -109,7 +101,6 @@ protected virtual async Task InternalNavigateToAsync(Type viewModelType, object
109101
else
110102
{
111103
var navigationPage = CurrentApplication.MainPage as CustomNavigationView;
112-
113104
if (navigationPage != null)
114105
{
115106
await navigationPage.PushAsync(page);
@@ -125,40 +116,23 @@ protected virtual async Task InternalNavigateToAsync(Type viewModelType, object
125116

126117
protected Type GetPageTypeForViewModel(Type viewModelType)
127118
{
128-
if (!_mappings.ContainsKey(viewModelType))
129-
{
130-
throw new KeyNotFoundException($"No map for ${viewModelType} was found on navigation mappings");
131-
}
132-
133-
return _mappings[viewModelType];
119+
var viewName = viewModelType.FullName.Replace("Model", string.Empty);
120+
var viewModelAssemblyName = viewModelType.GetTypeInfo().Assembly.FullName;
121+
var viewAssemblyName = string.Format(CultureInfo.InvariantCulture, "{0}, {1}", viewName, viewModelAssemblyName);
122+
var viewType = Type.GetType(viewAssemblyName);
123+
return viewType;
134124
}
135125

136-
protected Page CreateAndBindPage(Type viewModelType, object parameter)
126+
protected Page CreatePage(Type viewModelType, object parameter)
137127
{
138128
Type pageType = GetPageTypeForViewModel(viewModelType);
139-
140129
if (pageType == null)
141130
{
142-
throw new Exception($"Mapping type for {viewModelType} is not a page");
131+
throw new Exception($"Cannot locate page type for {viewModelType}");
143132
}
144133

145134
Page page = Activator.CreateInstance(pageType) as Page;
146-
ViewModelBase viewModel = ViewModelLocator.Instance.Resolve(viewModelType) as ViewModelBase;
147-
page.BindingContext = viewModel;
148-
149135
return page;
150136
}
151-
152-
private void CreatePageViewModelMappings()
153-
{
154-
_mappings.Add(typeof(BasketViewModel), typeof(BasketView));
155-
_mappings.Add(typeof(CatalogViewModel), typeof(CatalogView));
156-
_mappings.Add(typeof(CheckoutViewModel), typeof(CheckoutView));
157-
_mappings.Add(typeof(LoginViewModel), typeof(LoginView));
158-
_mappings.Add(typeof(MainViewModel), typeof(MainView));
159-
_mappings.Add(typeof(OrderDetailViewModel), typeof(OrderDetailView));
160-
_mappings.Add(typeof(ProfileViewModel), typeof(ProfileView));
161-
_mappings.Add(typeof(SettingsViewModel), typeof(SettingsView));
162-
}
163137
}
164138
}

src/Mobile/eShopOnContainers/eShopOnContainers.Core/Validations/ValidatableObject.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using eShopOnContainers.ViewModels.Base;
1+
using eShopOnContainers.Core.ViewModels.Base;
22
using System.Collections.Generic;
33
using System.Collections.ObjectModel;
44
using System.Linq;

src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ExtendedBindableObject.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using System.Reflection;
44
using Xamarin.Forms;
55

6-
namespace eShopOnContainers.ViewModels.Base
6+
namespace eShopOnContainers.Core.ViewModels.Base
77
{
88
public abstract class ExtendedBindableObject : BindableObject
99
{

src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ViewModelBase.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
using eShopOnContainers.Core;
2-
using eShopOnContainers.Core.Helpers;
1+
using eShopOnContainers.Core.Helpers;
32
using eShopOnContainers.Services;
43
using System.Threading.Tasks;
54

6-
namespace eShopOnContainers.ViewModels.Base
5+
namespace eShopOnContainers.Core.ViewModels.Base
76
{
87
public abstract class ViewModelBase : ExtendedBindableObject
98
{
@@ -28,14 +27,14 @@ public bool IsBusy
2827

2928
public ViewModelBase()
3029
{
31-
DialogService = ViewModelLocator.Instance.Resolve<IDialogService>();
32-
NavigationService = ViewModelLocator.Instance.Resolve<INavigationService>();
30+
DialogService = ViewModelLocator.Resolve<IDialogService>();
31+
NavigationService = ViewModelLocator.Resolve<INavigationService>();
3332
GlobalSetting.Instance.BaseEndpoint = Settings.UrlBase;
3433
}
3534

3635
public virtual Task InitializeAsync(object navigationData)
3736
{
38-
return Task.FromResult(false);
37+
return Task.FromResult(false);
3938
}
4039
}
4140
}
Lines changed: 59 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,62 @@
11
using Microsoft.Practices.Unity;
2-
using eShopOnContainers.Core.ViewModels;
32
using eShopOnContainers.Services;
43
using System;
4+
using System.Globalization;
5+
using System.Reflection;
56
using eShopOnContainers.Core.Services.Catalog;
67
using eShopOnContainers.Core.Services.OpenUrl;
78
using eShopOnContainers.Core.Services.RequestProvider;
89
using eShopOnContainers.Core.Services.Basket;
910
using eShopOnContainers.Core.Services.Identity;
1011
using eShopOnContainers.Core.Services.Order;
1112
using eShopOnContainers.Core.Services.User;
13+
using Xamarin.Forms;
1214

13-
namespace eShopOnContainers.ViewModels.Base
15+
namespace eShopOnContainers.Core.ViewModels.Base
1416
{
15-
public class ViewModelLocator
17+
public static class ViewModelLocator
1618
{
17-
private bool _useMockService;
18-
private readonly IUnityContainer _unityContainer;
19+
private static readonly IUnityContainer _unityContainer = new UnityContainer();
1920

20-
private static readonly ViewModelLocator _instance = new ViewModelLocator();
21+
public static readonly BindableProperty AutoWireViewModelProperty =
22+
BindableProperty.CreateAttached("AutoWireViewModel", typeof(bool), typeof(ViewModelLocator), default(bool), propertyChanged: OnAutoWireViewModelChanged);
2123

22-
public static ViewModelLocator Instance
23-
{
24-
get { return _instance; }
25-
}
26-
27-
public bool UseMockService
28-
{
29-
get { return _useMockService; }
30-
set { _useMockService = value; ; }
31-
}
24+
public static bool GetAutoWireViewModel(BindableObject bindable)
25+
{
26+
return (bool)bindable.GetValue(ViewModelLocator.AutoWireViewModelProperty);
27+
}
3228

33-
protected ViewModelLocator()
34-
{
35-
_unityContainer = new UnityContainer();
29+
public static void SetAutoWireViewModel(BindableObject bindable, bool value)
30+
{
31+
bindable.SetValue(ViewModelLocator.AutoWireViewModelProperty, value);
32+
}
3633

37-
// Services
38-
_unityContainer.RegisterType<IDialogService, DialogService>();
39-
RegisterSingleton<INavigationService, NavigationService>();
40-
_unityContainer.RegisterType<IOpenUrlService, OpenUrlService>();
41-
_unityContainer.RegisterType<IRequestProvider, RequestProvider>();
42-
_unityContainer.RegisterType<IIdentityService, IdentityService>();
34+
public static bool UseMockService { get; set; }
4335

44-
_unityContainer.RegisterType<ICatalogService, CatalogMockService>();
45-
_unityContainer.RegisterType<IBasketService, BasketMockService>();
46-
_unityContainer.RegisterType<IUserService, UserMockService>();
36+
public static void Initialize()
37+
{
38+
// Services
39+
_unityContainer.RegisterType<IDialogService, DialogService>();
40+
_unityContainer.RegisterType<INavigationService, NavigationService>(new ContainerControlledLifetimeManager());
41+
_unityContainer.RegisterType<IOpenUrlService, OpenUrlService>();
42+
_unityContainer.RegisterType<IRequestProvider, RequestProvider>();
43+
_unityContainer.RegisterType<IIdentityService, IdentityService>();
44+
_unityContainer.RegisterType<ICatalogService, CatalogMockService>();
45+
_unityContainer.RegisterType<IBasketService, BasketMockService>();
46+
_unityContainer.RegisterType<IUserService, UserMockService>();
4747

48-
// View models
49-
_unityContainer.RegisterType<BasketViewModel>();
50-
_unityContainer.RegisterType<CatalogViewModel>();
51-
_unityContainer.RegisterType<CheckoutViewModel>();
52-
_unityContainer.RegisterType<LoginViewModel>();
53-
_unityContainer.RegisterType<MainViewModel>();
54-
_unityContainer.RegisterType<OrderDetailViewModel>();
55-
_unityContainer.RegisterType<ProfileViewModel>();
56-
_unityContainer.RegisterType<SettingsViewModel>();
57-
}
48+
// View models
49+
_unityContainer.RegisterType<BasketViewModel>();
50+
_unityContainer.RegisterType<CatalogViewModel>();
51+
_unityContainer.RegisterType<CheckoutViewModel>();
52+
_unityContainer.RegisterType<LoginViewModel>();
53+
_unityContainer.RegisterType<MainViewModel>();
54+
_unityContainer.RegisterType<OrderDetailViewModel>();
55+
_unityContainer.RegisterType<ProfileViewModel>();
56+
_unityContainer.RegisterType<SettingsViewModel>();
57+
}
5858

59-
public void UpdateDependencies(bool useMockServices)
59+
public static void UpdateDependencies(bool useMockServices)
6060
{
6161
// Change injected dependencies
6262
if (useMockServices)
@@ -80,29 +80,31 @@ public void UpdateDependencies(bool useMockServices)
8080
}
8181
}
8282

83-
public T Resolve<T>()
83+
public static T Resolve<T>()
8484
{
8585
return _unityContainer.Resolve<T>();
8686
}
8787

88-
public object Resolve(Type type)
89-
{
90-
return _unityContainer.Resolve(type);
91-
}
92-
93-
public void Register<T>(T instance)
94-
{
95-
_unityContainer.RegisterInstance<T>(instance);
96-
}
88+
private static void OnAutoWireViewModelChanged(BindableObject bindable, object oldValue, object newValue)
89+
{
90+
var view = bindable as Element;
91+
if (view == null)
92+
{
93+
return;
94+
}
9795

98-
public void Register<TInterface, T>() where T : TInterface
99-
{
100-
_unityContainer.RegisterType<TInterface, T>();
101-
}
96+
var viewType = view.GetType();
97+
var viewName = viewType.FullName.Replace(".Views.", ".ViewModels.");
98+
var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
99+
var viewModelName = string.Format(CultureInfo.InvariantCulture, "{0}Model, {1}", viewName, viewAssemblyName);
102100

103-
public void RegisterSingleton<TInterface, T>() where T : TInterface
104-
{
105-
_unityContainer.RegisterType<TInterface, T>(new ContainerControlledLifetimeManager());
106-
}
101+
var viewModelType = Type.GetType(viewModelName);
102+
if (viewModelType == null)
103+
{
104+
return;
105+
}
106+
var viewModel = _unityContainer.Resolve(viewModelType);
107+
view.BindingContext = viewModel;
108+
}
107109
}
108110
}

src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/BasketViewModel.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
using eShopOnContainers.Core.Services.Basket;
55
using eShopOnContainers.Core.Services.User;
66
using eShopOnContainers.Core.ViewModels.Base;
7-
using eShopOnContainers.ViewModels.Base;
8-
using System;
97
using System.Collections.ObjectModel;
108
using System.Linq;
119
using System.Threading.Tasks;

0 commit comments

Comments
 (0)