Basic MVVM Template (with Ninject)
==================================
`Program` Class
---------------
Your program entry point should be changed to the following code snippet. This is more complex
than the default one for .NET Windows Forms projects, but you'll have a fully usable Ninject
kernel, emergency error handling on missing dependencies and cleanup of any services that
implement `IDisposable`.
```csharp
/// Setup code and main entry point for the application
internal static class Program {
/// The application's main entry point
[STAThread]
static void Main() {
try {
// Wrapping the application code in a method lets .NET's "fusion" linker to compile
// and run the Main() method, even when there are missing dependencies. This enables
// us to at least display some kind of error message rather than just fizzling out.
runApplication();
}
catch(Exception error) {
MessageBox.Show(
"Application failed to launch: " + error.Message,
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
}
}
///
/// Initializes services, creates the main window and keeps the application running
/// until the main window closes.
///
private static void runApplication() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Set up our dependency injector and all its bindings
using(var kernel = new StandardKernel()) {
setupBindings(kernel);
// Then let the window manager create the view model and show the window
var windowManager = kernel.Get();
Application.Run(windowManager.OpenRoot());
}
}
/// Setups up dependency bindings for the application
/// Dependency injector the bindings will be set up in
private static void setupBindings(IKernel kernel) {
kernel.Load();
kernel.Bind().ToConstant(kernel);
}
}
```
MainForm
--------
The main window should inherit from `ViewForm`. It won't change anything regarding how you use
Windows Forms designer and such, but it gives your main window a new property: `DataContext`.
The name `DataContext` comes from WPF and UWP and is used for data binding. For example, a list
control would have a `DataContext` property to which you can assign any kind of list object to
make the list control display its contents.
Similarly, the `DataContext` of your main window will be its view model. The `WindowManager` of
`Nuclex.Windows.Forms` checks if your Form inherits from `ViewModel` and automatically assigns
the view model to the `DataContext` if it can.
```csharp
/// Main window for the application
public partial class MainForm : ViewForm {
/// Initializes a new main window
public MainForm() {
InitializeComponent();
}
/// The view model for the main window under its proper type
private MainViewModel ViewModel {
get { return DataContext as MainViewModel; }
}
// ...
}
```
MainViewModel
-------------
As you could already see in the `Program` class at the top, rather than create and display
a form itself, the new `Program` class just asks the `WindowManager` to "open"
the `MainViewModel` and create its default view, which is discovered by name. Names it will
try are: `MainView`, `MainPage`, `MainForm`, `MainWindow`, `MainDialog` and `MainControl`.
```csharp
var windowManager = kernel.Get();
Application.Run(windowManager.OpenRoot());
```
Thanks to Ninject, our `MainViewModel` can have constructor parameters and Ninject will
try to satisfy them by looking in its bound services:
```csharp
/// View model for the application's main window
public class MainViewModel : ViewModel {
/// Initializes a new view model for the main view
/// Window manager used to display child windows
/// Service to display messages to the user
public MainViewModel(
IWindowManager windowManager,
IMessageService messageService,
) : base(windowManager) {
this.messageService = messageService;
}
/// Displays an example message to the user
public void ShowExampleMessage() {
this.messageService.Inform(
new MessageText() {
Caption = "Example",
Message = "This is an example message."
}
);
}
/// Reports an error to the user
/// Error that will be reported
protected override void ReportError(Exception exception) {
this.messageService.ReportError(
new MessageText() {
Caption = "Error",
Message = "An error has occurred: " + exception.Message
}
);
}
/// Displays messages to the user
private IMessageService messageService;
}
```