WPF MVVM bind Hyperlink RequestNavigate to View model

Viewed 8805

On WPF form I have a hyperlink that when clicked is supposed aggregate some data in database before redirecting to internal web page.

Currently XAML looks following:

<Hyperlink RequestNavigate="Hyperlink_RequestNavigate" IsEnabled="{Binding CanTakePayment}">
  Launch Payments Portal
</Hyperlink>

to do the db stuff Hyperlink_RequestNavigate method is used, that resides in View.xaml.cs

It looks something like:

private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
    var transactionReference = GetToken(100M, "13215", "product");
    var url = string.Format("{0}New?transactionReference={1}", Settings.Default.PaymentUrlWebsite, transactionReference);
    e.Handled = true;
}

I don't like this mechanism being here and would prefer to move it to View model.

What I tried to do is add to ViewModel property

public ICommand NavigateToTakePayment       
{
    get { return _navigateToTakePayment; }
    set { _navigateToTakePayment = value; }
}

and in XAML change binding to

<Hyperlink RequestNavigate="{Binding Path=NavigateToTakePayment}" IsEnabled="{Binding CanTakePayment}"> 
   Launch Payments Portal
</Hyperlink>

but it started giving me cast exceptions.

What is most appropriate way to move this mechanism from View to ViewModel?

3 Answers

HyperLink is a bit of a problem child. It does not support command binding.

It's possible to shoehorn support for command binding into it with an attached property, but it's easier to just modify a button to do the same thing.

<Style TargetType="Button" x:Key="HyperlinkStyledButton">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="Button">
        <TextBlock Foreground="DodgerBlue"
                   Text="{TemplateBinding Content}"
                   TextDecorations="Underline" 
                   Cursor="Hand" />
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

Then use the hyperlink like this:

<Button Command="{Binding OpenHttpLinkCommand}" Content="www.google.com" 
        Style="{StaticResource HyperlinkStyledButton}" ToolTip="Some custom tooltip"/>

Assuming that standard MVVM binding is working correctly:

In the ViewModel:

public ICommand OpenHttpLinkCommand { get; }

In the ViewModel constructor:

this.OpenHttpLinkCommand = new DelegateCommand(this.OnOpenHttpLinkCommand);

And the command to open the browser with a link:

private void OnOpenHttpLinkCommand()
{
    try
    {
        System.Diagnostics.Process.Start("http://www.google.com/");
    }
    catch (Exception)
    {
        // TODO: Error.
    }
}

Old thread, but for those still coming across it like me, Hyperlink does support ICommandBinding, but you need a derived class to do so:

using System.Windows.Documents;

public class HyperlinkCommander : Hyperlink
{
    protected override void OnClick()
    {
        Command.Execute(null);
    }
}

Then in your xaml use it just like you would when binding a Button to your ICommand in your ViewModel, in this case called HyperlinkClickerCommand. Something like this:

<localControls:HyperlinkCommander Command="{Binding HyperlinkClickerCommand}" >
    Click me!
</localControls:HyperlinkCommander>

With something like this near the top of your xaml:

<Window
    ... 
    xmlns:localControls="clr-namespace:TestProject.Controls"
    ...
>

Where HyperlinkClickerCommand is your command in your ViewModel that you are binding to. Your hyperlink as above will trigger the Execute of the bound command, and honour the CanExecute of it.

Related