2
\$\begingroup\$

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 IDocumentViews and IDocumentControllers, 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:

  1. How can I best implement Controller.TryClose() so it can be used from both the controller and the view, should I have two methods in IClose, CleanUp(), let the controller clean its business and Close() actually close the view as well as clean up?

  2. Sometimes the user will close the main shell. Here I can to loop through all open documents and check CanClose() of IGuardClose. 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?")?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.