PrismMVVM功能实现(通知、命令)

ZHIZRL / 2023-09-03 / 原文

常见的MVVM框架,基本围绕ICommand、INotifyPropertyChanged的封装实现绑定、通知等功能;而对于不同框架,在实现相同功能上,只是表现的形式有所不同,下图列举几种常见框架的功能区别:

功能\框架 Prism MVVMLight Micorsoft.Tookit.Mvvm
通知 BindableBase ViewModelBase ObservableObject
命令 DelegateCommand RelayCommand Async/RelayCommand 
事件聚合器 IEventAggregator IMessenger IMessenger
模块化 × ×
容器 × ×
依赖注入 × ×
导航 × ×
对话服务 × ×

通知(BindableBase)

BindableBase 主要实现 INotifyPropertyChanged 接口,用于监听属性更改时提供通知,实现方式为在属性 SET 中添加 RaisePropertyChanged() 方法,具体过程如下:

运行效果如下:

单一命令(DelegateCommand)

ViewModel 可以通过将命令接口实现为命令对象 (实现 ICommand 接口的对象)。视图与命令的交互以声明的方式定义,而无需在视图的代码隐藏文件中使用复杂的事件处理代码。实现 ICommand 接口很简单,Prism 提供了 DelegateCommand 此接口的实现,您可以在应用程序中轻松使用。

创建委托命令 

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

//无参、仅Excute构造方式
public class ArticleViewModel
{
    public DelegateCommand SubmitCommand { get; private set; }
    public ArticleViewModel()
    {
    SubmitCommand = new DelegateCommand(Submit);
    }
    void Submit()
    {
        //执行方法
    }
}
//有参、Execute和CanExecut同时构造方式
public class ArticleViewModel
{
    public DelegateCommand SubmitCommand { get; private set; }
    public ArticleViewModel()
    {
    SubmitCommand = new DelegateCommand<object>(Submit, CanSubmit);
    }
    void Submit(object parameter)
    {
    //执行方法
    }
    bool CanSubmit(object parameter)
    {
    return true;
    }
}
//无参、Execute和CanExecut同时构造方式
public class ArticleViewModel
{
    public DelegateCommand SubmitCommand { get; private set; }
    public ArticleViewModel()
    {
    SubmitCommand = new DelegateCommand(Submit, CanSubmit);
    }
    void Submit()
    {
    //implement logic
    }
    bool CanSubmit()
    {
    return true;
    }
}

从视图调用命令

使用 CommandParameter 属性定义命令参数。定义的参数类型在 DelegateCommand 泛型声明中指定。当用户与该控件交互时,该控件将自 动调用目标命令,并且命令参数(如果提供)将作为参数传递给命令的 Execute 方法。 

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

创建异步委托命令

在当今的 async / await 使用中,在委托中调用异步方法 Execute 是非常普遍的要求。每个人的第一直觉是他们需要一个 AsyncCommand ,但这种假设是错误的。 ICommand 本质上是同步的, Execute 并且 CanExecute 委托应该被视为事件。这意味着这 async void 是用于命令的完全有效的语法。有两种方法可以将异步方法与 DelegateCommand 结合使用:

方式1 

public class ArticleViewModel
{
    public DelegateCommand SubmitCommand { get; private set; }
    public ArticleViewModel()
    {
    SubmitCommand = new DelegateCommand(Submit);
    }
    async void Submit()
    {
    await SomeAsyncMethod();
    }
}

方式2

public class ArticleViewModel
{
    public DelegateCommand SubmitCommand { get; private set; }
    public ArticleViewModel()
    {
    SubmitCommand = new DelegateCommand(async ()=> await Submit());
    }
    Task Submit()
    {
    return SomeAsyncMethod();
    }
}

复合命令(CompositeCommand) 

在许多情况下,视图模型定义的命令将绑定到关联视图中的控件,以便用户可以直接从视图中调用该命令。但是,在某些情况下,您可能希望能够从应用程序 UI 的父视图中的控件调用一个或多个视图模型上的命令。

Prism 通过 CompositeCommand 类支持这种情况 。

该类 CompositeCommand 表示由多个子命令组成的命令。当调用复合命令时,依次调用它的每个子命令。在您需要将一组命令表示为 UI 中的单个命令或您希望调用多个命令来实现逻辑命令的情况下,它很有用。

该类 CompositeCommand 维护一个子命令( DelegateCommand 实例)列表。类的 Execute 方法 CompositeCommand 简单地依次调用Execute 每个子命令的方法。该 CanExecute 方法类似地调用 CanExecute 每个子命令的方法,但如果任何子命令无法执行,该 CanExecute 方法将返回 false 。也就是说,默认情况下,a CompositeCommand 只有在所有子命令都可以执行时才能执行。

创建复合命令

复合命令绑定到视图

在视图中,将“CompositeButton”按钮与到 TestCompositeCommand 命令绑定。

运行结果如下:

取消注册命令

如前面的示例所示,子命令是使用该 TestCompositeCommand.RegisterCommand 方法注册的。但是,当您不再希望响应 TestCompositeCommand或者您正在销毁 View/ViewModel 以进行垃圾回收时,您应该使用该 TestCompositeCommand.UnregisterCommand 方法取消注册子命令。

/// 取消命令注册例子
public void Destroy()
{
    TestCompositeCommand.UnregisterCommand(TestCommand1);
}

通过依赖注入、静态类来实现 CompositeCommand 全局可用/绑定等用法,请查阅Prism官方文档。