Filippo Biondi
Filippo Biondi

Reputation: 38

How to bind a DataTemplate datatype to a View which receives ViewModels a dependency injected by DI

I have a WPF application which implements navigation using MVVM, filling a different DataTemplate for each View within the same Window e.g. :

<Window.Resources>
    <DataTemplate DataType="{x:Type foo:FooViewModel}">
        <foo:FooView/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type bar:BarViewModel}">
        <bar:BarView/>
    </DataTemplate>
<Window.Resources>

(Switching between Views/UserControls using MVVM is Rachel Lim's article which has inspired the aforementioned approach)

What happens now is that FooView gets FooViewModel automatically injected as a dependency by DI (which in my case is Microsoft.Extensions.DependencyInjection on .Net Core3 Preview) e.g. :

public partial class FooView : UserControl
{
    public FooView(FooViewModel fooViewModel)
    {  
        this.InitializeComponent();
        this.DataContext = fooViewModel;
    }
}

At this point obviously the DataTemplate complains because the FooView does not define a parameter-less ctor (as per reference Type '{0}' is not usable as an object element)

Is there any way to bypass this issue and let FooView to use FooViewModel as DataContext?

Upvotes: 1

Views: 1560

Answers (1)

ASh
ASh

Reputation: 35679

DataTemplate

<DataTemplate DataType="{x:Type foo:FooViewModel}">
    <foo:FooView/>
</DataTemplate>

will assign an instance of FooViewModel to FooView.DataContext.

this.DataContext = fooViewModel; line in FooView control is useless, because DataContext will be overwritten in such scenario. I would say, it is perfectly fine not to pass view model via contrustor. It can be accessed from DataContext:

public partial class FooView : UserControl
{
    public FooView()
    {  
        this.InitializeComponent();
    }

    private FooViewModel Vm { get { return this.DataContext as FooViewModel; } }
}

In the navigation pattern you are using, View is a receiver of ViewModel. Current ViewModel is set by AppViewModel:

public class AppViewModel
{
    // simplified properties
    public ViewModelBase CurrentViewModel {get; set;}
    public ICommand ViewFooCommand {get;}
    public ICommand ViewBarCommand {get;}
} 

You are trying to make View an originator/producer of ViewModel, which conflicts with pattern.

Upvotes: 2

Related Questions