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

Prism with RadDocking

10 Answers 394 Views
Docking
This is a migrated thread and some comments may be shown as answers.
Alin
Top achievements
Rank 1
Alin asked on 15 Feb 2017, 07:54 AM

Hello,

I'm working on a WPF application using Prism 5 and Telerik UI for WPF R3 2016. The application loads dinamically 2 separate modules (dll) using UnityContainer and inject them in a RadDocking control as follows:

MainWindow.xaml

<Window.Resources>
        <Style TargetType="telerik:RadDocumentPane">
            <Setter Property="Header" Value="{Binding DataContext.Title}"/>
            <Setter Property="HeaderTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition />
                                <ColumnDefinition />
                            </Grid.ColumnDefinitions>
                            <ContentControl VerticalAlignment="Center" HorizontalAlignment="Center"
                                            Margin="0,0,7,0" Content="{Binding}"/>
                            <Button Grid.Column="1" Content="x">
                                <i:Interaction.Triggers>
                                    <i:EventTrigger EventName="Click">
                                        <prism:CloseTabAction/>
                                    </i:EventTrigger>
                                </i:Interaction.Triggers>
                            </Button>

                        </Grid>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <Button Content="Toolbar" Command="{Binding ShowToolbarCommand}" Width="50" Height="25" HorizontalAlignment="Left"></Button>
            <Button Content="Home" Command="{Binding ShowHomeCommand}" Width="50" Height="25" HorizontalAlignment="Left"></Button>
        </StackPanel>
        <DockPanel Grid.Row="1" LastChildFill="True">
            <telerikDocking:RadDocking x:Name="Docking" Background="Transparent"
                                      BorderThickness="0">
                <telerikDocking:RadDocking.DocumentHost>
                    <telerikDocking:RadSplitContainer >
                        <telerikDocking:RadPaneGroup regions:RegionManager.RegionName="{x:Static model:RegionNames.ContentRegion}"/>
                    </telerikDocking:RadSplitContainer>
                </telerikDocking:RadDocking.DocumentHost>
                <telerikDocking:RadSplitContainer MaxWidth="400" Width="175" MinWidth="175" InitialPosition="DockedLeft">
                    <telerikDocking:RadPaneGroup>
                        <telerikDocking:RadDocumentPane CanUserClose="False" PaneHeaderVisibility="Collapsed"
                                regions:RegionManager.RegionName="{x:Static model:RegionNames.LeftRegion}">
                        </telerikDocking:RadDocumentPane>
                    </telerikDocking:RadPaneGroup>
                </telerikDocking:RadSplitContainer>
            </telerikDocking:RadDocking>
        </DockPanel>
    </Grid>

CloseTabAction.cs

public class CloseTabAction : TriggerAction<Button>
    {
        protected override void Invoke(object parameter)
        {
            var args = parameter as RoutedEventArgs;
            if (args == null)
                return;
            var tabItem = FindParent<RadPane>(args.OriginalSource as DependencyObject);
            if (tabItem == null)
                return;
            var tabControl = FindParent<RadPaneGroup>(tabItem);
            if (tabControl == null)
                return;

            IRegion region = RegionManager.GetObservableRegion(tabControl).Value;
            if (region == null)
                return;
            RemoveItemFromRegion(tabItem.Content, region);
        }

        void RemoveItemFromRegion(object item, IRegion region)
        {
            var navigationContext = new NavigationContext(region.NavigationService, null);
            if (CanRemove(item, navigationContext))
            {
                InvokeOnNavigatedFrom(item, navigationContext);
                region.Remove(item);
            }
        }

        void InvokeOnNavigatedFrom(object item, NavigationContext navigationContext)
        {
            var navigationAwareItem = item as INavigationAware;
            if (navigationAwareItem != null)
                navigationAwareItem.OnNavigatedFrom(navigationContext);
            var frameworkElement = item as FrameworkElement;
            if (frameworkElement != null)
            {
                INavigationAware navigationAwareDataContext = frameworkElement.DataContext as INavigationAware;
                if (navigationAwareDataContext != null)
                {
                    navigationAwareDataContext.OnNavigatedFrom(navigationContext);
                }
            }
        }

        bool CanRemove(object item, NavigationContext navigationContext)
        {
            bool canRemove = true;
            var confirmRequestItem = item as IConfirmNavigationRequest;
            if (confirmRequestItem != null)
            {
                confirmRequestItem.ConfirmNavigationRequest(navigationContext, result =>
                {
                    canRemove = result;
                });
            }
            var frameworkElement = item as FrameworkElement;
            if (frameworkElement != null && canRemove)
            {
                IConfirmNavigationRequest confirmRequestDataContext = frameworkElement.DataContext as IConfirmNavigationRequest;
                if (confirmRequestDataContext != null)
                {
                    confirmRequestDataContext.ConfirmNavigationRequest(navigationContext, result =>
                    {
                        canRemove = result;
                    });
                }
            }
            return canRemove;
        }

        static T FindParent<T>(DependencyObject child) where T : DependencyObject
        {
            DependencyObject parentObject = VisualTreeHelper.GetParent(child);
            if (parentObject == null)
                return null;
            var parent = parentObject as T;
            if (parent != null)
                return parent;
            return FindParent<T>(parentObject);
        }
    }

