Hyperlink.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / Documents / Hyperlink.cs / 1305600 / Hyperlink.cs

                            //---------------------------------------------------------------------------- 
// File: Hyperlink.cs
//
// Description:
// Implementation of Underline element. 
//
// Copyright (C) 2004 by Microsoft Corporation.  All rights reserved. 
// 
//---------------------------------------------------------------------------
using System.ComponentModel; 
using System.Diagnostics;
using System.IO.Packaging;
using System.Security;
using System.Security.Permissions; 
using System.Text;
using System.Windows.Automation.Peers; 
using System.Windows.Controls;       // 
using System.Windows.Input;
using System.Windows.Markup; 
using System.Windows.Navigation;
using System.Windows.Shapes;
using MS.Internal;
using MS.Internal.AppModel; 
using System.Windows.Threading;
 
using CommonDependencyProperty=MS.Internal.PresentationFramework.CommonDependencyPropertyAttribute; 
using SecurityHelper=MS.Internal.PresentationFramework.SecurityHelper;
 
namespace System.Windows.Documents
{
    /// 
    /// Implements a Hyperlink element 
    /// 
    [UIPermissionAttribute(SecurityAction.InheritanceDemand, Unrestricted = true)] 
    [TextElementEditingBehaviorAttribute(IsMergeable = false, IsTypographicOnly = false)] 
    public class Hyperlink : Span, ICommandSource, IUriContext
    { 
        //-------------------------------------------------------------------
        //
        // Constructors
        // 
        //---------------------------------------------------------------------
 
        #region Constructors 

        // 
        // Static Ctor to create default style sheet
        //
        static Hyperlink()
        { 
            DefaultStyleKeyProperty.OverrideMetadata(typeof(Hyperlink), new FrameworkPropertyMetadata(typeof(Hyperlink)));
            _dType = DependencyObjectType.FromSystemTypeInternal(typeof(Hyperlink)); 
            FocusableProperty.OverrideMetadata(typeof(Hyperlink), new FrameworkPropertyMetadata(true)); 
            EventManager.RegisterClassHandler(typeof(Hyperlink), Mouse.QueryCursorEvent, new QueryCursorEventHandler(OnQueryCursor));
        } 

        /// 
        /// Initializes a new instance of Hyperlink element.
        ///  
        /// 
        /// To become fully functional this element requires at least one other Inline element 
        /// as its child, typically Run with some text. 
        /// 
        public Hyperlink() : base() 
        {
        }

        ///  
        /// Initializes a new instance of Hyperlink element and adds a given Inline element as its first child.
        ///  
        ///  
        /// Inline element added as an initial child to this Hyperlink element
        ///  
        public Hyperlink(Inline childInline) : base(childInline)
        {
        }
 
        /// 
        /// Creates a new Span instance. 
        ///  
        /// 
        /// Optional child Inline for the new Span.  May be null. 
        /// 
        /// 
        /// Optional position at which to insert the new Span.  May be null.
        ///  
        public Hyperlink(Inline childInline, TextPointer insertionPosition) : base(childInline, insertionPosition)
        { 
        } 

        ///  
        /// Creates a new Hyperlink instance covering existing content.
        /// 
        /// 
        /// Start position of the new Hyperlink. 
        /// 
        ///  
        /// End position of the new Hyperlink. 
        /// 
        ///  
        /// start and end must both be parented by the same Paragraph, otherwise
        /// the method will raise an ArgumentException.
        /// 
        public Hyperlink(TextPointer start, TextPointer end) : base(start, end) 
        {
            // After inserting this Hyperlink, we need to extract any child Hyperlinks. 
 
            TextPointer navigator = this.ContentStart.CreatePointer();
            TextPointer stop = this.ContentEnd; 

            while (navigator.CompareTo(stop) < 0)
            {
                Hyperlink hyperlink = navigator.GetAdjacentElement(LogicalDirection.Forward) as Hyperlink; 

                if (hyperlink != null) 
                { 
                    hyperlink.Reposition(null, null);
                } 
                else
                {
                    navigator.MoveToNextContextPosition(LogicalDirection.Forward);
                } 
            }
        } 
 
        #endregion Constructors
 
        //--------------------------------------------------------------------
        //
        // Public Methods
        // 
        //---------------------------------------------------------------------
 
        #region Public Methods 

        ///  
        /// This method does exactly the same operation as clicking the Hyperlink with the mouse, except the navigation is not treated as user-initiated.
        /// 
        public void DoClick()
        { 
            DoNonUserInitiatedNavigation(this);
        } 
 
        #region ICommandSource
 
