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?
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.
}
}
Problem with your app is that the ICommand
is not initialized before use.
I have a Command implementation like so:
public class RelayCommand : ICommand
{
Predicate<object> _canExecute;
Action<object> _execute;
bool _defaultBehaviourForCanExecute;
public RelayCommand(Action<object> execute, bool defaultBehaviourForCanExecute = true, Predicate<object> canExecute = null)
{
_canExecute = canExecute;
_execute = execute;
_defaultBehaviourForCanExecute = defaultBehaviourForCanExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute != null)
{
Logger.LogInformation("Evaluating can execute method for " + _canExecute.Method.DeclaringType + "->"+_canExecute.Method.Name);
return _canExecute.Invoke(parameter);
}
return _defaultBehaviourForCanExecute;
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, new EventArgs());
}
public void Execute(object parameter)
{
Logger.LogInformation("Executing command method for " + _execute.Method.DeclaringType + "->" + _execute.Method.Name);
_execute.Invoke(parameter);
RaiseCanExecuteChanged();
}
}
Now this is being initialized in my ViewModel
like so:
NavigateToTakePayment = new RelayCommand(navigateToTakePayment CommandMethod);//it also can take canExecute method if you need a condition before executing.
then in your xaml you use it like this:
<Hyperlink RequestNavigate="{Binding Path=NavigateToTakePayment}" IsEnabled="{Binding CanTakePayment}">
Launch Payments Portal
</Hyperlink>
BTW: when you Hyperlink needs to be disabled implement a canexecute
method
and then it will be done automatically. If you need more info I will update my answer.
Happy coding
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With