Example using Hyperlink in WPF

C#WpfXamlHyperlink

C# Problem Overview


I've seen several suggestions, that you can add hyperlink to WPF application through Hyperlink control.

Here's how I'm trying to use it in my code:

<Window
		xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
		xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
		mc:Ignorable="d" 
		x:Class="BookmarkWizV2.InfoPanels.Windows.UrlProperties"
		Title="UrlProperties" Height="754" Width="576">
	<Grid>
		<Grid.RowDefinitions>
			<RowDefinition></RowDefinition>
			<RowDefinition Height="40"/>
		</Grid.RowDefinitions>
		<Grid>
			<ScrollViewer ScrollViewer.VerticalScrollBarVisibility="Auto" Grid.RowSpan="2">
				<StackPanel >
					<DockPanel LastChildFill="True" Margin="0,5">
						<TextBlock Text="Url:" Margin="5" 
							DockPanel.Dock="Left" VerticalAlignment="Center"/>
						<TextBox Width="Auto">
							<Hyperlink NavigateUri="http://www.google.co.in">
									Click here
							</Hyperlink>   
						</TextBox>						
					</DockPanel >
				</StackPanel>
			</ScrollViewer>        
		</Grid>
		<StackPanel HorizontalAlignment="Right" Orientation="Horizontal" Margin="0,7,2,7" Grid.Row="1" >
			<Button Margin="0,0,10,0">
				<TextBlock Text="Accept" Margin="15,3" />
			</Button>
			<Button Margin="0,0,10,0">
				<TextBlock Text="Cancel" Margin="15,3" />
			</Button>
		</StackPanel>
	</Grid>
</Window>

I'm getting following error:

> Property 'Text' does not support values of type 'Hyperlink'.

What am I doing wrong?

C# Solutions


Solution 1 - C#

If you want your application to open the link in a web browser you need to add a HyperLink with the RequestNavigate event set to a function that programmatically opens a web-browser with the address as a parameter.

<TextBlock>           
    <Hyperlink NavigateUri="http://www.google.com" RequestNavigate="Hyperlink_RequestNavigate">
        Click here
    </Hyperlink>
</TextBlock>

In the code-behind you would need to add something similar to this to handle the RequestNavigate event:

private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
    // for .NET Core you need to add UseShellExecute = true
    // see https://docs.microsoft.com/dotnet/api/system.diagnostics.processstartinfo.useshellexecute#property-value
    Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
    e.Handled = true;
}

In addition you will also need the following imports:

using System.Diagnostics;
using System.Windows.Navigation;

It will look like this in your application:

oO

Solution 2 - C#

In addition to Fuji's response, we can make the handler reusable turning it into an attached property:

public static class HyperlinkExtensions
{
	public static bool GetIsExternal(DependencyObject obj)
	{
		return (bool)obj.GetValue(IsExternalProperty);
	}

	public static void SetIsExternal(DependencyObject obj, bool value)
	{
		obj.SetValue(IsExternalProperty, value);
	}
	public static readonly DependencyProperty IsExternalProperty =
		DependencyProperty.RegisterAttached("IsExternal", typeof(bool), typeof(HyperlinkExtensions), new UIPropertyMetadata(false, OnIsExternalChanged));

	private static void OnIsExternalChanged(object sender, DependencyPropertyChangedEventArgs args)
	{
		var hyperlink = sender as Hyperlink;

		if ((bool)args.NewValue)
			hyperlink.RequestNavigate += Hyperlink_RequestNavigate;
		else
			hyperlink.RequestNavigate -= Hyperlink_RequestNavigate;
	}

	private static void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
	{
		Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
		e.Handled = true;
	}
}

And use it like this:

<TextBlock>
    <Hyperlink NavigateUri="https://stackoverflow.com"
               custom:HyperlinkExtensions.IsExternal="true">
        Click here
    </Hyperlink>
</TextBlock>

Solution 3 - C#

If you want to localize string later, then those answers aren't enough, I would suggest something like:

<TextBlock>
    <Hyperlink NavigateUri="http://labsii.com/">
       <Hyperlink.Inlines>
            <Run Text="Click here"/>
       </Hyperlink.Inlines>
   </Hyperlink>
</TextBlock>

Solution 4 - C#

Hyperlink is not a control, it is a flow content element, you can only use it in controls which support flow content, like a TextBlock. TextBoxes only have plain text.

Solution 5 - C#

IMHO the simplest way is to use new control inherited from Hyperlink:

/// <summary>
/// Opens <see cref="Hyperlink.NavigateUri"/> in a default system browser
/// </summary>
public class ExternalBrowserHyperlink : Hyperlink
{
    public ExternalBrowserHyperlink()
    {
        RequestNavigate += OnRequestNavigate;
    }

    private void OnRequestNavigate(object sender, RequestNavigateEventArgs e)
    {
        Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
        e.Handled = true;
    }
}

Solution 6 - C#