        /// 
        ///     The DependencyProperty for RoutedCommand
        /// 
        public static readonly DependencyProperty CommandProperty = 
                DependencyProperty.Register(
                        "Command", 
                        typeof(ICommand), 
                        typeof(Hyperlink),
                        new FrameworkPropertyMetadata((ICommand)null, 
                            new PropertyChangedCallback(OnCommandChanged)));

        /// 
        /// Get or set the Command property 
        /// 
        [Bindable(true), Category("Action")] 
        [Localizability(LocalizationCategory.NeverLocalize)] 
        public ICommand Command
        { 
            get
            {
                return (ICommand)GetValue(CommandProperty);
            } 
            set
            { 
                SetValue(CommandProperty, value); 
            }
        } 

        private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Hyperlink h = (Hyperlink)d; 
            h.OnCommandChanged((ICommand)e.OldValue, (ICommand)e.NewValue);
        } 
 
        private void OnCommandChanged(ICommand oldCommand, ICommand newCommand)
        { 
            if (oldCommand != null)
            {
                UnhookCommand(oldCommand);
            } 
            if (newCommand != null)
            { 
                HookCommand(newCommand); 
            }
        } 

        private void UnhookCommand(ICommand command)
        {
            EventHandler handler = CanExecuteChangedHandler.GetValue(this); 
            if (handler != null)
            { 
                command.CanExecuteChanged -= handler; 
                CanExecuteChangedHandler.ClearValue(this);
            } 
            UpdateCanExecute();
        }

        private void HookCommand(ICommand command) 
        {
            EventHandler handler = new EventHandler(OnCanExecuteChanged); 
            CanExecuteChangedHandler.SetValue(this, handler); 
            command.CanExecuteChanged += handler;
            UpdateCanExecute(); 
        }

        private void OnCanExecuteChanged(object sender, EventArgs e)
        { 
            UpdateCanExecute();
        } 
 
        private void UpdateCanExecute()
        { 
            if (Command != null)
            {
                CanExecute = MS.Internal.Commands.CommandHelpers.CanExecuteCommandSource(this);
            } 
            else
            { 
                CanExecute = true; 
            }
        } 

        private bool CanExecute
        {
            get { return _canExecute; } 
            set
            { 
                if (_canExecute != value) 
                {
                    _canExecute = value; 
                    CoerceValue(IsEnabledProperty);
                }
            }
        } 

        // Returns true when this Hyperlink is hosted by an enabled 
        // TextEditor (eg, within a RichTextBox). 
        private bool IsEditable
        { 
            get
            {
                return (this.TextContainer.TextSelection != null &&
                        !this.TextContainer.TextSelection.TextEditor.IsReadOnly); 
            }
        } 
 
        /// 
        ///     Fetches the value of the IsEnabled property 
        /// 
        /// 
        ///     The reason this property is overridden is so that Hyperlink
        ///     can infuse the value for CanExecute into it. 
        /// 
        protected override bool IsEnabledCore 
        { 
            get
            { 
                return base.IsEnabledCore && CanExecute;
            }
        }
 
        /// 
        /// The DependencyProperty for the CommandParameter 
        ///  
        public static readonly DependencyProperty CommandParameterProperty =
                DependencyProperty.Register( 
                        "CommandParameter",
                        typeof(object),
                        typeof(Hyperlink),
                        new FrameworkPropertyMetadata((object)null)); 

        ///  
        /// Reflects the parameter to pass to the CommandProperty upon execution. 
        /// 
        [Bindable(true), Category("Action")] 
        [Localizability(LocalizationCategory.NeverLocalize)]
        public object CommandParameter
        {
            get 
            {
                return GetValue(CommandParameterProperty); 
            } 
            set
            { 
                SetValue(CommandParameterProperty, value);
            }
        }
 
        /// 
        ///     The DependencyProperty for Target property 
        ///     Flags:              None 
        ///     Default Value:      null
        ///  
        public static readonly DependencyProperty CommandTargetProperty =
                DependencyProperty.Register(
                        "CommandTarget",
                        typeof(IInputElement), 
                        typeof(Hyperlink),
                        new FrameworkPropertyMetadata((IInputElement)null)); 
 
        /// 
        ///     The target element on which to fire the command. 
        /// 
        [Bindable(true), Category("Action")]
        public IInputElement CommandTarget
        { 
            get
            { 
                return (IInputElement)GetValue(CommandTargetProperty); 
            }
            set 
            {
                SetValue(CommandTargetProperty, value);
            }
        } 

        #endregion 
 
        #endregion Public Methods
 
        //--------------------------------------------------------------------
        //
        // Public Properties
        // 
        //----------------------------------------------------------------------
 
        #region Public Properties 

