|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
// Description:
// Frame is a ContentControl with navigation and journaling capabilities, much like NavigationWindow.
// It can use its own journal ("island frame") or its prent's, if available.
//
using System.Collections;
using System.ComponentModel;
using System.Windows.Threading;
using System.Windows.Input;
using System.Windows.Automation.Peers;
using System.Windows.Media;
using System.Windows.Navigation;
using System.Windows.Markup;
using MS.Internal;
using MS.Internal.AppModel;
using MS.Internal.Utility;
using MS.Internal.KnownBoxes;
using MS.Internal.Telemetry.PresentationFramework;
namespace System.Windows.Navigation
{
/// <summary>
/// Journaling options for Frame
/// </summary>
[Serializable]
public enum JournalOwnership
{
/// <summary>
/// Whether or not this Frame will create and use its own journal depends on its parent.
/// If the Frame is hosted by another Frame or a NavigationWindow, it behaves as though
/// UseParentJournal was set. If it is not hosted by a Frame or NavigationWindow or all
/// containing frames have the UsesParentJournal setting, this frame will use its own journal.
/// Once a frame creates its own journal, switching to Automatic has no effect.
/// </summary>
Automatic = 0,
/// <summary>
/// The Frame has its own Journal which operates independent of the hosting container's
/// journal (if it has one).
/// </summary>
OwnsJournal,
/// <summary>
/// The Frame's journal entries are merged into the hosting container's journal, if available.
/// Otherwise navigations in this frame are not journaled.
/// </summary>
UsesParentJournal
};
/// <summary>
/// </summary>
public enum NavigationUIVisibility
{
/// <summary>
/// The navigation UI is visible when Frame has its own journal.
/// </summary>
Automatic = 0,
/// <summary>
/// </summary>
Visible,
/// <summary>
/// </summary>
Hidden
};
}
namespace System.Windows.Controls
{
/// <summary>
/// Frame control is an area that is used for loading a tree of elements.
/// It uses the application navigation model to populate its content.
/// Hence its content model is dictated solely by the NavigationService it aggregates which has
/// a Uri property that points to the Uri of the page that is to be loaded into the Frame.
/// There is also a Content property that returns the root element of the Framework tree being loaded from the Uri.
/// It is also possible to create a tree for the Frames content programmatically and set the Content property to it.
/// </summary>
[DefaultProperty("Source"), DefaultEvent("Navigated")]
[Localizability(LocalizationCategory.Ignore)]
#if OLD_AUTOMATION
[Automation(AccessibilityControlType = "Window")]
#endif
[ContentProperty]
[TemplatePart(Name = "PART_FrameCP", Type = typeof(ContentPresenter))]
public class Frame : ContentControl, INavigator, INavigatorImpl, IJournalNavigationScopeHost, IDownloader, IJournalState, IAddChild, IUriContext
{
#region Constructors
/// <summary>
/// Default constructor
/// </summary>
/// <remarks>
/// Automatic determination of current Dispatcher. Use alternative constructor
/// that accepts a Dispatcher for best performance.
/// </remarks>
public Frame() : base()
{
Init();
}
static Frame()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Frame), new FrameworkPropertyMetadata(typeof(Frame)));
_dType = DependencyObjectType.FromSystemTypeInternal(typeof(Frame));
ContentProperty.OverrideMetadata(
typeof(Frame),
new FrameworkPropertyMetadata(
null,
new CoerceValueCallback(CoerceContent)));
KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(Frame), new FrameworkPropertyMetadata(KeyboardNavigationMode.Local));
KeyboardNavigation.ControlTabNavigationProperty.OverrideMetadata(typeof(Frame), new FrameworkPropertyMetadata(KeyboardNavigationMode.Once));
NavigationService.NavigationServiceProperty.OverrideMetadata(
typeof(Frame),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnParentNavigationServiceChanged)));
ControlsTraceLogger.AddControl(TelemetryControls.Frame);
}
private static object CoerceContent(DependencyObject d, object value)
{
// whenever content changes, defer the change until the Navigate comes in
Frame f = (Frame) d;
if (f._navigationService.Content == value)
{
return value;
}
f.Navigate(value);
return DependencyProperty.UnsetValue;
}
/// <summary>
/// Initialize
/// </summary>
private void Init()
{
InheritanceBehavior = InheritanceBehavior.SkipToAppNow;
ContentIsNotLogical = true;
_navigationService = new NavigationService(this);
_navigationService.BPReady += new BPReadyEventHandler(_OnBPReady);
}
#endregion
#region IUriContext implementation
/// <summary>
/// Accessor for the base uri of the frame
/// </summary>
Uri IUriContext.BaseUri
{
get
{
return BaseUri;
}
set
{
BaseUri = value;
}
}
/// <summary>
/// Implementation for BaseUri
/// </summary>
protected virtual Uri BaseUri
{
get
{
return (Uri)GetValue(BaseUriHelper.BaseUriProperty);
}
set
{
SetValue(BaseUriHelper.BaseUriProperty, value);
}
}
#endregion IUriContext implementation
#region IDownloader implementation
NavigationService IDownloader.Downloader
{
get { return _navigationService; }
}
/// <summary>
/// Rasied when Content is rendered and ready for user interaction.
/// </summary>
public event EventHandler ContentRendered;
/// <summary>
/// This override fires the ContentRendered event.
/// </summary>
/// <param name="args"></param>
protected virtual void OnContentRendered(EventArgs args)
{
// After the content is rendered we want to check if there is an element that needs to be focused
// If there is - set focus to it
DependencyObject doContent = Content as DependencyObject;
if (doContent != null)
{
IInputElement focusedElement = FocusManager.GetFocusedElement(doContent) as IInputElement;
if (focusedElement != null)
focusedElement.Focus();
}
if (ContentRendered != null)
{
ContentRendered(this, args);
}
}
#endregion IDownloader implementation
#region Properties
/// <summary>
///
/// </summary>
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register(
"Source",
typeof(Uri),
typeof(Frame),
new FrameworkPropertyMetadata(
(Uri) null,
// The Journal flag tells the parser not to re-assign the property
// when doing journal navigation. See ParserContext.SkipJournaledProperties.
FrameworkPropertyMetadataOptions.Journal,
new PropertyChangedCallback(OnSourcePropertyChanged),
new CoerceValueCallback(CoerceSource)));
private static object CoerceSource(DependencyObject d, object value)
{
Frame frame = (Frame)d;
// If the Source property is coerced from NavService as a result of navigation, not from other
// source, e.g, SetValue, DataBinding, Style..., we should use NavService.Source.
if (frame._sourceUpdatedFromNavService)
{
Invariant.Assert(frame._navigationService != null, "_navigationService should never be null here");
// Turn this Assert on after fix the issue that NavService.Source is not absolute for SiteOfOrigin.
//Invariant.Assert(frame._navigationService.Source != null ? !frame._navigationService.Source.IsAbsoluteUri : true, "NavService's Source should always be relative");
return frame._navigationService.Source;
}
return value;
}
/// <summary>
/// Called when SourceProperty is invalidated on 'd'
/// </summary>
private static void OnSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Frame frame = (Frame)d;
// Don't navigate if the Source value change is from NavService as a result of a navigation happening.
if (! frame._sourceUpdatedFromNavService)
{
// We used to unhook first visual child here.
// Since we enabled styling for Frame. We're relying on Content property and ContentPresenter in Frame's style
// to add/remove content from VisualTree.
Uri uriToNavigate = BindUriHelper.GetUriToNavigate(frame, ((IUriContext)frame).BaseUri, (Uri)e.NewValue);
// Calling the internal Navigate from Frame and NavWin's Source DP's property changed callbacks
// We would not set value back in this case.
frame._navigationService.Navigate(uriToNavigate, null, false, true/* navigateOnSourceChanged */);
}
}
// This method is called from NavService whenever the NavService's Source value is updated.
// The INavigator uses this to update its SourceProperty.
// <param name="journalOrCancel">It indicates whether the NavService's Source value is as a result of
// calling Navigate API directly or from GoBack/GoForward, journal navigation, a cancellation</param>
void INavigatorImpl.OnSourceUpdatedFromNavService(bool journalOrCancel)
{
try
{
_sourceUpdatedFromNavService = true;
SetCurrentValueInternal(SourceProperty, _navigationService.Source);
}
finally
{
_sourceUpdatedFromNavService = false;
}
}
/// <summary>
/// URI to navigate to.
/// </summary>
[Bindable(true), CustomCategory("Navigation")]
public Uri Source
{
get { return (Uri) GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
/// <summary>
/// The DependencyProperty for the CanGoBack property.
/// Flags: None
/// Default Value: false
/// Readonly: true
/// </summary>
public static readonly DependencyProperty CanGoBackProperty =
JournalNavigationScope.CanGoBackProperty.AddOwner(typeof(Frame));
/// <summary>
/// The DependencyProperty for the CanGoForward property.
/// Flags: None
/// Default Value: false
/// Readonly: true
/// </summary>
public static readonly DependencyProperty CanGoForwardProperty =
JournalNavigationScope.CanGoForwardProperty.AddOwner(typeof(Frame));
/// <summary>
/// List of back journal entries. Available only when the frame owns its own journal.
/// </summary>
public static readonly DependencyProperty BackStackProperty =
JournalNavigationScope.BackStackProperty.AddOwner(typeof(Frame));
/// <summary>
/// List of back journal entries. Available only when the frame owns its own journal.
/// </summary>
public static readonly DependencyProperty ForwardStackProperty =
JournalNavigationScope.ForwardStackProperty.AddOwner(typeof(Frame));
/// <summary>
/// </summary>
public static readonly DependencyProperty NavigationUIVisibilityProperty =
DependencyProperty.Register(
"NavigationUIVisibility", typeof(NavigationUIVisibility), typeof(Frame),
new PropertyMetadata(NavigationUIVisibility.Automatic));
/// <summary>
/// </summary>
public NavigationUIVisibility NavigationUIVisibility
{
get { return (NavigationUIVisibility)GetValue(NavigationUIVisibilityProperty); }
set { SetValue(NavigationUIVisibilityProperty, value); }
}
/// <summary>
/// DependencyProperty for SandboxExternalContent property.
/// </summary>
public static readonly DependencyProperty SandboxExternalContentProperty =
DependencyProperty.Register(
"SandboxExternalContent",
typeof(bool),
typeof(Frame),
new FrameworkPropertyMetadata(BooleanBoxes.FalseBox,
new PropertyChangedCallback(OnSandboxExternalContentPropertyChanged), new CoerceValueCallback(CoerceSandBoxExternalContentValue)));
/// <summary>
/// If set to true, the navigated content is isolated.
/// </summary>
public bool SandboxExternalContent
{
get { return (bool) GetValue(SandboxExternalContentProperty); }
set
{
bool fSandBox = (bool)value;
SetValue(SandboxExternalContentProperty, fSandBox);
}
}
/// <summary>
/// Called when SandboxExternalContentProperty is invalidated on 'd'. If the value becomes
/// true, then the frame is refreshed to sandbox any content.
/// </summary>
private static void OnSandboxExternalContentPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Frame frame = (Frame)d;
bool fSandBox = (bool)e.NewValue;
if (fSandBox && !(bool)e.OldValue)
{
frame.NavigationService.Refresh();
}
}
private static object CoerceSandBoxExternalContentValue(DependencyObject d, object value)
{
bool fSandBox = (bool)value;
return fSandBox;
}
//
// JournalOwnership
/// <summary>
/// DependencyProperty for the JournalOwnership property
/// </summary>
public static readonly DependencyProperty JournalOwnershipProperty =
DependencyProperty.Register(
"JournalOwnership", typeof(JournalOwnership), typeof(Frame),
new FrameworkPropertyMetadata(
JournalOwnership.Automatic,
new PropertyChangedCallback(OnJournalOwnershipPropertyChanged),
new CoerceValueCallback(CoerceJournalOwnership)),
new ValidateValueCallback(ValidateJournalOwnershipValue));
private static bool ValidateJournalOwnershipValue(object value)
{
JournalOwnership jo = (JournalOwnership)value;
return jo == JournalOwnership.Automatic || jo == JournalOwnership.UsesParentJournal
|| jo == JournalOwnership.OwnsJournal;
}
private static void OnJournalOwnershipPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((Frame)d).OnJournalOwnershipPropertyChanged((JournalOwnership)e.NewValue);
}
private void OnJournalOwnershipPropertyChanged(JournalOwnership newValue)
{
// NavigationService.InvalidateJournalNavigationScope() will be called (recursively)
// when making an ownership change and will throw if there is a navigation in progress.
switch (_journalOwnership/*previous value*/)
{
case JournalOwnership.Automatic:
switch (newValue)
{
case JournalOwnership.OwnsJournal:
SwitchToOwnJournal();
break;
case JournalOwnership.UsesParentJournal:
SwitchToParentJournal();
break;
}
break;
case JournalOwnership.OwnsJournal:
Debug.Assert(_ownJournalScope != null);
switch (newValue)
{
case JournalOwnership.Automatic:
Debug.Fail("UsesOwnJournal->Automatic transition should be blocked by CoerceJournalOwnership().");
break;
case JournalOwnership.UsesParentJournal:
SwitchToParentJournal();
break;
}
break;
case JournalOwnership.UsesParentJournal:
Debug.Assert(_ownJournalScope == null);
switch (newValue)
{
case JournalOwnership.Automatic:
// The effective journal ownership is not going to change unless the
// frame is reparented (or, more unlikely, the parent's journal becomes
// unavailable). This invalidation is done only so that the next
// navigation causes a switch to UsesParentJournal, to be consistent
// with the initial transition from Automatic.
_navigationService.InvalidateJournalNavigationScope();
break;
case JournalOwnership.OwnsJournal:
SwitchToOwnJournal();
break;
}
break;
}
_journalOwnership = newValue;
}
private static object CoerceJournalOwnership(DependencyObject d, object newValue)
{
JournalOwnership prevValue = ((Frame)d)._journalOwnership;
// Switching from OwnsJournal to Automatic is not defined to have any useful effect.
// (Even if reparented, Frame will not relinquish its own journal to start using its new
// parent's one.) But in order to be able to maintain some stronger invariants, this transition
// is blocked here.
if (prevValue == JournalOwnership.OwnsJournal && (JournalOwnership)newValue == JournalOwnership.Automatic)
{
return JournalOwnership.OwnsJournal;
}
return newValue;
}
/// <summary>
/// Journal ownership setting for this frame
/// </summary>
public JournalOwnership JournalOwnership
{
get
{
Debug.Assert(_journalOwnership == (JournalOwnership)GetValue(JournalOwnershipProperty));
return _journalOwnership;
}
set
{
if (value != _journalOwnership)
{
SetValue(JournalOwnershipProperty, value);
}
}
}
/// <summary>
/// Frame's associated NavigationService.
/// </summary>
public NavigationService NavigationService
{
get
{
VerifyAccess();
return _navigationService;
}
}
#endregion
//------------------------------------------------------
//
// Protected Methods
//
//------------------------------------------------------
#region Protected Methods
/// <summary>
/// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
/// </summary>
protected override AutomationPeer OnCreateAutomationPeer()
{
return new FrameAutomationPeer(this);
}
/// <summary>
/// Add an object child to this control
/// </summary>
protected override void AddChild(object value)
{
throw new InvalidOperationException(SR.FrameNoAddChild);
}
/// <summary>
/// Add a text string to this control
/// </summary>
protected override void AddText(string text)
{
XamlSerializerUtil.ThrowIfNonWhiteSpaceInAddText(text, this);
}
#endregion
#region Event Handlers
private void _OnBPReady(Object o, BPReadyEventArgs e)
{
// update content property
SetCurrentValueInternal(ContentProperty, e.Content);
// Need to refresh everytime the child changed
InvalidateMeasure();
// We post a dispatcher work item to fire ContentRendered
// only if this is Loaded in the tree. If not, we will
// post it from the LoadedHandler. This guarantees that
// we don't fire ContentRendered on a subtree that is not
// connected to a PresentationSource
if (IsLoaded == true)
{
PostContentRendered();
}
else
{
// _postContentRenderedFromLoadedHandler == true means
// that we deferred to the Loaded event to PostConetentRendered
// for the previous content change and Loaded has not fired yet.
// Thus we don't want to hook up another event handler
if (_postContentRenderedFromLoadedHandler == false)
{
this.Loaded += new RoutedEventHandler(LoadedHandler);
_postContentRenderedFromLoadedHandler = true;
}
}
}
private void LoadedHandler(object sender, RoutedEventArgs args)
{
if (_postContentRenderedFromLoadedHandler == true)
{
PostContentRendered();
_postContentRenderedFromLoadedHandler = false;
this.Loaded -= new RoutedEventHandler(LoadedHandler);
}
}
/// <remarks> Keep this method in sync with Window.PostContentRendered(). </remarks>
private void PostContentRendered()
{
// Post the firing of ContentRendered as Input priority work item so
// that ContentRendered will be fired after render query empties.
if (_contentRenderedCallback != null)
{
// Content was changed again before the previous rendering completed (or at least
// before the Dispatcher got to Input priority callbacks).
_contentRenderedCallback.Abort();
}
_contentRenderedCallback = Dispatcher.BeginInvoke(DispatcherPriority.Input,
(DispatcherOperationCallback) delegate (object arg)
{
Frame thisRef = (Frame)arg;
thisRef._contentRenderedCallback = null;
thisRef.OnContentRendered(EventArgs.Empty);
return null;
},
this);
}
private void OnQueryGoBack(object sender, CanExecuteRoutedEventArgs e)
{
Debug.Assert(sender == this && _ownJournalScope != null);
e.CanExecute = _ownJournalScope.CanGoBack;
e.Handled = true;
}
private void OnGoBack(object sender, ExecutedRoutedEventArgs e)
{
Debug.Assert(sender == this && _ownJournalScope != null);
_ownJournalScope.GoBack();
e.Handled = true;
}
private void OnQueryGoForward(object sender, CanExecuteRoutedEventArgs e)
{
Debug.Assert(sender == this && _ownJournalScope != null);
e.CanExecute = _ownJournalScope.CanGoForward;
e.Handled = true;
}
private void OnGoForward(object sender, ExecutedRoutedEventArgs e)
{
Debug.Assert(sender == this && _ownJournalScope != null);
_ownJournalScope.GoForward();
e.Handled = true;
}
private void OnNavigateJournal(object sender, ExecutedRoutedEventArgs e)
{
Debug.Assert(sender == this && _ownJournalScope != null);
// The following checks are needed because anyone could send the NavigateJournal command.
FrameworkElement journalEntryUIElem = e.Parameter as FrameworkElement;
if (journalEntryUIElem != null)
{
JournalEntry je = journalEntryUIElem.DataContext as JournalEntry;
if (je != null)
{
if (_ownJournalScope.NavigateToEntry(je))
{
e.Handled = true;
}
}
}
}
private void OnQueryRefresh(object sender, CanExecuteRoutedEventArgs e)
{
Debug.Assert(sender == this && _ownJournalScope != null);
e.CanExecute = Content != null;
}
private void OnRefresh(object sender, ExecutedRoutedEventArgs e)
{
Debug.Assert(sender == this && _ownJournalScope != null);
_navigationService.Refresh();
e.Handled = true;
}
private void OnBrowseStop(object sender, ExecutedRoutedEventArgs e)
{
Debug.Assert(sender == this && _ownJournalScope != null);
_ownJournalScope.StopLoading();
e.Handled = true;
}
#endregion
#region Event Management
/// <summary>
/// When a Frame-content's event bubbling up Visual tree, we need to adjust
/// the source of the event to the frame itself.
/// </summary>
/// <param name="e">
/// Routed Event Args
/// </param>
/// <returns>
/// Returns new source. (Current Frame object)
/// </returns>
internal override object AdjustEventSource(RoutedEventArgs e)
{
e.Source=this;
return this;
}
#endregion
#region Overiding ContentControl implementation
/// <summary>
/// Return text representing this Frame
/// </summary>
internal override string GetPlainText()
{
if (this.Source != null)
{
return Source.ToString();
}
else
{
return String.Empty;
}
}
/// <summary>
/// This method is used by TypeDescriptor to determine if this property should
/// be serialized.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool ShouldSerializeContent()
{
// When uri of NavigationService is valid and can be used to
// relaod, we do not serialize content
Invariant.Assert(_navigationService != null, "_navigationService should never be null here");
return ( !_navigationService.CanReloadFromUri && Content != null);
}
#endregion Overding ContentControl implementation
#region NavigationService support
private static void OnParentNavigationServiceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Debug.Assert(d as Frame != null && ((Frame)d).NavigationService != null);
((Frame) d).NavigationService.OnParentNavigationServiceChanged();
}
/// <summary>
/// Called when the template's visual tree is created.
/// </summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
// Get the root element of the style
Visual v = TemplateChild;
if (v != null)
{
NavigationService.VisualTreeAvailable(v);
}
}
#region INavigatorImpl members
// Note: OnSourceUpdatedFromNavService is next to the other Source-related members of Frame.
Visual INavigatorImpl.FindRootViewer()
{
return NavigationHelper.FindRootViewer(this, "PART_FrameCP");
}
#endregion INavigatorImpl
#endregion NavigationService support
#region INavigator implementation
JournalNavigationScope INavigator.GetJournal(bool create)
{
return GetJournal(create);
}
/// <summary>
/// <see cref="INavigator.GetJournal"/>
/// </summary>
private JournalNavigationScope GetJournal(bool create)
{
Invariant.Assert(_ownJournalScope != null ^ _journalOwnership != JournalOwnership.OwnsJournal);
if (_ownJournalScope != null)
return _ownJournalScope;
JournalNavigationScope jns = GetParentJournal(create);
if (jns != null)
{
SetCurrentValueInternal(JournalOwnershipProperty, JournalOwnership.UsesParentJournal);
return jns;
}
if (create && _journalOwnership == JournalOwnership.Automatic)
{
SetCurrentValueInternal(JournalOwnershipProperty, JournalOwnership.OwnsJournal);
}
return _ownJournalScope;
}
/// <summary>
/// True when the frame has its own journal and there is a navigable entry in the forward stack.
/// If the frame doesn't own a journal, its parent or corresponding NavigationService may be
/// able to navigate it forward.
/// </summary>
public bool CanGoForward
{
get
{
bool canGoFwd = _ownJournalScope != null && _ownJournalScope.CanGoForward;
Debug.Assert(canGoFwd == (bool)GetValue(CanGoForwardProperty));
return canGoFwd;
}
}
/// <summary>
/// True when the frame has its own journal and there is a navigable entry in the back stack.
/// If the frame doesn't own a journal, its parent or corresponding NavigationService may be
/// able to navigate it back.
/// </summary>
public bool CanGoBack
{
get
{
bool canGoBack = _ownJournalScope != null && _ownJournalScope.CanGoBack;
Debug.Assert(canGoBack == (bool)GetValue(CanGoBackProperty));
return canGoBack;
}
}
/// <summary>
/// Adds a new journal entry to NavigationWindow's back history.
/// </summary>
/// <param name="state"> The custom content state (or view state) to be encapsulated in the
/// journal entry. If null, IProvideCustomContentState.GetContentState() will be called on
/// the NavigationWindow.Content or Frame.Content object.
/// </param>
public void AddBackEntry(CustomContentState state)
{
VerifyAccess();
_navigationService.AddBackEntry(state);
}
/// <summary>
/// Removes the first JournalEntry from the frame's back stack.
/// </summary>
/// <exception cref="InvalidOperationException"> The frame doesn't own a journal. </exception>
public JournalEntry RemoveBackEntry()
{
if (_ownJournalScope == null)
throw new InvalidOperationException(SR.InvalidOperation_NoJournal);
return _ownJournalScope.RemoveBackEntry();
}
/// <summary>
/// Navigates to the Uri and downloads the content. Whether the navigation is
/// performed synchronously or asynchronously depends on the current default navigation behavior.
/// </summary>
/// <param name="source">URI of the application or content being navigated to.</param>
/// <returns>bool indicating whether the navigation was successfully started or not</returns>
public bool Navigate(Uri source)
{
VerifyAccess();
return _navigationService.Navigate(source);
}
//
// INavigator.Navigate
//
/// <summary>
/// This method navigates this Frame to the given Uri.
/// </summary>
/// <param name="source">The URI to be navigated to.</param>
/// <param name="extraData">enables the develeoper to supply an extra object, that will be returned in the NavigationEventArgs of the Navigated event. The extra data enables the developer
/// to identify the source of the navigation, in the presence of
/// multiple navigations.
/// </param>
/// <returns>bool indicating whether the navigation was successfully started or not</returns>
public bool Navigate(Uri source, Object extraData)
{
VerifyAccess();
return _navigationService.Navigate(source, extraData);
}
/// <summary>
/// Navigates synchronously to an existing element tree.
/// </summary>
/// <param name="content">Root of the element tree being navigated to.</param>
/// <returns>bool indicating whether the navigation was successfully started or not</returns>
public bool Navigate(Object content)
{
VerifyAccess();
return _navigationService.Navigate(content);
}
//
// INavigator.Navigate(object)
//
/// <summary>
/// This method synchronously navigates this Frame to the
/// given Element.
/// </summary>
/// <param name="content">The Element to be navigated to.</param>
/// <param name="extraData">enables the develeoper to supply an extra object, that will be returned in the NavigationEventArgs of the Navigated event. The extra data enables the developer
/// to identify the source of the navigation, in the presence of
/// multiple navigations.
/// </param>
/// <returns>bool indicating whether the navigation was successfully started or not</returns>
public bool Navigate(Object content, Object extraData)
{
VerifyAccess();
return _navigationService.Navigate(content, extraData);
}
/// <summary>
/// Navigates the frame to the next journal entry. The operation is available only when
/// the frame has its own journal. If not, try navigating the parent or the corresponding
/// NavigationService.
/// </summary>
/// <exception cref="InvalidOperationException"> The frame doesn't own a journal. </exception>
/// <exception cref="InvalidOperationException"> There is no forward journal entry to go to. </exception>
public void GoForward()
{
if (_ownJournalScope == null)
throw new InvalidOperationException(SR.InvalidOperation_NoJournal);
_ownJournalScope.GoForward();
}
/// <summary>
/// Navigates the frame to the previous journal entry. The operation is available only when
/// the frame has its own journal. If not, try navigating the parent or the corresponding
/// NavigationService.
/// </summary>
/// <exception cref="InvalidOperationException"> The frame doesn't own a journal. </exception>
/// <exception cref="InvalidOperationException"> There is no back journal entry to go to. </exception>
public void GoBack()
{
if(_ownJournalScope == null)
throw new InvalidOperationException(SR.InvalidOperation_NoJournal);
_ownJournalScope.GoBack();
}
/// <summary>
/// StopLoading aborts asynchronous navigations that haven't been processed yet or that are
/// still being downloaded. SopLoading does not abort parsing of the downloaded streams.
/// The NavigationStopped event is fired only if the navigation was aborted.
/// </summary>
public void StopLoading()
{
VerifyAccess();
_navigationService.StopLoading();
}
/// <summary>
/// Reloads the current content.
/// </summary>
public void Refresh()
{
VerifyAccess();
_navigationService.Refresh();
}
/// <summary>
/// Uri for the current page. Getting this property always
/// returns the URI of the content thats currently displayed.
/// regardless of whether a navigation is in progress or not.
/// </summary>
/// <value></value>
public Uri CurrentSource
{
get
{
return _navigationService.CurrentSource;
}
}
/// <summary>
/// List of back journal entries. Available only when the frame has its own journal.
/// </summary>
public IEnumerable BackStack
{
get
{
IEnumerable backStack = _ownJournalScope == null ? null : _ownJournalScope.BackStack;
Debug.Assert(backStack == GetValue(BackStackProperty));
return backStack;
}
}
/// <summary>
/// List of forward journal entries. Available only when the frame has its own journal.
/// </summary>
public IEnumerable ForwardStack
{
get
{
IEnumerable fwdStack = _ownJournalScope == null ? null : _ownJournalScope.ForwardStack;
Debug.Assert(fwdStack == GetValue(ForwardStackProperty));
return fwdStack;
}
}
/// <summary>
/// Raised just before a navigation takes place. This event is fired for frame
/// navigations as well as top-level page navigations, so may fire multiple times
/// during the download of a page.
/// The NavigatingCancelEventArgs contain the uri or root element of the content
/// being navigated to and an enum value that indicates the type of navigation.
/// Canceling this event prevents the application from navigating.
/// Note: An application hosted in the browser cannot prevent navigation away from
/// the application by canceling this event.
/// Note: In the PDC build, if an application hosts the WebOC, this event is not raised
/// for navigations within the WebOC.
/// </summary>
public event NavigatingCancelEventHandler Navigating
{
add
{
_navigationService.Navigating += value;
}
remove
{
_navigationService.Navigating -= value;
}
}
/// <summary>
/// Raised at periodic intervals while a navigation is taking place.
/// The NavigationProgressEventArgs tell how many total bytes need to be downloaded and
/// how many have been sent at the moment the event is fired. This event can be used to provide
/// a progress indicator to the user.
/// </summary>
public event NavigationProgressEventHandler NavigationProgress
{
add
{
_navigationService.NavigationProgress += value;
}
remove
{
_navigationService.NavigationProgress -= value;
}
}
/// <summary>
/// Raised an error is encountered during a navigation.
/// The NavigationFailedEventArgs contains
/// the exception that was thrown. By default Handled property is set to false,
/// which allows the exception to be rethrown.
/// The event handler can prevent exception from throwing
/// to the user by setting the Handled property to true
/// </summary>
public event NavigationFailedEventHandler NavigationFailed
{
add
{
_navigationService.NavigationFailed += value;
}
remove
{
_navigationService.NavigationFailed -= value;
}
}
/// <summary>
/// Raised after navigation the target has been found and the download has begun. This event
/// is fired for frame navigations as well as top-level page navigations, so may fire
/// multiple times during the download of a page.
/// For an asynchronous navigation, this event indicates that a partial element tree
/// has been handed to the parser, but more bits are still coming.
/// For a synchronous navigation, this event indicates the entire tree has been
/// handed to the parser.
/// The NavigationEventArgs contain the uri or root element of the content being navigated to.
/// This event is informational only, and cannot be canceled.
/// </summary>
public event NavigatedEventHandler Navigated
{
add
{
_navigationService.Navigated += value;
}
remove
{
_navigationService.Navigated -= value;
}
}
//
// INavigator.LoadCompleted
//
/// <summary>
/// Raised after the entire page, including all images and frames, has been downloaded
/// and parsed. This is the event to handle to stop spinning the globe. The developer
/// should check the IsNavigationInitiator property on the NavigationEventArgs to determine
/// whether to stop spinning the globe.
/// The NavigationEventArgs contain the uri or root element of the content being navigated to,
/// and a IsNavigationInitiator property that indicates whether this is a new navigation
/// initiated by this Frame, or whether this navigation is being propagated down
/// from a higher level navigation taking place in a containing window or frame.
/// This event is informational only, and cannot be canceled.
/// </summary>
public event LoadCompletedEventHandler LoadCompleted
{
add
{
_navigationService.LoadCompleted += value;
}
remove
{
_navigationService.LoadCompleted -= value;
}
}
/// <summary>
/// Raised when a navigation or download has been interrupted because the user clicked
/// the Stop button, or the Stop method was invoked.
/// The NavigationEventArgs contain the uri or root element of the content being navigated to.
/// This event is informational only, and cannot be canceled.
/// </summary>
public event NavigationStoppedEventHandler NavigationStopped
{
add
{
_navigationService.NavigationStopped += value;
}
remove
{
_navigationService.NavigationStopped -= value;
}
}
/// <summary>
/// Raised when a navigation uri contains a fragment. This event is fired before the element is scrolled
/// into view and allows the listener to respond to the fragment in a custom way.
/// </summary>
public event FragmentNavigationEventHandler FragmentNavigation
{
add
{
_navigationService.FragmentNavigation += value;
}
remove
{
_navigationService.FragmentNavigation -= value;
}
}
#endregion INavigator implementation
#region IJournalNavigationScopeHost Members
void IJournalNavigationScopeHost.VerifyContextAndObjectState()
{
VerifyAccess();
}
void IJournalNavigationScopeHost.OnJournalAvailable()
{
// BackStackProperty & ForwardStackProperty should be set by JournalNavigationScope.
Debug.Assert(GetValue(BackStackProperty) == _ownJournalScope.BackStack);
}
bool IJournalNavigationScopeHost.GoBackOverride()
{
return false; // not overriding here
}
bool IJournalNavigationScopeHost.GoForwardOverride()
{
return false;
}
#endregion
#region IJournalState Members
/// <summary>
/// When Frame's parent container navigates away and if its content is not keep-alive,
/// the frame's current page needs to be remembered. In addition, if the frame has its own
/// journal, it has to be preserved too. Class FramePersistState captures this "metajournaling"
/// state. It will become part of the journal entry created for the navigation in the parent
/// container (stored within a DataStreams instance).
/// </summary>
#pragma warning disable SYSLIB0050
[Serializable]
private class FramePersistState : CustomJournalStateInternal
{
internal JournalEntry JournalEntry;
internal Guid NavSvcGuid;
internal JournalOwnership JournalOwnership;
internal Journal Journal;
internal override void PrepareForSerialization()
{
if (JournalEntry != null)
{
if (JournalEntry.IsAlive()) // not serializable
{
JournalEntry = null;
// Only the NavigationService GUID will be restored.
// See related case and explanation in Frame.GetJournalState().
}
else
{
Debug.Assert(JournalEntry.GetType().IsSerializable);
}
}
if (Journal != null)
{
Journal.PruneKeepAliveEntries();
}
}
};
#pragma warning restore SYSLIB0050
CustomJournalStateInternal IJournalState.GetJournalState(JournalReason journalReason)
{
if (journalReason != JournalReason.NewContentNavigation)
{
return null;
}
FramePersistState state = new FramePersistState();
// Save a JournalEntry for the current content.
state.JournalEntry = _navigationService.MakeJournalEntry(JournalReason.NewContentNavigation);
// The current Content may be null or may not want to be journaled (=> JournalEntry=null).
// But we still need to save and then restore the NS GUID - there may be other JEs keyed
// by this GUID value.
// i. There is a somewhat similar case in ApplicationProxyInternal._GetSaveHistoryBytesDelegate().
state.NavSvcGuid = _navigationService.GuidId;
state.JournalOwnership = _journalOwnership;
if (_ownJournalScope != null)
{
Debug.Assert(_journalOwnership == JournalOwnership.OwnsJournal);
// No need to make a copy here because this Frame object will be discarded.
// (Supposedly the parent container is navigating away.)
state.Journal = _ownJournalScope.Journal;
}
return state;
}
void IJournalState.RestoreJournalState(CustomJournalStateInternal cjs)
{
FramePersistState state = (FramePersistState)cjs;
_navigationService.GuidId = state.NavSvcGuid;
// Because the JournalOwnershipProperty doesn't have the FrameworkPropertyMetadataOptions.Journal
// flag, the parser will always set the value specified in markup, which may be different from
// state.JournalOwnership. So, at this point JournalOwnership is not necessarily Automatic
// (the default).
JournalOwnership = state.JournalOwnership;
if(_journalOwnership == JournalOwnership.OwnsJournal)
{
Invariant.Assert(state.Journal != null);
_ownJournalScope.Journal = state.Journal;
}
if(state.JournalEntry != null)
{
state.JournalEntry.Navigate(this, NavigationMode.Back);
}
}
#endregion IJournalState
//------------------------------------------------------
//
// Internal Methods
//
//------------------------------------------------------
#region Internal Methods
internal override void OnPreApplyTemplate()
{
base.OnPreApplyTemplate();
if (_ownJournalScope != null)
{
// This causes the Journal instance to be created. BackStackProperty and ForwardStackProperty
// should be set before the navigation chrome data-binds to them but after any Journal is
// restored from FramePersistState.
_ownJournalScope.EnsureJournal();
}
}
// Invalidate resources on the frame content if the content isn't
// reachable via the visual/logical tree
internal override void OnThemeChanged()
{
// If the frame does not have a template generated tree then its
// content is not reachable via a tree walk.
DependencyObject d;
if (!HasTemplateGeneratedSubTree && (d = Content as DependencyObject) != null)
{
FrameworkElement fe;
FrameworkContentElement fce;
Helper.DowncastToFEorFCE(d, out fe, out fce, false);
if (fe != null || fce != null)
{
TreeWalkHelper.InvalidateOnResourcesChange(fe, fce, ResourcesChangeInfo.ThemeChangeInfo);
}
}
}
#endregion Internal Methods
//------------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
#region Private Methods
private JournalNavigationScope GetParentJournal(bool create)
{
JournalNavigationScope jns = null;
NavigationService parentNS = _navigationService.ParentNavigationService;
if (parentNS != null)
{
jns = parentNS.INavigatorHost.GetJournal(create);
}
return jns;
}
/// <remarks> Does not update the JournalOwnershipProperty. </remarks>
private void SwitchToOwnJournal()
{
Debug.Assert(_ownJournalScope == null ^ _journalOwnership == JournalOwnership.OwnsJournal);
if (_ownJournalScope == null)
{
// Entries created for this frame in the parent's journal have to be removed.
JournalNavigationScope parentJns = GetParentJournal(false/*don't create*/);
if (parentJns != null)
{
parentJns.Journal.RemoveEntries(_navigationService.GuidId);
}
_ownJournalScope = new JournalNavigationScope(this);
_navigationService.InvalidateJournalNavigationScope();
// BackStackProperty & ForwardStackProperty should become available immediately if
// OwnsJournal is set *after* Frame is already loaded.
// See comment in OnPreApplyTemplate().
if (IsLoaded)
{
_ownJournalScope.EnsureJournal();
}
AddCommandBinding(new CommandBinding(NavigationCommands.BrowseBack, OnGoBack, OnQueryGoBack));
AddCommandBinding(new CommandBinding(NavigationCommands.BrowseForward, OnGoForward, OnQueryGoForward));
AddCommandBinding(new CommandBinding(NavigationCommands.NavigateJournal, OnNavigateJournal));
AddCommandBinding(new CommandBinding(NavigationCommands.Refresh, OnRefresh, OnQueryRefresh));
AddCommandBinding(new CommandBinding(NavigationCommands.BrowseStop, OnBrowseStop));
}
_journalOwnership = JournalOwnership.OwnsJournal;
}
/// <remarks> Does not update the JournalOwnershipProperty. </remarks>
private void SwitchToParentJournal()
{
Debug.Assert(_ownJournalScope == null ^ _journalOwnership == JournalOwnership.OwnsJournal);
if (_ownJournalScope != null)
{
_ownJournalScope = null;
_navigationService.InvalidateJournalNavigationScope();
JournalNavigationScope.ClearDPValues(this);
foreach (CommandBinding cb in _commandBindings)
{
base.CommandBindings.Remove(cb);
}
_commandBindings = null;
}
_journalOwnership = JournalOwnership.UsesParentJournal;
}
private void AddCommandBinding(CommandBinding b)
{
base.CommandBindings.Add(b);
// Store the CommandBinding reference so that it can be removed in case the frame loses
// its JournalNavigationScope.
if (_commandBindings == null)
{
_commandBindings = new List<CommandBinding>(6);
}
_commandBindings.Add(b);
}
#endregion Private Methods
//------------------------------------------------------
//
// Private Fields
//
//------------------------------------------------------
#region Private Fields
private bool _postContentRenderedFromLoadedHandler = false;
private DispatcherOperation _contentRenderedCallback;
private NavigationService _navigationService;
private bool _sourceUpdatedFromNavService;
/// <remarks> All changes should be made via the JournalOwnership property setter. </remarks>
private JournalOwnership _journalOwnership = JournalOwnership.Automatic;
private JournalNavigationScope _ownJournalScope;
private List<CommandBinding> _commandBindings;
#endregion Private Fields
#region DTypeThemeStyleKey
// Returns the DependencyObjectType for the registered ThemeStyleKey's default
// value. Controls will override this method to return approriate types.
internal override DependencyObjectType DTypeThemeStyleKey
{
get { return _dType; }
}
private static DependencyObjectType _dType;
#endregion DTypeThemeStyleKey
}
}
|