WPF MVVM绑定超链接RequestNavigate到视图模型

8

在 WPF 窗体上,我有一个超链接,当点击后应该聚合数据库中的一些数据,然后重定向到内部网页。

当前 XAML 如下所示:

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

要执行数据库操作,需要使用View.xaml.cs中的Hyperlink_RequestNavigate方法。

它看起来像这样:

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

我不喜欢这个机制在这里,希望将其移动到视图模型中。

我的尝试是向视图模型属性添加内容。

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

并且在XAML中将绑定更改为

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

但是它开始给我抛出强制转换异常。

将此机制从View移动到ViewModel的最合适方法是什么?


1
使用Command代替RequestNavigateRequestNavigate是一个事件处理程序,它需要在代码后台实现,而Command将期望绑定。 - XAMlMAX
@XAMlMAX 如果我使用命令,它可以正常运行并且没有错误地打开窗口,但是当我单击链接时,什么也不会发生,它不会跳转到 setter。 - Matas Vaitkevicius
你如何初始化你的NavigateToTakePayment命令?在初始化时才使用setter,UI需要调用getter来调用命令的Execute方法。希望这样说得清楚,如果不行的话,我会发布一个答案来帮助解释我的评论。 - XAMlMAX
我正在使用在初始化时为null的后备字段,你认为这是导致getter在点击时没有被调用的问题吗? - Matas Vaitkevicius
是的。我在我的WPF应用程序中使用RelayCommand。您需要在构造函数中初始化它,然后界面将调用后备字段的Execute(object parameter)方法。 - XAMlMAX
@XAMlMAX,这就是解决方法。与其调用setter,它会调用设置为后备属性的ICommand实现的public void Execute(object parameter)。谢谢。 - Matas Vaitkevicius
3个回答

9

HyperLink有些问题。它不支持命令绑定。

通过一个附加属性可以将命令绑定强行添加到它上面,但更简单的方法是修改一个按钮来实现相同的功能。

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

然后像这样使用超链接:
<Button Command="{Binding OpenHttpLinkCommand}" Content="www.google.com" 
        Style="{StaticResource HyperlinkStyledButton}" ToolTip="Some custom tooltip"/>

假设标准MVVM绑定正常工作:
在ViewModel中:
public ICommand OpenHttpLinkCommand { get; }

在ViewModel的构造函数中:

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

打开浏览器并访问链接的命令:

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

1
谢谢,Contango - 这对我来说完美地解决了问题。 - Tom
请查看本主题其他地方的我的帖子,了解如何使“Hyperlink”支持命令绑定。 - Jinlye

7
你的应用程序问题在于在使用之前未初始化ICommand
我有一个如下的命令实现:
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();
        }
    }  

现在我的ViewModel中正在初始化这个:
NavigateToTakePayment = new RelayCommand(navigateToTakePayment CommandMethod);//it also can take canExecute method if you need a condition before executing.  

然后在您的XAML中,您可以像这样使用它:

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

顺便说一下:当您需要禁用超链接时,请实现canexecute方法,然后它将自动完成。如果您需要更多信息,我会更新我的答案。
编码愉快!


0

虽然这是一个旧的帖子,但对于像我一样仍在遇到此问题的人来说,Hyperlink确实支持ICommandBinding,但您需要一个派生类来实现:

using System.Windows.Documents;

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

然后在您的 XAML 中,您可以像将 Button 绑定到 ViewModel 中的 ICommand 一样使用它,在本示例中称为 HyperlinkClickerCommand。类似于这样:

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

在你的 XAML 文件的顶部添加类似以下代码:

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

其中HyperlinkClickerCommand是您在ViewModel中绑定的命令。您上面的超链接将触发绑定命令的Execute,并遵守其CanExecute


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接