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?

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


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>

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.


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>

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

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>

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

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.


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

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();
        }
    }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.