Here’s another post that continues the topic of xLim extensibility and originates from Ayende’s blog. He has posted on an interesting problem of view composition. Here’s quick possible solution for the initial problem within the xLim 2 concept:

  1. Define reusable composite view that is just a wrapper for the list of views and their parameters

    public sealed class CompositeView : DesktopViewBase { protected override void OnLoad(CompositeViewParameters parameters) { Parameters = parameters; } public CompositeViewParameters Parameters { get; private set; } }

  2. Define reusable CompositeDesktopWorkspace class that can take any number of controls as an argument and create PanelWorkspace for each one.

    public class CompositeDesktopWorkspace : Workspace<compositeview, iviewinforeader=””> { public CompositeDesktopWorkspace(Control[] containerControls) { _workspaces = new IWorkspace[containerControls.Length]; for (int i = 0; i < containerControls.Length; i++) { _workspaces[i] = new PanelWorkspace(containerControls[i]); } }</compositeview,>

When asked for a view, CompositeDesktopWorkspace should load it. Load operation will fail if the view being loaded is not CompositeView, although one could add logic that will simply load any non-composite desktop view directly into the first PanelWorkspace.

   public void ShowViewByName(string name, IViewInfoReader info)
   {
      UnloadCurrentCompositeView();

      _viewContainer = _workspaceContainer.CreateInnerContainer();
      CompositeView view = 
         _viewContainer.ResolveByName<CompositeView>(name);
      // this will end up in some code checks and a call to "OnShow" 
      Show(view, info);
   }

Since the composite view is not a real view, we do not display it, but rather use its typed properties to load the appropriate sub-views. All the subviews are created within the shared _viewContainer. As result, if some sub-views require shared controller/manager (ICalendarSyncController, for example) in their constructors, then the first view will create that controller and the subsequent views will hook up to the existing instance.

   protected override void OnShow(CompositeView view, 
      IViewInfoReader viewInformation)
   {  
      view.LoadInfo(viewInformation);
      CompositeViewParameters parameters = view.Parameters;

      if (parameters.Count != _workspaces.Length)
         throw new InvalidOperationException();

      for (int i = 0; i < _workspaces.Length; i++)
      {
         _workspaces[i].SetContext(_viewContainer);
         _workspaces[i].ShowViewByName(parameters.ViewNames[i], 
            parameters.ViewInfos[i]);
      }

      Activate(view);
   }

Notes:

  • The shared controllers/managers have to be registered in IoC as container-owned singleton instances.
  • if the composite workspace allows opening and closing of sub views, everything will keep on working. If new view being opened needs some shared controller/manager to sync with or subscribe to, then it will pull it from the constructor (thus automatically hooking to the over views). The drawback is that all shared controllers/managers will be disposed only when the workspace closes the entire composite view.

It is quite easy to add on-the-fly configuration of the composite view by the end-users. User could have XtraLayoutControl (or its analogue) to customize the layout. When he is done with the customization, the workspace is reinitialized with the set of panels that were created within the LayoutControl (PanelWorkspace is created out of each one); then the CompositeViewParameters object is simply assembled based on the actual items that the user has dropped there. The assembled information is passed to the Composite workspace (and optionally persisted in the appropriate IDataNode). IoC and DI will settle down the rest.

I didn’t realize you could do that.