Composite application with CM

Apr 15, 2011 at 6:46 PM

Hey, I love Caliburn.Micro and I'm thinking of using it for a larger composite application in WPF that we are working on.

Until now we have used Prism, but the lack of MVVM support is a bit discouraging. First I was looking into CM to only to use the MVVM parts and use Prism for composition but the posts here  seem to discourage this.  Here are my questions:

  • Can CM be used for rather large WPF application that contain a lot of different modules? Or should I use Caliburn for this?
  • What's the best way to do this? I saw this hint and looked at this reference application, but could not find enough details how to do menus, toolbars, mdi, etc.
  • I created a small prototype how to do composition with CM here: https://github.com/lukebuehler/Caliburn-Composite-Prototype do you think this is a good way to do this or am I way off? It loads the "modules" from a certain folder and injects the views into different "regions" in the shell.
Apr 16, 2011 at 9:00 AM

Hi,
I think Rob has said that Caliburn is not going to be developed further, and Caliburn.Micro is the way to go.
I guess your App is Desktop WPF which is like what I'm doing, whereas most others I have found are Silverlight. There's another you can look at, www.ravendb.net.
As far as I can see your demo follows the same advice I'm using and the basic guidance I got from the HelloScreens example.
However, in a large App, will yours have a User login and a permissioning system?
In that case I think there needs to be a module loader as otherwise there is no control over who sees what modules - and if the User is not going to see it, should it be loaded at all?
And as the modules know what Menu and Toolbars they should load these need to be switched in in the right places and order. And a MessageBox service in the Shell so that any module can raise a dialog.
It all starts to get complex quickly.
But having looked at many other MVVM frameworks, I do like CM. It's events from xaml options are very convenient, easy to use and helps keeps control references out of the ViewModel.
I hope you'll update us with how you get on.

Apr 16, 2011 at 10:29 AM

I've happened to use both Caliburn and lately Caliburn.Micro on enterprise class applications using modularity.
In my experience, CM is perfectly fine to accomplish the task; also, it has the advantage of a greater flexibility.

Instead of following a predefined (and often overly-complex) modularity approach, you can plug the one that best fits your scenario.
You can model a specific module management (like you did in your prototype, creating a file-based module catalog) or leverage the MEF modularity capabilities. 

To support even complex modularity/composition scenarios you have several arrows in your quiver:

1) you can use a "pull" approach defining some well known interface and have the shell VM (or ancillary VMs) to deal with a variable number of items obtained through the container.
This is the approach you used in your prototype - which I found very effective, by the way;

2) you can follow a "push" path, defining an explicit interface for the shell VM and letting the modules to actively interact with it in particular moments of their lifecycle.
For example a module can register itself against the shell at load time.
You can read a discussion around this approach, focused on populating a toolbar model, here: http://caliburnmicro.codeplex.com/discussions/2475353);

3) you can use existing or custom platform abstractions (like IWindowManager) to access platform specific features, like opening dialogs.
See for example this thread about dealing with a Docking Manager: http://caliburnmicro.codeplex.com/discussions/231809

A final thought: I've happened to be strongly inclined to favor configurable, big and complex frameworks to accomplish some key application needs (like modularity), but I radically changed my mind about it. 
Using small, application-specific and very focused solution and conventions often lead me to better (and more manteinable) results.

 

Apr 16, 2011 at 5:18 PM
Edited Apr 16, 2011 at 5:22 PM

Great feedback, thank you guys!

The whole "pull" and "push" idea is becoming more clear to me now, I have been thinking about this before, but this helped me clarify.

  • Push: My experience with prism is using more of a push approach (especially when using Unity): the module class and some VMs get references to infrastructure services injected and then register their stuff. The only thing I really don't like is the "anti-pattern" that seems to appear: my constructors can have 5-10 services that are injected (infrastructure and then the actual business logic services)! For example a complex VM that has menu and toolbar items, dialogs, etc can quickly look like this: MyConstrucor(IMenuViewModel menu, IDialogViewModel dialog, ISomeOtherInfrastructureService service, etc). And so becomes quite difficult to test. Of course this can be partially avoided my having a module class in each module that takes a lot of the "pushing" out of the VMs.
  • Pull: The pull approach seem quite efficient to me too, for example I define a menu action in a module, I encapsulate all infos (name, icons, etc) in a class, add the Eport attribute and there it is. No registering it anywhere, it just plugs itself in. BUT of course this approach has it's drawbacks, especially how to change stuff in the shell at runtime, for example context sensitive toolbars. Opening new MDI tabs, etc. Does anyone has some experience mixing the pull and push based approach? Or how do you solve it? Interestingly, in the MEF guidance of Prism it looks quite like they do some kind of hybrid.

