Doraemon
Doraemon

Reputation: 33

WPF Ninject with Datacontext and constructor with parameters

Good day all, I have some experience with WPF but I am still at the beginning with MVVM and now I would like to use DI with ninject. When I worked with forms I used ninject normally by making a kernel with the bindings to load and inserting the parameters in the constructor, i see this is not possible now that i have to use DataContext. I have a main window with buttons and a contructor in View Models, in order to use the buttons in the window I that I need to pass the parameters from the binding in the constructor, but if I do so I get an error because the datacontext wants a parameterless constructor, I believe there is an obvious solution I could not find a specific one on the Ninject (sites) and online the only workarounds use some complicate factory pattern which seems to overkill, does anyone know how to solve the problem?

MainWindow.xaml

<Window x:Class="StretchApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        xmlns:local="clr-namespace:StretchApp"
        xmlns:vm="clr-namespace:StretchApp.ViewModels"
        mc:Ignorable="d"
        Title="MainWindow" Height="800" Width="800" Background="LightGray"
    DataContextChanged="MainWindow_DataContextChanged">
    <Window.DataContext>
        <vm:MainViewModel/>
    </Window.DataContext>

MainWindow.xaml.cs

namespace StretchApp
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, IHavePassword
    {
        private IKernel container;

        public MainWindow()
        {
            InitializeComponent();
            DataContextChanged += new DependencyPropertyChangedEventHandler(MainWindow_DataContextChanged);
        }
        void MainWindow_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            var dc = DataContext as IClosableViewModel;
            dc.CloseWindowEvent += new EventHandler(dc_CloseWindowEvent);
        }
        void dc_CloseWindowEvent(object sender, EventArgs e)
        {
            this.Close();
        }
        private void ConfigureContainer()
        {
            var kernel = new StandardKernel();
            kernel.Load(Assembly.GetExecutingAssembly());
        }

    }
}

App.xaml

<Application x:Class="StretchApp.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:StretchApp"
             xmlns:local1="clr-namespace:StretchApp.ViewModels">

    <Application.Resources>
    </Application.Resources>
</Application>

App.xaml.xs

sing Ninject;
using StretchApp.Helper;
using StretchApp.Helper.Interfaces;
using System.Windows;

namespace StretchApp
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private IKernel container;

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            var container = new StandardKernel();

            // Register types
            container.Bind<IEncrypterHelper>().To<EncrypterHelper>();
            container.Bind<Window>().To<MainWindow>();

            // Show the main window.
            MainWindow mw = new MainWindow();
            mw.Show();

        }
        private void ComposeObjects()
        {
            Current.MainWindow = this.container.Get<MainWindow>();
        }

        private void ConfigureContainer()
        {
            this.container = new StandardKernel();
            container.Bind<IEncrypterHelper>().To<EncrypterHelper>();

        }
    }
}

MainViewModel.cs

        public MainViewModel(IEncrypterHelper encrypterHelper)
        {
           _encrypterHelper = encrypterHelper;

        }

Upvotes: 1

Views: 970

Answers (1)

mm8
mm8

Reputation: 169220

Don't instantiate the view model in your XAML markup like this:

<Window.DataContext>
    <vm:MainViewModel/>
</Window.DataContext>

You would only do this in very simple scenarios and it's not possible to use this approach if you want to inject the MainViewModel with some dependencies. Instead you could set the DataContext property of the view programmatically, for example in your OnStartup method where you instantiate the MainWindow:

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);
    var container = new StandardKernel();

    // Register types
    container.Bind<IEncrypterHelper>().To<EncrypterHelper>();
    container.Bind<Window>().To<MainWindow>();

    // Show the main window.
    MainWindow mw = new MainWindow();
    mw.DataContext = container.Get<MainWindowViewModel>();
    mw.Show();
}

Upvotes: 5

Related Questions