-1

I am developing a C#/WPF/MVVM/UWP app which uses a ViewModelLocator which looks like this:

    public class ViewModelLocator
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
        "CA1822:MarkMembersAsStatic",
        Justification = "This non-static member is needed for data binding purposes.")]
    public MainPageViewModel MainPage
    {
        get
        {
            return ServiceLocator.Current.GetInstance<MainPageViewModel>();
        }
    }


    static ViewModelLocator()
    {
        // DEBUG LINE: var test = Views.ViewLocator.MainPageKey;
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

        if (ViewModelBase.IsInDesignModeStatic)
        {
            SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
        }
        else
        {
            SimpleIoc.Default.Register<IDataService, DataService>();
        }

        SimpleIoc.Default.Register<MainPageViewModel>();
    }
}

I have another class, a ViewLocator, for navigation purposes which looks like this:

    public class ViewLocator
{
    public static readonly string MainPageKey = typeof(MainPage).Name;
    public static readonly string WorkPageKey = typeof(WorkPage).Name;

    static ViewLocator()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

        var navigationService = new NavigationService();

        navigationService.Configure(MainPageKey, typeof(MainPage));
        navigationService.Configure(WorkPageKey, typeof(WorkPage));

        SimpleIoc.Default.Register<INavigationService>(() => navigationService);

        SimpleIoc.Default.Register<IDialogService, DialogService>();
    }
}

Before, both classes were combined in ViewModelLocator, however i figured that the ViewModelLocator as part of the "ViewModels-Side-of-things" should be unaware of the views and it's types, which is why I refactored that code into two classes.

My MainPageView then has a button, which triggers a navigation command in the MainPageView.cs

    public class MainPageViewModel : ViewModelBase
{
    private INavigationService _navigationService;

    public RelayCommand CreateNewImageCommand { get; private set; }

    public MainPageViewModel(INavigationService navigationService)
    {
        _navigationService = navigationService;

        CreateNewImageCommand = new RelayCommand(CreateNewImage, () => true);
    }

    public void CreateNewImage()
    {
        _navigationService.NavigateTo(Views.ViewLocator.WorkPageKey);
    }
}

For completeness, here is my App.xaml

<Application
...>

<Application.Resources>
    <v:ViewLocator x:Key="ViewLocator" />
    <vm:ViewModelLocator x:Key="ViewModelLocator" />
</Application.Resources>

Now what happens is this: if I don't have the DEBUG LINE in ViewModelLocator, at the point at which MainPage requests its ViewModel from the ViewModelLocator, the constructor of ViewLocator has not been called yet and return ServiceLocator.Current.GetInstance<MainPageViewModel>(); throws an exception. If I include the DEBUG LINE, this forces the ViewLocator Constructor to be run first, and everything works fine.

How can I achieve this behaviour without that odd debug line?

Xaser
  • 2,066
  • 2
  • 22
  • 45
  • I'm just going to leave this here [Is ServiceLocator anti-pattern](http://stackoverflow.com/questions/22795459/is-servicelocator-anti-pattern) – Liam May 12 '16 at 16:18
  • TL;DR You should really be using Dependency Injection not service locator pattern. Typically if starting from scratch it's a much better and easier pattern to understand. – Liam May 12 '16 at 16:21
  • @Liam I'm not a fan of IoC Containers or ServiceLocator or whatever you want to call it. It's just that I have been researching how to properly separate VM and V and this is the only working approach that satisfies me so far. I'm however very open to using DI - if you could please show me some reference code that employs it properly. – Xaser May 12 '16 at 16:23
  • 1
    Most people use an [IoC framework these days](http://stackoverflow.com/questions/21288/which-net-dependency-injection-frameworks-are-worth-looking-into). For example [Ninject](http://www.ninject.org/) – Liam May 12 '16 at 16:27
  • Have a look at Caliburn Micro; it will do the heavy lifting for you and you get IoC... yes, IoC is the way to go. – Meirion Hughes May 12 '16 at 16:43
  • I have been using MVVM Light and its SimpleIoC so far. I feel what you are saying is I should eliminate the ViewModelLocator. Reading about IoC and DI is all fine but I don't seem to be capable of figuring out how to use this with MVVM. Examples would be greatly appreciated. – Xaser May 12 '16 at 16:46

2 Answers2

0

You are running into the reason most developers do not use static classes and a service locator.

As suggested in your comments try using dependency injection. This allows inversion of control for the component that manages this wiring.

Look into MEF or Unity as a framework to use rather than rolling your own IoC. Both have advantages and disadvantages but either will work. They also allow for easier and cleaner testing.

MEF

Unity

N_tro_P
  • 673
  • 5
  • 15
0

Despite of all the comments and other answers, I think that I was on the track after all. To solve or rather bypass this problem, I have created added bootstrapping of the SimpleIoC Container in the Startup of the App and move the code from the ViewLocator's constructor there.

I also removed the ServiceLocator calls, which was sort of redundant and rely on SimpleIoC.Default now directly.

The Navigator is still injected into the ViewModels by the SimpleIoC Container. The ViewModelLocator however is necessary work in conjuction with the XAML code.

I recommend this answer as a starting point for others who are also struggling with this issue.

https://stackoverflow.com/a/25524753/2175012

After all, I have taken the side that ServiceLocators are no anti pattern but have to be used carefully.

Community
  • 1
  • 1
Xaser
  • 2,066
  • 2
  • 22
  • 45