C# 使用NInject在WPF中注入不带无参数构造函数的viewmodel类

C# 使用NInject在WPF中注入不带无参数构造函数的viewmodel类,c#,wpf,mvvm,dependency-injection,ninject,C#,Wpf,Mvvm,Dependency Injection,Ninject,我正在使用NInject解决我的第一个WPF应用程序的依赖关系。 下面是我的代码片段 我的App.xaml.cs像这样运行 public partial class App : Application { private IKernel container; protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); ConfigureContain

我正在使用NInject解决我的第一个WPF应用程序的依赖关系。 下面是我的代码片段

我的App.xaml.cs像这样运行

public partial class App : Application
{
    private IKernel container;

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        ConfigureContainer();
        ComposeObjects();
    }

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

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

    }
}
和视图模型

internal class TrackerViewModel : System.ComponentModel.INotifyPropertyChanged
{
    public TrackerViewModel(ISystemEvents systemEvents)
    {
        systemEvents.SessionSwitch += SystemEvents_SessionSwitch;
    }

    private void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
    {
    }
}
现在,当我启动应用程序时,在
InitializeComponent()
方法中的PresentationFramework.dll中发生了一个异常
类型为'System.NullReferenceException'的未处理异常

我知道这是因为viewmodel类没有无参数构造函数。但我无法理解为什么依赖注入器无法解决这个问题?我做错什么了吗


任何帮助都将不胜感激。

TrackServiceWModel将由自动生成的xaml设计器代码实例化,而不是由ninject实例化。 我从未使用过ninject,但我认为您需要配置容器以了解您的viewModel,然后为ninject注入viewModel以解决它及其依赖关系:

public class MainWindow : Window
{
    [Inject]
    public TrackerViewModel ViewModel { get; set; }

    public MainWindow()
    {
        InitializeComponent();
        DataContext = ViewModel;
    }
}

首先,我建议阅读这本书,特别是关于WPF的部分。但即使你没有读过,书中也有一个有用的例子


您已经确定需要从
App.xaml
文件中删除
StartupUri=“MainWindow.xaml”

但是,在使用DI时,不能以声明方式连接
DataContext
,否则它只能使用默认构造函数

<Window x:Class="WpfWithNinject.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="150" Width="350">

</Window>
trackservicewmodel
还需要进行一些返工,以便它可以将
IWindow
接受到其构造函数中。这允许
trackservicewmodel
控制自己的窗口环境,例如向用户显示模式对话框

internal class TrackerViewModel : System.ComponentModel.INotifyPropertyChanged
{
    private readonly IWindow window;

    public TrackerViewModel(ISystemEvents systemEvents, IWindow window)
    {
        if (systemEvents == null)
        {
            throw new ArgumentNullException("systemEvents");
        }
        if (window == null)
        {
            throw new ArgumentNullException("window");
        }

        systemEvents.SessionSwitch += SystemEvents_SessionSwitch;
        this.window = window;
    }

    private void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
    {
    }

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
}
调整窗口 您需要使用windows的抽象类型(
IWindow
)和帮助管理每个窗口的DI的抽象类型(
WindowAdapter
)来稍微调整框架

internal interface IWindow
{
    void Close();

    IWindow CreateChild(object viewModel);

    void Show();

    bool? ShowDialog();
}

internal class WindowAdapter : IWindow
{
    private readonly Window wpfWindow;

    public WindowAdapter(Window wpfWindow)
    {
        if (wpfWindow == null)
        {
            throw new ArgumentNullException("window");
        }

        this.wpfWindow = wpfWindow;
    }

    #region IWindow Members

    public virtual void Close()
    {
        this.wpfWindow.Close();
    }

    public virtual IWindow CreateChild(object viewModel)
    {
        var cw = new ContentWindow();
        cw.Owner = this.wpfWindow;
        cw.DataContext = viewModel;
        WindowAdapter.ConfigureBehavior(cw);

        return new WindowAdapter(cw);
    }

    public virtual void Show()
    {
        this.wpfWindow.Show();
    }

    public virtual bool? ShowDialog()
    {
        return this.wpfWindow.ShowDialog();
    }

    #endregion

    protected Window WpfWindow
    {
        get { return this.wpfWindow; }
    }

