user10785050
user10785050

Reputation:

Apply Ninject to work with "child viewmodels" in my C# WPF application

I'm having some problem with doing Ninject - dependency injection - on my WPF application.

I've managed to do the dependency injection correctly for my MainView.xaml and MainViewModel.cs, I did this by settings the datacontext in my App.xaml.cs. With this I set my MainWindow datatext to the viewmodel retrieved by the kernel from ninject.

Here is my App.xaml.cs, which I set as Startup instead of StartupUri in app.xaml

    private void Application_Startup(object sender, StartupEventArgs e)
    {
        var kernel = new StandardKernel(new CompositionModule());

        MainWindow mainWindow = new MainWindow();
        mainWindow.DataContext = kernel.Get<MainViewModel>();
        mainWindow.Show();
    }

So far it works perfect for MainViewModel, but my problem is that my MainViewModel have multiple child viewmodels.

Here's an example of my child viewmodel is setup my MainView.xaml looks like - From under you can see that my MainView.xaml split the main window into two parts. One part of the main window show the view1 and viewmodel1, and the other part show view2 and viewmodel2. Both are in the same window, I did this just to make it easier to show what these part do. My problem is that now I'm binding Content = Viewmodel1, but when I try to put parameter value in the constructor, my application doesn't execute the constructor code. It just skips it. My child viewmodels is of partial class, which concist of a viewmodel1.cs and viewmodel.commands.cs. The viewmodel.commands part only contain commands for GUI. The ContentTemplate is datatemplate in my xaml resource - resourcedictionary files loaded in app.xaml.

<telerik:RadSplitContainer Orientation="Vertical" telerik:DockingPanel.InitialSize="750,200">
            <telerik:RadPaneGroup IsContentPreserved="False" telerik:ProportionalStackPanel.RelativeSize="200,300" >
                <telerik:RadPane Header="{Binding ViewModel1.Title, Mode=TwoWay}" 
                             CanUserClose="False" CanUserPin="False"
                             CanDockInDocumentHost="True">
                    <ContentControl ContentTemplate="{StaticResource View1Template}"  
                                Content="{Binding Viewmodel1}" />
                </telerik:RadPane>
            </telerik:RadPaneGroup>
            <telerik:RadPaneGroup IsContentPreserved="False" telerik:ProportionalStackPanel.RelativeSize="100,120">
                <telerik:RadPane Header="ViewModel2.Tile" 
                             CanUserClose="False" CanUserPin="False"
                             CanDockInDocumentHost="True">
                    <ContentControl ContentTemplate="{StaticResource View2Template}"  
                                Content="{Binding Viewmodel2}" />
                </telerik:RadPane>
            </telerik:RadPaneGroup>
         </telerik:RadSplitContainer>

These two viewmodels under gets binded to Content in MainView.xaml.

public ViewModel1 ViewModel1 { get; set; } = new ViewModel1();
public ViewModel2 ViewModel2 { get; set; } = new ViewModel2();

How can I make dependency injection also work for my child viewmodels? It seems like the reason it doesn't work is because the constructor never gets executed whenever I put an interface as constructor parameter.

Thanks for any help I can get. It works perfectly for MainViewModel, but not for my resource files and other viewmodels.

Upvotes: 2

Views: 398

Answers (1)

Andy
Andy

Reputation: 12276

I posted this code on your previous question. When you set

public ViewModel1 ViewModel1 { get; set; } = new ViewModel1();

Then that will ignore ninject. You're just newing up a class.

As I read your question the sample code I posted seems to do what you are having a problem with. I tell it when I ask for an IVMOne then it should give me a new instance of VMOne. You do not seem to have the equivalent code.

    private void Application_Startup(object sender, StartupEventArgs e)
    {
        var nj = new StandardKernel();
        nj.Bind<IVMOne>().To<VMOne>().InTransientScope();  //  <<<<<<<<<<<<<<<

        MainWindow mw = new MainWindow();
        mw.DataContext = nj.Get<MainWindowViewModel>();
        mw.Show();
    }

See where I bind IVMOne? This is equivalent to your Viewmodel1 and 2. You don't have that in your code.

When I get MainWindowViewModel that has IVMOne as a ctor parameter.

class MainWindowViewModel
{
    public IVMOne VMOne { get; set; }
    public MainWindowViewModel(IVMOne vmOne)
    {
        VMOne = vmOne;
    }
}

Because I told it what to give me, it passes an instance of VMOne.

I see the text appear in my textblock when I spin this up.

    Title="MainWindow" Height="450" Width="800">
<Grid>
    <TextBlock Text="{Binding VMOne.Hello}"/>
</Grid>

If I put a breakpoint in on the mainwindowviewmodel ctor I have an instance of vmone there.

Could you try binding one of your viewmodels prior to getting mainwindowviewmodel?

When you Get mainwindowviewmodel it will use the ctor with the most parameters. Put a break point in that. Do you get an instance of your viewmodel there?

If not then put a different sln together. Not what you have now. Because that will be complicated and probably have external dependencies. Put in that the absolute minimum markup and code that demonstrates your issue without any external dependencies.

This is so someone can easily and quickly reproduce your issue.

Upvotes: 1

Related Questions