I suppose I can see your point, and I understand why you would like it to call the RequestNavigate method.
To answer your question, yes this is by design and it is not supposed to call RequestNavigate while switching tabs. However, you can modify this behavior to do what you want. Prism is open source. You should have the source code, you can add the project to your project and easily step through the code for the following:
TabControlRegionAdapter - Adapts the region to the tab control
public class TabControlRegionAdapter : RegionAdapterBase<TabControl>
{
/// <summary>
/// <see cref="Style"/> to set to the created <see cref="TabItem"/>.
/// </summary>
public static readonly DependencyProperty ItemContainerStyleProperty =
DependencyProperty.RegisterAttached("ItemContainerStyle", typeof(Style), typeof(TabControlRegionAdapter), null);
/// <summary>
/// Initializes a new instance of the <see cref="TabControlRegionAdapter"/> class.
/// </summary>
/// <param name="regionBehaviorFactory">The factory used to create the region behaviors to attach to the created regions.</param>
public TabControlRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory)
: base(regionBehaviorFactory)
{
}
/// <summary>
/// Gets the <see cref="ItemContainerStyleProperty"/> property value.
/// </summary>
/// <param name="target">Target object of the attached property.</param>
/// <returns>Value of the <see cref="ItemContainerStyleProperty"/> property.</returns>
public static Style GetItemContainerStyle(DependencyObject target)
{
if (target == null) throw new ArgumentNullException("target");
return (Style)target.GetValue(ItemContainerStyleProperty);
}
/// <summary>
/// Sets the <see cref="ItemContainerStyleProperty"/> property value.
/// </summary>
/// <param name="target">Target object of the attached property.</param>
/// <param name="value">Value to be set on the <see cref="ItemContainerStyleProperty"/> property.</param>
public static void SetItemContainerStyle(DependencyObject target, Style value)
{
if (target == null) throw new ArgumentNullException("target");
target.SetValue(ItemContainerStyleProperty, value);
}
/// <summary>
/// Adapts a <see cref="TabControl"/> to an <see cref="IRegion"/>.
/// </summary>
/// <param name="region">The new region being used.</param>
/// <param name="regionTarget">The object to adapt.</param>
protected override void Adapt(IRegion region, TabControl regionTarget)
{
if (regionTarget == null) throw new ArgumentNullException("regionTarget");
bool itemsSourceIsSet = regionTarget.ItemsSource != null;
if (itemsSourceIsSet)
{
throw new InvalidOperationException(Resources.ItemsControlHasItemsSourceException);
}
}
/// <summary>
/// Attach new behaviors.
/// </summary>
/// <param name="region">The region being used.</param>
/// <param name="regionTarget">The object to adapt.</param>
/// <remarks>
/// This class attaches the base behaviors and also keeps the <see cref="TabControl.SelectedItem"/>
/// and the <see cref="IRegion.ActiveViews"/> in sync.
/// </remarks>
protected override void AttachBehaviors(IRegion region, TabControl regionTarget)
{
if (region == null) throw new ArgumentNullException("region");
base.AttachBehaviors(region, regionTarget);
if (!region.Behaviors.ContainsKey(TabControlRegionSyncBehavior.BehaviorKey))
{
region.Behaviors.Add(TabControlRegionSyncBehavior.BehaviorKey, new TabControlRegionSyncBehavior { HostControl = regionTarget });
}
}
/// <summary>
/// Creates a new instance of <see cref="Region"/>.
/// </summary>
/// <returns>A new instance of <see cref="Region"/>.</returns>
protected override IRegion CreateRegion()
{
return new SingleActiveRegion();
}
}
And also, TabControlRegionSyncBehavior. This is the one which you could call RequestNavigate.
public class TabControlRegionSyncBehavior : RegionBehavior, IHostAwareRegionBehavior
{
///<summary>
/// The behavior key for this region sync behavior.
///</summary>
public const string BehaviorKey = "TabControlRegionSyncBehavior";
private static readonly DependencyProperty IsGeneratedProperty =
DependencyProperty.RegisterAttached("IsGenerated", typeof(bool), typeof(TabControlRegionSyncBehavior), null);
private TabControl hostControl;
/// <summary>
/// Gets or sets the <see cref="DependencyObject"/> that the <see cref="IRegion"/> is attached to.
/// </summary>
/// <value>A <see cref="DependencyObject"/> that the <see cref="IRegion"/> is attached to.
/// This is usually a <see cref="FrameworkElement"/> that is part of the tree.</value>
public DependencyObject HostControl
{
get
{
return this.hostControl;
}
set
{
TabControl newValue = value as TabControl;
if (newValue == null)
{
throw new InvalidOperationException(Resources.HostControlMustBeATabControl);
}
if (IsAttached)
{
throw new InvalidOperationException(Resources.HostControlCannotBeSetAfterAttach);
}
this.hostControl = newValue;
}
}
/// <summary>
/// Override this method to perform the logic after the behavior has been attached.
/// </summary>
protected override void OnAttach()
{
if (this.hostControl == null)
{
throw new InvalidOperationException(Resources.HostControlCannotBeNull);
}
this.SynchronizeItems();
this.hostControl.SelectionChanged += this.OnSelectionChanged;
this.Region.ActiveViews.CollectionChanged += this.OnActiveViewsChanged;
this.Region.Views.CollectionChanged += this.OnViewsChanged;
}
/// <summary>
/// Gets the item contained in the <see cref="TabItem"/>.
/// </summary>
/// <param name="tabItem">The container item.</param>
/// <returns>The item contained in the <paramref name="tabItem"/> if it was generated automatically by the behavior; otherwise <paramref name="tabItem"/>.</returns>
protected virtual object GetContainedItem(TabItem tabItem)
{
if (tabItem == null) throw new ArgumentNullException("tabItem");
if ((bool)tabItem.GetValue(IsGeneratedProperty))
{
return tabItem.Content;
}
return tabItem;
}
/// <summary>
/// Override to change how TabItem's are prepared for items.
/// </summary>
/// <param name="item">The item to wrap in a TabItem</param>
/// <param name="parent">The parent <see cref="DependencyObject"/></param>
/// <returns>A tab item that wraps the supplied <paramref name="item"/></returns>
protected virtual TabItem PrepareContainerForItem(object item, DependencyObject parent)
{
TabItem container = item as TabItem;
if (container == null)
{
object dataContext = GetDataContext(item);
container = new TabItem();
container.Content = item;
container.Style = TabControlRegionAdapter.GetItemContainerStyle(parent);
container.DataContext = dataContext; // To run with SL 2
container.Header = dataContext; // To run with SL 3
container.SetValue(IsGeneratedProperty, true);
}
return container;
}
/// <summary>
/// Undoes the effects of the <see cref="PrepareContainerForItem"/> method.
/// </summary>
/// <param name="tabItem">The container element for the item.</param>
protected virtual void ClearContainerForItem(TabItem tabItem)
{
if (tabItem == null) throw new ArgumentNullException("tabItem");
if ((bool)tabItem.GetValue(IsGeneratedProperty))
{
tabItem.Content = null;
}
}
/// <summary>
/// Creates or identifies the element that is used to display the given item.
/// </summary>
/// <param name="item">The item to get the container for.</param>
/// <param name="itemCollection">The parent's <see cref="ItemCollection"/>.</param>
/// <returns>The element that is used to display the given item.</returns>
protected virtual TabItem GetContainerForItem(object item, ItemCollection itemCollection)
{
if (itemCollection == null) throw new ArgumentNullException("itemCollection");
TabItem container = item as TabItem;
if (container != null && ((bool)container.GetValue(IsGeneratedProperty)) == false)
{
return container;
}
foreach (TabItem tabItem in itemCollection)
{
if ((bool)tabItem.GetValue(IsGeneratedProperty))
{
if (tabItem.Content == item)
{
return tabItem;
}
}
}
return null;
}
/// <summary>
/// Return the appropriate data context. If the item is a FrameworkElement it cannot be a data context in Silverlight, so we use its data context.
/// Otherwise, we just us the item as the data context.
/// </summary>
private static object GetDataContext(object item)
{
FrameworkElement frameworkElement = item as FrameworkElement;
return frameworkElement == null ? item : frameworkElement.DataContext;
}
private void SynchronizeItems()
{
List<object> existingItems = new List<object>();
if (this.hostControl.Items.Count > 0)
{
// Control must be empty before "Binding" to a region
foreach (object childItem in this.hostControl.Items)
{
existingItems.Add(childItem);
}
}
foreach (object view in this.Region.Views)
{
TabItem tabItem = this.PrepareContainerForItem(view, this.hostControl);
this.hostControl.Items.Add(tabItem);
}
foreach (object existingItem in existingItems)
{
this.Region.Add(existingItem);
}
}
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
// e.OriginalSource == null, that's why we use sender.
if (this.hostControl == sender)
{
foreach (TabItem tabItem in e.RemovedItems)
{
object item = this.GetContainedItem(tabItem);
// check if the view is in both Views and ActiveViews collections (there may be out of sync)
if (this.Region.Views.Contains(item) && this.Region.ActiveViews.Contains(item))
{
this.Region.Deactivate(item);
}
}
foreach (TabItem tabItem in e.AddedItems)
{
object item = this.GetContainedItem(tabItem);
if (!this.Region.ActiveViews.Contains(item))
{
this.Region.Activate(item);
}
}
}
}
private void OnActiveViewsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
this.hostControl.SelectedItem = this.GetContainerForItem(e.NewItems[0], this.hostControl.Items);
}
else if (e.Action == NotifyCollectionChangedAction.Remove
&& this.hostControl.SelectedItem != null
&& e.OldItems.Contains(this.GetContainedItem((TabItem)this.hostControl.SelectedItem)))
{
this.hostControl.SelectedItem = null;
}
}
private void OnViewsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
int startingIndex = e.NewStartingIndex;
foreach (object newItem in e.NewItems)
{
TabItem tabItem = this.PrepareContainerForItem(newItem, this.hostControl);
this.hostControl.Items.Insert(startingIndex, tabItem);
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (object oldItem in e.OldItems)
{
TabItem tabItem = this.GetContainerForItem(oldItem, this.hostControl.Items);
this.hostControl.Items.Remove(tabItem);
this.ClearContainerForItem(tabItem);
}
}
}
}
Of course, you'll have to figure out where to call RequestNavigate, such that you can actually cancel the TabSelectionChanging. Unfortunately, this event doesn't exist in WPF. I would resort to the trick recommended by Josh Smith How to Prevent a TabItem from changing