    private static void ConfigureBehavior(ContentWindow cw)
    {
        cw.WindowStartupLocation = WindowStartupLocation.CenterOwner;
        cw.CommandBindings.Add(new CommandBinding(PresentationCommands.Accept, (sender, e) => cw.DialogResult = true));
    }
}

public static class PresentationCommands
{
    private readonly static RoutedCommand accept = new RoutedCommand("Accept", typeof(PresentationCommands));

    public static RoutedCommand Accept
    {
        get { return PresentationCommands.accept; }
    }
}
然后我们为
main窗口
提供了一个专门的窗口适配器,它确保
DataContext
属性使用
ViewModel
正确初始化

internal class MainWindowAdapter : WindowAdapter
{
    private readonly ITrackerViewModelFactory vmFactory;
    private bool initialized;

    public MainWindowAdapter(Window wpfWindow, ITrackerViewModelFactory viewModelFactory)
        : base(wpfWindow)
    {
        if (viewModelFactory == null)
        {
            throw new ArgumentNullException("viewModelFactory");
        }

        this.vmFactory = viewModelFactory;
    }

    #region IWindow Members

    public override void Close()
    {
        this.EnsureInitialized();
        base.Close();
    }

    public override IWindow CreateChild(object viewModel)
    {
        this.EnsureInitialized();
        return base.CreateChild(viewModel);
    }

    public override void Show()
    {
        this.EnsureInitialized();
        base.Show();
    }

    public override bool? ShowDialog()
    {
        this.EnsureInitialized();
        return base.ShowDialog();
    }

    #endregion

    private void DeclareKeyBindings(TrackerViewModel vm)
    {
        //this.WpfWindow.InputBindings.Add(new KeyBinding(vm.RefreshCommand, new KeyGesture(Key.F5)));
        //this.WpfWindow.InputBindings.Add(new KeyBinding(vm.InsertProductCommand, new KeyGesture(Key.Insert)));
        //this.WpfWindow.InputBindings.Add(new KeyBinding(vm.EditProductCommand, new KeyGesture(Key.Enter)));
        //this.WpfWindow.InputBindings.Add(new KeyBinding(vm.DeleteProductCommand, new KeyGesture(Key.Delete)));
    }

    private void EnsureInitialized()
    {
        if (this.initialized)
        {
            return;
        }

        var vm = this.vmFactory.Create(this);
        this.WpfWindow.DataContext = vm;
        this.DeclareKeyBindings(vm);

        this.initialized = true;
    }
}
成分根 最后,您需要一种创建对象图的方法。你是在正确的地方做这件事的,但是你把它分成许多步骤并没有给自己带来任何好处。将容器作为应用程序级变量并不一定是一件好事——它会将容器作为应用程序级变量滥用

然后,
App.xaml.cs
文件将如下所示:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        // Begin Composition Root
        new StandardKernel(new MyApplicationModule()).Get<IWindow>().Show();

        // End Composition Root
    }
}
公共部分类应用程序:应用程序
{
启动时受保护的覆盖无效(StartupEventArgs e)
{
基础。启动时(e);
//从根开始合成
新的标准内核(新的MyApplicationModule()).Get().Show();
//末端组成根
}
}

定义自定义构造函数时,默认构造函数将从类中自动删除。因此,如果任何其他组件需要它,依赖注入器或其他,它将无法解决它。因此,必须显式定义无参数构造函数。这会在从viewmodel触发OnPropertyChanged事件时引发空引用异常。一个小问题是,关闭窗口时应用程序未退出。用这种方法退出应用程序有什么自然的方法吗?PSA:在.NET4.5中,存在一个bug,您可能需要向应用程序(App.xaml)添加一个属性,否则资源可能无法加载。正如解决方法所建议的,您只需设置
名称
,它就可以工作了。
internal class TrackerViewModel : System.ComponentModel.INotifyPropertyChanged
{
    private readonly IWindow window;

    public TrackerViewModel(ISystemEvents systemEvents, IWindow window)
    {
        if (systemEvents == null)
        {
            throw new ArgumentNullException("systemEvents");
        }
        if (window == null)
        {
            throw new ArgumentNullException("window");
        }

        systemEvents.SessionSwitch += SystemEvents_SessionSwitch;
        this.window = window;
    }

    private void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
    {
    }

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
}
internal interface IWindow
{
    void Close();

    IWindow CreateChild(object viewModel);

    void Show();

    bool? ShowDialog();
}

