Reputation: 25367
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?
Upvotes: 185
Views: 253038
Reputation: 33437
I used the marked answer in this question and I had an issue with it.
It returns an exception: {"The system cannot find the file specified."}
After a bit of investigation. It turns out that if your WPF application is a .NET Core application, 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 add UseShellExecute
and set it to true
:
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri){ UseShellExecute = true });
Upvotes: 27
Reputation: 478
XMAL binding example is as flowing:
<TextBlock>
<Hyperlink Command="local:MyCommands.ViewDetails" CommandParameter="{Binding}">
<TextBlock Text="{Binding Path=Name}"/>
</Hyperlink>
</TextBlock>
Upvotes: 1
Reputation: 22333
IMHO the simplest way is to use new class 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;
}
}
Upvotes: 25
Reputation: 9990
If you want to localize string later, then those answers aren't enough, I would suggest something like:
<TextBlock>
<Hyperlink NavigateUri="https://speechcentral.net/">
<Hyperlink.Inlines>
<Run Text="Click here"/>
</Hyperlink.Inlines>
</Hyperlink>
</TextBlock>
Upvotes: 34
Reputation: 7058
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>
Upvotes: 74
Reputation: 26362
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://learn.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:
Upvotes: 384
Reputation: 5914
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();
}
}
}
Upvotes: 4
Reputation: 311315
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>
Upvotes: 28
Reputation: 1076
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();
}
}
}
Upvotes: 1
Reputation: 185410
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.
Upvotes: 32
Reputation: 1648
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.
Upvotes: 4