I have a WinForms MVC application that uses Ninject for it's Dependency Injection (DI) / IoC Container. I have build quite a nice framework that allows the main shell (which uses a Docking Container to house an manipulate windows - like Visual Studio), to manipulate IDocument
types (actual documents .txt, .xlsx etc.) and ITool
types (my utilities, like file system explorer tree view, Command Window etc.). I communicate from the views (which are blind and deaf to the fact the controllers exist) to their controllers via event handlers. So the architecture is like this:
IView.cs:
public interface IView
{
string DisplayName { get; set; }
}
IController.cs:
public interface IController
{
bool IsDirty { get; set; }
IView View { get; }
}
IDocumentView.cs:
public interface IDocumentView : IView, IActivate, IDeactivate
{
bool StatusBarVisible { get; set; }
}
IDocumentController.cs:
public interface IDocumentController : IController
{
bool Handles(string path);
DocumentView New(string fileName);
DocumentView Open(string path);
void Save();
string FilePath { get; set; }
IEnumerable<DocumentFileType> FileTypes { get; }
}
I then have an abstract DocumentView
class that handles so common behaviors of all IDocument
types.
DocumentController.cs:
public abstract class DocumentController : IDocumentController, IClose, IGuardClose, IDisposable
{
// Lots of stuff...
}
IGuardClose.cs:
public interface IGuardClose
{
void CanClose(Action<bool> callback);
}
IClose.cs:
public interface IClose
{
bool TryClose(object sender, FormClosingEventArgs e);
}
DocumentView.cs: [TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider))] public abstract class DocumentView : DockContent, IDocumentView, IViewManagment { public virtual EventHandler OnViewLoaded { get; set; } public virtual EventHandler OnViewClosing { get; set; }
public virtual EventHandler<EventArgs> ViewActivated { get; set; }
public virtual EventHandler<EventArgs> ViewDeactivate { get; set; }
public abstract string DisplayName { get; set; }
public virtual bool StatusBarVisible { get; set; }
}
IViewManagement.cs:
public interface IViewManagment
{
EventHandler OnViewLoaded { get; set; }
EventHandler<FormClosingEventArgs> OnViewClosing { get; set; }
}
They allow me to wire my controller up to listen for the loaded and losing events triggered from the view without the view knowing anything about it, but herein lies my problem.
For my actual IDocumentView
s and IDocumentController
s, I inherit from DocumentView
and DocumentController
respectively. This works well and all is fine, I can open new documents, open from files system you name it. My issue, is to do with closure. If I click the views "X" button to close the view, I need (and do) let the controller clean up some resources it is using (a FileSystemWatcher
for the opened file etc.), but I also need to be able to close the view from the controller, so the TryClose
method cannot merely do View.Close()
for all calls as clearly this will envolve a stack overflow, as the View.Close()
request would lead to another call to the controller TryClose
etc.
My questions:
How can I best implement
Controller.TryClose()
so it can be used from both the controller and the view, should I have two methods inIClose
,CleanUp()
, let the controller clean its business andClose()
actually close the view as well as clean up?Sometimes the user will close the main shell. Here I can to loop through all open documents and check
CanClose()
ofIGuardClose
. Assuming I can easily access the controllers from the main shell in a for loop/foreach loop, how best could I implement a close all (with safety - "some documents are unsaved do you want to save now?")?