Note too that Hyperlink does not have to be used for navigation. You can connect it to a command.

For example:

<TextBlock>
  <Hyperlink Command="{Binding ClearCommand}">Clear</Hyperlink>
</TextBlock>

Solution 7 - C#

I used the answer in this question and I got an issue with it.

It return exception: {"The system cannot find the file specified."}

After a bit of investigation. It turns out that if your WPF application is CORE, you need to change UseShellExecute to true.

This is mentioned in Microsoft docs: > true if the shell should be used when starting the process; false if > the process should be created directly from the executable file. The > default is true on .NET Framework apps and false on .NET Core apps.

So to make this work you need to added UseShellExecute and set it to true:

Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri){ UseShellExecute = true });

Solution 8 - C#

I liked Arthur's idea of a reusable handler, but I think there's a simpler way to do it:

private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
    if (sender.GetType() != typeof (Hyperlink))
	    return;
    string link = ((Hyperlink) sender).NavigateUri.ToString();
    Process.Start(link);
}

Obviously there could be security risks with starting any kind of process, so be carefull.

Solution 9 - C#

One of the most beautiful ways in my opinion (since it is now commonly available) is using behaviours.

It requires:

  • nuget dependency: Microsoft.Xaml.Behaviors.Wpf
  • if you already have behaviours built in you might have to follow this guide on Microsofts blog.

xaml code:

xmlns:Interactions="http://schemas.microsoft.com/xaml/behaviors"

AND

<Hyperlink NavigateUri="{Binding Path=Link}">
	<Interactions:Interaction.Behaviors>
		<behaviours:HyperlinkOpenBehaviour ConfirmNavigation="True"/>
	</Interactions:Interaction.Behaviors>
	<Hyperlink.Inlines>
		<Run Text="{Binding Path=Link}"/>
	</Hyperlink.Inlines>
</Hyperlink>

behaviour code:

using System.Windows;
using System.Windows.Documents;
using System.Windows.Navigation;
using Microsoft.Xaml.Behaviors;

namespace YourNameSpace
{
	public class HyperlinkOpenBehaviour : Behavior<Hyperlink>
	{
		public static readonly DependencyProperty ConfirmNavigationProperty = DependencyProperty.Register(
			nameof(ConfirmNavigation), typeof(bool), typeof(HyperlinkOpenBehaviour), new PropertyMetadata(default(bool)));

		public bool ConfirmNavigation
		{
			get { return (bool) GetValue(ConfirmNavigationProperty); }
			set { SetValue(ConfirmNavigationProperty, value); }
		}

		/// <inheritdoc />
		protected override void OnAttached()
		{
			this.AssociatedObject.RequestNavigate += NavigationRequested;
			this.AssociatedObject.Unloaded += AssociatedObjectOnUnloaded;
			base.OnAttached();
		}

		private void AssociatedObjectOnUnloaded(object sender, RoutedEventArgs e)
		{
			this.AssociatedObject.Unloaded -= AssociatedObjectOnUnloaded;
			this.AssociatedObject.RequestNavigate -= NavigationRequested;
		}

		private void NavigationRequested(object sender, RequestNavigateEventArgs e)
		{
			if (!ConfirmNavigation || MessageBox.Show("Are you sure?", "Question", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
			{
				OpenUrl();
			}

			e.Handled = true;
		}

		private void OpenUrl()
		{
//			Process.Start(new ProcessStartInfo(AssociatedObject.NavigateUri.AbsoluteUri));
			MessageBox.Show($"Opening {AssociatedObject.NavigateUri}");
		}

		/// <inheritdoc />
		protected override void OnDetaching()
		{
			this.AssociatedObject.RequestNavigate -= NavigationRequested;
			base.OnDetaching();
		}
	}
}

Solution 10 - C#

Hope this help someone as well.

using System.Diagnostics;
using System.Windows.Documents;

namespace Helpers.Controls
{
    public class HyperlinkEx : Hyperlink
    {
        protected override void OnClick()
        {
            base.OnClick();

            Process p = new Process()
            {
                StartInfo = new ProcessStartInfo()
                {
                    FileName = this.NavigateUri.AbsoluteUri
                }
            };
            p.Start();
        }
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionArsen ZahrayView Question on Stackoverflow
Solution 1 - C#eanderssonView Answer on Stackoverflow
Solution 2 - C#Arthur NunesView Answer on Stackoverflow
Solution 3 - C#Ivan IčinView Answer on Stackoverflow
Solution 4 - C#H.B.View Answer on Stackoverflow
Solution 5 - C#Lu55View Answer on Stackoverflow
Solution 6 - C#Drew NoakesView Answer on Stackoverflow
Solution 7 - C#Maytham FahmiView Answer on Stackoverflow
Solution 8 - C#GrantView Answer on Stackoverflow
Solution 9 - C#DblView Answer on Stackoverflow
Solution 10 - C#jaysonragasaView Answer on Stackoverflow