This is a migrated thread and some comments may be shown as answers.

Binding with RadDocking

11 Answers 498 Views
Docking
This is a migrated thread and some comments may be shown as answers.
Haytham
Top achievements
Rank 1
Haytham asked on 27 Apr 2015, 01:37 PM

Greetings,

I'm doing some experimentation trying to get Catel MVVM framework to work with Raddocking. I'm inheriting from DockingPanesFactory and modifying the RadPane CreatePaneForItem method as shown below:

01.protected override RadPane CreatePaneForItem(object item)
02.        {
03.            Argument.IsNotNull("item", item);
04. 
05.            var viewModel = item as DockViewModelBase;
06.            Type viewModelType = viewModel.GetType();
07. 
08.            GetImpelementedInterface(viewModelType);
09.             
10.            Log.Debug("'{0}' is not null. Constructing pane", (object)viewModel.GetType().Name);
11. 
12.            var pane = (_isImplentingIToolPane) ? new RadPane() : new RadDocumentPane();
13.             
14.            Log.Debug("Constructing instance of view for the pane using injection of data context");
15. 
16.            Type view = _viewLocator.ResolveView(viewModelType);
17. 
18.            pane.DataContext = viewModel;
19.            pane.Content = view;
20.            pane.Header = viewModel.Header;
21.            //pane.IsActive = viewModel.IsActive;
22.            //pane.CanFloat = viewModel.CanFloat;
23.             
24.            Log.Debug("Pane: {0} - DataContext: {1} - View: {2}", pane.Header,pane.DataContext.GetType().Name,view.Name);
25. 
26.            return pane;
27.            //return base.CreatePaneForItem(item);
28.        }

When I check on pane.Content and pane.DataContext I could see that the view model is contained in the DataContext and the view is contained in the Content property as well. However, it's looking like there is an error in binding as I get this message:

1.System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'ElementName=DropDownButtonElement'. BindingExpression:Path=IsChecked; DataItem=null; target element is 'DropDownMenu' (Name='DropDownMenuElement'); target property is 'IsOpen' (type 'Boolean')

 The pane displays fine but without any content. I only get name space of the view inside the pane.

 

11 Answers, 1 is accepted

Sort by
0
Haytham
Top achievements
Rank 1
answered on 29 Apr 2015, 01:41 AM

After further reading I've realized that this isn't a binding issue. I have partially resolved this for now and got it working. In a way still not exactly sure why the first approach of setting the pane.Content to view type didn't work.

protected override RadPane CreatePaneForItem(object item)
        {
            Argument.IsNotNull(() => item);
 
            var viewModel = item as DockViewModelBase;
            if (viewModel != null)
            {
                Type viewModelType = viewModel.GetType();
 
                _isImplentingIToolPane = GetImpelementedInterface(viewModelType);
 
                Log.Debug("'{0}' is not null. Constructing pane", (object)viewModel.GetType().Name);
 
                var pane = (_isImplentingIToolPane) ? new RadPane() : new RadDocumentPane();
 
                Log.Debug("Constructing instance of view for the pane");
 
                object view = CreateViewContent(viewModel, viewModelType);
 
                pane.DataContext = ((IView)view).DataContext;
                pane.Content = view;
                pane.Header = viewModel.Header;
                pane.IsActive = viewModel.IsActive;
                pane.CanFloat = viewModel.CanFloat;
                pane.IsPinned = viewModel.IsPinned;
 
                Log.Debug("Pane: {0} - DataContext: {1}", pane.Header, pane.DataContext.GetType().Name);
 
                return pane;
            }
 
            Log.Debug("Failed to create pane.");
            return null;
        }
 
        private object CreateViewContent(object viewModel, Type viewModelType)
        {
            Type viewType = _viewLocator.ResolveView(viewModelType);
            FrameworkElement view = ViewHelper.ConstructViewWithViewModel(viewType, viewModel);
            _viewManager.RegisterView((IView)view);
 
            return view;
        }

0
Michele
Top achievements
Rank 2
answered on 15 May 2015, 05:20 AM

Hello haytam,

I started looking at catel and was looking for a feedback with rad docking usage... Are you satisfied , do you see any particular clue?

thanks

0
Haytham
Top achievements
Rank 1
answered on 15 May 2015, 05:39 AM