I've overrided Prism ScopedRegionNavigationContentLoader in order to allow views injections using navigation to create ScopedRegions:

public class ScopedRegionNavigationContentLoader: IRegionNavigationContentLoader
    {
        private readonly IServiceLocator serviceLocator;

        /// <summary>
        /// Initializes a new instance of the <see cref="RegionNavigationContentLoader"/> class with a service locator.
        /// </summary>
        /// <param name="serviceLocator">The service locator.</param>
        public ScopedRegionNavigationContentLoader(IServiceLocator serviceLocator)
        {
            this.serviceLocator = serviceLocator;
        }

        /// <summary>
        /// Gets the view to which the navigation request represented by <paramref name="navigationContext"/> applies.
        /// </summary>
        /// <param name="region">The region.</param>
        /// <param name="navigationContext">The context representing the navigation request.</param>
        /// <returns>
        /// The view to be the target of the navigation request.
        /// </returns>
        /// <remarks>
        /// If none of the views in the region can be the target of the navigation request, a new view
        /// is created and added to the region.
        /// </remarks>
        /// <exception cref="ArgumentException">when a new view cannot be created for the navigation request.</exception>
        public object LoadContent(IRegion region, NavigationContext navigationContext)
        {
            if (region == null) throw new ArgumentNullException("region");
            if (navigationContext == null) throw new ArgumentNullException("navigationContext");

            string candidateTargetContract = this.GetContractFromNavigationContext(navigationContext);

            var candidates = this.GetCandidatesFromRegion(region, candidateTargetContract);

            var acceptingCandidates =
                candidates.Where(
                    v =>
                    {
                        var navigationAware = v as INavigationAware;
                        if (navigationAware != null && !navigationAware.IsNavigationTarget(navigationContext))
                        {
                            return false;
                        }

                        var frameworkElement = v as FrameworkElement;
                        if (frameworkElement == null)
                        {
                            return true;
                        }

                        navigationAware = frameworkElement.DataContext as INavigationAware;
                        return navigationAware == null || navigationAware.IsNavigationTarget(navigationContext);
                    });


            var view = acceptingCandidates.FirstOrDefault();

            if (view != null)
            {
                return view;
            }

            view = this.CreateNewRegionItem(candidateTargetContract);

            region.Add(view, null, CreateRegionManagerScope(view));

            return view;
        }

        private bool CreateRegionManagerScope(object view)
        {
            bool createRegionManagerScope = false;

            var viewHasScopedRegions = view as ICreateRegionManagerScope;
            if (viewHasScopedRegions != null)
                createRegionManagerScope = viewHasScopedRegions.CreateRegionManagerScope;

            return createRegionManagerScope;
        }

        /// <summary>
        /// Provides a new item for the region based on the supplied candidate target contract name.
        /// </summary>
        /// <param name="candidateTargetContract">The target contract to build.</param>
        /// <returns>An instance of an item to put into the <see cref="IRegion"/>.</returns>
        protected virtual object CreateNewRegionItem(string candidateTargetContract)
        {
            object newRegionItem;
            try
            {           
                newRegionItem = this.serviceLocator.GetInstance<object>(candidateTargetContract);
            }
            catch (ActivationException e)
            {
                throw new InvalidOperationException(
                    string.Format(CultureInfo.CurrentCulture, "Cannot create navigation target", candidateTargetContract),
                    e);
            }
            return newRegionItem;
        }

        /// <summary>
        /// Returns the candidate TargetContract based on the <see cref="NavigationContext"/>.
        /// </summary>
        /// <param name="navigationContext">The navigation contract.</param>
        /// <returns>The candidate contract to seek within the <see cref="IRegion"/> and to use, if not found, when resolving from the container.</returns>
        protected virtual string GetContractFromNavigationContext(NavigationContext navigationContext)
        {
            if (navigationContext == null) throw new ArgumentNullException("navigationContext");

            var candidateTargetContract = UriParsingHelper.GetAbsolutePath(navigationContext.Uri);
            candidateTargetContract = candidateTargetContract.TrimStart('/');
            return candidateTargetContract;
        }

        /// <summary>
        /// Returns the set of candidates that may satisfiy this navigation request.
        /// </summary>
        /// <param name="region">The region containing items that may satisfy the navigation request.</param>
        /// <param name="candidateNavigationContract">The candidate navigation target as determined by <see cref="GetContractFromNavigationContext"/></param>
        /// <returns>An enumerable of candidate objects from the <see cref="IRegion"/></returns>
        protected virtual IEnumerable<object> GetCandidatesFromRegion(IRegion region, string candidateNavigationContract)
        {
            if (region == null) throw new ArgumentNullException("region");
            return region.Views.Where(v =>
                string.Equals(v.GetType().Name, candidateNavigationContract, StringComparison.Ordinal) ||
                string.Equals(v.GetType().FullName, candidateNavigationContract, StringComparison.Ordinal));
        }
    }

 

