Prism_Commanding(2)

Commanding

除了提供对要在视图中显示或编辑的数据的访问之外,ViewModel还可能定义可由用户执行的一个或多个动作或操作。用户可以通过UI执行的动作或操作通常被定义为命令。命令提供了一种方便的方法来表示可以轻松绑定到UI中的控件的操作或操作。它们封装了实现操作或操作的实际代码,并有助于使其与视图中的实际可视化表示分离。

当用户与视图交互时,用户可以以多种不同的方式直观地表示和调用命令。在大多数情况下,它们是通过鼠标单击调用的,但也可以通过快捷键按下,触摸手势或任何其他输入事件来调用它们。视图中的控件是绑定到ViewModel命令的数据,以便用户可以使用控件定义的任何输入事件或手势来调用它们。视图中的UI控件与命令之间的交互可以是双向的。在这种情况下,可以在用户与UI交互时调用该命令,并且可以在启用或禁用基础命令时自动启用或禁用UI。

ViewModel可以将命令实现为命令对象(实现ICommand接口的对象)。可以以声明方式定义视图与命令的交互,而无需在视图的代码隐藏文件中使用复杂的事件处理代码。例如,某些控件固有地支持命令并提供Command可以是绑定到ICommandViewModel提供的对象的数据的属性。在其他情况下,命令行为可用于将控件与ViewModel提供的命令方法或命令对象相关联。

实现ICommand界面很简单。Prism提供了DelegateCommand这个界面的实现,您可以在应用程序中轻松使用它。

[Using Delegate Commands视频教程](Prism.assets/Prism - Using Delegate Commands.mp4)

DelegateCommand

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

public class ArticleViewModel
{
    public DelegateCommand SubmitCommand { get; private set; }

    public ArticleViewModel()
    {
        SubmitCommand = new DelegateCommand(Submit, CanSubmit);
    }

    void Submit(object parameter)
    {
        //implement logic
    }

    bool CanSubmit(object parameter)
    {
        return true;
    }
}
 
 

DelegateCommand对象上调用Execute方法时,它只是通过您在构造函数中指定的委托将调用转发到ViewModel类中的方法。同样,CanExecute调用该方法时,将调用ViewModel类中的相应方法。CanExecute构造函数中方法的委托是可选的。如果没有指定一个委托,DelegateCommand将始终返回trueCanExecute

DelegateCommand班是一个泛型类型。type参数指定传递给ExecuteCanExecute方法的命令参数的类型。在前面的示例中,command参数是type object非通用的版本DelegateCommand类也通过棱镜用于提供当没有所需的命令参数,并且被定义为如下:

public class ArticleViewModel
{
    public DelegateCommand SubmitCommand { get; private set; }

    public ArticleViewModel()
    {
        SubmitCommand = new DelegateCommand(Submit, CanSubmit);
    }

    void Submit()
    {
        //implement logic
    }

    bool CanSubmit()
    {
        return true;
    }
}

所述DelegateCommand故意阻止使用值类型(int,双,布尔等)。因为ICommand需要一个object具有值类型T将导致当意外行为CanExecute(null)XAML初始化命令绑定期间被调用。使用default(T)被认为是被拒绝作为解决方案,因为实现者无法区分有效值和默认值。如果要将值类型用作参数,则必须使用DelegateCommand>或使用简写?语法(DelegateCommand)使其可为空。

从View调用DelegateCommands

有许多方法可以将视图中的控件与ViewModel提供的命令对象相关联。某些WPF,Xamarin.Forms和UWP控件可以通过Command属性轻松地绑定到命令对象。

也可以使用CommandParameter属性选择性地定义命令参数。期望参数的类型在DelegateCommand泛型声明中指定。当用户与该控件交互时,控件将自动调用目标命令,并且命令参数(如果提供)将作为参数传递给命令的Execute方法。在前面的示例中,按钮将SubmitCommand在单击时自动调用。此外,如果CanExecute指定了委托,则CanExecute返回时将自动禁用该按钮,如果返回false则将启用该按钮true

Raising Change Notifications

ViewModel通常需要指示命令CanExecute状态的更改,以便UI中绑定到该命令的任何控件都将更新其启用状态以反映绑定命令的可用性。在DelegateCommand提供了几种这些通知发送到用户界面。

RaiseCanExecuteChanged

RaiseCanExecuteChanged每当需要手动更新绑定的UI元素的状态时,请使用该方法。例如,当IsEnabled属性值更改时,我们调用RaiseCanExecuteChanged属性的setter来通知UI状态更改。

private bool _isEnabled;
public bool IsEnabled
{
    get { return _isEnabled; }
    set
    {
        SetProperty(ref _isEnabled, value);
        SubmitCommand.RaiseCanExecuteChanged();
    }
}

ObservesProperty

如果命令应在属性值更改时发送通知,则可以使用该ObservesProperty方法。使用该ObservesProperty方法时,只要提供的属性的值发生更改,DelegateCommand将自动调用RaiseCanExecuteChanged以通知UI状态更改。

public class ArticleViewModel : BindableBase
{
    private bool _isEnabled;
    public bool IsEnabled
    {
        get { return _isEnabled; }
        set { SetProperty(ref _isEnabled, value); }
    }

    public DelegateCommand SubmitCommand { get; private set; }

    public ArticleViewModel()
    {
        SubmitCommand = new DelegateCommand(Submit, CanSubmit).ObservesProperty(() => IsEnabled);
    }

    void Submit()
    {
        //implement logic
    }

    bool CanSubmit()
    {
        return IsEnabled;
    }
}

注意

使用该ObservesProperty方法时,您可以链接注册多个属性以供观察。示例:ObservesProperty(() => IsEnabled).ObservesProperty(() => CanSave)

ObservesCanExecute

如果您CanExecute是简单Boolean属性的结果,则可以省去声明CanExecute委托,并使用该ObservesCanExecute方法。ObservesCanExecute当注册的属性值发生变化时,它不仅会向UI发送通知,而且还会使用与实际CanExecute委托相同的属性。

public class ArticleViewModel : BindableBase
{
    private bool _isEnabled;
    public bool IsEnabled
    {
        get { return _isEnabled; }
        set { SetProperty(ref _isEnabled, value); }
    }

    public DelegateCommand SubmitCommand { get; private set; }

    public ArticleViewModel()
    {
        SubmitCommand = new DelegateCommand(Submit, CanSubmit).ObservesCanExecute(() => IsEnabled);
    }

    void Submit()
    {
        //implement logic
    }
}
警告

不要尝试链式注册ObservesCanExecute方法。CanExcute代表只能观察到一个属性。

基于Task-Based的DelegateCommand

async/await调用Execute委托内部的异步方法是一个非常常见的要求。每个人的第一直觉是他们需要一个AsyncCommand,但这种假设是错误的。ICommand本质上是同步的,并且代表ExecuteCanExecute代表应被视为事件。这意味着这async void是一个非常有效的语法用于命令。使用异步方法有两种方法DelegateCommand

  • 方法一
public class ArticleViewModel
{
    public DelegateCommand SubmitCommand { get; private set; }

    public ArticleViewModel()
    {
        SubmitCommand = new DelegateCommand(Submit);
    }

    async void Submit()
    {
        await SomeAsyncMethod();
    }
}
  • 方法二
public class ArticleViewModel
{
    public DelegateCommand SubmitCommand { get; private set; }

    public ArticleViewModel()
    {
        SubmitCommand = new DelegateCommand(async ()=> await Submit());
    }

    Task Submit()
    {
        return SomeAsyncMethod();
    }
}

你可能感兴趣的:(Prism_Commanding(2))