【Prism005】命令
简介
命令提供了一种方便的方式来表示可以轻松绑定到UI中控件的动作或操作。它们封装了实现动作或操作的实际代码,并有助于使其与视图中的实际视觉表示分离。
创建命令
public class ArticleViewModel { public DelegateCommand SubmitCommand { get; private set; } public ArticleViewModel() { SubmitCommand = new DelegateCommand<object>(Submit, CanSubmit); } void Submit(object parameter) { //implement logic } bool CanSubmit(object parameter) { return true; } }
命令绑定
<Button Command="{Binding SubmitCommand}" CommandParameter="OrderId"/>
异步命令示例
private DelegateCommand _submitAsyncClick; public DelegateCommand SubmitAsyncClickCommand => _submitAsyncClick ?? (_submitAsyncClick = new DelegateCommand(async () => await SubmitAsync())); public async Task SubmitAsync() { await Task.Run(() => { Thread.Sleep(5000); }); }
复合命令
在许多情况下,视图模型定义的命令将绑定到关联视图中的控件,以便用户可以从视图中直接调用该命令。但是,在某些情况下,您可能希望能够从应用程序UI的父视图中的控件调用一个或多个视图模型上的命令。
例如,如果应用程序允许用户同时编辑多个项目,则您可能希望允许用户使用由应用程序工具栏或功能区中的按钮表示的单个命令保存所有项目。在这种情况下,“全部保存”命令将调用视图模型实例为每个项目实现的每个保存命令,如下图所示。
核心概念:CompositeCommand
CompositeCommand类表示由多个子命令组成的命令。调用复合命令时,依次调用其每个子命令。在需要在UI中将一组命令表示为单个命令的情况下,或者在需要调用多个命令以实现逻辑命令的情况下,它非常有用。
CompositeCommand类维护子命令列表(DelegateCommand实例)。CompositeCommand类的Execute方法只是依次调用每个子命令的Execute方法。CanExecute方法类似地调用每个子命令的CanExecute方法,但如果无法执行任何子命令,CanExecute方法将返回false。换句话说,默认情况下,仅当所有子命令都可以执行时,才能执行CompositeCommand。
创建复合命令
通常,CompositeCommand在整个应用程序中共享,并且需要全局可用。使用CompositeCommand注册子命令时,在整个应用程序中使用CompositeCommand的同一实例,这一点很重要。这要求在应用程序中将CompositeCommand定义为单例。这可以通过使用依赖项注入(DI)或将CompositeCommand定义为静态类来实现。
为了提高代码的可维护性和可测试性,建议使用依赖项注入方法。
方式1:使用依赖注入:
public interface IApplicationCommands { CompositeCommand SaveCommand { get; } }
public class ApplicationCommands : IApplicationCommands { private CompositeCommand _saveCommand = new CompositeCommand(); public CompositeCommand SaveCommand { get { return _saveCommand; } } }
public partial class App : PrismApplication { protected override void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterSingleton<IApplicationCommands, ApplicationCommands>(); } }
public DelegateCommand UpdateCommand { get; private set; } public TabViewModel(IApplicationCommands applicationCommands) { UpdateCommand = new DelegateCommand(Update); applicationCommands.SaveCommand.RegisterCommand(UpdateCommand); }
方式2:使用静态类
public static class ApplicationCommands { public static CompositeCommand SaveCommand = new CompositeCommand(); }
public DelegateCommand UpdateCommand { get; private set; } public TabViewModel() { UpdateCommand = new DelegateCommand(Update); ApplicationCommands.SaveCommand.RegisterCommand(UpdateCommand); }
绑定命令
创建CompositeCommand后,现在必须将它们绑定到UI元素以调用命令。
使用DI时,必须公开IAApplicationCommand以绑定到视图。在视图的ViewModel中,在构造函数中请求IAApplicationCommand,并将IAApplicationCommand类型的属性设置为实例。
public class MainWindowViewModel : BindableBase { private IApplicationCommands _applicationCommands; public IApplicationCommands ApplicationCommands { get { return _applicationCommands; } set { SetProperty(ref _applicationCommands, value); } } public MainWindowViewModel(IApplicationCommands applicationCommands) { ApplicationCommands = applicationCommands; } }
<Button Content="Save" Command="{Binding ApplicationCommands.SaveCommand}"/>
如果您使用的是静态类方法,下面的代码示例将显示如何将按钮绑定到WPF中的静态ApplicationCommand类。
<Button Content="Save" Command="{x:Static local:ApplicationCommands.SaveCommand}" />
注销命令
如前面的示例所示,子命令是使用CompositeCommand注册的RegisterCommand方法。但是,如果您不再希望响应CompositeCommand,或者正在销毁视图/视图模型以进行垃圾收集,则应使用CompositeCommand注销子命令。取消注册命令方法。
public void Destroy() { _applicationCommands.UnregisterCommand(UpdateCommand); }
当不再需要视图/视图模型(准备好进行GC)时,必须从CompositeCommand中注销命令。否则将导致内存泄漏。
只在活动视图中执行命令
父视图级别的复合命令通常用于协调如何调用子视图级别的命令。在某些情况下,您会希望对所有显示的视图执行命令,如前面描述的“全部保存”命令示例所示。在其他情况下,您将希望仅在活动视图上执行该命令。在这种情况下,复合命令将仅在被视为活动的视图上执行子命令;它不会在非活动视图上执行子命令。例如,您可能希望在应用程序的工具栏上实现一个缩放命令,该命令只缩放当前活动的项目,如下图所示。
为了支持此场景,Prism提供了IActiveAware接口。IActiveAware接口定义了一个IsActive属性,该属性在实现者处于活动状态时返回true,以及一个IsActiveChanged事件,该事件在活动状态更改时引发。
您可以在视图或视图模型上实现IActiveAware接口。它主要用于跟踪视图的活动状态。视图是否处于活动状态由特定控件内的视图决定。例如,对于选项卡控件,有一个适配器可将当前选定选项卡中的视图设置为活动。
DelegateCommand类还实现了IActiveAware接口。通过在构造函数中为monitorCommandActivity参数指定true,可以将CompositeCommand配置为评估子DelegateCommand的活动状态(以及CanExecute状态)。当此参数设置为true时,CypIdCeMoMand类将在确定CANEXECUTE方法的返回值时以及在执行方法中执行子命令时考虑每个子Auto TeaCeMoMand的活动状态。
public class ApplicationCommands : IApplicationCommands { private CompositeCommand _saveCommand = new CompositeCommand(true); public CompositeCommand SaveCommand { get { return _saveCommand; } } }
当monitorCommandActivity参数为true时,CompositeCommand类将显示以下行为:
CanExecute:仅当可以执行所有活动命令时才返回true。不活动的子命令将完全不被考虑。
Execute:执行所有活动命令。不活动的子命令将完全不被考虑。
通过在ViewModels上实现IActiveAware界面,当视图处于活动或非活动状态时,您将收到通知。当视图的活动状态更改时,可以更新子命令的活动状态。然后,当用户调用composite命令时,将调用活动子视图上的命令。
public class TabViewModel : BindableBase, IActiveAware { private bool _isActive; public bool IsActive { get { return _isActive; } set { _isActive = value; OnIsActiveChanged(); } } public event EventHandler IsActiveChanged; public DelegateCommand UpdateCommand { get; private set; } public TabViewModel(IApplicationCommands applicationCommands) { UpdateCommand = new DelegateCommand(Update); applicationCommands.SaveCommand.RegisterCommand(UpdateCommand); } private void Update() { //implement logic } private void OnIsActiveChanged() { UpdateCommand.IsActive = IsActive; //set the command as active IsActiveChanged?.Invoke(this, new EventArgs()); //invoke the event for all listeners } }
浙公网安备 33010602011771号