Everything works fine for creation / add RadPanes except Closing and Moving event.

On Close, the the exception is: An unhandled exception of type 'System.InvalidCastException' occurred in Telerik.Windows.Controls.Docking.dll

Additional information: Unable to cast object of type 'ModuleA.Views.ModuleAHomeView' to type 'Telerik.Windows.Controls.RadPane'.

but hostControl is a valid RadPaneGroup and Items collection DO contain ModuleA.Views.ModuleAHomeView

On Move,  I've got a NullReferenceExceltopn at Telerik.Windows.Controls.RadPane.OnHeaderMouseLeftButtonDown(Object sender, MouseButtonEventArgs e)

 

Any help would be highly appreciated.

Best regards,

Alin Halatiu

10 Answers, 1 is accepted

Sort by
0
Nasko
Telerik team
answered on 16 Feb 2017, 11:48 AM
Hi Alin,

In order to make everything work as expected with your current implemented logic the "ModuleAHomeView" and "ModuleBHomeView" should derive from RadPane or RadDocumentPane. Thus all the functionalities associated with RadPane should be working as expected.

Attached is a sample that demonstrates the described above approach.

Hope this helps.

Regards,
Nasko
Telerik by Progress
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
0
Alin
Top achievements
Rank 1
answered on 16 Feb 2017, 12:16 PM

Works like a charm. Thank you very much.

 

0
Alin
Top achievements
Rank 1
answered on 17 Feb 2017, 12:32 PM

Hello Nasko,

I have an exception in your solution, when I try to a RadDocumentPane:

Insertion index was out of range. Must be non-negative and less than or equal to size.
Parameter name: index

 

