C# Prism:命令(Commanding)

翻译自原文: https://prismlibrary.com/docs/commands/commanding.html

除了为视图提供要显示或编辑的数据的访问权限外,ViewModel 还可能定义一个或多个用户可以执行的操作或行为。用户可以通过 UI 执行的操作或行为通常被定义为命令。命令提供了一种方便的方式来表示可以轻松绑定到 UI 控件的操作或行为。它们封装了实现操作或行为的实际代码,并帮助将其与在视图中的实际可视表示分离。

命令(Commanding)可以以许多不同的方式在用户与视图交互时进行视觉表示和调用。在大多数情况下,它们是由于鼠标点击而被调用,但它们也可以由于快捷键按键、触摸手势或任何其他输入事件而被调用。视图中的控件与 ViewModel 的命令进行数据绑定,以便用户可以使用控件定义的任何输入事件或手势来调用它们。视图中 UI 控件与命令之间的交互可以是双向的。在这种情况下,命令可以在用户与 UI 交互时被调用,并且在底层命令变为启用或禁用时,UI 可以自动启用或禁用。

ViewModel 可以将命令实现为命令对象(实现 ICommand 接口的对象)。视图与命令的交互可以在视图的代码后台文件中不需要复杂的事件处理代码的情况下声明式定义。例如,某些控件本质上支持命令并提供一个 Command 属性,该属性可以与 ViewModel 提供的 ICommand 对象进行数据绑定。在其他情况下,可以使用命令行为将控件与由 ViewModel 提供的命令方法或命令对象关联起来。

实现 ICommand 接口是简单明了的。Prism 提供了 DelegateCommand 接口的实现,您可以直接在应用程序中使用。

Note:DelegateCommand 可以在位于 Prism.Core NuGet 包中的 Prism.Commands 命名空间中找到。

Prism 的 DelegateCommand 类封装了两个委托,它们各自引用 ViewModel 类中实现的一个方法。它通过调用这些委托来实现 ICommand 接口的 Execute 和 CanExecute 方法。您可以在 DelegateCommand 类的构造函数中指定 ViewModel 方法的委托。例如,以下代码示例展示了如何通过指定 OnSubmit 和 CanSubmit ViewModel 方法的委托来构造代表“提交”命令的 DelegateCommand 实例。然后,该命令通过一个只读属性在视图中公开,该属性返回对 DelegateCommand 的引用。

public class ArticleViewModel
{
    public DelegateCommand<object> SubmitCommand { get; private set; }

    public ArticleViewModel()
    {
        SubmitCommand = new DelegateCommand<object>(Submit, CanSubmit);
    }

    void Submit(object parameter)
    {
        //implement logic
    }

    bool CanSubmit(object parameter)
    {
        return true;
    }
}

当在 DelegateCommand 对象上调用 Execute 方法时,它只是通过在构造函数中指定的委托将调用转发到 ViewModel 类中的方法。同样,当调用 CanExecute 方法时,将调用 ViewModel 类中对应的方法。构造函数中的 CanExecute 方法的委托是可选的。如果未指定委托,则 DelegateCommand 将始终返回 true 以用于 CanExecute。

DelegateCommand 类是一个泛型类型。类型参数指定传递给 Execute 和 CanExecute 方法的命令参数的类型。在上面的示例中,命令参数的类型为 object。Prism 还提供了一个非泛型版本的 DelegateCommand 类,用于在不需要命令参数的情况下使用,其定义如下:

public class ArticleViewModel
{
    public DelegateCommand SubmitCommand { get; private set; }

    public ArticleViewModel()
    {
        SubmitCommand = new DelegateCommand(Submit, CanSubmit);
    }

    void Submit()
    {
        //implement logic
    }

    bool CanSubmit()
    {
        return true;
    }
}
Note:DelegateCommand 有意不使用值类型(int、double、bool 等)。因为 ICommand 需要接收一个 object,如果 T 是一个值类型,在 XAML 初始化命令绑定期间调用 CanExecute(null) 会导致意外的行为。社区曾考虑使用 default(T) 作为解决方案,但被拒绝了,因为实现者无法区分有效值和默认值。如果要使用值类型作为参数,则必须通过使用 DelegateCommand<Nullable<int>> 或缩写语法(DelegateCommand<int?>)将其设置为可空。

从视图中调用DelegateCommand

