Caliburn Micro Docking Implementation?

34 posts, 1 answers
  1. Steve
    Steve avatar
    19 posts
    Member since:
    Apr 2009

    Posted 22 Jul 2011 Link to this post

    Has anyone succeeded in creating a docking implementation that used Caliburn Micro? What I am trying to do is have a docking shell where the only thing it knows about is a toolbox pinned on the side and dynamically loads document panes from external XAPs. The dynamically loading and stuff is easy but I am having trouble figuring out what needs to happen to allow Caliburn to handle the dynamic view creation and have them as the content of dynamically created panes.

    I have the Telerik conventions for Caliburn Micro and I'm looking at the RadTabControl changes to allow the conventions to work but docking is a lot more complicated so I am not sure where to begin.

    Any help would be greatly appreciated.

    Steve
  2. Steve
    Steve avatar
    19 posts
    Member since:
    Apr 2009

    Posted 26 Jul 2011 Link to this post

    I was able to get it working. I don't think what I have done is 100% ideal but it is good enough for now to see results and can hopefully be tweaked in the future to be better. The key is that CM has to be told to explicitly about the RadPane.

    RadPane pane = new RadPane();
    Caliburn.Micro.View.SetModel(pane, viewModel);

  3. DevCraft banner
  4. Marc
    Marc avatar
    88 posts
    Member since:
    Jul 2011

    Posted 06 Oct 2011 Link to this post

    Hi Steve,

    thank you for sharing your ideas. Do you think it's possible to attach the source code of what you did to make Caliburn.Micro's navigation infrastructure work with RadDocking. This would be really helpful for me and probably others as well.


    Thanks in advance,
    Stephan
  5. Steve
    Steve avatar
    19 posts
    Member since:
    Apr 2009

    Posted 19 Oct 2011 Link to this post

    Stephan,

    Sorry I didn't get back to you till now. Here is some of the code. There are some bugs and problems with it but it at least proves the concept. I haven't spent any more time on this piece since I was able to get it to at least partially work. Let me know if you have any questions about it and I'll try to respond quicker :).

    Here is the view model. You may have to substitute your MEF loading but the idea is here.
    [Export(typeof(DockingShellHostViewModel)), PartCreationPolicy(CreationPolicy.NonShared)]
    public class DockingShellHostViewModel : Conductor<IModule>.Collection.AllActive
    {
       #region Members
     
       private ModuleDefinition m_moduleDefinitionToLoad;
     
       #endregion
     
       #region Constructor
     
       [ImportingConstructor]
       public DockingShellHostViewModel(
          ICompositionCatalogService a_catalogService)
       {
          CatalogService = a_catalogService;
     
          ModuleRadPaneGroups = new ObservableCollection<RadPaneGroup>();
       }
     
       #endregion
     
       #region Properties
     
       private ICompositionCatalogService CatalogService { get; set; }
     
       public ObservableCollection<RadPaneGroup> ModuleRadPaneGroups { get; set; }
     
       #endregion
     
       #region Methods
     
       public void LoadModule(ModuleDefinition a_moduleDefinition)
       {
          m_moduleDefinitionToLoad = a_moduleDefinition;
          IdeaBlade.EntityModel.Coroutine.Start(LoadModule);
       }
     
       private IEnumerable<INotifyCompleted> LoadModule()
       {
          yield return CatalogService.AddXap(m_moduleDefinitionToLoad.XapName);
     
          object returnModule = CatalogService.GetInstance(typeof(IModule), m_moduleDefinitionToLoad.Module.ToString(), CreationPolicy.NonShared);
          if (returnModule != null)
          {
             if (ModuleRadPaneGroups.Count == 0)
             {
                RadPaneGroup group = new RadPaneGroup();
                ModuleRadPaneGroups.Add(group);
             }
     
             RadPane pane = new RadPane();
             pane.Header = m_moduleDefinitionToLoad.Title;
     
             // This line tells Caliburn Micro to find the associated view and assign it as the content of the RadPane
             // instead of the view model. This is the glue that makes it all work.
             Caliburn.Micro.View.SetModel(pane, returnModule);
             // Add the pane to the collection so that it is displayed in the UI.
             ModuleRadPaneGroups[0].AddItem(pane, Telerik.Windows.Controls.Docking.DockPosition.Center);
     
             pane.Focus();
             pane.IsSelected = true;
     
             // Activate the item so that it's life cycle is started.
             this.ActivateItem(returnModule as IModule);
          }
       }
     
       #endregion
    }

    Here is the view. Nothing to it.

                 x:Class="..."
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                 xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
                 xmlns:cal="http://www.caliburnproject.org"
                 mc:Ignorable="d"
                 d:DesignHeight="300"
                 d:DesignWidth="400">
     
       <UserControl.Resources>
          <ng:ImageWrapper x:Key="ImageWrapper" />
          <ng:InvertBoolConverter x:Key="InvertBoolConverter" />
       </UserControl.Resources>
     
       <Grid x:Name="LayoutRoot"
             Background="White">
          <Grid.RowDefinitions>
             <RowDefinition Height="Auto" />
             <RowDefinition Height="*" />
          </Grid.RowDefinitions>
          <telerik:RadMenu>
             <telerik:RadMenuItem Header="View">
             </telerik:RadMenuItem>
          </telerik:RadMenu>
          <telerik:RadDocking Grid.Row="1">
             <telerik:RadDocking.DocumentHost>
                <telerik:RadSplitContainer ItemsSource="{Binding ModuleRadPaneGroups}">
                </telerik:RadSplitContainer>
             </telerik:RadDocking.DocumentHost>
          </telerik:RadDocking>
       </Grid>
    </UserControl>

    Steve
  6. Marc
    Marc avatar
    88 posts
    Member since:
    Jul 2011

    Posted 19 Oct 2011 Link to this post

    Thank you Steve,

    meanwhile I wrote my own implementation. It basically acts as a mediator between a given IConductor and a (optionally given) RadDocking control using the respective events of each one and creating necessary stuff like panes etc. on the fly.

    However, thank you for answering.

    Cheers,
    Stephan
  7. Steve
    Steve avatar
    19 posts
    Member since:
    Apr 2009

    Posted 19 Oct 2011 Link to this post

    Stephan,

    Glad you were able to get something together. Sounds like the approach you took was a lot better. Would you be able to expound on the idea or post code?

    Thanks,
    Steve
  8. Answer
    Marc
    Marc avatar
    88 posts
    Member since:
    Jul 2011

    Posted 19 Oct 2011 Link to this post

    There you go:

    public class DockManager : IDockManager
        {
            private IConductActiveItem _conductor;
            private RadDocking _dock;
     
            private bool _activatedFromViewModel;
            private bool _actuallyClosing;
            private bool _deactivateFromViewModel;
            private bool _deactivatingFromView;
     
     
            public void Link(IConductor conductor, FrameworkElement dock = null)
            {
                if (_conductor != null || _dock != null)
                {
                    throw new InvalidOperationException("Dock manager is already linked");
                }
     
                _conductor = conductor as IConductActiveItem;
                _dock = dock as RadDocking ?? FindDock();
     
                if (_conductor == null || _dock == null)
                {
                    throw new InvalidOperationException("Invalid conductor or docking control");
                }
     
                _conductor.ActivationProcessed += OnActivationProcessed;
                _dock.ActivePaneChanged += OnActivePaneChanged;
                _dock.PreviewClose += OnPreviewClose;
                _dock.Close += OnClose;
            }
     
     
            protected virtual void OnActivationProcessed(object s, ActivationProcessedEventArgs e)
            {
                if (!e.Success)
                {
                    return;
                }
     
                object viewModel = e.Item;
                RadPane pane = FindPane(viewModel);
                if (pane == null)
                {
                    _activatedFromViewModel = true;
     
                    pane = CreatePane(viewModel);
                    AttachPane(viewModel, pane);
     
                    var deactivatable = viewModel as IDeactivate;
                    if (deactivatable != null)
                    {
                        deactivatable.Deactivated += OnDeactivated;
                    }
                }
     
                _dock.ActivePane = pane;
            }
     
     
            protected virtual void OnActivePaneChanged(object s, ActivePangeChangedEventArgs e)
            {
                if (_activatedFromViewModel)
                {
                    _activatedFromViewModel = false;
                    return;
                }
     
                RadPane pane = e.NewPane;
                if (pane == null)
                {
                    return;
                }
     
                _conductor.ActivateItem(pane.DataContext);
            }
     
     
            protected virtual void OnDeactivated(object s, DeactivationEventArgs e)
            {
                if (!e.WasClosed)
                {
                    return;
                }
     
                ((IDeactivate) s).Deactivated -= OnDeactivated;
     
                if (_deactivatingFromView)
                {
                    return;
                }
     
                _deactivateFromViewModel = true;
                RemovePane(FindPane(s));
                _deactivateFromViewModel = false;
            }
     
     
            protected virtual void OnPreviewClose(object s, StateChangeEventArgs e)
            {
                RadPane pane = e.Panes.FirstOrDefault();
                if (pane == null)
                {
                    return;
                }
     
                var guard = pane.DataContext as IGuardClose;
                if (guard == null)
                {
                    return;
                }
     
                if (e.Handled)
                {
                    return;
                }
     
                if (_actuallyClosing)
                {
                    _actuallyClosing = false;
                    return;
                }
     
                bool runningAsync = false;
                bool shouldEnd = false;
     
                guard.CanClose(canClose =>
                {
                    Execute.OnUIThread(() =>
                    {
                        if (runningAsync && canClose)
                        {
                            _actuallyClosing = true;
                            pane.IsHidden = true;
                        }
                        else
                        {
                            e.Handled = !canClose;
                        }
     
                        shouldEnd = true;
                    });
                });
     
                if (shouldEnd)
                {
                    return;
                }
     
                e.Handled = true;
                runningAsync = true;
            }
     
     
            protected virtual void OnClose(object s, StateChangeEventArgs e)
            {
                RadPane pane = e.Panes.FirstOrDefault();
                if (pane == null)
                {
                    return;
                }
     
                var deactivatable = pane.DataContext as IDeactivate;
                if (deactivatable == null)
                {
                    return;
                }
     
                if (_deactivateFromViewModel)
                {
                    return;
                }
     
                _deactivatingFromView = true;
                RemovePane(pane);
                deactivatable.Deactivate(true);
                _deactivatingFromView = false;
            }
     
     
            protected virtual RadDocking FindDock()
            {
                return Application.Current.RootVisual.FindChildByType<RadDocking>();
            }
     
     
            protected virtual RadPane FindPane(object viewModel)
            {
                return _dock.Panes.FirstOrDefault(p => p.DataContext == viewModel);
            }
     
     
            protected virtual RadPane CreatePane(object viewModel)
            {
                RadPane pane = EnsurePane(viewModel, ViewLocator.LocateForModel(viewModel, null, null));
                ViewModelBinder.Bind(viewModel, pane, null);
     
                var haveDisplayName = viewModel as IHaveDisplayName;
                if (haveDisplayName != null && !ConventionManager.HasBinding(pane, HeaderedContentControl.HeaderProperty))
                {
                    pane.Header = new DisplayDataControl();
                }
     
                return pane;
            }
     
     
            protected virtual RadPane EnsurePane(object viewModel, object view)
            {
                var pane = view as RadPane;
     
                if (pane == null)
                {
                    pane = new RadPane { DataContext = viewModel, Content = view };
                    pane.SetValue(View.IsGeneratedProperty, true);
                }
     
                return pane;
            }
     
     
            protected virtual void AttachPane(object viewModel, RadPane pane)
            {
                DockState? dockState = GetDockState(viewModel);
     
                RadSplitContainer splitContainer;
                if (dockState == null)
                {
                    splitContainer = _dock.DocumentHost as RadSplitContainer;
                    if (splitContainer == null)
                    {
                        splitContainer = new RadSplitContainer();
                        splitContainer.SetValue(View.IsGeneratedProperty, true);
                        _dock.DocumentHost = splitContainer;
                    }
                }
                else
                {
                    splitContainer = _dock.Items.OfType<RadSplitContainer>().FirstOrDefault(
                        x => x.GetValue(RadDocking.DockStateProperty) as DockState? == dockState);
                    if (splitContainer == null)
                    {
                        splitContainer = new RadSplitContainer { InitialPosition = dockState.Value };
                        splitContainer.SetValue(View.IsGeneratedProperty, true);
                        _dock.Items.Add(splitContainer);
                    }
                }
     
                RadPaneGroup paneGroup = splitContainer.Items.OfType<RadPaneGroup>().FirstOrDefault();
                if (paneGroup == null)
                {
                    paneGroup = new RadPaneGroup { IsContentPreserved = true };
                    paneGroup.SetValue(View.IsGeneratedProperty, true);
                    paneGroup.SelectedItemRemoveBehaviour = SelectedItemRemoveBehaviour.SelectNone;
                    splitContainer.Items.Add(paneGroup);
                }
     
                paneGroup.AddItem(pane, DockPosition.Center);
            }
     
     
            protected virtual void RemovePane(RadPane pane)
            {
                if (pane == null)
                {
                    return;
                }
     
                RadPaneGroup paneGroup = pane.PaneGroup;
                pane.RemoveFromParent();
                if (paneGroup == null || paneGroup.HasItems)
                {
                    return;
                }
     
                RadSplitContainer splitContainer = paneGroup.ParentContainer;
                paneGroup.RemoveFromParent();
                if (splitContainer == null || splitContainer.HasItems)
                {
                    return;
                }
     
                if (splitContainer.IsInDocumentHost)
                {
                    _dock.DocumentHost = null;
                }
                else
                {
                    _dock.Items.Remove(splitContainer);
                }
            }
     
     
            protected virtual DockState? GetDockState(object viewModel)
            {
                if (viewModel is IHavePaneSettings)
                {
                    var paneSettings = (IHavePaneSettings) viewModel;
     
                    switch (paneSettings.InitialState)
                    {
                        case PaneState.DockedLeft:
                            return DockState.DockedLeft;
                        case PaneState.DockedTop:
                            return DockState.DockedTop;
                        case PaneState.DockedRight:
                            return DockState.DockedRight;
                        case PaneState.DockedBottom:
                            return DockState.DockedBottom;
                    }
                }
                return null;
            }
        }
  9. Steve
    Steve avatar
    19 posts
    Member since:
    Apr 2009

    Posted 19 Oct 2011 Link to this post

    Thank you Stephan. That works like a charm. It will be a great example for extending CM and making it work in a real world app.

    Much appreciated,
    Steve
  10. Marc
    Marc avatar
    88 posts
    Member since:
    Jul 2011

    Posted 19 Oct 2011 Link to this post

    Thank you Steve,

    maybe I'll send this to the Caliburn.Micro community someday.

    Two things I'd like to mention:
    - When closing the selected pane via UI (not via VM) the RadDocking control always selects the first pane (not what I actually want) even though I set the according behaviour to select-none. More about this here: http://www.telerik.com/community/forums/silverlight/docking/support-for-selecteditemremovebehaviour.aspx
    - I never tested the code in OnPreviewClosed when ExecuteOnUIThread runs asynchronously. In case you ever do that, let me know what happened.

    Cheers,
    Stephan

  11. Derek
    Derek avatar
    39 posts
    Member since:
    Aug 2012

    Posted 19 Oct 2011 Link to this post

    Stephen, can you post the IDockManager & IHavePaneSettings interfaces?
  12. Marc
    Marc avatar
    88 posts
    Member since:
    Jul 2011

    Posted 19 Oct 2011 Link to this post

    Sure. They're rather simple and generic.

    public interface IDockManager
        {
            void Link(IConductor conductor, FrameworkElement dock = null);
        }

    public interface IHavePaneSettings
        {
            PaneState InitialState { get; }
        }
     
     
        public enum PaneState
        {
            DockedCenter,
            DockedLeft,
            DockedTop,
            DockedRight,
            DockedBottom
        }
  13. Derek
    Derek avatar
    39 posts
    Member since:
    Aug 2012

    Posted 19 Oct 2011 Link to this post

    Thanks, I guess I only needed IDocmanager.  Nice work on this.
  14. Steve
    Steve avatar
    19 posts
    Member since:
    Apr 2009

    Posted 19 Oct 2011 Link to this post

    You could simply not implement the interface. I didn't.

    Stephan, I would definitely submit it to CM people. It would be nice if Telerik would compile a group of functionality devoted to working with CM. I have found tidbits spread around the forums and other places like making the RadWindow work, conventions, etc.
  15. Marc
    Marc avatar
    88 posts
    Member since:
    Jul 2011

    Posted 19 Oct 2011 Link to this post

    Yes, I know. I probably found the same stuff. But unfortunately nothing that fit my needs.
    And yes, I will submit this to the CM community in some way. Thank you for your interest.
  16. Derek
    Derek avatar
    39 posts
    Member since:
    Aug 2012

    Posted 19 Oct 2011 Link to this post

    Send it to Rob. rob.eisenberg at gmail.com
  17. Marc
    Marc avatar
    88 posts
    Member since:
    Jul 2011

    Posted 19 Oct 2011 Link to this post

    To the boss himself ???
  18. Derek
    Derek avatar
    39 posts
    Member since:
    Aug 2012

    Posted 19 Oct 2011 Link to this post

    Yeah, or post it in the forum.

    related:http://caliburnmicro.codeplex.com/discussions/231809
  19. Paolo
    Paolo avatar
    423 posts
    Member since:
    Jun 2009

    Posted 07 Nov 2011 Link to this post

    Hello,
    I'm new to CM and Docking..... I've tried to paste in a class
    [Export(typeof(DockingShellHostViewModel)), PartCreationPolicy(CreationPolicy.NonShared)]
    public class DockingShellHostViewModel
    ...

    but I got an unknown type for ModuleDefinition and IModule ... what am I missing?
  20. Steve
    Steve avatar
    19 posts
    Member since:
    Apr 2009

    Posted 07 Nov 2011 Link to this post

    Paolo,

    I wouldn't use the code that I posted. It is not ideal. Stephan's implementation is a lot better and uses CM to the max.

    Just so you know, those classes are specific to my implementation. You can replace IModule with whatever interface you are using for MEF. I am dynamically downloading XAPs and modules so they implement IModule. ModuleDefinition is simply a class that has information about the IModule. ModuleDefinition is used because the XAP might need to be downloaded.

    Not sure if that is clear enough for you.

    Steve
  21. Derek
    Derek avatar
    39 posts
    Member since:
    Aug 2012

    Posted 07 Nov 2011 Link to this post

    I implemented this a little differently.  I followed an example from the forums combined with the WindowManager impl.
    https://gist.github.com/1345512

    edit: I should add, I used Stephen's excellent example and a post from the CM forums as well.
  22. Paolo
    Paolo avatar
    423 posts
    Member since:
    Jun 2009

    Posted 07 Nov 2011 Link to this post

    Hello Derek,
    the implementation you've got is a replacement of RadDocking or it's a must to implement in order to use caliburn & raddocking?
    Thanks
  23. Derek
    Derek avatar
    39 posts
    Member since:
    Aug 2012

    Posted 07 Nov 2011 Link to this post

    it doesn't replace RadDocking.  It lets you use RadDocking like WindowManager.
  24. Marc
    Marc avatar
    88 posts
    Member since:
    Jul 2011

    Posted 08 Nov 2011 Link to this post

    hi guys,

    i just wanted to let you know that i really sent my code to rob eisenberg who kindly reviewed it. he suggested to send it to the author of Caliburn.Micro.Telerik which i tried (see here). additionally i posted it to the CM forum (see here). and now, for the sake of completeness here. below is my current version (custom stuff stripped out). just in case any of you use my previous version: please update because there is a bug (on close an item must be removed through the conductor which in turn deactivates it if possible - previously the items itself got deactivated directly and therefore not removed from the conductor's list).

    if anybody finds another bug - please let me know...

    regards,
    stephan


    // A dock manager acts as a mediator between a conductor and an optionally
    // given docking control which is typically found in the view of the conductor.
    // Even though the interface is pretty simple and generic, it might suffice many
    // if not all (?) docking libraries.
    public interface IDockManager
    {
        void Link(IConductor conductor, FrameworkElement dock = null);
    }
     
    // A view model may wish to implement a dedicated interface (probably dependent
    // on the functionality of the concrete docking library) like this to express
    // certain docking relevant preferences which can be addressed by the docking manager.
    public interface IHaveDockPreferences
    {
        DockingState InitialState { get; }
    }
     
    public enum DockingState
    {
        DockedCenter,
        DockedLeft,
        DockedTop,
        DockedRight,
        DockedBottom
    }
     
    // A dock manager is usually injected in the according conductor view model.
    // Here we omit the concrete dock control and let the dock manager find it in the
    // visual tree. This of course requires that the Link-method is called after the
    // conductor's view (which hopefully contains the docking control) is loaded.
    public class WorkspaceViewModel : Conductor<object>.Collection.OneActive
    {
        [Import]
        public IDockManager DockManager { get; set; }
     
        protected override void OnViewLoaded(object view)
        {
            base.OnViewLoaded(view);
             
            DockManager.Link(this);
     
            ActivateItem(SomePlainViewModel);
            ActivateItem(SomeOtherViewModelWhichImplementsIHaveDockPreferences);
             
            ...
        }
         
        ...
    }
     
    // My implementation of IDockManager for Telerik's RadDocking (SL). It basically acts as a
    // mediator between a given IConductor (IConductActiveItem in this case) and an optionally
    // given docking control (RadDocking in this case) using the respective events of each one
    // and creating/removing necessary stuff like panes etc. on the fly.
    [Export(typeof(IDockManager))]
    public class DockManager : IDockManager
    {
        private IConductActiveItem _conductor;
        private RadDocking _dock;
     
        private bool _activatedFromViewModel;
        private bool _actuallyClosing;
        private bool _deactivatingFromView;
     
     
        public void Link(IConductor conductor, FrameworkElement dock = null)
        {
            if (_conductor != null || _dock != null)
            {
                throw new InvalidOperationException("Dock manager is already linked");
            }
     
            _conductor = conductor as IConductActiveItem;
            _dock = dock as RadDocking ?? FindDock();
     
            if (_conductor == null || _dock == null)
            {
                throw new InvalidOperationException("Invalid conductor or docking control");
            }
     
            _conductor.ActivationProcessed += OnActivationProcessed;
            _dock.ActivePaneChanged += OnActivePaneChanged;
            _dock.PreviewClose += OnPreviewClose;
            _dock.Close += OnClose;
        }
     
     
        protected virtual void OnActivationProcessed(object s, ActivationProcessedEventArgs e)
        {
            if (!e.Success)
            {
                return;
            }
     
            object viewModel = e.Item;
            RadPane pane = FindPane(viewModel);
            if (pane == null)
            {
                _activatedFromViewModel = true;
     
                pane = CreatePane(viewModel);
                AttachPane(viewModel, pane);
     
                var deactivatable = viewModel as IDeactivate;
                if (deactivatable != null)
                {
                    deactivatable.Deactivated += OnDeactivated;
                }
            }
     
            _dock.ActivePane = pane;
        }
     
     
        protected virtual void OnActivePaneChanged(object s, ActivePangeChangedEventArgs e)
        {
            if (_activatedFromViewModel)
            {
                _activatedFromViewModel = false;
                return;
            }
     
            RadPane pane = e.NewPane;
            if (pane == null)
            {
                return;
            }
     
            _conductor.ActivateItem(pane.DataContext);
        }
     
     
        protected virtual void OnDeactivated(object s, DeactivationEventArgs e)
        {
            if (!e.WasClosed)
            {
                return;
            }
     
            ((IDeactivate) s).Deactivated -= OnDeactivated;
     
            if (_deactivatingFromView)
            {
                return;
            }
     
            RemovePane(FindPane(s));
        }
     
     
        protected virtual void OnPreviewClose(object s, StateChangeEventArgs e)
        {
            RadPane pane = e.Panes.FirstOrDefault();
            if (pane == null)
            {
                return;
            }
     
            var guard = pane.DataContext as IGuardClose;
            if (guard == null)
            {
                return;
            }
     
            if (e.Handled)
            {
                return;
            }
     
            if (_actuallyClosing)
            {
                _actuallyClosing = false;
                return;
            }
     
            bool runningAsync = false;
            bool shouldEnd = false;
     
            guard.CanClose(canClose =>
            {
                Execute.OnUIThread(() =>
                {
                    if (runningAsync && canClose)
                    {
                        _actuallyClosing = true;
                        pane.IsHidden = true;
                    }
                    else
                    {
                        e.Handled = !canClose;
                    }
     
                    shouldEnd = true;
                });
            });
     
            if (shouldEnd)
            {
                return;
            }
     
            e.Handled = true;
            runningAsync = true;
        }
     
     
        protected virtual void OnClose(object s, StateChangeEventArgs e)
        {
            RadPane pane = e.Panes.FirstOrDefault();
            if (pane == null)
            {
                return;
            }
     
            _deactivatingFromView = true;
            _conductor.CloseItem(pane.DataContext);
            RemovePane(pane);
            _deactivatingFromView = false;
        }
     
     
        protected virtual RadDocking FindDock()
        {
            return Application.Current.RootVisual.FindChildByType<RadDocking>();
        }
     
     
        protected virtual RadPane FindPane(object viewModel)
        {
            return _dock.Panes.FirstOrDefault(pane => pane.DataContext == viewModel);
        }
     
     
        protected virtual RadPane CreatePane(object viewModel)
        {
            RadPane pane = EnsurePane(viewModel, ViewLocator.LocateForModel(viewModel, null, null));
            ViewModelBinder.Bind(viewModel, pane, null);
     
            var haveDisplayName = viewModel as IHaveDisplayName;
            if (haveDisplayName != null && !ConventionManager.HasBinding(pane, HeaderedContentControl.HeaderProperty))
            {
                var binding = new Binding("DisplayName") { Mode = BindingMode.TwoWay };
                pane.SetBinding(HeaderedContentControl.HeaderProperty, binding);
            }
     
            return pane;
        }
     
     
        protected virtual RadPane EnsurePane(object viewModel, object view)
        {
            var pane = view as RadPane;
     
            if (pane == null)
            {
                pane = new RadPane { DataContext = viewModel, Content = view };
                pane.SetValue(View.IsGeneratedProperty, true);
            }
     
            return pane;
        }
     
     
        protected virtual void AttachPane(object viewModel, RadPane pane)
        {
            DockState? dockState = GetDockState(viewModel);
     
            RadSplitContainer splitContainer;
            if (dockState == null)
            {
                splitContainer = _dock.DocumentHost as RadSplitContainer;
                if (splitContainer == null)
                {
                    splitContainer = new RadSplitContainer();
                    splitContainer.SetValue(View.IsGeneratedProperty, true);
                    _dock.DocumentHost = splitContainer;
                }
            }
            else
            {
                splitContainer = _dock.Items.OfType<RadSplitContainer>().FirstOrDefault(
                    container => container.GetValue(RadDocking.DockStateProperty) as DockState? == dockState);
                if (splitContainer == null)
                {
                    splitContainer = new RadSplitContainer { InitialPosition = dockState.Value };
                    splitContainer.SetValue(View.IsGeneratedProperty, true);
                    _dock.Items.Add(splitContainer);
                }
            }
     
            RadPaneGroup paneGroup = splitContainer.Items.OfType<RadPaneGroup>().FirstOrDefault();
            if (paneGroup == null)
            {
                paneGroup = new RadPaneGroup { IsContentPreserved = true };
                paneGroup.SetValue(View.IsGeneratedProperty, true);
                paneGroup.SelectedItemRemoveBehaviour = SelectedItemRemoveBehaviour.SelectNone;
                splitContainer.Items.Add(paneGroup);
            }
     
            paneGroup.AddItem(pane, DockPosition.Center);
        }
     
     
        protected virtual void RemovePane(RadPane pane)
        {
            if (pane == null)
            {
                return;
            }
     
            RadPaneGroup paneGroup = pane.PaneGroup;
            pane.RemoveFromParent();
            if (paneGroup == null || paneGroup.HasItems)
            {
                return;
            }
     
            RadSplitContainer splitContainer = paneGroup.ParentContainer;
            paneGroup.RemoveFromParent();
            if (splitContainer == null || splitContainer.HasItems)
            {
                return;
            }
     
            if (splitContainer.IsInDocumentHost)
            {
                _dock.DocumentHost = null;
            }
            else
            {
                _dock.Items.Remove(splitContainer);
            }
        }
     
     
        protected virtual DockState? GetDockState(object viewModel)
        {
            if (viewModel is IHaveDockPreferences)
            {
                var dockPreferences = (IHaveDockPreferences) viewModel;
     
                switch (dockPreferences.InitialState)
                {
                    case DockingState.DockedLeft:
                        return DockState.DockedLeft;
                    case DockingState.DockedTop:
                        return DockState.DockedTop;
                    case DockingState.DockedRight:
                        return DockState.DockedRight;
                    case DockingState.DockedBottom:
                        return DockState.DockedBottom;
                }
            }
            return null;
        }
    }

  25. Chuck
    Chuck avatar
    10 posts
    Member since:
    Sep 2012

    Posted 16 Nov 2011 Link to this post

    Stephan,
    This worked like a charm for us. Thanks for posting!
  26. Paolo
    Paolo avatar
    423 posts
    Member since:
    Jun 2009

    Posted 28 Nov 2011 Link to this post

    Hello to everybody,
    now that I've created my single models I wish to attach them at load on the workspace but I've no idea about adding them... my workspaceviewmodel (the one that's load from the bootstrapper and that holds a reference to DockManager is defined as )

    [Export]
       public class WorkspaceViewModel : Conductor<object>.Collection.OneActive
       {
           [Import]
           public IDockManager DockManager { get; set; }
     
           private IWindowManager _manager;
           private IApplicationRepository _repository;
     
           [ImportingConstructor]
           public WorkspaceViewModel(IApplicationRepository repository, IWindowManager manager)
           {
               base.DisplayName = "myapp";
               _manager = manager;
               _repository = repository;
           }
     
     
           protected override void OnViewLoaded(object view)
           {
               base.OnViewLoaded(view);
     
               DockManager.Link(this);
     
               UserMessagesViewModel model = new UserMessagesViewModel(_repository);
     
               ActivateItem(model);
           }
       }

    The single viewmodels are defined as 

    [Export]
        public class UserMessagesViewModel : Screen
        {
    ...
    }

    I've tried using the ActivateItem but in that way I can't set the startup position... how can I do that?
    I need to add a menu to the top of the View (that permits the user to select witch operation to do) should I define a model for this? and how can this object tells the dockmanager to position the specific view?
    Thanks

    Paolo



  27. Paolo
    Paolo avatar
    423 posts
    Member since:
    Jun 2009

    Posted 28 Nov 2011 Link to this post

    duplicate post... sorry... I recevied a Server Error message
  28. Paolo
    Paolo avatar
    423 posts
    Member since:
    Jun 2009

    Posted 01 Dec 2011 Link to this post

    anyone can help me ?
    Thanks
  29. Miroslav Nedyalkov
    Admin
    Miroslav Nedyalkov avatar
    1718 posts

    Posted 06 Dec 2011 Link to this post

    Hello Paolo,

    Could you please explain in more details what you need to achieve and how you interact with the Docking control? Is the ActiveItem the ActivePane property of the Docking control? If you could prepare a sample project that demonstrates the idea this would be of great help for us to understand the problem you are experiencing.

    Regards,
    Miroslav Nedyalkov
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  30. Paolo
    Paolo avatar
    423 posts
    Member since:
    Jun 2009

    Posted 06 Dec 2011 Link to this post

    Hello Miroslav,I've gone a little bit further in these days and I'm able to load an item, so this part is ok... I've got still some iussues about how to organize my models in the sense : I've got 30 view/viewmodels...should my WorkSpaceViewModel contain a reference to each view viewmodel? is there some Activator/Factory that can help me?
  31. Paolo
    Paolo avatar
    423 posts
    Member since:
    Jun 2009

    Posted 06 Dec 2011 Link to this post

    Each time I reply to this post I got a server error message... can you please verify?
Back to Top
DevCraft banner