Using XamDockManager with Caliburn.Micro

I asked a question on StackOverflow about using the Infragistics XamDockManger with Caliburn.Micro, Marco Amendola came up with several ways to do it and I chose to implement my own docking manager. If you want to skip my ramblings you can find the code on bitbucket or as an attachment to this post. You will need to add your own versions of InfragisticsWPF4.DockManager.v10.3.dll and InfragisticsWPF4.v10.3.dll, it will probably work with older versions as well, but I have not tested this.

My XamDockManagerDockAwareWindowManager class is heavily based on the code posted in a thread on the Caliburn.Micro discussion forum. The biggest difference between them is I had to create a XamDockManagerHelper class to create the necessary split panes, which Avalon Dock appears to do automatically. It is a simple class that checks the XamDockManager to see if a SplitPane has already been created for that required position, if so it returns it or creates a new one in the required position.

private static class XamDockManagerHelper
		{
			public static PaneLocation GetSplitPaneLocation(SplitPane pane)
			{
                return XamDockManager.GetPaneLocation(pane);
			}

			public static SplitPane FindSplitPaneWithLocation(XamDockManager dockManager,PaneLocation location)
			{
				return dockManager.Panes.FirstOrDefault(p => GetSplitPaneLocation(p) == location);
			}

			public static SplitPane FindSplitPaneWithLocationOrCreate(XamDockManager dockManager, InitialPaneLocation location)
			{
				return FindSplitPaneWithLocationOrCreate(dockManager,location.ToPaneLocation());
			}

			public static SplitPane FindSplitPaneWithLocationOrCreate(XamDockManager dockManager,PaneLocation location)
			{
				SplitPane pane = FindSplitPaneWithLocation(dockManager,location);

				if (pane != null)
					return pane;

				pane = new SplitPane();
				XamDockManager.SetInitialLocation(pane, location.ToInitialPaneLocation());

				return pane;
			}

			public static TabGroupPane FindTabGroupPane(XamDockManager dockManager)
			{
				TabGroupPane tabs;

				dockManager.TryFindChild<TabGroupPane>(out tabs);

				return tabs;
			}

		}

The code makes use of a couple of extension methods that convert between an InitialPaneLocation enum and a PaneLocation enum.

public static class PaneLocationEnum
	{
		public static InitialPaneLocation ToInitialPaneLocation(this PaneLocation location)
		{
			InitialPaneLocation initialPaneLocation;

			switch (location)
			{
				case PaneLocation.DockedLeft:
				case PaneLocation.DockedRight:
				case PaneLocation.DockedTop:
				case PaneLocation.DockedBottom:
				case PaneLocation.FloatingOnly:

					initialPaneLocation = (InitialPaneLocation)Enum.Parse(typeof(InitialPaneLocation), location.ToString());

					break;
				case PaneLocation.Floating: //DockableFloating
					initialPaneLocation = (InitialPaneLocation)Enum.Parse(typeof(InitialPaneLocation), "DockableFloating");
					break;

				case PaneLocation.Unpinned: //Not
				case PaneLocation.Document: //Not
				case PaneLocation.Unknown: //Not
					throw new InvalidOperationException("Can not convert PaneLocation to InitialPaneLocation");

				default:
					throw new ArgumentOutOfRangeException("location");
			}

			return initialPaneLocation;
		}

		public static PaneLocation ToPaneLocation(this InitialPaneLocation location)
		{
			PaneLocation paneLocation;

			switch (location)
			{
				case InitialPaneLocation.DockedLeft:
				case InitialPaneLocation.DockedTop:
				case InitialPaneLocation.DockedRight:
				case InitialPaneLocation.DockedBottom:
				case InitialPaneLocation.FloatingOnly:

					paneLocation = (PaneLocation) Enum.Parse(typeof (PaneLocation), location.ToString());

					break;
				case InitialPaneLocation.DockableFloating:

					paneLocation = (PaneLocation)Enum.Parse(typeof(PaneLocation), "Floating");

					break;
				default:
					throw new ArgumentOutOfRangeException("location");
			}

			return paneLocation;
		}
	}

The XamDockManger window manager class can be used in exactly the same way as the standard Caliburn.Micro window manager, here is the ShellViewModel from the example code.

 [Export(typeof(IShell))]
    public class ShellViewModel : Screen, IShell
    {

        private readonly IDockAwareWindowManager _windowManager;

        [ImportingConstructor]
        public ShellViewModel(IDockAwareWindowManager windowManager)
        {
            _windowManager = windowManager;
        }

        public void OpenDocked()
        {
            _windowManager.ShowDockedWindow(new DialogViewModel(), null, true, InitialPaneLocation.DockedLeft);
        }

        public void OpenFloating()
        {
            _windowManager.ShowFloatingWindow(new DialogViewModel());
        }

        public void OpenDocument()
        {
            _windowManager.ShowDocumentWindow(new DialogViewModel());
        }

    }

You can find the complete code on bitbucket or as an attachment to this post, I expect the code can be improved in many ways but I hope that it helps someone!