C# Disciples

my life in Avalon ….

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 :D

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

March 21, 2008 - Posted by marlongrech | .Net 3.0, .Net 3.5, WPF | | 25 Comments

25 Comments »

  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!

    Comment by Ted | March 21, 2008 | Reply

  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?

    Comment by marlongrech | March 21, 2008 | Reply

  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

    Comment by marlongrech | March 21, 2008 | Reply

  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!

    Comment by Ted | March 21, 2008 | Reply

  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 :)

    Comment by marlongrech | March 21, 2008 | Reply

  6. Ahh, ok, didn’t know that! Thanks for clearing it up!

    Comment by Ted | March 21, 2008 | Reply

  7. 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

    Comment by marlongrech | March 21, 2008 | Reply

  8. 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!

    Comment by Ted | March 22, 2008 | Reply

  9. Hi Ted,

    What would you pass as a UIElement for the AddExecutedHandler ?

    Comment by marlongrech | March 22, 2008 | Reply

  10. 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.

    Comment by Ted | March 22, 2008 | Reply

  11. 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

    Comment by marlongrech | March 23, 2008 | Reply

  12. [...] See also Part 2 [...]

    Pingback by More than Just MVC for WPF « C# Disciples | March 23, 2008 | Reply

  13. 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!

    Comment by Ted | March 23, 2008 | Reply

  14. Thanks dude… it was really nice talking to you :)

    Comment by marlongrech | March 23, 2008 | Reply

  15. [...] Roy Osherove – Every Build They BreakMark Russinovich – RunAs Shell ExtensionMarlon Grech – Creating a File Explorer in WPF using MVC+M Technorati tags: RunAs, WPF, [...]

    Pingback by Interesting Finds: March 25, 2008 « Hank Wallace | April 2, 2008 | Reply

  16. 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).

    Comment by Jeremy | May 16, 2008 | Reply

  17. mmm… I only use Page when Navigation from the Window….

    Comment by Marlon Grech | May 16, 2008 | Reply

  18. 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

    Comment by Niclas | June 11, 2008 | Reply

  19. “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…

    Comment by marlongrech | June 12, 2008 | Reply

  20. 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

    Comment by dutt | June 13, 2008 | Reply

  21. yea sure you can… with regards to how one can distinguish between one Controller and another, the question would probable be why would you want to?

    Comment by marlongrech | June 13, 2008 | Reply

  22. 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

    Comment by dutt | June 13, 2008 | Reply

  23. Since the message is a string you can append something to the message per instance you create… that could be one Idea….

    Comment by marlongrech | June 13, 2008 | Reply

  24. 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

    Comment by Cyril | April 3, 2009 | Reply

  25. Yea that is one this I will be addressing shortly…

    Thanks for the comments

    Comment by Marlon Grech | April 3, 2009 | Reply


Leave a comment