Ejemplo usando Hyperlink en WPF

160

He visto varias sugerencias, que puede agregar un hipervínculo a la aplicación WPF a través del Hyperlinkcontrol.

Así es como estoy tratando de usarlo en mi 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>

Recibo el siguiente error:

La propiedad 'Texto' no admite valores de tipo 'Hipervínculo'.

¿Qué estoy haciendo mal?

Arsen Zahray
fuente

Respuestas:

331

Si desea que su aplicación abra el enlace en un navegador web , debe agregar un HyperLink con el evento RequestNavigate establecido en una función que abre mediante programación un navegador web con la dirección como parámetro.

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

En el código subyacente, deberá agregar algo similar a esto para controlar el evento RequestNavigate.

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

Además, también necesitará las siguientes importaciones.

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

Se vería así en su aplicación.

oO

Eandersson
fuente
66
Nota: RequestNavigateEventArgsestá en el System.Windows.Navigationespacio de nombres.
Ben
2
Gracias, pero ¿hay alguna forma de especificar el texto del enlace ("Haga clic aquí" en este caso) a través del enlace?
Agent007
66
Simplemente coloque un bloque de texto dentro del hipervínculo nuevamente y enlace la propiedad de texto
KroaX
2
Nota # 2: Processy ProcessStartInfoambos están en el System.Diagnosticsespacio de nombres.
user2023861
3
Comentario importante : debe tener un NavigateUri no vacío o el evento RequestNavigate nunca se llama
MuiBienCarlota
60

Además de la respuesta de Fuji, podemos hacer que el controlador sea reutilizable convirtiéndolo en una propiedad adjunta:

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

Y úsalo así:

<TextBlock>
<Hyperlink NavigateUri="http://stackoverflow.com" custom::HyperlinkExtensions.IsExternal="true">
       Click here
    </Hyperlink>
 </TextBlock>
Arthur Nunes
fuente
Solución elegante Gracias
Jeson Martajaya
30

Hyperlinkno es un control, es un elemento de contenido de flujo , solo puede usarlo en controles que admitan contenido de flujo, como a TextBlock. TextBoxessolo tiene texto plano.

media pensión
fuente
26

Si desea localizar la cadena más tarde, entonces esas respuestas no son suficientes, sugeriría algo como:

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

En mi humilde opinión, la forma más sencilla es utilizar un nuevo control heredado 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
fuente
16

Tenga en cuenta también que Hyperlinkno tiene que usarse para la navegación. Puedes conectarlo a un comando.

Por ejemplo:

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

Utilicé la respuesta en esta pregunta y tuve un problema con ella.

Devolver excepción: {"The system cannot find the file specified."}

Después de un poco de investigación. Resulta que si su aplicación WPF es .CORE que necesita para hacer UseShellExecutea true.

Esto se menciona en los documentos de Microsoft :

verdadero si se debe usar el shell al iniciar el proceso; falso si el proceso debe crearse directamente desde el archivo ejecutable. El valor predeterminado es verdadero en las aplicaciones .NET Framework y falso en las aplicaciones .NET Core.

Entonces, para que esto funcione, debe agregar UseShellExecutea true:

Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri){ UseShellExecute = true });
maytham-ɯɐɥʇʎɐɯ
fuente
Tuve este mismo problema y vine aquí para ver cómo solucionarlo, pero aún persiste con UseShelExecute = truealguna idea de por qué.
High Plains Grifter
@HighPlainsGrifter, así que solo para entender que hiciste UseShelExecute = true pero ¿todavía tienes el mismo problema? si ese es el caso, intente ejecutar su estudio visual en modo administrador (ejecutar como administrador) Creo que este proceso necesita acceder a los recursos que requieren privilegios de administrador. Y esto es válido solo para proyectos .core. avíseme si eso ayuda para que pueda actualizar mi respuesta.
maytham-ɯɐɥʇʎɐɯ
sí, me estoy ejecutando como mi usuario administrador y tengo Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true });y aparece el error "System.ComponentModel.Win32Exception: 'El sistema no puede encontrar el archivo especificado'" cuando intento seguir el hipervínculo
High Plains Grifter
@HighPlainsGrifter no estoy seguro de lo que puede ser, si tiene código fuente, estoy dispuesto a pasar algún tiempo depurándolo, pero no prometo nada. :)
maytham-ɯɐɥʇʎɐɯ
Lamentablemente, no es un código que se pueda compartir, simplemente no he tenido que usar un hipervínculo por ahora en lugar de retrasar el proyecto. Gracias de cualquier manera.
High Plains Grifter
4

Me gustó la idea de Arthur de un controlador reutilizable, pero creo que hay una manera más sencilla de hacerlo:

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

Obviamente, podría haber riesgos de seguridad al iniciar cualquier tipo de proceso, así que tenga cuidado.

Conceder
fuente
1

Espero que esto ayude a alguien también.

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

En mi opinión, una de las formas más hermosas (ya que ahora está comúnmente disponible) es mediante el uso de comportamientos.

Requiere:

  • dependencia nuget: Microsoft.Xaml.Behaviors.Wpf
  • Si ya tiene comportamientos incorporados, es posible que deba seguir esta guía en el blog Microsofts.

código xaml:

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

Y

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

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
fuente