5.【DevExpress MVVM】命令

目录

1. CanExecute条件

2. 带参数的命令

3. 异步命令

4. 命令触发器

5. Non-POCO命令

5.1 DevExpress DelegateCommand对象

5.2 自定义命令类


标准的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属性装饰它们。有关更多细节,请参见此帮助主题:约定和属性。

要将按钮关联到你的命令,请使用BindCommandWithCommand方法。

//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条件:

  1. UI命令绑定初始化;
  2. 调用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对象

DelegateCommandSystem.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);

运行例子:Commands with Parameters

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!");
    }
}

运行例子:Commands with Parameters

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离线英文原版文档下载

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值