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