Binding with RadDocking

12 posts, 0 answers
  1. Haytham
    Haytham avatar
    4 posts
    Member since:
    Apr 2015

    Posted 27 Apr 2015 Link to this post

    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.

     

  2. Haytham
    Haytham avatar
    4 posts
    Member since:
    Apr 2015

    Posted 28 Apr 2015 in reply to Haytham Link to this post

    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;
            }

  3. UI for WPF is Visual Studio 2017 Ready
  4. Paolo
    Paolo avatar
    423 posts
    Member since:
    Jun 2009

    Posted 15 May 2015 Link to this post

    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

  5. Haytham
    Haytham avatar
    4 posts
    Member since:
    Apr 2015

    Posted 15 May 2015 in reply to Paolo Link to this post

    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();
            }
        }
    }

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

    Posted 15 May 2015 in reply to Haytham Link to this post

    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?

     

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

    Posted 16 May 2015 in reply to Paolo Link to this post

    Excuse me again,

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

    thanks

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

    Posted 18 May 2015 in reply to Paolo Link to this post

    Haytham,

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

    have you got a repo on github ,something  else?

  9. Haytham
    Haytham avatar
    4 posts
    Member since:
    Apr 2015

    Posted 18 May 2015 in reply to Paolo Link to this post

    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.

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

    Posted 18 May 2015 in reply to Haytham Link to this post

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

    Thanks

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

    Posted 20 May 2015 Link to this post

    Haytham can you please share it somewhere?

    Thanks

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

    Posted 25 May 2015 in reply to Paolo Link to this post

    Haytham can you please give me some help?
  13. Paolo
    Paolo avatar
    423 posts
    Member since:
    Jun 2009

    Posted 21 Dec 2015 in reply to Paolo Link to this post

    Haytham are you still here?
Back to Top
UI for WPF is Visual Studio 2017 Ready