在WPF中使用超链接的示例


160

我看到了一些建议,您可以通过Hyperlink控件将超链接添加到WPF应用程序。

这是我尝试在代码中使用它的方式:

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

我收到以下错误:

属性“文本”不支持“超链接”类型的值。

我究竟做错了什么?

Answers:


331

如果希望您的应用程序在Web浏览器中打开链接,则需要将带有RequestNavigate事件集的HyperLink添加到以编程方式打开以地址为参数的Web浏览器的函数。

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

在后面的代码中,您需要添加与此类似的内容来处理RequestNavigate事件。

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

另外,您还将需要以下导入。

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

在您的应用程序中看起来像这样。

O


6
注意:RequestNavigateEventArgs位于System.Windows.Navigation名称空间中。

2
谢谢,但是有没有办法通过绑定指定链接文本(在这种情况下为“单击此处”)?
Agent007

6
只需将文本块再次放入超链接并绑定文本属性
KroaX 2014年

2
注意#2:ProcessProcessStartInfo都在System.Diagnostics名称空间中。
user2023861 2014年

3
重要说明:您必须拥有一个非空的NavigateUri或事件RequestNavigate永远不会被调用
MuiBienCarlota

60

除了Fuji的响应之外,我们还可以使处理程序可重用,将其转换为附加属性:

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

并像这样使用它:

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

优雅的解决方案。谢谢
Jeson Martajaya

30

Hyperlink不是一个控制,它是一个流内容元素,你只能用它控制哪些支持流的内容,就像一个TextBlockTextBoxes仅具有纯文本。


26

如果您想稍后对字符串进行本地化,那么这些答案还不够,我建议您执行以下操作:

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

21

恕我直言,最简单的方法是使用继承自的新控件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;
    }
}

16

还要注意,Hyperlink不必将其用于导航。您可以将其连接到命令。

例如:

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

16

我在此问题中使用了答案,但对此有疑问。

它返回异常: {"The system cannot find the file specified."}

经过一点调查。事实证明,如果你的WPF应用程序。核心是你需要做UseShellExecutetrue

Microsoft docs中提到了这一点:

如果在启动过程时应使用外壳,则为true;否则为true。如果应直接从可执行文件创建进程,则返回false。在.NET Framework应用程序上,默认值为true;在.NET Core应用程序上,默认值为false。

因此,要进行此工作,您需要添加UseShellExecutetrue

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

我遇到了同样的问题,来到这里看看如何解决它,但是仍然存在UseShelExecute = true任何想法,为什么?
High Plains Grifter,

@HighPlainsGrifter只是为了了解您使UseShelExecute = true,但仍然存在相同的问题?如果是这种情况,请尝试以管理员模式运行Visual Studio(以管理员身份运行),我认为此过程需要访问需要管理员权限的资源。而且,这仅适用于.core项目。让我知道是否有帮助,以便我更新我的答案。
maytham-ɯɐɥʇʎɐɯ

是的,我以管理员用户身份运行,并Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true });在尝试跟随超链接时遇到错误“ System.ComponentModel.Win32Exception:'系统找不到指定的文件'”
High Plains Grifter

@HighPlainsGrifter不知道它可以是什么,如果您有源代码,我很乐意花一些时间调试它,但不会承诺任何事情。:)
maytham-ɯɐɥʇʎɐɯ

可悲的是,不是真正可共享的代码-我现在暂时不必使用超链接,而不必承担该项目。不管怎么说,还是要谢谢你。
High Plains Grifter

4

我喜欢Arthur关于可重用处理程序的想法,但是我认为有一种更简单的方法可以做到:

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

显然,启动任何类型的过程都可能会带来安全风险,因此请务必小心。


1

希望这也对某人有所帮助。

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();
        }
    }
}

0

我认为(因为它现在普遍可用)是最漂亮的方式之一就是使用行为。

这个需要:

  • nuget依赖项: Microsoft.Xaml.Behaviors.Wpf
  • 如果您已经内置了行为,则可能必须遵循Microsoft博客上的本指南

XAML代码:

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

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

行为代码:

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();
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.