I'm also kinda moving away from the whole "one framework to rule them all" approach. Robs talk at MIX10 about creating your own framework was really inspiring in that matter!

Regarding the requirements, it's not gonna need a permission system but we will need following:

  • A file menu
  • A toolbar
  • And a MDI style workspace environment. For which we use AvalonDock. Most of the layout will be left to the user how he prefers it, i.e. he can move the panels around to wherever he wants.
  • The ability to load modules on demand.

I think I'll take some time and implement the ideas, and feedback of the other threads into my prototype and see how it feels.

PS: I've been searching for more details on Caliburn VS. CM but I've seen Rob saying that Caliburn will be discontinued?

Coordinator
Apr 16, 2011 at 5:38 PM

Caliburn basically exists to support existing apps. All new development should be done with Micro. Future development will be on Micro. After v2.0 release of Caliburn, no further releases will be made unless we find a catastrophic bug.

Apr 16, 2011 at 6:22 PM

Thanks! Now that you've said it, it's on the very top of the documentation... how could I miss that! http://caliburn.codeplex.com/documentation

Also just listened to the .NetRocks podcast, where this point is made sufficiently clear. http://www.dotnetrocks.com/default.aspx?showNum=638

Apr 16, 2011 at 9:18 PM

I was just comparing the Prism4 MEF composition with how composition could work with CM. For this, the "RI - StockTrader Reference Implementation" is especially  interesting which makes extensive use of MEF and the MVVM pattern.

What I just realized is -and what might be of interest to other who do this comparison- that what basically becomes the most important part of Prism is the region handling stuff because they take a View-First approach. BUT if you adhere to the ViewModel-First approach of Caliburn this becomes irrelevant. Here's how it works in Prism4:

  1. The bootstrapper loads the modules.
  2. Prism finds the Views with ViewExportAttributes, which register themselves in regions. For example here.
  3. The region manager then creates and instance of the view and injects the VM, hence the view first approach.

On the other hand in CM:

  1. The bootstrapper loads the modules.
  2. MEF finds the ViewModels to inject into the ShellVM.
  3. The ViewModelBinder resolves all the views, hence the view model first approach.

Rob hints at this in the .NetRocks podcast by the way. Now, what does that imply? By choosing the VM first way, a lot of Prism is not needed anymore, the only interesting parts are (maybe) the event aggregator and the extensive module support (although I'm not sure if it still applies). On the other hand if you choose to use Prism's view first approach then it makes little sense to use CM.

Please correct me if I'm understanding this wrong. I'm not exactly sure how this applies to Unity and Prism4/Caliburn.Micro.

Coordinator
Apr 16, 2011 at 11:53 PM

I think you have gleaned the important parts :) It's quite a significant difference in approaches. In my experience, solving UI problems with VM-First tends to be easier than with View-First. I should probably write an article with some concrete examples....but there are only so many hours in the day and I have several other things on the todo list still ;) Also, I would like to note that CM has en event aggregator too...

Apr 19, 2011 at 6:19 PM
Edited Apr 19, 2011 at 6:24 PM

I've added some features to the protoype:

  • MDI using AvalonDock, based on BladeWise's idea of extending the WindowManger.
  • Menu's by creating an ActionItemManager.  It can be used also for toolbars.

The ActionItemManager is used like this:

 

//setup the menu
menuManger.WithParent("Customer")
      .ShowItem(new ShowCustomerAction(windowManager))
      .WithScopeOf(this)
      .ShowItem(new AddCustomerAction());

 

