Creating a File Explorer in WPF using MVC+M

Introduction

In my previous article I explained how one can use the Mediator Pattern together with the MVC Pattern to structure the code in a very neat way. If you did not read my previous article I would suggest that you do, since I am basing this post on the assumption that readers already read that post.

I am writing this post because some friends from WPF Disciple suggested that I use a better example for the MVC+M. I agree 100% with them since the example that I did in my previous post was not the best one I could pick up…

Today I’ve got a new problem that I want to solve using the MVC+M. As the title of the post suggest, we are going to build a File Explorer and hopefully it will look something like this.

Yea, that’s where AvalonControlsLibrary and all other projects of mine live😀

This application is very simple to explain because I would assume that everyone reading this post has used a File Explorer. Basically we have a TreeView that shows the directories and when a directory is selected the list of files in that directory show up on screen.

The Problem

The problem is that we do not have a fixed list of objects. The list is generated on the fly. If we had to load all directories in memory we would have a big performance hit + I don’t want to imagine the memory consumption. So for the TreeView I used the Flyweight Design Pattern. I will not go into details of this pattern since it is not the purpose of this post, but to summarize, each node loads a dummy child so that the user can expand the items. Once an item is expanded the TreeView controller loads the child directories of the directory being expanded.

You’ll say “OK… But what does this have to do with the Mediator?”. The Mediator comes in handy for our second problem. When an item is Selected the list of files show up in the file list (on the right hand side of our application).

So the problem here is, we have a Dynamic Hierarchal List + we have a Treeview (which does not have an IsSynchronizedWithCurrentItem property that can make the underlying ICollectionView notify us with the currently selected item). Speaking of which we now don’t even have one ICollectionView, instead we have one for each level.

How can we tell the File List Controller that an item is selected without coupling it with the Tree View Controller?

The Solution (or at least, how I would do this…)

In such a case the Mediator is key, because the mediator will make the communication between the 2 controller very easy and loosely coupled. Let me first introduce the entities we have here

Views

– DirectorySelectorView

– FileSelectorView

Controllers

– DirectorySelectorController (mentioned above as TreeView contoller)

– FileSelectorController (mentioned above as File List Controller)

Model

– FileSystemDataService

OK so lets’ do this… And guess what we are going to do this in 3 steps once again.

Step 1 – Publish the Directory Selected message via the Mediator
Step 2 – Register the FileSelectorController to receive a notification for the Directory Selected message.
Step 3 – Handle the Directory Selected message in the FileSelectorController and get the files to display.

Lets now talk in C#. The following is the code to do the above mentioned 3 steps.

Step 1

//event handler for the selecting changed
void ItemSelected(object sender, RoutedEventArgs e)
{
    TreeView treeView = (TreeView)e.OriginalSource;
    //Send a message that an item is selected and pass the object selected
    Mediator.NotifyColleagues(
        Messages.DirectorySelectedChanged, treeView.SelectedItem);
}

Step 2

public FileSelectorController()
{
    Mediator.Register(this, new[]
    {
        Messages.DirectorySelectedChanged
    });
}

Step 3

public override void MessageNotification(string message, object args)
{
    switch (message)
    {
        case Messages.DirectorySelectedChanged:
            //load all files for the directory specified
            LoadFiles((DirectoryDisplayItem)args);
            break;
    }
}

That’s all folks …. Download the full source code to see all the rest…

Some Side notes to keep in mind

Developers are like artists. Everyone has his own style. For example, right now the Mediator implementation that I did, is using an interface driven design where you have an interface IColleague and the mediator notifies the colleagues passing the message that is being sent. One can implement a Mediator that uses strongly typed delegate so that you can avoid doing the switch statement (in the MessageNotification method) and implementing the IColleague interface for each controller. So the implementation of such a Mediator would look like this

Register( IDictionary<string, Action> callbacks);

The Colleague that want to register simple do the following

