1
votes

Following several guides I have a application layout like below using WPF .NET 4.7.1 and MVVM-Light. I'm totally new to WPF btw.

App.xaml:

<Application x:Class="My.App" 
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
         xmlns:viewmodel="clr-namespace:My.ViewModel" 
         StartupUri="View\MainView.xaml">
<Application.Resources>
    <ResourceDictionary>
        <viewmodel:ViewModelLocator x:Key="Locator" />
    </ResourceDictionary>
</Application.Resources>

That registers the "ViewModelLocator" class as a resources and sets the WPF startup to "View/MainView.xaml".

MainView.xaml:

<Window x:Class="My.View.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<Window.DataContext>
    <Binding  Path="Main" Source="{StaticResource Locator}"/>
</Window.DataContext>

Where the ViewModelLocator is used like a Service Locator Pattern. Here setting the DataContext to my "MainViewModel" (not shown). As much as I do not like this, I can live with it in the WPF XAML context. However now it turns out that I need a dependency in the code-behind of the view (not the ViewModel).

MainView.cs:

public partial class MainView : INotifyPropertyChanged
{
   public MainView()
   {
       // Need to access dependency here.          
   }
}

Now I could just call the ViewModelLocator directly in that constructor and have that resolve from my IoC container - but then I've completely given in and accepting that pattern.

I would prefer to have the dependency injected in the ctor of course, and if that is possible, I would also leave the ViewModelLocator entirely and inject the ViewModel here.

So question is, are there some standard way of instructing WPF application to use my container? And if yes, is it adviceable to go down that path and not use the ViewModelLocator thing?

1

1 Answers

4
votes

You absolutely do not have to use the ViewModelLocator (Side note, the service locator pattern has had it's fair share of criticism lately as an anti-pattern, but I'll let you form your own opinion). MVVM Light and other Libraries basically give you access to a tool kit. You don't need to use all of the tools, and you should only use what is necessary for your specific domain.

Outside of the ViewModelLocator, there are two patterns known as ViewModel First and View First both have their pro's and cons. However both provide a means to decouple your code, which means it's not difficult to switch later.

As for constructing an application using MVVM Light without the service locator, my implementation of the View First method looks something like this.

I've heard the opinion that ViewModel First is preferred, however I find View First to be more simplistic for Test Driven Development (TDD)

App.xaml.cs (Application Code Behind)

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        var bootStrapper = new BootStrapper();
        //Container Builder
        var container = bootStrapper.BootStrap();
        var mainWindow = container.Resolve<MainWindow>();
        mainWindow.Show();
    }
}

BootStrapper.cs (I'm using AutoFac in this case, but you can easily substitute.)

public class BootStrapper
{
    public IContainer BootStrap()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<MainWindow>().AsSelf();
        builder.RegisterType<MainWindowViewModel>().AsSelf();
        return builder.Build();
    }
}

MainWindowViewModel.cs

//I rolled my own ViewModelBase, but you can use MVVM Light's ViewModelBase
public class MainWindowViewModel : ViewModelBase
{
    public string DisplayProgram
    {
        get { return _displayProgram; }
        //MVVM Light's ViewModelBase uses RaisePropertyChanged();
        set { _displayProgram = value; OnPropertyChanged(); }
    }

    public void Initialize()
    {
        //Called from view code behind.
    }
}

MainWindow.xaml.cs (MainWindow Code Behind)

//When MainWindow.Show()..
public partial class MainWindow : Window
{
    private readonly MainWindowViewModel _viewModel;
    //Container resolves dependencies
    public MainWindow(MainWindowViewModel viewModel)
    {
        //Let base Window class do its thing.
        InitializeComponent();

        //Handle loaded event
        Loaded += MainWindowLoaded;

        //Hold on to the MainWindowViewModel, and set it as the windows DataContext            
        _viewModel = viewModel;
        DataContext = _viewModel;
    }

    private void MainWindowLoaded(object sender, RoutedEventArgs e)
    {
        _viewModel.Initialize();
    }
}