I'm currently working on a shell of Raddocking and Catel. So far it looks like it is working but I have not tested it extensively and I'm still trying to make the code a little cleaner i.e. I'm throwing an InvalidOperationException. Anyway, I will paste whatever I have for you to see. So far, I see no issues having Catel work with Raddocking. As a matter of fact I am convinced now that Catel is superior to other MVVM frameworks (maybe not ReactiveUI :) )

public class CustomDockingPanesFactory : DockingPanesFactory
    {
        private static readonly ILog Log = LogManager.GetCurrentClassLogger();
 
        private readonly IViewLocator _viewLocator = ServiceLocator.Default.ResolveType<IViewLocator>();
        private readonly IViewManager _viewManager = ServiceLocator.Default.ResolveType<IViewManager>();
        private bool _isImplentingIToolPane;
 
        protected override RadPane CreatePaneForItem(object item)
        {
            Argument.IsNotNull(() => item);
 
            var viewModel = item as DockViewModelBase;
            if (viewModel != null)
            {
                Type viewModelType = viewModel.GetType();
 
                _isImplentingIToolPane = GetImplementedInterface(viewModelType);
 
                Log.Debug("'{0}' is not null. Constructing pane", (object)viewModel.GetType().Name);
 
                var pane = (_isImplentingIToolPane) ? new RadPane() : new RadDocumentPane();
 
                Log.Debug("Constructing instance of view for the pane");
 
                object view = CreateViewContent(viewModel, viewModelType);
 
                pane.DataContext = ((IView)view).DataContext;
                pane.Content = view;
                pane.Header = viewModel.Header;
                pane.IsActive = viewModel.IsActive;
                pane.CanFloat = viewModel.CanFloat;
                pane.IsPinned = viewModel.IsPinned;
 
                Log.Debug("Pane: {0} - DataContext: {1}", pane.Header, pane.DataContext.GetType().Name);
 
                return pane;
            }
 
            Log.Debug("Failed to create pane.");
            return null;
        }
 
        private object CreateViewContent(object viewModel, Type viewModelType)
        {
            Type viewType = _viewLocator.ResolveView(viewModelType);
            FrameworkElement view = ViewHelper.ConstructViewWithViewModel(viewType, viewModel);
            _viewManager.RegisterView((IView)view);
 
            return view;
        }
 
        private bool GetImplementedInterface(Type viewModelType)
        {
            Argument.IsNotNull(() => viewModelType);
 
            IEnumerable<Type> typeInterfaces = viewModelType.GetInterfaces();
 
            foreach (Type type in typeInterfaces) // My viewmodels inherit ViewModelBase and also implement interfaces for either the document or the tool pane
            {
                if (type.Name == "IToolPane")
                {
                    Log.Debug("{0} implements IToolPane", viewModelType.Name);
                    return true;
                }
 
                if (type.Name == "IDocumentPane")
                {
                    Log.Debug("{0} implements IDocumentPane", viewModelType.Name);
                    return false;
                }
            }
 
            Log.ErrorAndThrowException<InvalidOperationException>("{0} does not implement IToolPane or IDocumentPane", viewModelType.Name);
            return false;
        }
 
        protected override void AddPane(RadDocking radDocking, RadPane pane)
        {
            RadPaneGroup group = null;
            var paneModel = pane.DataContext as DockViewModelBase;
             
             
            if (paneModel != null && _isImplentingIToolPane)
            {
                switch (paneModel.SavedDockState)
                {
                    case DockState.DockedRight:
                        group = radDocking.SplitItems.ToList().FirstOrDefault(i => i.Control.Name == "RightGroup") as RadPaneGroup;
                        if (group != null)
                        {
                            group.Items.Add(pane);
                        }
                        return;
 
                    case DockState.DockedBottom:
                        group = radDocking.SplitItems.ToList().FirstOrDefault(i => i.Control.Name == "BottomGroup") as RadPaneGroup;
                        if (group != null)
                        {
                            group.Items.Add(pane);
                        }
                        return;
 
                    case DockState.DockedLeft:
                        group = radDocking.SplitItems.ToList().FirstOrDefault(i => i.Control.Name == "LeftGroup") as RadPaneGroup;
                        if (group != null)
                        {
                            group.Items.Add(pane);
                        }
                        return;
 
                    case DockState.FloatingDockable:
                        RadSplitContainer splitContainer = radDocking.GeneratedItemsFactory.CreateSplitContainer();
                        group = radDocking.GeneratedItemsFactory.CreatePaneGroup();
                        splitContainer.Items.Add(group);
                        group.Items.Add(pane);
                        radDocking.Items.Add(splitContainer);
                        pane.MakeFloatingDockable();
                        return;
 
                    case DockState.FloatingOnly:
                        RadSplitContainer foSplitContainer = radDocking.GeneratedItemsFactory.CreateSplitContainer();
                        group = radDocking.GeneratedItemsFactory.CreatePaneGroup();
                        foSplitContainer.Items.Add(group);
                        group.Items.Add(pane);
                        radDocking.Items.Add(foSplitContainer);
                        pane.MakeFloatingOnly();
                        return;
 
                    case DockState.DockedTop:
                    default:
                        return;
                }
            }
 
            if (paneModel != null && !_isImplentingIToolPane)
            {
                group = radDocking.SplitItems.ToList().FirstOrDefault(i => i.Control.Name == "DocumentGroup") as RadPaneGroup;
                if (group != null)
                {
                    group.Items.Add(pane);
                }
            }
        }
 
        /// <summary>
        /// Removes the <paramref name="pane" /> from the <see cref="T:Telerik.Windows.Controls.RadDocking" /> layout. By default clears the Header, Content, DataContext and call RemoveFromParent method.
        /// </summary>
        /// <param name="pane">The <see cref="T:Telerik.Windows.Controls.RadPane" /> to remove.</param>
        protected override void RemovePane(RadPane pane)
        {
            pane.DataContext = null;
            pane.Content = null;
            pane.RemoveFromParent();
        }
    }
}