When the scope (e.g screen) is activated or deactivated the action items are activated as well. But I don't want all menu items to be bound to the scope so the scope can be entered whenever. What do you think, is this a good way of doing it or am I misusing the IActivatable and IDeactivatable interfaces?

Feedback is appreciated!

The source is here: https://github.com/lukebuehler/Caliburn-Composite-Prototype

Apr 19, 2011 at 6:26 PM
Edited Apr 19, 2011 at 7:40 PM

I suppose it is a nice design.

I must admit that I preferred to promote MenuItems and ToolBarItems to view-model-defined entities, to allow for a fine-grain customization. This way I could deal even with context menu items sharing and complex scenarios where the MenuItems are not a simple way to activate an Action (e.g. a context menu used to display a color picker). As soon as I have a sample ready I will post a bit of code.  

Apr 19, 2011 at 6:42 PM
Edited Apr 19, 2011 at 6:49 PM

So you mean that you would register the actual MenuItems instead of VMs like this?

//setup the menu
menuManger.WithParent("Customer")
      .ShowItem(new MenuItem()
      .ShowItem(new DerivedOfMenuItem);
My idea is that I'll create several different ActionItems that are then displayed with different templates, e.g. ColorPickerActionItem. I'll need something like this in the toolbar for example, because I'll have a drop down selection in the toolbar (like the Release/Debug drop down in VisualStudio).
On a side note: How would you do a master-details view model? Would you use the OneActive conductor?
Apr 19, 2011 at 7:37 PM
Edited Apr 19, 2011 at 7:40 PM

I was probably not clear in my previous post. Our approaches are more or less the same, except that I have one more layer of abstraction. 

My scenario involves a UI that can be dinamically expanded using plug-ins. To deal with this scenario in view-models-first, I preferred to define interfaces to identify the critical parts of the application (IShell, IMenu, IMenuItem, IContextMenu, IToolBar, IRibbon, IRibbonGroup, IRibbonItem, IStatusBar etc.), then I defined some specialized view-models (some of them inheriting from IScreen, others simply from INotifyPropertyChangedEx, since I didn't need the IViewAware interface) and their associated views.

This means that in my case I explicitly compose the UI at the view-model level; as an example, given these interfaces

public interface IMenu
{
    BindableCollection<IMenuItem> Items { get; }
}

public IShell : IScreen
{
   IMenu Menu { get; }
}

plug-ins can extend the Menu capabilities with something like this:

IoC.Get<IShell>.Menu.Items.Add(new MyActionMenu(() => DoSomething()));

To cope with standard functionalities (like MenuItems used to invoke Actions, or simple MenuItems used  to display a boolean state), I defined standard view-models and associated views.

Re-reading your post, I just noticed that your approach (except maybe for the interfaces part) is more or less the same as mine... I was fooled by the name you've given to the menu items, since I was under the wrong impression that ShowCustomerAction and alike were something like Action wrappers, and not first class view-models. My bad!

Apr 19, 2011 at 8:02 PM

Yes, my items are VMs, I just struggle to find a good name that applies for menuItems and toolbarItems since I want to share the base class, but I even thought this might clash with the action stuff of MC... my bad!

My scenario is the same like yours. I have a shell and the plugins, I call them modules, can extend the shell. I really like your approach of nesting it all in the IShell interface, then the module can hold a single reference to the IShell interface and do all "extending" through that. My only concern is how testable that is, I assume you have a mock or something for the whole shell interface? If the modules only request the fine grained interfaces where they extend, it might be easier to test? But I'm not sure on this one. But I'm sure that it's easier to read and probably maintain if everything is accessed through the shell interface.

Thanks for helping me out here! The WPF app that I'm working on has right now about 50MV's and its already difficult to maintain, that's why I decided to go back to square one and design a good architecture for it, CM has helped me quite a lot, but now I'm trying to kinda extract the higher level best practices.

Apr 19, 2011 at 8:31 PM

Honestly I tend to think that best practices are quite subjective! :)

Regarding the interfaces approach, I register both the IShell and some of the parts of the UI that can be extended (IRibbon, the main IMenu , IStatusBar); note that the individual parts of the shell are dependencies of the IShell view-model, so that it would be possible to replace the current implementation with a new one with zero efforts.

About testability, all-in-all I tend to think that using interfaces can make testing simpler, since the application modules are highly decoupled. But honestly, it's just a personal preference... :)

Aug 11, 2011 at 4:56 PM

Hello, I started with the project that lukebuehler had on GitHub and found that the architecture had some issues:

First off, I wanted my modules to be able to intercept a CanClose() call and refuse a close attempt at the window level (the X).  The way I got that to work was to have DockViewModel inherite from  Conductor<object>.Collection.OneActive and then change the saving of the tabVMs as 

        [ImportingConstructor]
        public DockViewModel([ImportMany]IEnumerable<IDockViewModel> tabVMs)
        {
            this.Items.AddRange(tabVMs);
        }

This worked once I added a CanClose call to the ShellViewModel that passed it on to the DockViewModel.

The next thing I wanted to do was save the layout.  The issue there was there wasn't a clean way to trigger the OnDeactivate in a separate DockViewModel.  The Screen of DockViewModel is never activated or initialized.

What I finally did was move the avalon:DockingManager component into the ShellView and then move the contents of DockViewModel into the ShellViewModel.

Is this still inline with the original intent of how Caliburn MVVM applications are supposed to be structured?  Or am I missing something?

Thanks,

Nathan Brown

Aug 11, 2011 at 5:37 PM
Edited Aug 11, 2011 at 5:39 PM

Hi Nathan,

I created this project mostly for myself a while ago to see how a modular design of an app could look like. Since then the app that I'm working on (based on caliburn.micro) has progressed more, but I find that some of the concepts that the my GItHub project shows are still valid, but by no means canonical! So if you find that a certain changes to whatever I did suits your need better go for it! I was already aware for example that persisting the avalon dock layout will require some changes to the project.

I've been using now the CM framework for about half a year for a bigger application that I'm working on and have refactored the module architecture several times without much hassle. So as you project progresses you can still adapt! For example I've switched in several places from the "pull" to the "push" approach that is mentioned above, but that took usually only an hour or two to refactor.

Aug 25, 2011 at 4:40 PM

@bladewise do you have an example of your implementation for menu etc.

Aug 26, 2011 at 9:01 AM

Well, I just have a test application I used to develop this stuff, but is highly coupled with some internal assemblies and third-party libraries. Moreover, there are some parts that I am not so happy about (the way I decided to use Uris in the view-model to associate an image in the view).

Regarding menus, anyway, I just defined interfaces and base classes as I stated above:

    #region Namespaces
    using Caliburn.Micro;

    #endregion

    /// <summary>
    ///   Interface used to define a menu.
    /// </summary>
    public interface IMenu : IParent<IMenuItem>, IUnique, IChild
    {
        #region Properties
        /// <summary>
        ///   Gets the collection of menu items.
        /// </summary>
        /// <value>The collection of menu items.</value>
        BindableCollection<IMenuItem> Items { get; }
        #endregion
    }

    /// <summary>
    ///   Interface used to identify a menu item.
    /// </summary>
    public interface IMenuItem : IChild, IHaveDisplayName, IHaveDescription, IHavePriority, IParent<IMenuItem>, IHaveImageResource, IUnique
    {
        #region Properties
        /// <summary>
        ///   Gets the collection of menu items.
        /// </summary>
        /// <value>The collection of menu items.</value>
        BindableCollection<IMenuItem> Items { get; }
        #endregion
    }
The view-models inherit from PropertyChangedBase (more or less) and implement proper interfaces. Note that I use such interfaces to be able to navigate the view-model tree (IChild<T> has a Parent property, while IParent<T> has a function to retrieve its children).

Aug 27, 2011 at 2:11 AM
Edited Aug 27, 2011 at 2:11 AM

actually took me a minute of thinking then i had that "I see what you did there epiphany",  I do assume that IHaveDescription, IHavePriority, IUnique and IHaveImageResource are custom interfaces that give description, ordering, icon and some way to force unique ids (Guid?) of the individual item in the collection.

That is actually very clean!

Thanks.

Aug 27, 2011 at 1:32 PM

You got it perfectly! I am sorry for not providing more information (I was quite in a rush), but I am quite glad that you could figure everything out just from that!

IHaveDescriptio just provides a string Description property that I tend to bind to Tooltips.

IHavePriority has an IComparable Priority field used to sort views inside a collection.

IUnique, as you guessed has an Id property of type Guid, that can be used as a key to either retrieve associated resources, or persist some view-model state.

IHaveImageResource provides an URI ImageUri property, that can be used to define an image resource associated to the view-model. I'm going to drop this, anyway, since I realized that this approach creates 'hard' references between view and view-model, and does not take into account for skinning (i.e. changing the application appearance by defining known resource overrides). I alredy developed a nice MarkupExtension used to set resource reference through a binding over the resource key, the next step will be define a convention over keys, create a manager to map the view-model -> image resource key association and plumb it inside CM. This way I should be able to rely on conventions for images to, support skinning through dynamic resource references and so on...

Sep 5, 2011 at 2:16 PM

Quite interesting Thread, and thank's a lot for your Sample, Luke. I'm just trying to extend the example in order to support WPF Ribbon.

@BladeWise: I guess your Ribbon-Interfaces are not "for the public". Or do you see a possibility to share some of this classes/interfaces? Ribbon is quite more complicated than Menu/Toolbar and currently I'm thinking about how to support the bunch of different elements (RibbonButton, Gallery, ComobBox etc.)

Sep 5, 2011 at 3:05 PM

The current implementation is based on the Divelements Ribbon, but I am working on providing a WPF Ribbon version. As soon as I have a working version I am going to update this post. :) By the way, my current implementation will just deal with: sections (tabs), ribbon groups, multi-button groups, quick items (shortcuts). More complicated views (e.g. galleries) should be quite easy to develop, while I have still no clear idea about how to handle contextual tabs...

