Hyperlink.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / System / Windows / Documents / Hyperlink.cs / 2 / 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 MS.Internal.PresentationFramework;
using System.Windows.Threading; 
 
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.
        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.
            // 
            if (s_criticalNavigateUriProtectee.Value == d.GetHashCode()) 
                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;

            if (cachedUri == null || cachedUri.Equals(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 

        //--------------------------------------------------------------------
        //
        // 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)
        { 
            Hyperlink hl = element as Hyperlink; 

            // 
            // We treat FixedPage seperately to maintain backward compatibility
            // with the original separate FixedPage implementation of this, which
            // calls the GetLinkUri method.
            // 
            if (hl == null)
            { 
                targetUri = FixedPage.GetLinkUri(element, targetUri); 
            }
 
            //
            // 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.
//---------------------------------------------------------------------------- 
// 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 MS.Internal.PresentationFramework;
using System.Windows.Threading; 
 
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.
        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.
            // 
            if (s_criticalNavigateUriProtectee.Value == d.GetHashCode()) 
                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;

            if (cachedUri == null || cachedUri.Equals(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 

        //--------------------------------------------------------------------
        //
        // 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)
        { 
            Hyperlink hl = element as Hyperlink; 

            // 
            // We treat FixedPage seperately to maintain backward compatibility
            // with the original separate FixedPage implementation of this, which
            // calls the GetLinkUri method.
            // 
            if (hl == null)
            { 
                targetUri = FixedPage.GetLinkUri(element, targetUri); 
            }
 
            //
            // 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