0
Michele
Top achievements
Rank 2
answered on 15 May 2015, 07:47 AM

I'm currently using Raddocking with Caliburn Micro but I think it's slow... I want to give a try to Catel... have to plan migration efforts... what about usage with other Telerik Controls?

 

0
Michele
Top achievements
Rank 2
answered on 16 May 2015, 01:00 PM

Excuse me again,

Is it fast to add a rad pane or are you facing lag/UI freeze?

thanks

0
Michele
Top achievements
Rank 2
answered on 18 May 2015, 07:52 AM

Haytham,

can you please share how do you add a Viewmodel to the CustomDockingPanesFactory?

have you got a repo on github ,something  else?

0
Haytham
Top achievements
Rank 1
answered on 18 May 2015, 12:49 PM

Hello,

No I don't have it on GitHub since like I mentioned above it is still work in progress and the code is no where near presentable.

The view model can be resolved using Catel's service locator and then passed on through the observable item.

/// <summary>
        /// Private method called to create a view model.
        /// </summary>
        /// <param name="viewModel"></param>
        /// <returns></returns>
        private IViewModel ResolveViewModel(string viewModel)
        {
            Argument.IsNotNull(()=>viewModel);
 
            Log.Debug("Resolving the main view model '{0}'", viewModel);
 
            Type type = TypeCache.GetType(viewModel);
 
            if (type == null)
            {
                Log.ErrorAndThrowException<NullReferenceException>("Could not retrieve type for 'moduleMainViewModelTypeName'");
            }
 
            return (IViewModel)_serviceLocator.ResolveType(type);
        }
 
        /// <summary>
        /// Private method called to create a view model.
        /// </summary>
        /// <param name="viewModelType"></param>
        /// <returns></returns>
        private IViewModel ResolveViewModel(Type viewModelType)
        {
            Argument.IsNotNull(()=>viewModelType);
 
            Log.Debug("Resolving the main view model - '{0}'", viewModelType.Name);
            return (IViewModel)_serviceLocator.ResolveType(viewModelType);
        }

The view model for docking looks like this:

