Building a WinForms Application using MVC and Ninj

2019-05-17 00:50发布

问题:

I am having to re-write a large WinForms application and I want to use MVC to allow increased testing capability etc. I want to also adopt Ninject as my IoC container as it is lightweight, fast and will increase the exstensibility of my application going forward.

I have done a great deal of reading and I have managed to make a start on the arcitecture of this new application. However, I am not sure i have the right idea when using Ninject. The code...

Starting with Program.cs and related classes...

static class Program
{
    [STAThread]
    static void Main()
    {
        FileLogHandler fileLogHandler = new FileLogHandler(Utils.GetLogFilePath());
        Log.LogHandler = fileLogHandler;
        Log.Trace("Program.Main(): Logging initialized");

        CompositionRoot.Initialize(new ApplicationModule());

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(CompositionRoot.Resolve<ApplicationShellView>());
    }
}

public class CompositionRoot
{
    private static IKernel _ninjectKernel;

    public static void Initialize(INinjectModule module)
    {
        _ninjectKernel = new StandardKernel(module);
    }

    public static T Resolve<T>()
    {
        return _ninjectKernel.Get<T>();
    }
}

public class ApplicationModule : NinjectModule
{
    public override void Load()
    {
        Bind(typeof(IApplicationShellView)).To(typeof(ApplicationShellView));
    }
}

An my ApplicationShellView is

public partial class ApplicationShellView : Form, IApplicationShellView
{
    public ApplicationShellView()
    {
        InitializeComponent();
    }

    public void InitializeView()
    {
        dockPanel.Theme = vS2012LightTheme;
    }
}

with interface

public interface IApplicationShellView
{
    void InitializeView();
}

The controller for this view is

public class ApplicationShellController
{
    private IApplicationShellView view;

    public ApplicationShellController(IApplicationShellView view)
    {
        view.InitializeView();
    }
}

Currently the controller is redundant, and although this code works and my view displays, I have some important questions...

  1. Should I be using the ApplicationShellController to initialize my form, currently this is not using MVC "pattern"?
  2. It feels like I have written a Service Locator, and from what I have read, this is bad. How else should I be using Ninject for IoC to initialize my application?
  3. Any other advice as to what I am doing right[if anything!]/wrong?

Thanks very much for your time.

回答1:

  1. No you should not be initializing your controller, this exactly what IoC and Ninject are for. When you initialize your view/form, Ninject should make the view fetch the controller it depends on, which will auto fetch controllers it depends on and so on.
    Of course this won't work like you've set it up right now. For starters, your view needs to know the controller it depends on.

    public partial class ApplicationShellView : Form, IApplicationShellView
    {
        private IApplicationShellController _controller;
    
        public ApplicationShellView()
        {
            InitializeComponent();
            init();
    
            //InitializeView()
        }
    
        private void init() {
            _controller = NinjectProgram.Kernel.Get<IApplicationShellController>();
            //Because your view knows the controller you can always pass himself as parameter or even use setter to inject
            //For example:  _controller.SetView1(this);
        }
    
        public void InitializeView()
        {
            dockPanel.Theme = vS2012LightTheme;
        }
    }
    
    public class ApplicationShellController : IApplicationShellController
    {
    
        //Implementes functionality for the MainForm.
    
        public ApplicationShellController()
        {
            //Also possible to add other controllers with DI
        }
    }
    
  2. This does indeed look like a Service Locator, simply initializing your view should do be sufficient.

    public class NinjectProgram
    {
        //Gets the inject kernal for the program.
        public static IKernel Kernel { get; protected set; }
    }
    
    public class Program : NinjectProgram
    {
        [STAThread]
        private static void Main()
        {
            Kernel = new StandardKernel();
            Kernel.Load(new ApplicationModule());
    
            Application.Run(new ApplicationShellView());
        }
    }
    
    public class ApplicationModule : NinjectModule
    {
        public override void Load()
        {
            //Here is where we define what implementations map to what interfaces.
            Bind<IApplicationShellController>().To<ApplicationShellController>();
    
            //We can also load other modules this project depends on.
            Kernel.Load(new NinjectModule());
        }
    }
    
  3. Don't try and make it too complicated, a good start is important but you can always apply changes when and where needed during development.

I believe the following GitHub project might be a good starting point: Example of how you might use Ninject within a WinForms application.

If you have any more questions, just leave a comment and I'll try to answer them as soon as possible