Register( new Dictionary<string, Action>
{
     { Messages.SelectionChanged, delegate(object obj){ //DoSomething here  } }
}

This would totally eliminate the switch statement and the ICollegue interface…

One thing that I have been discussing with some friend from WPF Disciples is, What about big systems? Would big systems have to define loads of Messages?

The answer (at least how I see it from previous experiences working with this pattern) is NO. Why? Because the messages would be only for the controllers part. The other parts of the system would not need such a thing because if the controllers can talk together, the other entities would not need to. If we take the example of the File Explorer, the Views do not need to communicate because the controllers are synchronizing with each other. The views are just reading the data from the controllers. The model does not need this system either because the model will just give data that is being requested from the controller. So, please do not misinterpret this and use this system all around because you would create a MONSTER!

Josh Smith, also pointed out a very good point. What about memory leaks? If controllers do not unregister from the mediator once that the contoller is disposed than the controller would still exist in memory. This is a fact! I did not implement an unregister in the mediator because till now I only had controllers that do not get disposed throughout the application life cycle. Yet to implement an unregister would be just a piece of cake. All you have to do is to ask the mediator to remove the reference to a specific controller when the contoller is being disposed.

Besides the Mediator, there are loads of other ways of doing this, yet I decided to stick to this one because currently I feel that this pattern is given me the flexibility I need.

Conclusion

I like how this works because I feel that I can have a loosely coupled and high cohesion code base.

Loosely coupled, because the controllers know nothing about each other and I can just disable or reuse one of them at any point in time.
High cohesion, because knowing that I can make controllers communicate in an easy way, I create controllers that have one responsibility.

Credits

Thanks to all WPF Disciples for the brilliant discussion
My friend Karl Agius for even some more discussions

Download the full source code of the Demo application.

kick it on DotNetKicks.com

35 thoughts on “Creating a File Explorer in WPF using MVC+M

  1. Is there any particular reason as to why you choose not to use RoutedUICommands?

    Why not use the built in WPF CommandManager as the equivalent of your mediator?

    Create a new RoutedUICommand, for instance called “DirectorySelectedChanged”.

    in your controller’s constructor register a command binding:
    CommandManager.RegisterClassCommandBinding(typeof(object), new CommandBinding(Commands.DirectorySelectedChanged, directorySelectedChanged));

    which will invoke your function “directorySelectedChanged” in your controller, whenever you fire the “DirectorySelectedChanged” command from your view.

    Like this the different controllers can communicate with each others without knowing about each others. The trick seems to be that you register with the CommandManager and set the Type parameter to “typeof(object)” (or whatever base-class all your controllers are.)

    Cheers!

  2. That’s a very good question. Yet the problem is that RoutedCommands (which are RoutedEvents) use event bubbling which means that in order to get notified you must be above the element raised the event in the Logical Tree.

    The approach of the Mediator does not have this limitation and you can be where ever you want in the logical tree.

    Did I make this clear?

  3. Hello everyone,

    My friend Karl (http://karlagius.wordpress.com/) was trying out the MVC+M and he came up with a very cool example. Basically he create a photo viewer and the user can Zoom In/Out the image being viewed. The zoom can be applied by entering text in a textbox or by using a silder.

    This shows how 2 controllers can send the same message via the mediator system and the other view(in this case the photo viewer) handle the message notification. If one would want to change the app to add functionality nothing would change in the other controllers because there is no coupling at all.

    Thanks Karl…

    Download the application source code from here
    http://cid-96f8d49aa44c79c1.skydrive.live.com/self.aspx/Public/PictureMesserNew.zip

  4. I guess I’m still missing something…🙂
    Just like in your example here, you have a window with two views, each view has its own controller. Using RoutedUICommands it’s no problem firing a command from one controller, which will be picked up by the other controller, even if they know nothing about each other. Hence they are both logical children of the window, but that doesn’t stop the commands from passing through (since they get routed through the CommandManager), which doesn’t fit with what you say.
    There’s no problem for the “DirectorySelectorController” to Execute a command, that the “FileSelectorController” will pick up, independent of their hierarchy, as long as “FileSelectorController” register to listen for commands through the CommandManager.
    I do this all the time, works like a charm!🙂

    Cheers!

  5. Ok, now I understood what you mean… Yes you can use that approach for sure… Yet it is a bit dangerous. If you have multiple instances of the controller you get surprises. Basically the registration to the CommandManager happens with the type of class and so the handler can get called multiple times.

    Well at the end of the day, it all boils down to a matter of taste… You can do one thing in a million different ways🙂

  6. No problem🙂

    I figured it out in a very painful way… I was doing unit tests and for some strange reason the tests where passing when I was running each test method alone but when I ran the whole list they were failing… Then I figured it out… Using the Command Manager in that way makes the registration of command handlers mess up when having multiple instances of the controllers registering… In order to find this out I dissasembled the command manager code and stayed 1 full day looking at it’s implemenation… LOL

    It was painful…

    Regards

  7. Heh! I believe you!🙂
    This got me all curious though, so I played around with it. Just like you said, if I have several controllers listening for commands, only the first one registering will receive commands. In hindsight it sort of makes sense, at least if you’re considering the way “CanExecute” is set up in WPF’s command pattern, that WPF is designed to only have 1 receiver of a specific command.

    However, if I in the controllers instead of registering with the CommandManager, instead call CommandManager.AddExecutedHandler, with a function that will be kind of a switchboard for commands in that controller (just like your “MessageNotification” function you implement in the FileSelectorController) it seems to be working just fine.

    Did you try anything along those lines as well, and hit some brickwall?

    Cheers!

  8. When I played around with it I passed in “Application.Current.MainWindow”.

    It’s actually a very good question you’re raising…
    I tried executing Commands from another window, and to get it to work I had to pass “Application.Current.MainWindow” as the IInputElement parameter of the command’s Execute method.

    This seems to work in any way though, if I issue a command in a controller that is tied to a view in a certain window, this can be picked up in any other window of the application as well, which is neat. I guess it works since I’m explicitly raising / listening to commands through the main window, even if the command isn’t actually raised/listened for in the main window.

  9. Nice find Ted. I didn’t know that…

    Yet, if you think about it a command is a bit of an overhead for each message you want to pass to controllers.. The Mediator offers a way how you can do this with just 1 method call.

    Besides that, with the Mediator you can easily Unit Test the communication by Mocking the mediator. With the approach that you are suggesting the Application.Current would not work for unit tests because there would be no Application running…

    Regards

  10. Pingback: More than Just MVC for WPF « C# Disciples

  11. True… I guess I was just desperately trying to get WPFs Command pattern to be what I want it to be.

    Btw, thanks for your excellent blog-post/article, keep ’em coming!🙂

    Cheers!

  12. Pingback: Interesting Finds: March 25, 2008 « Hank Wallace

  13. Nice article🙂 I learned a lot of things while reading it.

    A simple question not directly related to your design pattern. Why do you use UserControl objects to implement your views, and not Page objects (that’s the way we did it in Alambic).

  14. Hi there,
    To start with i would like to thank your for your fantastic article, easy as hell to grasp!!!!

    Im just started of trying to implement my first WPF application or that sake my first desktop application. In the search for the good recipie, not ending with spaghetti code i would like to ask for your advice.

    Lets take your File explorer as an example but extend it just a bit.
    Lets say that we added a context menu in both of your views, DirectorySelectorView and in the FileSelectorView. The context menu would allow the user to delete any file or directory.
    How should I then best synchronize these two views, removing a just deleted node by the end user?
    Should the controller where a deletion take place notify the other controller via your Mediator? If true, what if we imagine a much more complex user interface where several views is needed to be updated, removing the deleted node in one view?
    Still learning and in doubt if i really understand all the different flavours of MVC, wouldnt the classic MVC pattern help out here? If one view deletes a node, it delete the underlying object in the model. The model notifies all views that depends on it.

    Clearly confused how to solve this, do you have any solution for me`?

    Best Regards Niclas

  15. “If one view deletes a node, it delete the underlying object in the model. The model notifies all views that depends on it.”

    I think that this would be your best option…

  16. Really A nice article🙂
    However I just want to know- Can we use same controller for more than 1 views. e.g. FileSelectorView1, FileSelectorView2
    can use same controller “FileSelectorController” if yes than how will Controller distinct among Views based on Message Notification “DirectorySelectedChanged”(which is be same for Both Views).

    Regards

  17. E.g. suppose we have two DirectorySelectorView1 which display C drive Listng, DirectorySelectorView2 D Drive listing. And We have also two FileSelectorView1 and FileSelectorView2 corresponding to DirectorySelectorView1 and DirectorySelectorView2 respectively.

    Now If I use same controller for Both FileSelectorView1 and FileSelectorView2 then same Message is Registered using public FileSelectorController(){
    Mediator.Register(this, new[]{
    Messages.DirectorySelectedChanged});}

    Now when DirectorySelectorView1 fire This message DirectorySelectedChanged then which FileSelectorController is going to catch it.

    Regards

  18. Hello there !

    I found your post quite recently, and this pattern is really interesting !

    Anyway, I was wondering : wouldn’t it be good to solve the memory leak issue using weak references in your Mediator ? This way, you won’t risk referencing an unused controller and let the garbage collector do its job !
    I don’t master the reference part, so I’d like to have your feedback on this please ?🙂

    Keep on blogging, great articles in here !

    Best regards

  19. Pingback: Article revisited: MVVM + M (implementing a Fly Weight TreeView) « C# Disciples

  20. “So for the TreeView I used the Flyweight Design Pattern. … to summarize, each node loads a dummy child so that the user can expand the items. Once an item is expanded the TreeView controller loads the child directories of the directory being expanded.”

    That is not what the FlyWeight Design Pattern does. This is “lazy-loading” or “on-demand-loading”.

    A correct example of FlyWeight can be found in DOM libraries, with the implementation of namespaces (Dom4J comes to mind, but there are plenty of other examples where single instances are shared amongst several “hosts”).

    Cheers, Dan

  21. “are you summerizing my article or is this some kind of question??”

    Neither. It’s a remark about an inaccuracy in your article.

    Best, Daniel

  22. Hi Marlon,
    Nice idea, clean implementation.
    Any licensing issues for use in commercial software?

    Don’t see any info on your website.

    Thanks!

  23. Fair enough. Less concerned with warranties than any licensing issues. The code itself looks very straight-forward.

    Using your code saves me the hassle of a rewrite.

    Thanks!

  24. Looks like an implementation of Cocoa’s NSNotificationCenter to me. I did something similar in my WinForms days before I was made redundant.

  25. I have looked at your project in Blend 4 and it’s just what I needed. Super

    I though have one question. How can I get the selected path and filename to lets say a TextBox.Text?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s