public class DockViewModelBase : ViewModelBase, IDockViewModelBase
    {
        private IDefaultPaneSettings _defaultPaneSettings;
        public IPaneSettingsService PaneSettingsService {get; set;}
 
        public DockViewModelBase()
        {
        }
        public DockViewModelBase(IPaneSettingsService paneSettingsService)
        {
            this.PaneSettingsService = paneSettingsService;
        }
 
        public virtual void RetrieveDefaultPaneSettings(Type defaultSettingsType)
        {
            Argument.IsNotNull(() => defaultSettingsType);
 
            _defaultPaneSettings = PaneSettingsService.GetDefaultConfiguration(defaultSettingsType);
            this.Header = _defaultPaneSettings.Header;
            this.DefaultDockState = _defaultPaneSettings.SavedDockState;
            this.SavedDockState = _defaultPaneSettings.SavedDockState;
            this.CurrentDockState = _defaultPaneSettings.SavedDockState;
            this.PreviousDockState = _defaultPaneSettings.SavedDockState;
            this.IsActive = true;
            this.IsHidden = false;
            this.CanFloat = _defaultPaneSettings.CanFloat;
            this.IsFloating = _defaultPaneSettings.IsFloating;
            this.IsPinned = _defaultPaneSettings.IsPinned;
        }
 
        public virtual string Header { get; set; }
         
        /// <summary>
        /// This is set by the originator of the module for the view model
        /// </summary>
        public DockState DefaultDockState { get; set; }
        
        /// <summary>
        /// This is the value that will be extracted from a file. This will be saved to a file if the user changes the dock state for the pane
        /// </summary>
        public DockState SavedDockState { get; set; }
         
        /// <summary>
        /// Current dock state for the pane
        /// </summary>
        public DockState CurrentDockState { get; set; }
        
        /// <summary>
        /// The last dock state for the pane before changing it to current
        /// </summary>
        public DockState PreviousDockState { get; set; }
         
        public bool IsActive { get; set; }
 
        public bool IsHidden { get; set; }
         
        public bool CanFloat { get; set; }
        
        public bool IsFloating { get; set; }
         
        public bool IsPinned { get; set; }
         
        /// <summary>
        /// A flag indicating whether current dock state is the same as the default dock state
        /// </summary>
        public bool IsDefaultDockState { get; set; }
 
        public Type ViewType;
         
        public event EventHandler<EventArgs> Activated;
        public event EventHandler<EventArgs> Deactivated;
    }

I have created a class that retrieves the settings of the pane. Also, and in order to determine where the view model would be hosted on the Dock they have to implements either a IToolPane interface or an IDocumentPane interface

public interface IDocumentPane : IDockViewModelBase
   {
        
   }
public interface IToolPane : IDockViewModelBase
   {
        
   }

And now the view model for the docking panes.

public class DockingPanesViewModel : ViewModelBase
    {
        private static readonly ILog Log = LogManager.GetCurrentClassLogger();
 
        private readonly IDockingPanesService _dockingPanesService;
        private readonly ICollection<IViewModel> _whenAvailableViewModels;
        public DockingPanesViewModel(IDockingPanesService dockingPanesService)
        {
            Argument.IsNotNull(()=> dockingPanesService);
              
            this.Panes = new ObservableItemCollection<DockViewModelBase>();
            this._dockingPanesService = dockingPanesService;
            _whenAvailableViewModels = new List<IViewModel>();
 
            this._whenAvailableViewModels = LoadAvailableViewModels();
 
            ShowPanesAtStart(_whenAvailableViewModels);
        }
 
        private void ShowPanesAtStart(ICollection<IViewModel> whenAvailableViewModels)
        {
            Argument.IsNotNull(() => whenAvailableViewModels);
 
            foreach (var viewModel1 in whenAvailableViewModels)
            {
                var viewModel = (DockViewModelBase) viewModel1;
                Panes.Add(viewModel);
            }
        }
 
        private ICollection<IViewModel> LoadAvailableViewModels()
        {
            return _dockingPanesService.CreateWhenAvailableViewModels();
        }
 
        
        public ObservableCollection<DockViewModelBase> Panes
        {
            get { return GetValue<ObservableCollection<DockViewModelBase>>(PanesProperty); }
            set { SetValue(PanesProperty, value); }
        }
        public static readonly PropertyData PanesProperty = RegisterProperty("Panes", typeof(ObservableCollection<DockViewModelBase>), null);
    }

I'm using a modified ModuleInfo and ModuleCatalog from the Prism library to pass on the view models at start up. The above code should help you get started.

Note that all this code does is test loading view models into the panes. I have not optimized it for asynchronous work yet.

If this isn't helpful I could upload the whole project to OneDrive.

Like I said this is work in progress.

0
Michele
Top achievements
Rank 2
answered on 18 May 2015, 01:20 PM

Hello if you can share the code it would be great , have you also got an email I can write you on?

Thanks

0
Michele
Top achievements
Rank 2
answered on 20 May 2015, 08:11 AM

Haytham can you please share it somewhere?

Thanks

0
Michele
Top achievements
Rank 2
answered on 25 May 2015, 07:58 AM
Haytham can you please give me some help?
0
Michele
Top achievements
Rank 2
answered on 21 Dec 2015, 11:02 AM
Haytham are you still here?
Tags
Docking
Asked by
Haytham
Top achievements
Rank 1
Answers by
Haytham
Top achievements
Rank 1
Michele
Top achievements
Rank 2
Share this question
or