Search code examples
c#wpfxamldata-bindingwpf-controls

How to bind a command to Hyperlink in c# wpf?


I know I can create a handler to RequestNavigate event with the following code:

private static readonly ProcessStartInfo s_previewLinkProcessStartInfo = new() { UseShellExecute = true };
private void PreviewLink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
{
    s_previewLinkProcessStartInfo.FileName = e.Uri.AbsoluteUri;
    using (Process.Start(s_previewLinkProcessStartInfo))
        e.Handled = true;
}

The code above works perfectly. But if I use the same code but with a command, it will throw a System.InvalidOperationException: "Failed to convert resource to object.":

public ICommand PreviewLinkNavigateCommand => new RelayCommand(obj =>
{
    s_previewLinkProcessStartInfo.FileName = PreviewLink.NavigateUri.AbsoluteUri;
    using (Process.Start(s_previewLinkProcessStartInfo)) { } // throws a System.InvalidOperationException: "Failed to convert resource to object."
});

Xaml:

<TextBlock Padding="10 0 10 0">
    <Hyperlink x:Name="PreviewLink" FontSize="14" 
               NavigateUri="{Binding SelectedMedia.Source.AbsoluteUri}"
               Command="{Binding PreviewLinkNavigateCommand}">
        Preview link
    </Hyperlink>
</TextBlock>

RelayCommand:

internal class RelayCommand : ICommand
{
    private readonly Action<object?> _execute;
    private readonly Func<object?, bool>? _canExecute;

    public event EventHandler? CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

    public RelayCommand(Action<object?> execute, Func<object?, bool>? canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object? parameter) => _canExecute?.Invoke(parameter) ?? true;
    public void Execute(object? parameter) => _execute(parameter);
}

Solution

  • As BionicCode have said I should bind a Command with a CommandParameter as uri.

    .xaml.cs:

    private static readonly ProcessStartInfo s_previewLinkProcessStartInfo = new() { UseShellExecute = true };
    public ICommand PreviewLinkNavigateCommand => new RelayCommand(obj =>
    {
        s_previewLinkProcessStartInfo.FileName = (string?)obj;
        Process.Start(s_previewLinkProcessStartInfo)?.Dispose();
    });
    

    .xaml

    <TextBlock Padding="10 0 10 0">
        <Hyperlink x:Name="PreviewLink" FontSize="14" 
                   Command="{Binding PreviewLinkNavigateCommand}"
                   CommandParameter="URI">
            Preview link
        </Hyperlink>
    </TextBlock>