at System.Collections.ArrayList.Insert(Int32 index, Object value)
   at MS.Internal.Controls.InnerItemCollectionView.Insert(Int32 index, Object item)
   at System.Windows.Controls.ItemCollection.Insert(Int32 insertIndex, Object insertItem)
   at Microsoft.Practices.Prism.Regions.Behaviors.SelectorItemsSourceSyncBehavior.Views_CollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
   at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
   at Microsoft.Practices.Prism.Regions.ViewsCollection.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
   at Microsoft.Practices.Prism.Regions.ViewsCollection.NotifyAdd(IList items, Int32 newStartingIndex)
   at Microsoft.Practices.Prism.Regions.ViewsCollection.NotifyAdd(Object item)
   at Microsoft.Practices.Prism.Regions.ViewsCollection.SourceCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
   at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
   at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
   at System.Collections.ObjectModel.ObservableCollection`1.InsertItem(Int32 index, T item)
   at System.Collections.ObjectModel.Collection`1.Add(T item)
   at Microsoft.Practices.Prism.Regions.Region.InnerAdd(Object view, String viewName, IRegionManager scopedRegionManager)
   at Microsoft.Practices.Prism.Regions.Region.Add(Object view, String viewName, Boolean createRegionManagerScope)
   at Core.Prism.ScopedRegionNavigationContentLoader.LoadContent(IRegion region, NavigationContext navigationContext) in C:\Solution\Infrastructure\Core\Prism\ScopedRegionNavigationContentLoader.cs:line 77
   at Microsoft.Practices.Prism.Regions.RegionNavigationService.ExecuteNavigation(NavigationContext navigationContext, Object[]

activeViews, Action`1 navigationCallback)

More then this, I'm not able to reinject any view after this.

Thank you in advance for your help.

Best regards,
Alin Halatiu

0
Alin
Top achievements
Rank 1
answered on 17 Feb 2017, 12:33 PM
Hello Nasko,
I have an exception in your solution, when I try to MOVE a RadDocumentPane:
Insertion index was out of range. Must be non-negative and less than or equal to size.
Parameter name: index

at System.Collections.ArrayList.Insert(Int32 index, Object value)
   at MS.Internal.Controls.InnerItemCollectionView.Insert(Int32 index, Object item)
   at System.Windows.Controls.ItemCollection.Insert(Int32 insertIndex, Object insertItem)
   at Microsoft.Practices.Prism.Regions.Behaviors.SelectorItemsSourceSyncBehavior.Views_CollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
   at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
   at Microsoft.Practices.Prism.Regions.ViewsCollection.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
   at Microsoft.Practices.Prism.Regions.ViewsCollection.NotifyAdd(IList items, Int32 newStartingIndex)
   at Microsoft.Practices.Prism.Regions.ViewsCollection.NotifyAdd(Object item)
   at Microsoft.Practices.Prism.Regions.ViewsCollection.SourceCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
   at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
   at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
   at System.Collections.ObjectModel.ObservableCollection`1.InsertItem(Int32 index, T item)
   at System.Collections.ObjectModel.Collection`1.Add(T item)
   at Microsoft.Practices.Prism.Regions.Region.InnerAdd(Object view, String viewName, IRegionManager scopedRegionManager)
   at Microsoft.Practices.Prism.Regions.Region.Add(Object view, String viewName, Boolean createRegionManagerScope)
   at Core.Prism.ScopedRegionNavigationContentLoader.LoadContent(IRegion region, NavigationContext navigationContext) in C:\Solution\Infrastructure\Core\Prism\ScopedRegionNavigationContentLoader.cs:line 77
   at Microsoft.Practices.Prism.Regions.RegionNavigationService.ExecuteNavigation(NavigationContext navigationContext, Object[]
activeViews, Action`1 navigationCallback)
More then this, I'm not able to reinject any view after this.
Thank you in advance for your help.
Best regards,
Alin Halatiu
0
Alin
Top achievements
Rank 1
answered on 17 Feb 2017, 12:34 PM
Hello Nasko,
I have an exception in your solution, when I try to MOVE a RadDocumentPane:
Insertion index was out of range. Must be non-negative and less than or equal to size.
Parameter name: index

