How to do MDI Menus

Apr 21, 2011 at 7:33 AM


I am trying to do an MDI application using WPF. Doing so with CM's Conductor/Screen pattern is really simple, but...

For this app shall replace an existing WinForms app, it has to have an MDI menu. That's the kind of menu you find in almost every application under the "Window" Menu. It should list all Screens in the Items collection of my MainViewModel, then a separator, then one or two special items that will perform custom actions.

I'd expect the View to contain something like this:

<MenuItem x:Name='MdiMenu'/>

paired by the ViewModel to contain this:

public class MainViewModel : Conductor<IScreen>.Collection.OneActive

  // lots of stuff doing MDI items management

  // MDI Menu handling
  private ObservableCollection<MenuItemModel> _mdiMenu;

  public ObservableCollection<MenuItemModel> MdiMenu
    get { return _mdiMenu }
    set { _mdiMenu = value; NotifyOfPropertyChange(() => MdiMenu); }

  private void InitMdiMenu()
    MdiMenu = new ObservableCollection<MenuItemModel> ();
    // TODO: insert Items members here..

    // separator 
    MdiMenu.Add(new MenuItemModel { IsSeaparator = true });
    MdiMenu.Add(new MenuItemModel { DisplayName = "Do something weird", Command= () => DoSomethingWeird() });

// this is the MenuItem class - PropertyChanged-details excluded for clarity
public class MenuItemModel : PropertyChangedBase
  public string DisplayName {get; set; }
  public bool IsSaparator {get; set; }
  public Action<void> Command { get; set; }

Unfortunately, this doesn't work out of the box - I just get an error stating 'ItemsCollection must be empty befor setting ItemsSource'.

I couldn't find an example of menu item binding - could you pleas point me to one?
If in any way possible, I'd like to avoid customizing the Conventions and extending CM's functionality - I belive there is some magic yet unknown to me that makes this thing a breeze...

Thanks for your help


Apr 21, 2011 at 8:35 AM

The issue you have encountered is not related to CM specifically. It seems that you are adding items to the ItemsControl.Items collection and then setting the ItemsControl.ItemsSource. This scenario (mixing ItemsSource and Items) is not supported by any ItemsControl.

Note that, due to the default convention for a MenuItem, CM is probably setting a binding on the ItemsSource property for you, so if you hard-coded some menu items in the UI, the view could generate such error.

In case you are really placing some hard-coded menu items on the UI, try to remove them from the view and place them in the proper VM collection.

Apr 21, 2011 at 9:46 AM



thanks for the response. I figured out the cause of the error - it was just some wrong code inside the datatemplate.

But I still have no real clue about how to connect the MDI menu with the Items collection of the ViewModel - mirroring all additions and deletions, and handling Activation of a view from the MDI menu the same way as it will happen from the TabControl tabs. But I can't figure out how to bind the command to the menu item...

The simple way (so I thought) would be to bind the MenuItemsSource to the Items collection, but this just gives me a list of "Caliburn.Micro.Screen" menu entries .-(

And the additional question is how to add custom menu items to the collection and still make sure only active items are shown in the menu

Apr 21, 2011 at 10:47 AM

MenuItem are ugly beasts. You cannot use the Menu.ItemsSource and have full control over the individual MenuItem views (the views are always wrapped into an ItemContainer, which is a MenuItem itself).

The simplest thing you can do is to re-define the ItemContainerStyle of the Menu hosting the entries, and set your bindings manually (Content, cm:Message.Attach or Command, Visibility and so on).

There are alternative approaches to deal with MenuItems in a more flexible way (e.g. using attached behaviours and specialized collections used to retrieve views), but I suppose we can discuss this if everything else fails! :)

Regarding the fact that some entries should disappear if the corresponding view-model is not active, you could bind Visibility to the IsActive property, provided by the IActivate interface.

Apr 21, 2011 at 2:39 PM

Thanks for the advice.

Having red it, I have decided to implement an ObservableCollection of my own MenuItemModel class and bind the MenuItem properties via a style and setters. Add/Remove of MDI screens is handled manually: I have a flag in every MenuItemModel if it is an MDI item or not, and on through overriding ActivateItem and OnDeactivate I simply remove all Mdi meu items and add them back again. It seems to be working right now, so I'll move on to the next mystery.

Nevertheless - CM is so much simplifying my life in creating (mostly simple) WPF apps, I don't know how I have lived without it :-)