Exemplo usando hiperlink no WPF

160

Eu já vi várias sugestões, que você pode adicionar hiperlink ao aplicativo WPF através do Hyperlinkcontrole.

Aqui está como eu estou tentando usá-lo no meu código:

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

Estou recebendo o seguinte erro:

A propriedade 'Text' não suporta valores do tipo 'Hyperlink'.

O que estou fazendo de errado?

Arsen Zahray
fonte

Respostas:

331

Se você deseja que seu aplicativo abra o link em um navegador da Web, adicione um HyperLink com o evento RequestNavigate definido para uma função que programaticamente abre um navegador da Web com o endereço como parâmetro.

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

No code-behind, você precisará adicionar algo semelhante a isso para manipular o evento RequestNavigate.

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

Além disso, você também precisará das seguintes importações.

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

Ficaria assim em seu aplicativo.

oO

eandersson
fonte
6
Nota: RequestNavigateEventArgsestá no System.Windows.Navigationespaço para nome.
12123 Ben
2
Obrigado, mas existe alguma maneira de especificar o texto do link ("Clique aqui" neste caso) através da ligação?
Agent007
6
Basta colocar um TextBlock dentro do Hyperlink novamente e ligar os TextProperty
KroaX
2
Nota # 2: Processe ProcessStartInfoestão ambos no System.Diagnosticsespaço para nome.
precisa saber é o seguinte
3
Observação importante : você deve ter um NavigateUri não vazio ou o evento RequestNavigate nunca é chamado
MuiBienCarlota 7/16
60

Além da resposta da Fuji, podemos tornar o manipulador reutilizável, transformando-o em uma propriedade anexada:

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

E use-o assim:

<TextBlock>
<Hyperlink NavigateUri="http://stackoverflow.com" custom::HyperlinkExtensions.IsExternal="true">
       Click here
    </Hyperlink>
 </TextBlock>
Arthur Nunes
fonte
Solução elegante. Obrigado
Jeson Martajaya 19/11/19
30

Hyperlinknão é um controle, é um elemento de conteúdo de fluxo , você pode usá-lo apenas em controles que suportam conteúdo de fluxo, como a TextBlock. TextBoxespossui apenas texto sem formatação.

HB
fonte
26

Se você deseja localizar a string mais tarde, essas respostas não são suficientes, sugeriria algo como:

<TextBlock>
    <Hyperlink NavigateUri="http://labsii.com/">
       <Hyperlink.Inlines>
            <Run Text="Click here"/>
       </Hyperlink.Inlines>
   </Hyperlink>
</TextBlock>
Ivan Ičin
fonte
21

IMHO a maneira mais simples é usar o novo controle herdado de 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;
    }
}
Lu55
fonte
16

Observe também que Hyperlinknão precisa ser usado para navegação. Você pode conectá-lo a um comando.

Por exemplo:

<TextBlock>
  <Hyperlink Command="{Binding ClearCommand}">Clear</Hyperlink>
</TextBlock>
Drew Noakes
fonte
16

Eu usei a resposta nesta pergunta e tive problemas com ela.

Ele retorna exceção: {"The system cannot find the file specified."}

Depois de um pouco de investigação. Acontece que, se seu aplicativo WPF for .CORE, você precisará fazê UseShellExecute-lo true.

Isso é mencionado nos documentos da Microsoft :

true se o shell deve ser usado ao iniciar o processo; false se o processo deve ser criado diretamente a partir do arquivo executável. O padrão é verdadeiro nos aplicativos .NET Framework e falso nos aplicativos .NET Core.

Então, para fazer este trabalho você precisa adicionado UseShellExecutepara true:

Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri){ UseShellExecute = true });
maytham-ɯɐɥʇʎɐɯ
fonte
Eu tive esse mesmo problema e vim aqui para ver como corrigi-lo, mas ele ainda persiste com UseShelExecute = truealguma idéia do porquê?
High Plains Grifter
@HighPlainsGrifter, apenas para entender que você fez UseShelExecute = true, mas ainda tem o mesmo problema? se for esse o caso, tente executar seu visual studio no modo de administrador (execute como administrador). Acho que esse processo precisa acessar os recursos que exigiam privilégios de administrador. E essa coisa verdadeira é válida apenas para projetos .core. deixe-me saber se isso ajuda para que eu possa atualizar minha resposta.
maytham-ɯɐɥʇʎɐɯ
sim, estou executando como meu usuário administrador Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true });e recebo o erro "System.ComponentModel.Win32Exception: 'O sistema não pode encontrar o arquivo especificado'" quando tento seguir o hiperlink
High Plains Grifter
@HighPlainsGrifter não tem mais certeza do que pode ser, se você tiver o código-fonte, estou ansioso para passar algum tempo depurando-o, mas não prometo nada. :)
maytham-ɯɐɥʇʎɐɯ
Infelizmente, não é um código compartilhável - apenas agora não uso um hiperlink em vez de manter o projeto em andamento. Obrigado de qualquer maneira.
High Plains Grifter
4

Gostei da ideia de Arthur de manipulador reutilizável, mas acho que há uma maneira mais simples de fazer isso:

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

Obviamente, pode haver riscos de segurança ao iniciar qualquer tipo de processo, portanto, tenha cuidado.

Conceder
fonte
1

Espero que isso ajude alguém também.

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

Uma das maneiras mais bonitas na minha opinião (já que agora está disponível) é usar comportamentos.

Isso requer:

  • dependência de pepita: Microsoft.Xaml.Behaviors.Wpf
  • se você já possui comportamentos incorporados, pode ser necessário seguir este guia no blog da Microsofts.

código xaml:

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

E

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

código de comportamento:

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