I am a relatively experienced WPF developer who has had good exposure to MVVM. I have been developing legacy WinForms applications for a while and have recently been asked to fully re-write a large one.
To do this I want to use TDD and in order to facilitate this and make the application as extensible as possible going forwards (opposite to what the old one was), I want to incorporate Dependency Injection and use the MVC pattern.
I would like some guidance on a few things:
- Is my use of Ninject for the DI Container proper and correct?
- Is my use of MVC proper and correct?
- How could I improve my code?
Program.cs (the main entry point of the WinForms application):
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 DependencyModule());
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(CompositionRoot.Resolve<ApplicationShellView>());
}
}
DependencyModule.cs
public class DependencyModule : NinjectModule
{
public override void Load()
{
Bind<IApplicationShellView>().To<ApplicationShellView>();
Bind<IDocumentController>().To<SpreadsheetController>();
Bind<ISpreadsheetView>().To<SpreadsheetView>();
}
}
CompositionRoot.cs
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 static IEnumerable<T> ResolveAll<T>()
{
return ninjectKernel.GetAll<T>();
}
}
ApplicationShellView.cs (the main form of the application)
public partial class ApplicationShellView : C1RibbonForm, IApplicationShellView
{
private ApplicationShellController controller;
public ApplicationShellView()
{
this.controller = new ApplicationShellController(this);
InitializeComponent();
InitializeView();
}
public void InitializeView()
{
dockPanel.Extender.FloatWindowFactory = new CustomFloatWindowFactory();
dockPanel.Theme = vS2012LightTheme;
}
private void ribbonButtonTest_Click(object sender, EventArgs e)
{
controller.OpenNewSpreadsheet();
}
public DockPanel DockPanel
{
get { return dockPanel; }
}
}
Where:
public interface IApplicationShellView
{
void InitializeView();
DockPanel DockPanel { get; }
}
ApplicationShellController.cs
public class ApplicationShellController
{
private IApplicationShellView shellView;
private List<IDocumentController> documentControllerCache = new List<IDocumentController>();
[Inject]
public ApplicationShellController(IApplicationShellView view)
{
this.shellView = view;
}
public void OpenNewSpreadsheet(DockState dockState = DockState.Document)
{
SpreadsheetController controller = (SpreadsheetController)GetDocumentController("new.xlsx");
SpreadsheetView view = (SpreadsheetView)controller.New("new.xlsx");
view.Show(shellView.DockPanel, dockState);
documentControllerCache.Add(controller);
}
private IDocumentController GetDocumentController(string path)
{
return CompositionRoot.ResolveAll<IDocumentController>()
.Cast<IDocumentController>()
.Where(provider => provider.Handles(path))
.Select(provider => provider)
.FirstOrDefault();
}
public IApplicationShellView ShellView { get { return shellView; } }
}
SpreadsheetController.cs
public class SpreadsheetController : IDocumentController
{
private ISpreadsheetView view;
public SpreadsheetController(ISpreadsheetView view)
{
this.view = view;
this.view.SetController(this);
}
public bool Handles(string path)
{
string extension = Path.GetExtension(path);
if (!String.IsNullOrEmpty(extension))
{
if (FileTypes.Any(ft => ft.FileExtension.CompareNoCase(extension)))
return true;
}
return false;
}
public void SetViewActive(bool isActive)
{
((SpreadsheetView)view).ShowIcon = isActive;
}
public IDocumentView New(string fileName)
{
// Opens a new file correctly.
}
public IDocumentView Open(string path)
{
// Opens an Excel file correctly.
}
public IEnumerable<DocumentFileType> FileTypes
{
get
{
return new List<DocumentFileType>()
{
new DocumentFileType("CSV", ".csv" ),
new DocumentFileType("Excel", ".xls"),
new DocumentFileType("Excel10", ".xlsx")
};
}
}
}
Where the implemented interface is:
public interface IDocumentController
{
bool Handles(string path);
void SetViewActive(bool isActive);
IDocumentView New(string fileName);
IDocumentView Open(string path);
IEnumerable<DocumentFileType> FileTypes { get; }
}
Now the view ascociated with this controller is:
public partial class SpreadsheetView : DockContent, ISpreadsheetView
{
private IDocumentController controller;
public SpreadsheetView()
{
InitializeComponent();
}
private void SpreadsheetView_Activated(object sender, EventArgs e)
{
controller.SetViewActive(true);
}
private void SpreadsheetView_Deactivate(object sender, EventArgs e)
{
controller.SetViewActive(false);
}
public void SetController(IDocumentController controller)
{
this.controller = controller;
Log.Trace("SpreadsheetView.SetController(): Controller set successfully");
}
public string DisplayName
{
get { return Text; }
set { Text = value; }
}
public WorkbookView WorkbookView
{
get { return workbookView; }
set { workbookView = value; }
}
public bool StatusBarVisible
{
get { return statusStrip.Visible; }
set { statusStrip.Visible = value; }
}
public string StatusMessage
{
get { return statusLabelMessage.Text; }
set { statusLabelMessage.Text = value; }
}
}
The view interfaces are:
public interface ISpreadsheetView : IDocumentView
{
WorkbookView WorkbookView { get; set; }
}
And:
public interface IDocumentView
{
void SetController(IDocumentController controller);
string DisplayName { get; set; }
bool StatusBarVisible { get; set; }
}
All this code works and "feels okay". But I am not experienced with either MVC (at least not using WinForms) or Ninject/DI (I have used MEF extensively, but that is slightly difference that what Ninject does).
Note: I am implementing IDocumentView
because I want to also open and dock other types of documents (code and text etc.).
Could you provide and guidance as to what I have done so far and answer the questions above?