at System.Collections.ArrayList.Insert(Int32 index, Object value)
   at MS.Internal.Controls.InnerItemCollectionView.Insert(Int32 index, Object item)
   at System.Windows.Controls.ItemCollection.Insert(Int32 insertIndex, Object insertItem)
   at Microsoft.Practices.Prism.Regions.Behaviors.SelectorItemsSourceSyncBehavior.Views_CollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
   at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
   at Microsoft.Practices.Prism.Regions.ViewsCollection.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
   at Microsoft.Practices.Prism.Regions.ViewsCollection.NotifyAdd(IList items, Int32 newStartingIndex)
   at Microsoft.Practices.Prism.Regions.ViewsCollection.NotifyAdd(Object item)
   at Microsoft.Practices.Prism.Regions.ViewsCollection.SourceCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
   at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
   at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
   at System.Collections.ObjectModel.ObservableCollection`1.InsertItem(Int32 index, T item)
   at System.Collections.ObjectModel.Collection`1.Add(T item)
   at Microsoft.Practices.Prism.Regions.Region.InnerAdd(Object view, String viewName, IRegionManager scopedRegionManager)
   at Microsoft.Practices.Prism.Regions.Region.Add(Object view, String viewName, Boolean createRegionManagerScope)
   at Core.Prism.ScopedRegionNavigationContentLoader.LoadContent(IRegion region, NavigationContext navigationContext) in C:\Solution\Infrastructure\Core\Prism\ScopedRegionNavigationContentLoader.cs:line 77
   at Microsoft.Practices.Prism.Regions.RegionNavigationService.ExecuteNavigation(NavigationContext navigationContext, Object[]
activeViews, Action`1 navigationCallback)
More then this, I'm not able to reinject any view after this.
Thank you in advance for your help.
Best regards,
Alin Halatiu
0
Alin
Top achievements
Rank 1
answered on 17 Feb 2017, 12:34 PM
Hello Nasko,
I have an exception in your solution, when I try to a MOVE RadDocumentPane:
Insertion index was out of range. Must be non-negative and less than or equal to size.
Parameter name: index

at System.Collections.ArrayList.Insert(Int32 index, Object value)
   at MS.Internal.Controls.InnerItemCollectionView.Insert(Int32 index, Object item)
   at System.Windows.Controls.ItemCollection.Insert(Int32 insertIndex, Object insertItem)
   at Microsoft.Practices.Prism.Regions.Behaviors.SelectorItemsSourceSyncBehavior.Views_CollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
   at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
   at Microsoft.Practices.Prism.Regions.ViewsCollection.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
   at Microsoft.Practices.Prism.Regions.ViewsCollection.NotifyAdd(IList items, Int32 newStartingIndex)
   at Microsoft.Practices.Prism.Regions.ViewsCollection.NotifyAdd(Object item)
   at Microsoft.Practices.Prism.Regions.ViewsCollection.SourceCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
   at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
   at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
   at System.Collections.ObjectModel.ObservableCollection`1.InsertItem(Int32 index, T item)
   at System.Collections.ObjectModel.Collection`1.Add(T item)
   at Microsoft.Practices.Prism.Regions.Region.InnerAdd(Object view, String viewName, IRegionManager scopedRegionManager)
   at Microsoft.Practices.Prism.Regions.Region.Add(Object view, String viewName, Boolean createRegionManagerScope)
   at Core.Prism.ScopedRegionNavigationContentLoader.LoadContent(IRegion region, NavigationContext navigationContext) in C:\Solution\Infrastructure\Core\Prism\ScopedRegionNavigationContentLoader.cs:line 77
   at Microsoft.Practices.Prism.Regions.RegionNavigationService.ExecuteNavigation(NavigationContext navigationContext, Object[]
activeViews, Action`1 navigationCallback)
More then this, I'm not able to reinject any view after this.
Thank you in advance for your help.
Best regards,
Alin Halatiu
0
Nasko
Telerik team
answered on 17 Feb 2017, 12:58 PM
Hello Alin,

I was not able to reproduce the issue using the sample project sent in my previous response. Such exception could be observed if the PaneGroup region is not properly mapped with the adapter. Could you please, modify the sample (if any modifications have been made) and provide some steps how to reproduce the issue? Reproducing the exception on our side will be of great help in order to provide you with a prompt solution.

We are looking forward to hearing from you.

Regards,
Nasko
Telerik by Progress
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
0
Alin
Top achievements
Rank 1
answered on 17 Feb 2017, 03:04 PM
Since I'm not able to upload the project here, I'll put it on ticket 1090317.
0
Nasko
Telerik team
answered on 20 Feb 2017, 06:26 AM
Hi Alin,

We have checked the ticket with ID 1090317, but no sample project has been attached. Could you please, let us know if you were able to resolve the issue?

If the issue is still present please, send us a sample so, we could be able to investigate it further.


Regards,
Nasko
Telerik by Progress
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
0
Alin
Top achievements
Rank 1
answered on 20 Feb 2017, 01:01 PM
Please check the ticket 1091223.
1091223
1091223
1091223
Tags
Docking
Asked by
Alin
Top achievements
Rank 1
Answers by
Nasko
Telerik team
Alin
Top achievements
Rank 1
Share this question
or