Esempio di utilizzo del collegamento ipertestuale in WPF


160

Ho visto diversi suggerimenti, che è possibile aggiungere collegamenti ipertestuali all'applicazione WPF attraverso il Hyperlinkcontrollo.

Ecco come sto cercando di usarlo nel mio codice:

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

Ricevo il seguente errore:

La proprietà "Testo" non supporta i valori di tipo "Collegamento ipertestuale".

Che cosa sto facendo di sbagliato?

Risposte:


331

Se si desidera che l'applicazione apra il collegamento in un browser Web, è necessario aggiungere un HyperLink con l' evento RequestNavigate impostato su una funzione che apre a livello di programmazione un browser Web con l'indirizzo come parametro.

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

Nel code-behind è necessario aggiungere qualcosa di simile a questo per gestire l'evento RequestNavigate.

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

Inoltre avrai bisogno delle seguenti importazioni.

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

Sembrerebbe così nella tua applicazione.

oO


6
Nota: RequestNavigateEventArgsè nello System.Windows.Navigationspazio dei nomi.
Ben

2
Grazie, ma esiste un modo per specificare il testo del link ("Fare clic qui" in questo caso) tramite associazione?
Agent007,

6
Inserisci semplicemente un blocco testo all'interno del collegamento ipertestuale e lega la proprietà del testo
KroaX

2
Nota # 2: Processe ProcessStartInfosono entrambi nello System.Diagnosticsspazio dei nomi.
user2023861

3
Nota importante : è necessario disporre di un NavigateUri non vuoto o di un RequestNavigate non chiamato mai
MuiBienCarlota

60

Oltre alla risposta di Fuji, possiamo rendere riutilizzabile il gestore trasformandolo in una proprietà collegata:

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 usalo in questo modo:

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

Soluzione elegante. Grazie
Jeson Martajaya,

30

Hyperlinknon è un controllo, è un elemento di contenuto di flusso , puoi usarlo solo in controlli che supportano il contenuto di flusso, come un TextBlock. TextBoxeshanno solo testo normale.


26

Se vuoi localizzare la stringa in un secondo momento, queste risposte non sono sufficienti, suggerirei qualcosa del tipo:

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

21

IMHO il modo più semplice è usare un nuovo controllo ereditato da 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

Nota anche che Hyperlinknon deve essere utilizzato per la navigazione. Puoi collegarlo a un comando.

Per esempio:

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

16

Ho usato la risposta in questa domanda e ho avuto problemi con esso.

Restituisce un'eccezione: {"The system cannot find the file specified."}

Dopo un po 'di indagine. Si scopre che se la vostra applicazione WPF è .CORE è necessario fare UseShellExecutea true.

Questo è menzionato nei documenti Microsoft :

vero se la shell deve essere utilizzata all'avvio del processo; false se il processo deve essere creato direttamente dal file eseguibile. Il valore predefinito è true nelle app .NET Framework e false nelle app .NET Core.

Quindi per farlo funzionare devi aggiungere UseShellExecutea true:

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

Ho avuto lo stesso problema e sono venuto qui per vedere come risolverlo, ma persiste ancora con UseShelExecute = truequalche idea del perché?
High Plains Grifter

@HighPlainsGrifter quindi solo per capire che hai fatto UseShelExecute = true ma hai ancora lo stesso problema? in tal caso prova a eseguire Visual Studio in modalità amministratore (esegui come amministratore) Penso che questo processo debba accedere alle risorse che richiedono il privilegio di amministratore. E questa cosa vera è valida solo per i progetti .core. fammi sapere se questo aiuta così posso aggiornare la mia risposta.
Maytham-ɯɐɥʇʎɐɯ

sì, sono in esecuzione come utente amministratore e ho Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true });e ricevo l'errore "System.ComponentModel.Win32Exception: 'Il sistema non riesce a trovare il file specificato'" quando provo a seguire il collegamento ipertestuale
High Plains Grifter

@HighPlainsGrifter non sono sicuro di cosa possa essere, se hai il codice sorgente sto bene a passare un po 'di tempo a debug ma non prometto nulla. :)
maytham-ɯɐɥʇʎɐɯ

Purtroppo non è un codice condivisibile, per ora non ho dovuto usare un collegamento ipertestuale piuttosto che tenere il progetto in sospeso. Grazie comunque.
High Plains Grifter

4

Mi è piaciuta l'idea di Arthur di un gestore riutilizzabile, ma penso che ci sia un modo più semplice per farlo:

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

Ovviamente potrebbero esserci rischi per la sicurezza con l'avvio di qualsiasi tipo di processo, quindi fai attenzione.


1

Spero che questo aiuti anche qualcuno.

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

Uno dei modi più belli secondo me (dato che ora è comunemente disponibile) sta usando i comportamenti.

Richiede:

  • dipendenza da nuget: Microsoft.Xaml.Behaviors.Wpf
  • se hai già comportamenti incorporati potresti dover seguire questa guida sul blog di Microsofts.

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

codice di 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();
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.