        ///  
        /// Contains the target URI to navigate when hyperlink is clicked
        /// 
        [CommonDependencyProperty]
        public static readonly DependencyProperty NavigateUriProperty = 
            DependencyProperty.Register(
                      "NavigateUri", 
                      typeof(Uri), 
                      typeof(Hyperlink),
                      new FrameworkPropertyMetadata( 
                             (Uri)null,
                             new PropertyChangedCallback(OnNavigateUriChanged),
                             new CoerceValueCallback(CoerceNavigateUri)));
 
        /// 
        /// Coerce value callback for NavigateUri. 
        ///  
        /// Element to coerce NavigateUri for.
        /// New value for NavigateUri. 
        /// Coerced value.
        /// 
        /// Critical: Implements part of the anti-spoofing feature.
        /// TreatAsSafe: This method changes no state and returns no protected info. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal static object CoerceNavigateUri(DependencyObject d, object value) 
        {
            // 
            // If the element for which NavigateUri is being changed is the protected element,
            // we don't let the update go through. This cancels NavigateUri modifications in
            // the critical period when the URI is shown on the status bar.
            // An example attack: 
            //      void hl_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
            //      { 
            //          hl.NavigateUri = null; 
            //          hl.DoClick();
            //          hl.NavigateUri = new Uri("http://www.evil.com"); 
            //      }
            // (Or, instead of setting NavigateUri=null, add a handler for Hyperlink.RequestNavigateEvent and
            //  set e.Handled=true.)
            // 
            if (s_criticalNavigateUriProtectee.Value == d.GetHashCode() && ShouldPreventUriSpoofing)
            { 
                value = DependencyProperty.UnsetValue; 
            }
 
            return value;
        }

        ///  
        /// Provide public access to NavigateUriProperty property. Content the URI to navigate.
        ///  
        [Bindable(true), CustomCategory("Navigation")] 
        [Localizability(LocalizationCategory.Hyperlink)]
        public Uri NavigateUri 
        {
            get
            {
                return (Uri)GetValue(NavigateUriProperty); 

            } 
            set 
            {
                SetValue(NavigateUriProperty, value); 
            }
        }

        ///  
        /// Contains the target window to navigate when hyperlink is clicked
        ///  
        public static readonly DependencyProperty TargetNameProperty 
            = DependencyProperty.Register("TargetName", typeof(String), typeof(Hyperlink),
                                          new FrameworkPropertyMetadata(string.Empty)); 

        /// 
        /// Provide public access to TargetNameProperty property.  The target window to navigate.
        ///  
        [Bindable(true), CustomCategory("Navigation")]
        [Localizability( 
            LocalizationCategory.None, 
            Modifiability = Modifiability.Unmodifiable)
        ] 
        public string TargetName
        {
            get
            { 
                return (string)GetValue(TargetNameProperty);
            } 
            set 
            {
                SetValue(TargetNameProperty, value); 
            }
        }

        #endregion Public Properties 

        //------------------------------------------------------------------- 
        // 
        // Public Events
        // 
        //----------------------------------------------------------------------

        #region Public Events
 
        // **
 
 

 
        /// 
        /// Navigate Event
        /// 
        public static readonly RoutedEvent RequestNavigateEvent = EventManager.RegisterRoutedEvent( 
                                                    "RequestNavigate",
                                                    RoutingStrategy.Bubble, 
                                                    typeof(RequestNavigateEventHandler), 
                                                    typeof(Hyperlink));
 
        /// 
        /// Add / Remove RequestNavigateEvent handler
        /// 
        public event RequestNavigateEventHandler RequestNavigate 
        {
            add 
            { 
                AddHandler(RequestNavigateEvent, value);
            } 
            remove
            {
                RemoveHandler(RequestNavigateEvent, value);
            } 
        }
 
        ///  
        /// Event correspond to left mouse button click
        ///  
        public static readonly RoutedEvent ClickEvent = System.Windows.Controls.Primitives.ButtonBase.ClickEvent.AddOwner(typeof(Hyperlink));

        /// 
        /// Add / Remove ClickEvent handler 
        /// 
        [Category("Behavior")] 
        public event RoutedEventHandler Click { add { AddHandler(ClickEvent, value); } remove { RemoveHandler(ClickEvent, value); } } 

        ///  
        /// StatusBar event
        /// 
        internal static readonly RoutedEvent RequestSetStatusBarEvent = EventManager.RegisterRoutedEvent(
                                                    "RequestSetStatusBar", 
                                                    RoutingStrategy.Bubble,
                                                    typeof(RoutedEventHandler), 
                                                    typeof(Hyperlink)); 

        #endregion Public Events 

        //-------------------------------------------------------------------
        //
        // Protected Methods 
        //
        //--------------------------------------------------------------------- 
 
        #region Protected Methods
 