有许多方法可以将视图中的控件与 ViewModel 提供的命令对象关联起来。某些 WPF、Xamarin.Forms 和 UWP 控件可以通过 Command 属性轻松地数据绑定到命令对象。

<Button Command="{Binding SubmitCommand}" CommandParameter="OrderId"/>

通过CommandParameter属性也可以选择性地定义命令参数。在DelegateCommand<T>的泛型声明中指定了预期参数的类型。当用户与该控件交互时,控件将自动调用目标命令,如果提供了命令参数,则该参数将作为参数传递给命令的Execute方法。在上面的示例中,单击按钮将自动调用SubmitCommand。此外,如果指定了CanExecute委托,则如果CanExecute返回false,则该按钮将自动禁用,如果返回true,则启用该按钮。

触发变更通知

ViewModel 经常需要指示命令的 CanExecute 状态已更改,以便任何绑定到该命令的 UI 控件将更新其启用状态,以反映绑定命令的可用性。DelegateCommand 提供了几种向 UI 发送这些通知的方法。

RaiseCanExecuteChanged

当您需要手动更新绑定 UI 元素的状态时,请使用 RaiseCanExecuteChanged 方法。例如,当 IsEnabled 属性值更改时,我们在属性的 setter 中调用 RaiseCanExecuteChanged 来通知 UI 状态更改。

    private bool _isEnabled;
    public bool IsEnabled
    {
        get { return _isEnabled; }
        set
        {
            SetProperty(ref _isEnabled, value);
            SubmitCommand.RaiseCanExecuteChanged();
        }
    }

ObservesProperty

在某些情况下,当属性值改变时,命令应该发送通知,此时可以使用ObservesProperty方法。当使用ObservesProperty方法时,只要提供的属性的值发生改变,DelegateCommand将自动调用RaiseCanExecuteChanged方法来通知UI状态的改变。

public class ArticleViewModel : BindableBase
{
    private bool _isEnabled;
    public bool IsEnabled
    {
        get { return _isEnabled; }
        set { SetProperty(ref _isEnabled, value); }
    }

    public DelegateCommand SubmitCommand { get; private set; }

    public ArticleViewModel()
    {
        SubmitCommand = new DelegateCommand(Submit, CanSubmit).ObservesProperty(() => IsEnabled);
    }

    void Submit()
    {
        //implement logic
    }

    bool CanSubmit()
    {
        return IsEnabled;
    }
}
Note:你可以在使用ObservesProperty方法时链接多个属性进行观察。示例: ObservesProperty(() => IsEnabled).ObservesProperty(() => CanSave)。

ObservesCanExecute

如果您的 CanExecute 是一个简单的布尔值属性,您可以使用 ObservesCanExecute 方法来消除声明 CanExecute 委托的需要。ObservesCanExecute 不仅会在注册的属性值发生变化时向 UI 发送通知,还会将同一属性用作实际的 CanExecute 委托。

public class ArticleViewModel : BindableBase
{
    private bool _isEnabled;
    public bool IsEnabled
    {
        get { return _isEnabled; }
        set { SetProperty(ref _isEnabled, value); }
    }

    public DelegateCommand SubmitCommand { get; private set; }

    public ArticleViewModel()
    {
        SubmitCommand = new DelegateCommand(Submit).ObservesCanExecute(() => IsEnabled);
    }

    void Submit()
    {
        //implement logic
    }
}
Warning:不要尝试链式注册 ObservesCanExecute 方法。只能观察一个属性作为 CanExecute 委托。

实现一个基于任务的DelegateCommand

在现代的async/await世界中,在Execute委托中调用异步方法是一个非常常见的需求。每个人的第一反应是他们需要一个AsyncCommand,但这个假设是错误的。ICommand本质上是同步的,而Execute和CanExecute委托应该被视为事件。这意味着使用async void对于命令来说是完全有效的语法。使用DelegateCommand使用异步方法有两种方法。

Option 1:

public class ArticleViewModel
{
    public DelegateCommand SubmitCommand { get; private set; }

    public Article ViewModel()
    {
        SubmitCommand = new DelegateCommand(Submit);
    }

    async void Submit()
    {
        await SomeAsyncMethod();
    }
}

Option 2:

public class ArticleViewModel
{
    public DelegateCommand SubmitCommand { get; private set; }

    public Article ViewModel()
    {
        SubmitCommand = new DelegateCommand(async ()=> await Submit());
    }

    Task Submit()
    {
        return SomeAsyncMethod();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dextercai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值