目录
5.1 DevExpress DelegateCommand对象
标准的WinForms应用程序中,操作通常是在事件处理程序中执行的。例如,要在用户单击按钮时刷新数据,您需要处理ButtonClick事件并检索数据源记录。
这个标准技术不适合分层的MVVM的原则。从数据源提取数据的代码应该属于ViewModel层,而不是视图。在MVVM中,这些任务是通过命令——封装动作的ViewModel对象来完成的。将UI元素绑定到该对象以实现所需的层分离:视图代码现在只有绑定代码,而所有业务逻辑都保留在ViewModel中,可以安全地更改。
DevExpress MVVM框架将所有public void 方法视为可绑定命令。下面的代码说明了如何声明使用服务显示消息框的命令。您可以通过以下链接在DevExpress Demo Center中查看完整的例子:Simple Command。
//POCO ViewModel
public class ViewModelWithSimpleCommand {
//command
public void DoSomething() {
var msgBoxService = this.GetService<IMessageBoxService>();
msgBoxService.ShowMessage("Hello!");
}
}
重要:
名称以“Command”结尾的方法,会引发一个异常——重新命名这些方法或用Command属性装饰它们。有关更多细节,请参见此帮助主题:约定和属性。
要将按钮关联到你的命令,请使用BindCommand或WithCommand方法。
//View层代码
mvvmContext.ViewModelType = typeof(ViewModelWithSimpleCommand);
var fluent = mvvmContext.OfType<ViewModelWithSimpleCommand>();
//或者 #1
fluent.BindCommand(commandButton, x => x.DoSomething);
//或者 #2
fluent.WithCommand(x => x.DoSomething).Bind(commandButton1);
WithCommand方法允许您同时绑定多个按钮。运行例子: Bind to Multiple UI Elements.
//View
var fluent = mvvmContext.OfType<ViewModelWithSimpleCommand>();
fluent.WithCommand(x => x.DoSomething)
.Bind(commandButton1)
.Bind(commandButton2);
1. CanExecute条件
要指定一个条件来决定命令是否应该运行,只需要声明一个返回值为布尔类型的方法,其名称以“Can”开头,后面跟着相关的命令名称。这些方法称为CanExecute条件。
//ViewModel
public class ViewModelWithConditionalCommand {
//命令
public void DoSomething() {
var msgBoxService = this.GetService<IMessageBoxService>();
msgBoxService.ShowMessage("Hello!");
}
//CanExecute条件
public bool CanDoSomething() {
return (2 + 2) == 4;
}
}
你还可以忽略CanExecute名称要求,并使用 [Command] 属性注解来手动分配命令条件。
[Command(CanExecuteMethodName = "DoSomethingCriteria")]
public void DoSomething() {
//command
}
如果CanExecute条件返回false,框架将更改链接到该命令的UI元素的状态(禁用、取消选中或隐藏该元素)。上面的代码示例来自以下例子:Command with CanExecute condition。运行此这里修改条件,使其始终返回false。“Execute Command”按钮被禁用,因为它的相关命令不能再运行。
//ViewModel
public bool CanDoSomething() {
//always "false"
return (2 + 2) == 5;
}
当:框架检查CanExecute条件:
- UI命令绑定初始化;
- 调用RaiseCanExecuteChanged方法。
在下面的示例中,每当SelectedEntity属性更改时,就会重新检查CanDoSomething条件的返回值。
//Bindable Property
public virtual MyEntity SelectedEntity{ get; set; }
//OnChanged callback for the bindable property
protected void OnSelectedEntityChanged(){
this.RaiseCanExecuteChanged(x=>x.DoSomething());
}
//Command
public void DoSomething() {
//. . .
}
//CanExecute condition
public bool CanDoSomething() {
//. . .
}
2. 带参数的命令
DevExpress MVVM框架接受公共空方法一个参数作为参数化命令。您可以使用这个参数在View和ViewModel之间传递数据。
运行例子:Parameterized Command。
//ViewModel
public class ViewModelWithParametrizedCommand {
public void DoSomething(object p) {
var msgBoxService = this.GetService<IMessageBoxService>();
msgBoxService.ShowMessage(string.Format("The parameter is {0}.", p));
}
}
//View
mvvmContext.ViewModelType = typeof(ViewModelWithParametrizedCommand);
var fluent = mvvmContext.OfType<ViewModelWithParametrizedCommand>();
object parameter = 5;
fluent.BindCommand(commandButton, x => x.DoSomething, x => parameter);
你还可以给CanExecute条件添加一个参数。
运行例子:Parameterized Command with CanExecute Condition
//ViewModel
public class ViewModelWithParametrizedConditionalCommand {
public void DoSomething(int p) {
var msgBoxService = this.GetService<IMessageBoxService>();
msgBoxService.ShowMessage(string.Format(
"The parameter is {0}.", p));
}
public bool CanDoSomething(int p) {
return (2 + 2) == p;
}
}
//View
mvvmContext.ViewModelType = typeof(ViewModelWithParametrizedConditionalCommand);
var fluent = mvvmContext.OfType<ViewModelWithParametrizedConditionalCommand>();
int parameter = 4;
fluent.BindCommand(commandButton, x => x.DoSomething, x => parameter);
3. 异步命令
如果你需要执行一个延迟的或连续的操作,使用异步命令。要创建异步命令,请声明System.Threading.Tasks返回类型(可以使用异步/等待语法)的一个公共方法。将UI元素绑定到命令的代码保持不变。框架在命令运行时禁用此元素。
运行例子:Async Command
//ViewModel
public class ViewModelWithAsyncCommand {
public async Task DoSomethingAsync() {
// do some work here
await Task.Delay(1000);
}
}
//View
mvvmContext.ViewModelType = typeof(ViewModelWithAsyncCommand);
var fluent = mvvmContext.OfType<ViewModelWithAsyncCommand>();
fluent.BindCommand(commandButton, x => x.DoSomethingAsync);
任务支持取消令牌,允许您检查IsCancellationRequested属性,并在此属性返回true时中止任务。如果将此代码添加到异步命令中,请使用BindCancelCommand方法创建一个UI元素来停止正在执行的异步命令。DevExpress MVVM框架锁定这个取消按钮,只有在运行相关的异步命令时才启用它。
运行例子:Async Command with Cancellation
//ViewModel
public class ViewModelWithAsyncCommandAndCancellation {
public async Task DoSomethingAsynchronously() {
var dispatcher = this.GetService<IDispatcherService>();
var asyncCommand = this.GetAsyncCommand(x => x.DoSomethingAsynchronously());
for(int i = 0; i <= 100; i++) {
if(asyncCommand.IsCancellationRequested)
break;
// do some work here
await Task.Delay(25);
await UpdateProgressOnUIThread(dispatcher, i);
}
await UpdateProgressOnUIThread(dispatcher, 0);
}
public int Progress {
get;
private set;
}
//update the "Progress" property bound to the progress bar within a View
async Task UpdateProgressOnUIThread(IDispatcherService dispatcher, int progress) {
await dispatcher.BeginInvoke(() => {
Progress = progress;
this.RaisePropertyChanged(x => x.Progress);
});
}
}
//View
mvvmContext.ViewModelType = typeof(ViewModelWithAsyncCommandAndCancellation);
var fluent = mvvmContext.OfType<ViewModelWithAsyncCommandAndCancellation>();
fluent.BindCommand(commandButton, x => x.DoSomethingAsynchronously);
fluent.BindCancelCommand(cancelButton, x => x.DoSomethingAsynchronously);
fluent.SetBinding(progressBar, p => p.EditValue, x => x.Progress);
WithCommand连续调用API方法,还支持可取消异步命令。
mvvmContext.ViewModelType = typeof(ViewModelWithAsyncCommandAndCancellation);
// Initialize the Fluent API
var fluent = mvvmContext.OfType<ViewModelWithAsyncCommandAndCancellation>();
// Binding for buttons
fluent.WithCommand(x => x.DoSomethingAsynchronously)
.Bind(commandButton)
.BindCancel(cancelButton);
4. 命令触发器
触发器允许您执行与命令相关联的其他View操作。有三种触发器类型,取决于触发触发器的条件。
- Before触发器——允许您在目标命令执行之前执行操作。
mvvmContext.ViewModelType = typeof(ViewModelWithSimpleCommand);
var fluent = mvvmContext.OfType<ViewModelWithSimpleCommand>();
fluent.BindCommand(commandButton, x => x.DoSomething);
fluent.WithCommand(x => x.DoSomething)
.Before(() => XtraMessageBox.Show("The target command is about to be executed"));
- After触发器——允许您在目标命令完成后执行操作。
mvvmContext.ViewModelType = typeof(ViewModelWithSimpleCommand);
var fluent = mvvmContext.OfType<ViewModelWithSimpleCommand>();
fluent.BindCommand(commandButton, x => x.DoSomething);
fluent.WithCommand(x => x.DoSomething)
.After(() => XtraMessageBox.Show("The target command has been executed"));
- CanExecute条件触发器——允许您在目标命令的CanExecute条件更改时执行操作。
var fluent = mvvmContext.OfType<ViewModelWithSimpleCommandAndCanExecute>();
fluent.BindCommand(commandButton, x => x.DoSomething);
// When the CanExecute condition changes, the message shows up
fluent.WithCommand(x => x.DoSomething)
.OnCanExecuteChanged(() => XtraMessageBox.Show("The CanExecute condition has changed"));
5. Non-POCO命令
上面描述的POCO类命令允许您使用最直接、最不会出错的语法。DevExpress MVVM框架还支持其他命令类型——以确保对遗留项目进行无麻烦的迁移。
5.1 DevExpress DelegateCommand对象
DelegateCommand是System.Windows.Input.ICommand接口的一个实现。
运行例子:Simple Commands
DelegateCommand command = new DelegateCommand(() => {
XtraMessageBox.Show("Hello!");
});
commandButton.BindCommand(command);
运行例子:Commands with CanExecute Conditions
Func<bool> canExecute = () => (2 + 2 == 4);
DelegateCommand command = new DelegateCommand(() => {
XtraMessageBox.Show("Hello!");
}, canExecute);
commandButton.BindCommand(command);
DelegateCommand<object> command = new DelegateCommand<object>((v) => {
XtraMessageBox.Show(string.Format("The parameter is {0}.", v));
});
object parameter = 5;
commandButton.BindCommand(command, () => parameter);
运行例子:Commands with Parameterized CanExecute Conditions
Func<int, bool> canExecute = (p) => (2 + 2 == p);
DelegateCommand<int> command = new DelegateCommand<int>((v) => {
XtraMessageBox.Show(string.Format("The parameter is {0}.", v));
}, canExecute);
int parameter = 4;
commandButton.BindCommand(command, () => parameter);
5.2 自定义命令类
这些是具有至少一个执行方法的任何自定义类型的对象。如果需要,您可以添加CanExecute方法和CanExecuteChanged事件。
运行例子:Simple Commands
CommandObject command = new CommandObject();
commandButton.BindCommand(command);
public class CommandObject {
public void Execute(object parameter) {
XtraMessageBox.Show("Hello!");
}
}
CommandObjectWithParameter command = new CommandObjectWithParameter();
int parameter = 4;
commandButton.BindCommand(command, () => parameter);
public class CommandObjectWithParameter {
public void Execute(object parameter) {
XtraMessageBox.Show(string.Format(
"The parameter is {0}.", parameter));
}
public bool CanExecute(object parameter) {
return object.Equals(2 + 2, parameter);
}
}
【DevExpress MVVM】中文翻译系列.文章目录
DevExpress.WindowsForms.v20.1.chm离线英文原版文档下载