internal class WindowAdapter : IWindow
{
    private readonly Window wpfWindow;

    public WindowAdapter(Window wpfWindow)
    {
        if (wpfWindow == null)
        {
            throw new ArgumentNullException("window");
        }

        this.wpfWindow = wpfWindow;
    }

    #region IWindow Members

    public virtual void Close()
    {
        this.wpfWindow.Close();
    }

    public virtual IWindow CreateChild(object viewModel)
    {
        var cw = new ContentWindow();
        cw.Owner = this.wpfWindow;
        cw.DataContext = viewModel;
        WindowAdapter.ConfigureBehavior(cw);

        return new WindowAdapter(cw);
    }

    public virtual void Show()
    {
        this.wpfWindow.Show();
    }

    public virtual bool? ShowDialog()
    {
        return this.wpfWindow.ShowDialog();
    }

    #endregion

    protected Window WpfWindow
    {
        get { return this.wpfWindow; }
    }

    private static void ConfigureBehavior(ContentWindow cw)
    {
        cw.WindowStartupLocation = WindowStartupLocation.CenterOwner;
        cw.CommandBindings.Add(new CommandBinding(PresentationCommands.Accept, (sender, e) => cw.DialogResult = true));
    }
}

public static class PresentationCommands
{
    private readonly static RoutedCommand accept = new RoutedCommand("Accept", typeof(PresentationCommands));

    public static RoutedCommand Accept
    {
        get { return PresentationCommands.accept; }
    }
}
internal class MainWindowAdapter : WindowAdapter
{
    private readonly ITrackerViewModelFactory vmFactory;
    private bool initialized;

    public MainWindowAdapter(Window wpfWindow, ITrackerViewModelFactory viewModelFactory)
        : base(wpfWindow)
    {
        if (viewModelFactory == null)
        {
            throw new ArgumentNullException("viewModelFactory");
        }

        this.vmFactory = viewModelFactory;
    }

    #region IWindow Members

    public override void Close()
    {
        this.EnsureInitialized();
        base.Close();
    }

    public override IWindow CreateChild(object viewModel)
    {
        this.EnsureInitialized();
        return base.CreateChild(viewModel);
    }

    public override void Show()
    {
        this.EnsureInitialized();
        base.Show();
    }

    public override bool? ShowDialog()
    {
        this.EnsureInitialized();
        return base.ShowDialog();
    }

    #endregion

    private void DeclareKeyBindings(TrackerViewModel vm)
    {
        //this.WpfWindow.InputBindings.Add(new KeyBinding(vm.RefreshCommand, new KeyGesture(Key.F5)));
        //this.WpfWindow.InputBindings.Add(new KeyBinding(vm.InsertProductCommand, new KeyGesture(Key.Insert)));
        //this.WpfWindow.InputBindings.Add(new KeyBinding(vm.EditProductCommand, new KeyGesture(Key.Enter)));
        //this.WpfWindow.InputBindings.Add(new KeyBinding(vm.DeleteProductCommand, new KeyGesture(Key.Delete)));
    }

    private void EnsureInitialized()
    {
        if (this.initialized)
        {
            return;
        }

        var vm = this.vmFactory.Create(this);
        this.WpfWindow.DataContext = vm;
        this.DeclareKeyBindings(vm);

        this.initialized = true;
    }
}
public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        // Begin Composition Root
        var container = new StandardKernel();

        // Register types
        container.Bind<ISystemEvents>().To<MySystemEvents>();
        container.Bind<ITrackerViewModelFactory>().To<TrackerViewModelFactory>();
        container.Bind<Window>().To<MainWindow>();
        container.Bind<IWindow>().To<MainWindowAdapter>();

        // Build the application object graph
        var window = container.Get<IWindow>();

        // Show the main window.
        window.Show();

        // End Composition Root
    }
}
using Ninject.Modules;
using System.Windows;

public class MyApplicationModule : NinjectModule
{
    public override void Load()
    {
        Bind<ISystemEvents>().To<MySystemEvents>();
        Bind<ITrackerViewModelFactory>().To<TrackerViewModelFactory>();
        Bind<Window>().To<MainWindow>();
        Bind<IWindow>().To<MainWindowAdapter>();
    }
}
public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        // Begin Composition Root
        new StandardKernel(new MyApplicationModule()).Get<IWindow>().Show();

        // End Composition Root
    }
}