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?
The Hyperlink element is an inline-level content element that is used to add a hyperlink to a FlowDocument contents. You can add hyperlink support to any Inline element. The Hyperlink element is an inline-level content element that is used to add a hyperlink to a FlowDocument contents.
In XAML, the creation of content elements is implicit, so you can add the link text directly to the Hyperlink, and the Hyperlink directly to the TextBlock element. The Span element with the xml:space="preserve" attribute is used to preserve white space around the hyperlink.
Hello, Link button in ASP.NET at runtime works like a hyperlink, but you can write certain code with the use of a link button by generating its click event, in short the link button control looks like a hyperlink control but works like a button... Here is some code that might help you out.
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:
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>
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>
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.
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>
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;
}
}
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 });
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.
One of the most beautiful ways in my opinion (since it is now commonly available) is using behaviours.
It requires:
Microsoft.Xaml.Behaviors.Wpf
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();
}
}
}
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();
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With