11-13 Commands Prism官网案例学习
声明
原文出处如下:
这是一篇记录作者学习Prism的随笔,该随笔的内容是作者通过5个资源学习汇总而成,主要是为了方便自己以后拾遗温习所用,如果文中内容看不懂,推荐直接阅读相关原文。
11-13 Commands
DelegateCommand委托命令
复习案例
Xaml
<Button Content="{Binding Click}" Command="{Binding ClickCommand}" CommandParameter="{Binding CheckName}" Margin="5" Background="LightSalmon" FontSize="28"/>
CS
private DelegateCommand<string> _clickCommand;
public DelegateCommand<string> ClickCommand =>
_clickCommand ?? (_clickCommand = new DelegateCommand<string>(ExecuteClickCommand).ObservesCanExecute(() => IsCheck));
void ExecuteClickCommand(string parameter)
{
this.Time =parameter+ DateTime.Now.ToString();
}
public MainWindowViewModel()
{
this.CheckName = "请选择是否开始";
this.Click = "点我点我点我";
}
基础
- 命令属性 继承自ICommand接口,并且有三个函数成员
- CanExecuteChanged,事件CanExecuteChanged
- CanExecute,一个返回值bool的,且带一个参数为object的CanExecute方法
- Execute,一个无返回值且带一个参数为object的Execute方法
- RaiseCanExecuteChanged方法就是内部调用ICommand接口下的CanExecuteChanged事件去调用CanExecute方法
Prism中的委托命令
-
引入名称空间using Prism.Commands,我们所定义的DelegateCommand类型就在该命名空间下
-
语法糖
- cmd-使用Execute方法创建DelegateCommand属性
- cmdfull-使用Execute和CanExecute方法创建DelegateCommand属性
- cmdg-创建通用的DelegateCommand 属性
- cmdgfull-创建通用的DelegateCommand 具有Execute和CanExecute方法的属性
-
实现简述
-
普通实现
using Prism.Commands; using Prism.Mvvm; using System; using System.Windows.Controls; namespace CommandSample.ViewModels { public class MainWindowViewModel: BindableBase { private bool _isCanExcute; public bool IsCanExcute { get { return _isCanExcute; } set { SetProperty(ref _isCanExcute, value); // GetCurrentTimeCommand.RaiseCanExecuteChanged(); } } private string _currentTime; public string CurrentTime { get { return _currentTime; } set { SetProperty(ref _currentTime, value); } } private DelegateCommand _getCurrentTimeCommand; public DelegateCommand GetCurrentTimeCommand => _getCurrentTimeCommand ?? (_getCurrentTimeCommand = new DelegateCommand(ExecuteGetCurrentTimeCommand, CanExecuteGetCurrentTimeCommand)); void ExecuteGetCurrentTimeCommand() { this.CurrentTime = DateTime.Now.ToString(); } bool CanExecuteGetCurrentTimeCommand() { return IsCanExcute; } } }
在代码中,我们通过using Prism.Mvvm引入继承BindableBase,因为我们要用到属性改变通知方法SetProperty,这在我们上一篇就知道了,再来我们using Prism.Commands,我们所定义的DelegateCommand类型就在该命名空间下,我们知道,ICommand接口是有三个函数成员的,事件CanExecuteChanged,一个返回值bool的,且带一个参数为object的CanExecute方法,一个无返回值且带一个参数为object的Execute方法,很明显我们实现的GetCurrentTimeCommand命令就是一个不带参数的命令
还有一个值得注意的是,我们通过Checkbox的IsChecked绑定了一个bool属性IsCanExcute,且在CanExecute方法中return IsCanExcute,我们都知道CanExecute控制着Execute方法的是否能够执行,也控制着Button的IsEnable状态,而在IsCanExcute的set方法我们增加了一句:
GetCurrentTimeCommand.RaiseCanExecuteChanged();其实通过prism源码我们可以知道RaiseCanExecuteChanged方法就是内部调用ICommand接口下的CanExecuteChanged事件去调用CanExecute方法
其实上述prism还提供了一个更简洁优雅的写法:
[
](javascript:void(0)😉private bool _isCanExcute; public bool IsCanExcute { get { return _isCanExcute; } set { SetProperty(ref _isCanExcute, value);} } private DelegateCommand _getCurrentTimeCommand; public DelegateCommand GetCurrentTimeCommand => _getCurrentTimeCommand ?? (_getCurrentTimeCommand = new DelegateCommand(ExecuteGetCurrentTimeCommand).ObservesCanExecute(()=> IsCanExcute)); void ExecuteGetCurrentTimeCommand() { this.CurrentTime = DateTime.Now.ToString(); }[
](javascript:void(0)😉其中用了ObservesCanExecute方法,其实在该方法内部中也是会去调用RaiseCanExecuteChanged方法
我们通过上面代码我们可以会引出两个问题:
- 如何创建带参数的DelegateCommand?
- 假如控件不包含依赖属性Command,我们要用到该控件的事件,如何转为命令?
-
-
具体实现
SetProperty(ref _isEnabled, value);属性变更的通知,当视图状态更新后,会通知VM更改
_isEnabled。ExecuteDelegateCommand.RaiseCanExecuteChanged();这段代码,则会通知ExecuteDelegateCommand的可执行状态更改了,让他重新获取下可执行状态,那他是怎么获取可执行状态的呢?我们看下这个Command:
ExecuteDelegateCommand = new DelegateCommand(Execute, CanExecute);*new 的时候,有两个参数,第一个是Action(无返回类型的方法)Execute(需要执行的方法),第二个是一个Func,就是一个返回布尔型的方法CanExecute来获取command的可执行状态,当上面通知他可执行状态变更后,他就会重新调用CanExecute方法来获取目前的可执行状态(也就是按钮的可按下状态),来看下这个方法:
private bool CanExecute() { return IsEnabled; }很简单,直接返回了
IsEnabled,而他是跟视图的CheckBox的IsChecked绑定的,当然也可以返回_isEnabled,而我更倾向后面这个,Public那个是给外人用的,蛤蛤。当然可执行状态,还有其他的更优雅的写法,也就不用写
ExecuteDelegateCommand.RaiseCanExecuteChanged();了,具体代码如下:DelegateCommandObservesProperty = new DelegateCommand(Execute, CanExecute) .ObservesProperty(() => IsEnabled); DelegateCommandObservesCanExecute = new DelegateCommand(Execute) .ObservesCanExecute(() => IsEnabled);下面这个是带参数的命令(command),他的回调函数需要一个string类型的参数,在new的时候要指定入参类型:
ExecuteGenericDelegateCommand = new DelegateCommand<string>(ExecuteGeneric) .ObservesCanExecute(() => IsEnabled);回调函数ExecuteGeneric:
private void ExecuteGeneric(string parameter) { UpdateText = parameter; } -
带参数的绑定
那么,其实已经很明显了,我们之前创建DelegateCommand不是泛型版本,当创建一个泛型版本的DelegateCommand
,那么T就是我们要传入的命令参数的类型 ,那么,我们现在可以把触发命令的Button本身作为命令参数传入Button Command="{Binding ExecuteGenericDelegateCommand}" CommandParameter="Passed Parameter" //博客案例 <Button x:Name="mybtn" FontSize="30" Content="Click Me" Margin="10" Height="60" Command="{Binding GetCurrentTimeCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}}"/>
复合命令 CompositeCommands
官方案例
创建全局复合命令
using Prism.Commands;
namespace UsingCompositeCommands.Core
{
public interface IApplicationCommands
{
CompositeCommand SaveCommand { get; }
}
public class ApplicationCommands : IApplicationCommands
{
private CompositeCommand _saveCommand = new CompositeCommand();
public CompositeCommand SaveCommand
{
get { return _saveCommand; }
}
}
}
2.通过IOC容器注册其为单例
using System.Windows;
using Prism.Ioc;
using Prism.Modularity;
using Prism.Unity;
using UsingCompositeCommands.Core;
using UsingCompositeCommands.Views;
namespace UsingCompositeCommands
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : PrismApplication
{
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
//注册子窗体模块
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
moduleCatalog.AddModule<ModuleA.ModuleAModule>();
}
//通过IOC容器注册IApplicationCommands为单例
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<IApplicationCommands, ApplicationCommands>();
}
}
}
模块中---给复合命令注册子命令
using Prism.Commands;
using Prism.Mvvm;
using System;
using UsingCompositeCommands.Core;
namespace ModuleA.ViewModels
{
public class TabViewModel : BindableBase
{
IApplicationCommands _applicationCommands;
private string _title;
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
private bool _canUpdate = true;
public bool CanUpdate
{
get { return _canUpdate; }
set { SetProperty(ref _canUpdate, value); }
}
private string _updatedText;
public string UpdateText
{
get { return _updatedText; }
set { SetProperty(ref _updatedText, value); }
}
public DelegateCommand UpdateCommand { get; private set; }
public TabViewModel(IApplicationCommands applicationCommands)
{
_applicationCommands = applicationCommands;
UpdateCommand = new DelegateCommand(Update).ObservesCanExecute(() => CanUpdate);
_applicationCommands.SaveCommand.RegisterCommand(UpdateCommand);
}
private void Update()
{
UpdateText = $"Updated: {DateTime.Now}";
}
}
}
主窗口中的绑定
using Prism.Mvvm;
using UsingCompositeCommands.Core;
namespace UsingCompositeCommands.ViewModels
{
public class MainWindowViewModel : BindableBase
{
private string _title = "Prism Unity Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
private IApplicationCommands _applicationCommands;
public IApplicationCommands ApplicationCommands
{
get { return _applicationCommands; }
set { SetProperty(ref _applicationCommands, value); }
}
public MainWindowViewModel(IApplicationCommands applicationCommands)
{
ApplicationCommands = applicationCommands;
}
}
}
博客案例
prism提供CompositeCommand类支持复合命令,什么是复合命令,我们可能有这种场景,一个主界面的不同子窗体都有其各自的业务,假如我们可以将上面的例子稍微改下,我们分为三个不同子窗体,三个分别来显示当前年份,月日,时分秒,我们希望在主窗体提供一个按钮,点击后能够使其同时显示,这时候就有一种关系存在了,主窗体按钮依赖于三个子窗体的按钮,而子窗体的按钮不依赖于主窗体的按钮
下面是创建和使用一个prism标准复合命令的流程:
- 创建一个全局的复合命令
- 通过IOC容器注册其为单例
- 给复合命令注册子命令
- 绑定复合命令
1.创建一个全局的复合命令#
首先,我们创建一个类库项目,新增ApplicationCommands类作为全局命令类,代码如下:
[
](javascript:void(0)😉
public interface IApplicationCommands
{
CompositeCommand GetCurrentAllTimeCommand { get; }
}
public class ApplicationCommands : IApplicationCommands
{
private CompositeCommand _getCurrentAllTimeCommand = new CompositeCommand();
public CompositeCommand GetCurrentAllTimeCommand
{
get { return _getCurrentAllTimeCommand; }
}
}
[
](javascript:void(0)😉
其中我们创建了IApplicationCommands接口,让ApplicationCommands实现了该接口,目的是为了下一步通过IOC容器注册其为全局的单例接口
2.通过IOC容器注册其为单例#
我们创建一个新的项目作为主窗体,用来显示子窗体和使用复合命令,关键部分代码如下:
App.cs代码:
[
](javascript:void(0)😉
using Prism.Unity;
using Prism.Ioc;
using System.Windows;
using CompositeCommandsSample.Views;
using Prism.Modularity;
using CompositeCommandsCore;
namespace CompositeCommandsSample
{
public partial class App : PrismApplication
{
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
//通过IOC容器注册IApplicationCommands为单例
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<IApplicationCommands, ApplicationCommands>();
}
//注册子窗体模块
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
moduleCatalog.AddModule<CommandSample.CommandSampleMoudle>();
}
}
}
[
](javascript:void(0)😉
3.给复合命令注册子命令#
我们在之前的CommandSample解决方案下面的Views文件夹下新增两个UserControl,分别用来显示月日和时分秒,在其ViewModels文件夹下面新增两个UserControl的ViewModel,并且将之前的MainWindow也改为UserControl,大致结构如下图:
关键部分代码:
GetHourTabViewModel.cs:
[
](javascript:void(0)😉
IApplicationCommands _applicationCommands;
public GetHourTabViewModel(IApplicationCommands applicationCommands)
{
_applicationCommands = applicationCommands;
//给复合命令GetCurrentAllTimeCommand注册子命令GetHourCommand
_applicationCommands.GetCurrentAllTimeCommand.RegisterCommand(GetHourCommand);
}
private DelegateCommand _getHourCommand;
public DelegateCommand GetHourCommand =>
_getHourCommand ?? (_getHourCommand = new DelegateCommand(ExecuteGetHourCommand).ObservesCanExecute(() => IsCanExcute));
void ExecuteGetHourCommand()
{
this.CurrentHour = DateTime.Now.ToString("HH:mm:ss");
}
[
](javascript:void(0)😉
GetMonthDayTabViewModel.cs:
[
](javascript:void(0)😉
IApplicationCommands _applicationCommands;
public GetMonthDayTabViewModel(IApplicationCommands applicationCommands)
{
_applicationCommands = applicationCommands;
//给复合命令GetCurrentAllTimeCommand注册子命令GetMonthCommand
_applicationCommands.GetCurrentAllTimeCommand.RegisterCommand(GetMonthCommand);
}
private DelegateCommand _getMonthCommand;
public DelegateCommand GetMonthCommand =>
_getMonthCommand ?? (_getMonthCommand = new DelegateCommand(ExecuteCommandName).ObservesCanExecute(()=>IsCanExcute));
void ExecuteCommandName()
{
this.CurrentMonthDay = DateTime.Now.ToString("MM:dd");
}
[
](javascript:void(0)😉
MainWindowViewModel.cs:
[
](javascript:void(0)😉
IApplicationCommands _applicationCommands;
public MainWindowViewModel(IApplicationCommands applicationCommands)
{
_applicationCommands = applicationCommands;
//给复合命令GetCurrentAllTimeCommand注册子命令GetYearCommand
_applicationCommands.GetCurrentAllTimeCommand.RegisterCommand(GetYearCommand);
}
private DelegateCommand _getYearCommand;
public DelegateCommand GetYearCommand =>
_getYearCommand ?? (_getYearCommand = new DelegateCommand(ExecuteGetYearCommand).ObservesCanExecute(()=> IsCanExcute));
void ExecuteGetYearCommand()
{
this.CurrentTime =DateTime.Now.ToString("yyyy");
}
[
](javascript:void(0)😉
CommandSampleMoudle.cs:
[
](javascript:void(0)😉
using CommandSample.ViewModels;
using CommandSample.Views;
using Prism.Ioc;
using Prism.Modularity;
using Prism.Regions;
namespace CommandSample
{
public class CommandSampleMoudle : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
IRegion region= regionManager.Regions["ContentRegion"];
var mainWindow = containerProvider.Resolve<MainWindow>();
(mainWindow.DataContext as MainWindowViewModel).Title = "GetYearTab";
region.Add(mainWindow);
var getMonthTab = containerProvider.Resolve<GetMonthDayTab>();
(getMonthTab.DataContext as GetMonthDayTabViewModel).Title = "GetMonthDayTab";
region.Add(getMonthTab);
var getHourTab = containerProvider.Resolve<GetHourTab>();
(getHourTab.DataContext as GetHourTabViewModel).Title = "GetHourTab";
region.Add(getHourTab);
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
}
[
](javascript:void(0)😉
4.绑定复合命令#
主窗体xaml代码:
[
](javascript:void(0)😉
<Window x:Class="CompositeCommandsSample.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
xmlns:local="clr-namespace:CompositeCommandsSample"
mc:Ignorable="d" prism:ViewModelLocator.AutoWireViewModel="True"
Title="MainWindow" Height="650" Width="800">
<Window.Resources>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding DataContext.Title}"/>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Content="GetCurrentTime" FontSize="30" Margin="10" Command="{Binding ApplicationCommands.GetCurrentAllTimeCommand}"/>
<TabControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion"/>
</Grid>
</Window>
[
](javascript:void(0)😉
MainWindowViewModel.cs:
[
](javascript:void(0)😉
using CompositeCommandsCore;
using Prism.Mvvm;
namespace CompositeCommandsSample.ViewModels
{
public class MainWindowViewModel:BindableBase
{
private IApplicationCommands _applicationCommands;
public IApplicationCommands ApplicationCommands
{
get { return _applicationCommands; }
set { SetProperty(ref _applicationCommands, value); }
}
public MainWindowViewModel(IApplicationCommands applicationCommands)
{
this.ApplicationCommands = applicationCommands;
}
}
}
IActiveAware命令
using Prism;
using Prism.Commands;
using Prism.Mvvm;
using System;
using UsingCompositeCommands.Core;
namespace ModuleA.ViewModels
{
public class TabViewModel : BindableBase, IActiveAware
{
IApplicationCommands _applicationCommands;
private string _title;
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
private bool _canUpdate = true;
public bool CanUpdate
{
get { return _canUpdate; }
set { SetProperty(ref _canUpdate, value); }
}
private string _updatedText;
public string UpdateText
{
get { return _updatedText; }
set { SetProperty(ref _updatedText, value); }
}
public DelegateCommand UpdateCommand { get; private set; }
public TabViewModel(IApplicationCommands applicationCommands)
{
_applicationCommands = applicationCommands;
UpdateCommand = new DelegateCommand(Update).ObservesCanExecute(() => CanUpdate);
_applicationCommands.SaveCommand.RegisterCommand(UpdateCommand);
}
private void Update()
{
UpdateText = $"Updated: {DateTime.Now}";
}
//*****************************
bool _isActive;
public bool IsActive
{
get { return _isActive; }
set
{
_isActive = value;
OnIsActiveChanged();
}
}
private void OnIsActiveChanged()
{
UpdateCommand.IsActive = IsActive;
IsActiveChanged?.Invoke(this, new EventArgs());
}
public event EventHandler IsActiveChanged;
}
}
博客实现基于Task的命令
首先我们在界面新增一个新的按钮,用来绑定新的基于Task的命令,我们将要做的就是点击该按钮后,第一个Textbox的在5秒后显示"Hello Prism!",且期间UI界面不阻塞
xaml界面新增按钮代码如下:
<Button x:Name="mybtn1" FontSize="30" Content="Click Me 1" Margin="10" Height="60" Command="{Binding AsyncCommand}" />
MainWindowViewModel新增代码:
[
](javascript:void(0)😉
private DelegateCommand _asyncCommand;
public DelegateCommand AsyncCommand =>
_asyncCommand ?? (_asyncCommand = new DelegateCommand(ExecuteAsyncCommand));
async void ExecuteAsyncCommand()
{
await ExampleMethodAsync();
}
async Task ExampleMethodAsync()
{
await Task.Run(()=>
{
Thread.Sleep(5000);
this.CurrentTime = "Hello Prism!";
} );
}
[
](javascript:void(0)😉
也可以更简洁的写法:
[
](javascript:void(0)😉
private DelegateCommand _asyncCommand;
public DelegateCommand AsyncCommand =>
_asyncCommand ?? (_asyncCommand = new DelegateCommand( async()=>await ExecuteAsyncCommand()));
Task ExecuteAsyncCommand()
{
return Task.Run(() =>
{
Thread.Sleep(5000);
this.CurrentTime = "Hello Prism!";
});
}
[
](javascript:void(0)😉


浙公网安备 33010602011771号