Sep 5, 2011 at 3:20 PM

Hm... if you "only" have Interfaces for Tabs and Groups, where would you append the Buttons, ComboBoxes, DropDownButtons etc.
I assumed that it would be by code similar to lukes sample with the menuitems.

For example something like this.

ribbonManger.TabItem("Customer")
    .TabGroup("MyGroup")
      .ShowItem(new FirstButtonVM())
      .ShowItem(new SecondButtonVM())
      .ShowItem(new ComboBoxVM())

But in such case, somewhere in FirstButtonVM it must be stated, with Kind of RibbonButton you would like to have.
ComboBox would be more difficult anyway.
But i think I still have an incomplete understanding :)

Sep 5, 2011 at 3:32 PM

I use few interfaces (e.g. IRibbonItem and IMultiRibbonItem), so comboboxes, multi-buttons and so on are dealt with specific VMs.

In this sense, your example matches exactly what I do (except for the fact that I use something like Ribbon.Sections["Customer"].Groups["MyGroup"].Items.Add(...)).

Even regarding the kind, I use different VMs to define Action, Toggle or Checkable ones. I prefer numerous simple classes, instead of few multi-purpose ones.

Sep 5, 2011 at 4:10 PM

Thank you. Then I will try to follow this path in the next days.
Probably a lot of work with much more "learning by doing". But anyways, I/we have to find something reliable for the future. For the time being we are (re)-writing our first product in NET/WPF, but several more will follow during the months/years. And probably each product will have there own Ribbon-Tab (including contextual tabs) but visibility will depend on some further things/rights to be considered.

Therefore I was thinking about Prism, but I don't wont to lose the comfort of caliburn.

Jul 3, 2012 at 9:52 PM
Edited Jul 3, 2012 at 9:53 PM
BladeWise wrote:

... I am working on providing a WPF Ribbon version. As soon as I have a working version I am going to update this post...

Hi Bladewise, do you have any code or ideas you are able to share to help a CM newbie figure out how to dynamically update the main menu menuitems when switching tabs into different viewmodels? Please see my post at http://caliburnmicro.codeplex.com/discussions/361609. Thanks!