        /// 
        /// This is the method that responds to the MouseButtonEvent event.
        /// 
        /// Event arguments 
        /// Kept around for backward compatibility in derived classes.
        protected internal override void OnMouseLeftButtonDown(MouseButtonEventArgs e) 
        { 
            base.OnMouseLeftButtonDown(e);
 
            if (IsEnabled && (!IsEditable || ((Keyboard.Modifiers & ModifierKeys.Control) != 0)))
            {
                OnMouseLeftButtonDown(this, e);
            } 
        }
 
        ///  
        /// This is the method that responds to the MouseButtonEvent event.
        ///  
        /// Event arguments
        /// 
        /// Added for the NavigateUri = null case, which won't have event handlers hooked
        /// up since OnNavigateUriChanged isn't ever called. However, we want to have the 
        /// sequence of commands and Click event triggered even in this case for Hyperlink.
        ///  
        ///  
        ///     Critical - Calls critical static OnMouseLeftButtonUp.
        ///  
        [SecurityCritical]
        protected internal override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonUp(e); 

            OnMouseLeftButtonUp(this, e); 
        } 

        #region Spoofing prevention and status bar access 

        /// 
        /// Cached URI for spoofing countermeasures.
        ///  
        /// 
        /// We keep one per thread in case multiple threads would be involved in the spoofing attack. 
        ///  
        /// 
        ///     Critical for set - Changing the cached URI can open up for spoofing attacks. 
        /// 
        [ThreadStatic]
        private static SecurityCriticalDataForSet s_cachedNavigateUri;
 
        /// 
        /// Identification code of the hyperlink element currently protected against spoofing attacks. 
        /// This code is checked during the NavigateUri coerce value callback in order to protect the 
        /// NavigateUri from changing during the critical period between showing the URI on the status
        /// bar and clearing it, which is the timeframe where spoofing attacks can occur. 
        /// 
        /// 
        /// We keep one per thread in case multiple threads would be involved in the spoofing attack.
        ///  
        /// 
        ///     Critical for set - Changing the identification code will make the element vulnerable 
        ///                        for spoofing. 
        /// 
        [ThreadStatic] 
        private static SecurityCriticalDataForSet s_criticalNavigateUriProtectee;

        /// 
        /// Caches a target URI for spoofing prevention. 
        /// 
        /// Hyperlink object for which the target URI is to be cached. 
        /// Target URI the user expects to be navigate to. 
        /// 
        ///     Critical - Sets the cached URI that prevents spoofing attacks. 
        /// 
        [SecurityCritical]
        private static void CacheNavigateUri(DependencyObject d, Uri targetUri)
        { 
            //
            // This prevents against multi-threaded spoofing attacks. 
            // 
            d.VerifyAccess();
 
            s_cachedNavigateUri.Value = targetUri;
        }

        ///  
        /// Navigates to the specified URI if it matches the pre-registered cached target URI (spoofing prevention).
        ///  
        /// Source for the RequestNavigateEventArgs. 
        /// URI to navigate to.
        /// Target window for the RequestNavigateEventArgs. 
        /// 
        ///     Critical - Implements the anti-spoofing mechanism and clears the anti-spoofing cache after navigation took place.
        ///     TreatAsSafe - Navigation is considered safe; if the target is a browser window the UserInitiatedNavigationPermission will be demanded.
        ///                   Only if navigation took place, the anti-spoofing cache will be cleared. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private static void NavigateToUri(IInputElement sourceElement, Uri targetUri, string targetWindow) 
        {
            Debug.Assert(targetUri != null); 

            //
            // This prevents against multi-threaded spoofing attacks.
            // 
            DependencyObject dObj = (DependencyObject)sourceElement;
            dObj.VerifyAccess(); 
 
            //
            // Spoofing countermeasure makes sure the URI hasn't changed since display in the status bar. 
            //
            Uri cachedUri = Hyperlink.s_cachedNavigateUri.Value;
            // ShouldPreventUriSpoofing is checked last in order to avoid incurring a first-chance SecurityException
            // in common scenarios. 
            if (cachedUri == null || cachedUri.Equals(targetUri) || !ShouldPreventUriSpoofing)
            { 
                // 

                // We treat FixedPage seperately to maintain backward compatibility 
                // with the original separate FixedPage implementation of this, which
                // calls the GetLinkUri method.
                if (!(sourceElement is Hyperlink))
                { 
                    targetUri = FixedPage.GetLinkUri(sourceElement, targetUri);
                } 
 
                RequestNavigateEventArgs navigateArgs = new RequestNavigateEventArgs(targetUri, targetWindow);
                navigateArgs.Source = sourceElement; 
                sourceElement.RaiseEvent(navigateArgs);

                if (navigateArgs.Handled)
                { 
                    //
                    // The browser's status bar should be cleared. Otherwise it will still show the 
                    // hyperlink address after navigation has completed. 
                    // !! We have to do this after the current callstack is unwound in order to keep
                    // the anti-spoofing state valid. A particular attach is to do a bogus call to 
                    // DoClick() in a mouse click preview event and then change the NavigateUri.
                    //
                    dObj.Dispatcher.BeginInvoke(DispatcherPriority.Send,
                        new System.Threading.SendOrPostCallback(ClearStatusBarAndCachedUri), sourceElement); 
                }
            } 
        } 

        ///  
        /// Updates the status bar to reflect the current NavigateUri.
        /// 
        /// 
        ///     Critical - Sets the cached URI (CacheNavigateUri) and s_criticalNavigateUriProtectee 
        ///                which prevent spoofing attacks.
        ///                Calls the critical RequestSetStatusBarEventArgs ctor. 
        ///  
        [SecurityCritical]
        private static void UpdateStatusBar(object sender) 
        {
            IInputElement element = (IInputElement)sender;
            DependencyObject dObject = (DependencyObject)sender;
 
            Uri targetUri = (Uri)dObject.GetValue(GetNavigateUriProperty(element));
 
            // 
            // Keep the identification code for the element that's to be protected against spoofing
            // attacks because its URI is shown on the status bar. 
            //
            s_criticalNavigateUriProtectee.Value = dObject.GetHashCode();

            // 
            // Cache URI for spoofing countermeasures.
            // 
            CacheNavigateUri(dObject, targetUri); 

            RequestSetStatusBarEventArgs args = new RequestSetStatusBarEventArgs(targetUri); 
            element.RaiseEvent(args);
        }

        // The implementation of Hyperlink.NavigateUri and FixedPage.NavigateUri are unified, 
        // but the DPs themselves are not. FixedPage.NavigateUri is attached; Hyperlink.Navigate
        // is a regular DP. Use this method to get the property DP based on the element. 
        private static DependencyProperty GetNavigateUriProperty(object element) 
        {
            Hyperlink hl = element as Hyperlink; 
            return (hl == null) ? FixedPage.NavigateUriProperty : NavigateUriProperty;
        }

        ///  
        /// Clears the status bar.
        ///  
        ///  
        ///     Critical - Clears the cached URI and s_criticalNavigateUriProtectee which prevent
        ///                spoofing attacks. 
        ///                Note: Upstream spoofing should be prevented (e.g. OnMouseLeave) because
        ///                      clearing the identification code in s_criticalNavigateUriProtectee
        ///                      will disable spoofing detection.
        ///  
        [SecurityCritical]
        private static void ClearStatusBarAndCachedUri(object sender) 
        { 
            IInputElement element = (IInputElement)sender;
 
            //
            // Clear the status bar first, from this point on we're not protecting against spoofing
            // anymore.
            // 
            element.RaiseEvent(RequestSetStatusBarEventArgs.Clear);
 
            // 
            // Invalidate cache URI for spoofing countermeasures.
            // 
            CacheNavigateUri((DependencyObject)sender, null);

            //
            // Clear the identification code for the element that was protected against spoofing. 
            //
            s_criticalNavigateUriProtectee.Value = null; 
        } 

        #endregion 

        /// 
        /// Navigate to URI specified in NavigateUri property and mark the hyperlink as visited
        ///  
        /// 
        /// Some forms of navigation are not allowed in the internet zone. 
        /// As such there are cases where this API will demand for fulltrust. 
        ///
        /// This method is kept of backward compatibility and isn't a real event handler anymore. 
        /// It should remain in here however for subclasses that want to override it either to
        /// redefine behavior or to get notified about the click event.
        /// 
        protected virtual void OnClick() 
        {
            if (AutomationPeer.ListenerExists(AutomationEvents.InvokePatternOnInvoked)) 
            { 
                AutomationPeer peer = ContentElementAutomationPeer.CreatePeerForElement(this);
                if (peer != null) 
                    peer.RaiseAutomationEvent(AutomationEvents.InvokePatternOnInvoked);
            }

            DoNavigation(this); 
            RaiseEvent(new RoutedEventArgs(Hyperlink.ClickEvent, this));
 
            MS.Internal.Commands.CommandHelpers.ExecuteCommandSource(this); 
        }
 
        /// 
        /// This is the method that responds to the KeyDown event.
        /// 
        ///  
        /// This method is kept for backward compatibility.
        ///  
        ///  
        ///     Critical - Calls into static critical OnKeyDown method.
        ///  
        [SecurityCritical]
        protected internal override void OnKeyDown(KeyEventArgs e)
        {
            if (!e.Handled && e.Key == Key.Enter) 
            {
                OnKeyDown(this, e); 
            } 
            else
            { 
                base.OnKeyDown(e);
            }
        }
 
        //
        //  This property 
        //  1. Finds the correct initial size for the _effectiveValues store on the current DependencyObject 
        //  2. This is a performance optimization
        // 
        internal override int EffectiveValuesInitialSize
        {
            get { return 19; }
        } 

        ///  
        /// Creates AutomationPeer () 
        /// 
        protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer() 
        {
            return new System.Windows.Automation.Peers.HyperlinkAutomationPeer(this);
        }
 
        #endregion Protected Methods
 
        #region IUriContext implementation 

        ///  
        /// IUriContext interface is implemented by Hyperlink element so that it
        /// can hold on to the base URI used by parser.
        /// The base URI is needed to resolve NavigateUri property
        ///  
        /// Base Uri
        Uri IUriContext.BaseUri 
        { 
            get
            { 
                return  BaseUri;
            }
            set
            { 
                BaseUri = value;
            } 
        } 

        ///  
        ///    Implementation for BaseUri
        /// 
        protected virtual Uri BaseUri
        { 
            get
            { 
                return (Uri)GetValue(BaseUriHelper.BaseUriProperty); 
            }
            set 
            {
                SetValue(BaseUriHelper.BaseUriProperty, value);
            }
        } 

        #endregion IUriContext implementation 
 

        //------------------------------------------------------------------- 
        //
        // Internal Properties
        //
        //---------------------------------------------------------------------- 

        #region Internal Properties 
 
        /// 
        /// The content spanned by this Hyperlink represented as plain text. 
        /// 
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        internal string Text
        { 
            get
            { 
                return TextRangeBase.GetTextInternal(this.ContentStart, this.ContentEnd); 
            }
        } 

        #endregion Internal Properties

        //------------------------------------------------------------------- 
        //
        // Private Methods 
        // 
        //----------------------------------------------------------------------
 
        #region Private Methods

        // QueryCursorEvent callback.
        // If this Hyperlink is editable, use the editor cursor unless 
        // the control key is down.
        private static void OnQueryCursor(object sender, QueryCursorEventArgs e) 
        { 
            Hyperlink link = (Hyperlink)sender;
 
            if (link.IsEnabled && link.IsEditable)
            {
                if ((Keyboard.Modifiers & ModifierKeys.Control) == 0)
                { 
                    e.Cursor = link.TextContainer.TextSelection.TextEditor._cursor;
                    e.Handled = true; 
                } 
            }
        } 

        #endregion Private Methods

        #region Private Properties 
        //--------------------------------------------------------------------
        // 
        // Private Properties 
        //
        //--------------------------------------------------------------------- 

        /// 
        /// Critical: Sets s_shouldPreventUriSpoofing.
        /// Not TreatAsSafe just to help prevent the remote possibility of calling this under elevation 
        /// from framework code, since the result of the Demand is cached.
        ///  
        static bool ShouldPreventUriSpoofing 
        {
            [SecurityCritical] 
            get
            {
                if (!s_shouldPreventUriSpoofing.Value.HasValue)
                { 
                    try
                    { 
                        (new System.Net.WebPermission(PermissionState.Unrestricted)).Demand(); 
                        s_shouldPreventUriSpoofing.Value = false;
                    } 
                    catch (SecurityException)
                    {
                        s_shouldPreventUriSpoofing.Value = true;
                    } 
                }
                return (bool)s_shouldPreventUriSpoofing.Value; 
            } 
        }
        static SecurityCriticalDataForSet s_shouldPreventUriSpoofing; 

        #endregion Private Properties

        //-------------------------------------------------------------------- 
        //
        // Private Fields 
        // 
        //---------------------------------------------------------------------
 
        #region Private Fields

        private bool _canExecute = true;
 
        private static readonly UncommonField CanExecuteChangedHandler = new UncommonField();
 
        #endregion Private Fields 

        //------------------------------------------------------------------- 
        //
        // Navigation control
        //
        //--------------------------------------------------------------------- 

        #region Navigation control 
 
        /// 
        /// Records the IsPressed property attached to elements with hyperlink functionality. 
        /// 
        private static readonly DependencyProperty IsHyperlinkPressedProperty =
                DependencyProperty.Register(
                        "IsHyperlinkPressed", 
                        typeof(bool),
                        typeof(Hyperlink), 
                        new FrameworkPropertyMetadata(false)); 

        internal static void OnNavigateUriChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            IInputElement element = d as IInputElement;

            // 
            // We only set up spoofing prevention for known objects that are IInputElements.
            // However, for backward compatibility we shouldn't make this callback fail since 
            // other places such as FixedTextBuilder use NavigateUri e.g. for serialization. 
            //
            if (element != null) 
            {
                Uri navigateUri = (Uri)e.NewValue;

                // 
                // We use a different code path for Path, Canvas, Glyphs and FixedPage to maintain backward compatibility
                // with the original separate Hyperlink implementation of this (which didn't execute CanNavigateToUri). 
                // 
                if (navigateUri != null)
                { 
                    FrameworkElement fe = d as FrameworkElement;

                    if (fe != null && ((fe is Path) || (fe is Canvas) || (fe is Glyphs) || (fe is FixedPage)))
                    { 
                        if (FixedPage.CanNavigateToUri(navigateUri))
                        { 
                            SetUpNavigationEventHandlers(element); 
                            fe.Cursor = Cursors.Hand;
                        } 
                        else
                        {
                            fe.Cursor = Cursors.No;
                        } 
                    }
                    else 
                    { 
                        FrameworkContentElement fce = d as FrameworkContentElement;
 
                        if (fce != null && (fce is Hyperlink))
                        {
                            SetUpNavigationEventHandlers(element);
                        } 
                    }
                } 
            } 
        }
 
        /// 
        ///     Critical - Hooks up event handlers that are responsible to set up anti-spoofing mitigations
        ///                and event handlers that are critical because of the risk for replay attacks.
        ///     TreatAsSafe - We're hooking up event handlers for trusted events from the input system. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private static void SetUpNavigationEventHandlers(IInputElement element) 
        {
            // 
            // We only support FixedPage.NavigateUri to be attached to those four elements (aka pseudo-hyperlinks):
            // Path, Canvas, Glyph, FixedPage.
            //
            // We can get away with the UIElement events event for the Hyperlink which is a ContentElement 
            // because of the aliasing present on those.
            // 
 
            //
            // Hyperlink already has instance handlers for the following events. To avoid handling the event twice, 
            // we only hook up the static handlers for pseudo-hyperlinks.
            //
            if (!(element is Hyperlink))
            { 
                SetUpEventHandler(element, UIElement.KeyDownEvent, new KeyEventHandler(OnKeyDown)); //initiates navigation
                SetUpEventHandler(element, UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(OnMouseLeftButtonDown)); //capture hyperlink pressed state 
                SetUpEventHandler(element, UIElement.MouseLeftButtonUpEvent, new MouseButtonEventHandler(OnMouseLeftButtonUp)); //can initiate navigation 
            }
 
            SetUpEventHandler(element, UIElement.MouseEnterEvent, new MouseEventHandler(OnMouseEnter)); //set status bar
            SetUpEventHandler(element, UIElement.MouseLeaveEvent, new MouseEventHandler(OnMouseLeave)); //clear status bar
        }
 
        private static void SetUpEventHandler(IInputElement element, RoutedEvent routedEvent, Delegate handler)
        { 
            // 
            // Setting NavigateUri causes navigation event handlers to be set up.
            // Doing this repeatedly would keep adding handlers; therefore remove any handler first. 
            //
            element.RemoveHandler(routedEvent, handler);
            element.AddHandler(routedEvent, handler);
        } 

        ///  
        /// This is the method that responds to the KeyDown event. 
        /// 
        ///  
        ///     Critical - Calls DoUserInitiatedNavigation. We also want to protect against replay attacks.
        /// 
        [SecurityCritical]
        private static void OnKeyDown(object sender, KeyEventArgs e) 
        {
            if (!e.Handled && e.Key == Key.Enter) 
            { 
                //
                // Keyboard navigation doesn't reveal the URL on the status bar, so there's no spoofing 
                // attack possible. We clear the cache here and allow navigation to go through.
                //
                CacheNavigateUri((DependencyObject)sender, null);
 
                if (e.UserInitiated)
                { 
                    DoUserInitiatedNavigation(sender); 
                }
                else 
                {
                    DoNonUserInitiatedNavigation(sender);
                }
 
                e.Handled = true;
            } 
        } 

        ///  
        /// This is the method that responds to the MouseLeftButtonEvent event.
        /// 
        private static void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        { 
            IInputElement element = (IInputElement)sender;
            DependencyObject dp = (DependencyObject)sender; 
 
            // Hyperlink should take focus when left mouse button is clicked on it
            // This is consistent with all ButtonBase controls and current Win32 behavior 
            element.Focus();

            // It is possible that the mouse state could have changed during all of
            // the call-outs that have happened so far. 
            if (e.ButtonState == MouseButtonState.Pressed)
            { 
                // Capture the mouse, and make sure we got it. 
                Mouse.Capture(element);
                if (element.IsMouseCaptured) 
                {
                    // Though we have already checked this state, our call to CaptureMouse
                    // could also end up changing the state, so we check it again.
 
                    //
                    // ISSUE - Leave this here because of 1111993. 
                    // 
                    if (e.ButtonState == MouseButtonState.Pressed)
                    { 
                        dp.SetValue(IsHyperlinkPressedProperty, true);
                    }
                    else
                    { 
                        // Release capture since we decided not to press the button.
                        element.ReleaseMouseCapture(); 
                    } 
                }
            } 

            e.Handled = true;
        }
 
        /// 
        /// This is the method that responds to the MouseLeftButtonUpEvent event. 
        ///  
        /// 
        ///     Critical - Calls DoUserInitiatedNavigation. We also want to protect against replay attacks 
        ///                and can't assume the IsHyperlinkPressed DP hasn't been tampered with.
        /// 
        [SecurityCritical]
        private static void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e) 
        {
            IInputElement element = (IInputElement)sender; 
            DependencyObject dp = (DependencyObject)sender; 

            if (element.IsMouseCaptured) 
            {
                element.ReleaseMouseCapture();
            }
 
            //
            // ISSUE - Leave this here because of 1111993. 
            // 
            if ((bool)dp.GetValue(IsHyperlinkPressedProperty))
            { 
                dp.SetValue(IsHyperlinkPressedProperty, false);

                // Make sure we're mousing up over the hyperlink
                if (element.IsMouseOver) 
                {
                    if (e.UserInitiated) 
                    { 
                        DoUserInitiatedNavigation(sender);
                    } 
                    else
                    {
                        DoNonUserInitiatedNavigation(sender);
                    } 
                }
            } 
 
            e.Handled = true;
        } 

        /// 
        /// Fire the event to change the status bar.
        ///  
        /// 
        ///     Critical - Calls UpdateStatusBar to set the cached URI that prevents spoofing attacks. 
        ///  
        [SecurityCritical]
        private static void OnMouseEnter(object sender, MouseEventArgs e) 
        {
            UpdateStatusBar(sender);
        }
 
        /// 
        /// Set the status bar text back to empty 
        ///  
        /// 
        ///     Critical - Calls ClearStatusBarAndCachedUri to clear the cached URI that prevents spoofing attacks. 
        /// 
        [SecurityCritical]
        private static void OnMouseLeave(object sender, MouseEventArgs e)
        { 
            IInputElement ee = (IInputElement)sender;
 
            // 
            // Prevent against replay attacks. We expect the mouse not to be over the
            // element, otherwise someone tries to circumvent the spoofing countermeasures 
            // while we're in the critical period between OnMouseEnter and OnMouseLeave.
            //
            if (!ee.IsMouseOver)
            { 
                ClearStatusBarAndCachedUri(sender);
            } 
        } 

        ///  
        ///     Critical - Asserts UserInitatedNavigationPermission.
        /// 
        [SecurityCritical]
        private static void DoUserInitiatedNavigation(object sender) 
        {
            CodeAccessPermission perm = SecurityHelper.CreateUserInitiatedNavigationPermission(); 
            perm.Assert(); 

            try 
            {
                DispatchNavigation(sender);
            }
            finally 
            {
                CodeAccessPermission.RevertAssert(); 
            } 
        }
 
        /// 
        ///     Critical - Sets the cached URI that prevents spoofing attacks.
        ///     TreatAsSafe - We don't prevent spoofing in non user-initiated scenarios.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private static void DoNonUserInitiatedNavigation(object sender) 
        { 
            CacheNavigateUri((DependencyObject)sender, null);
            DispatchNavigation(sender); 
        }

        /// 
        /// Dispatches navigation; if the object is a Hyperlink we go through OnClick 
        /// to preserve the original event chain, otherwise we call our DoNavigation
        /// method. 
        ///  
        private static void DispatchNavigation(object sender)
        { 
            Hyperlink hl = sender as Hyperlink;
            if (hl != null)
            {
                // 
                // Call the virtual OnClick on Hyperlink to keep old behavior.
                // 
                hl.OnClick(); 
            }
            else 
            {
                DoNavigation(sender);
            }
        } 

        ///  
        /// Navigate to URI specified in the object's NavigateUri property. 
        /// 
        private static void DoNavigation(object sender) 
        {
            IInputElement element = (IInputElement)sender;
            DependencyObject dObject = (DependencyObject)sender;
 
            Uri inputUri = (Uri)dObject.GetValue(GetNavigateUriProperty(element));
            string targetWindow = (string)dObject.GetValue(TargetNameProperty); 
            RaiseNavigate(element, inputUri, targetWindow); 
        }
 
        /// 
        /// Navigate to URI. Used by OnClick and by automation.
        /// 
        /// Source for the RequestNavigateEventArgs. 
        /// URI to navigate to.
        /// Target window for the RequestNavigateEventArgs. 
        internal static void RaiseNavigate(IInputElement element, Uri targetUri, string targetWindow) 
        {
            // 
            // Do secure (spoofing countermeasures) navigation.
            //
            if (targetUri != null)
            { 
                NavigateToUri(element, targetUri, targetWindow);
            } 
        } 

        #endregion 

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

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK