Exemple d'utilisation d'un lien hypertexte dans WPF

160

J'ai vu plusieurs suggestions, que vous pouvez ajouter un lien hypertexte vers l'application WPF via le Hyperlinkcontrôle.

Voici comment j'essaye de l'utiliser dans mon 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>

J'obtiens l'erreur suivante:

La propriété "Texte" ne prend pas en charge les valeurs de type "Lien hypertexte".

Qu'est-ce que je fais mal?

Arsen Zahray
la source

Réponses:

331

Si vous souhaitez que votre application ouvre le lien dans un navigateur Web, vous devez ajouter un HyperLink avec l' événement RequestNavigate défini sur une fonction qui ouvre par programme un navigateur Web avec l'adresse comme paramètre.

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

Dans le code-behind, vous devez ajouter quelque chose de similaire pour gérer l'événement RequestNavigate.

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

En outre, vous aurez également besoin des importations suivantes.

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

Cela ressemblerait à ceci dans votre application.

oO

eandersson
la source
6
Remarque: se RequestNavigateEventArgstrouve dans l' System.Windows.Navigationespace de noms.
Ben
2
Merci, mais y a-t-il un moyen de spécifier le texte du lien ("Cliquez ici" dans ce cas) par liaison?
Agent007
6
Mettez simplement un bloc de texte dans le lien hypertexte à nouveau et liez la propriété de texte
KroaX
2
Remarque n ° 2: Processet ProcessStartInfosont tous les deux dans l' System.Diagnosticsespace de noms.
user2023861
3
Remarque importante : vous devez avoir un NavigateUri non vide ou un événement RequestNavigate n'est jamais appelé
MuiBienCarlota
60

En plus de la réponse de Fuji, nous pouvons rendre le gestionnaire réutilisable en le transformant en propriété attachée:

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

Et utilisez-le comme ceci:

<TextBlock>
<Hyperlink NavigateUri="http://stackoverflow.com" custom::HyperlinkExtensions.IsExternal="true">
       Click here
    </Hyperlink>
 </TextBlock>
Arthur Nunes
la source
Solution élégante. Merci
Jeson Martajaya
30

Hyperlinkn'est pas un contrôle, c'est un élément de contenu de flux , vous ne pouvez l'utiliser que dans des contrôles qui prennent en charge le contenu de flux, comme un TextBlock. TextBoxesn'ont que du texte brut.

HB
la source
26

Si vous souhaitez localiser la chaîne plus tard, ces réponses ne suffisent pas, je suggérerais quelque chose comme:

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

À mon humble avis, le moyen le plus simple est d'utiliser un nouveau contrôle hérité 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
la source
16

Notez également qu'il Hyperlinkn'est pas nécessaire d'utiliser pour la navigation. Vous pouvez le connecter à une commande.

Par exemple:

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

J'ai utilisé la réponse dans cette question et j'ai eu un problème avec elle.

Il renvoie une exception: {"The system cannot find the file specified."}

Après un peu d'enquête. Il se avère que si votre application WPF est .CORE que vous devez faire UseShellExecutepour true.

Ceci est mentionné dans la documentation Microsoft :

true si le shell doit être utilisé lors du démarrage du processus; false si le processus doit être créé directement à partir du fichier exécutable. La valeur par défaut est true sur les applications .NET Framework et false sur les applications .NET Core.

Donc, pour que cela fonctionne, vous devez ajouter UseShellExecuteà true:

Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri){ UseShellExecute = true });
maytham-ɯɐɥʇʎɐɯ
la source
J'ai eu ce même problème et je suis venu ici pour voir comment le résoudre, mais il persiste toujours avec UseShelExecute = trueune idée pourquoi?
High Plains Grifter
@HighPlainsGrifter donc juste pour comprendre que vous avez fait UseShelExecute = true mais que vous avez toujours le même problème? si tel est le cas, essayez d'exécuter votre studio visuel en mode administrateur (exécuté en tant qu'administrateur). Et cette chose vraie n'est valable que pour les projets .core. faites-moi savoir si cela aide afin que je puisse mettre à jour ma réponse.
maytham-ɯɐɥʇʎɐɯ
oui, je cours en tant qu'utilisateur administrateur et j'ai Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true });et j'obtiens l'erreur "System.ComponentModel.Win32Exception: 'Le système ne trouve pas le fichier spécifié'" lorsque j'essaie de suivre le lien hypertexte
High Plains Grifter
@HighPlainsGrifter ne sait pas ce que cela peut être, si vous avez du code source, je suis sur le point de passer du temps à le déboguer mais je ne promet rien. :)
maytham-ɯɐɥʇʎɐɯ
Code pas vraiment partageable malheureusement - j'ai juste dû ne pas utiliser d'hyperlien pour l'instant plutôt que de retarder le projet. Merci quand même.
High Plains Grifter
4

J'ai aimé l'idée d'Arthur d'un gestionnaire réutilisable, mais je pense qu'il existe un moyen plus simple de le faire:

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

De toute évidence, il pourrait y avoir des risques de sécurité avec le démarrage de tout type de processus, alors soyez prudent.

Subvention
la source
1

J'espère que cela aidera aussi quelqu'un.

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
la source
0

L'un des plus beaux moyens à mon avis (car il est maintenant couramment disponible) est d'utiliser les comportements.

Cela demande:

  • dépendance nuget: Microsoft.Xaml.Behaviors.Wpf
  • si vous avez déjà des comportements intégrés, vous devrez peut-être suivre ce guide sur le blog Microsofts.

code xaml:

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

ET

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

code de comportement:

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