在本章中,我们将学习如何向MVVM应用程序添加交互性以及如何干净地调用逻辑.您还将看到所有这些都是通过维护松散耦合和良好的结构来完成的,这是MVVM模式的核心.要理解这一切,首先让我们了解命令.
命令模式已有详细记录并经常使用几十年的设计模式.在这种模式中有两个主要的角色,即调用者和接收者.
调用程序是一段可以执行命令逻辑的代码.
通常,它是用户在UI框架环境中与之交互的UI元素.
它可能只是应用程序中其他地方的另一块逻辑代码.
接收者是在调用者触发时执行的逻辑.
在MVVM的上下文中,接收器通常是ViewModel中需要调用的方法.
在这些之间二,你有一个障碍层,这意味着调用者和接收者不必明确地了解彼此.这通常表示为向调用者公开的接口抽象,并且该接口的具体实现能够调用接收者.
让我们看一个您将学习的简单示例命令以及如何使用它们在View和ViewModel之间进行通信.在本章中,我们将继续上一章中的相同示例.
在StudentView.xaml文件中,我们有一个ListBox,用于连接ViewModel中的学生数据.现在让我们添加一个用于从ListBox中删除学生的按钮.
重要的是在按钮上使用命令非常简单,因为它们具有连接到ICommand的命令属性.
因此,我们可以在ViewModel上公开一个具有ICommand的属性,并通过按钮的命令属性绑定它,如下面的代码所示.
<Button Content = "Delete" Command = "{Binding DeleteCommand}" HorizontalAlignment = "Left" VerticalAlignment = "Top" Width = "75" />
让我们在你的项目中添加一个新类,它将实现ICommand接口.以下是ICommand接口的实现.
using System; using System.Windows.Input; namespace MVVMDemo { public class MyICommand : ICommand { Action _TargetExecuteMethod; Func<bool> _TargetCanExecuteMethod; public MyICommand(Action executeMethod) { _TargetExecuteMethod = executeMethod; } public MyICommand(Action executeMethod, Func<bool> canExecuteMethod){ _TargetExecuteMethod = executeMethod; _TargetCanExecuteMethod = canExecuteMethod; } public void RaiseCanExecuteChanged() { CanExecuteChanged(this, EventArgs.Empty); } bool ICommand.CanExecute(object parameter) { if (_TargetCanExecuteMethod != null) { return _TargetCanExecuteMethod(); } if (_TargetExecuteMethod != null) { return true; } return false; } // Beware - should use weak references if command instance lifetime is longer than lifetime of UI objects that get hooked up to command // Prism commands solve this in their implementation public event EventHandler CanExecuteChanged = delegate { }; void ICommand.Execute(object parameter) { if (_TargetExecuteMethod != null) { _TargetExecuteMethod(); } } } }
如您所见,这是一个简单的委托实现ICommand我们有两个委托给一个executeMethod,一个给canExecuteMethod,可以在构造时传入.
在上面的实现中,有两个重载的构造函数,一个只用于executeMethod和对于executeMethod和我都可以执行一个canExecuteMethod.
让我们在StudentView Model类中添加MyICommand类型的属性.现在我们需要在StudentViewModel中构造一个实例.我们将使用带有两个参数的MyICommand的重载构造函数.
public MyICommand DeleteCommand { get; set;} public StudentViewModel() { LoadStudents(); DeleteCommand = new MyICommand(OnDelete, CanDelete); }
现在添加OnDelete和CanDelete方法的实现.
private void OnDelete() { Students.Remove(SelectedStudent); } private bool CanDelete() { return SelectedStudent != null; }
我们还需要添加一个新的SelectedStudent,以便用户可以从ListBox中删除Selected Item.
private Student _selectedStudent; public Student SelectedStudent { get { return _selectedStudent; } set { _selectedStudent = value; DeleteCommand.RaiseCanExecuteChanged(); } }
以下是ViewModel类的完整实现.
using MVVMDemo.Model; using System.Collections.ObjectModel; using System.Windows.Input; using System; namespace MVVMDemo.ViewModel { public class StudentViewModel { public MyICommand DeleteCommand { get; set;} public StudentViewModel() { LoadStudents(); DeleteCommand = new MyICommand(OnDelete, CanDelete); } public ObservableCollection<Student> Students { get; set; } public void LoadStudents() { ObservableCollection<Student> students = new ObservableCollection<Student>(); students.Add(new Student { FirstName = "Mark", LastName = "Allain" }); students.Add(new Student { FirstName = "Allen", LastName = "Brown" }); students.Add(new Student { FirstName = "Linda", LastName = "Hamerski" }); Students = students; } private Student _selectedStudent; public Student SelectedStudent { get { return _selectedStudent; } set { _selectedStudent = value; DeleteCommand.RaiseCanExecuteChanged(); } } private void OnDelete() { Students.Remove(SelectedStudent); } private bool CanDelete() { return SelectedStudent != null; } } }
在StudentView.xaml中,我们需要在ListBox中添加SelectedItem属性,它将绑定到SelectStudent属性.
<ListBox ItemsSource = "{Binding Students}" SelectedItem = "{Binding SelectedStudent}"/>
以下是完整的xaml文件.
<UserControl x:Class = "MVVMDemo.Views.StudentView" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d = "http://schemas.microsoft.com/expression/blend/2008" xmlns:local = "clr-namespace:MVVMDemo.Views" xmlns:viewModel = "clr-namespace:MVVMDemo.ViewModel" xmlns:data = "clr-namespace:MVVMDemo.Model" xmlns:vml = "clr-namespace:MVVMDemo.VML" vml:ViewModelLocator.AutoHookedUpViewModel = "True" mc:Ignorable = "d" d:DesignHeight = "300" d:DesignWidth = "300"> <UserControl.Resources> <DataTemplate DataType = "{x:Type data:Student}"> <StackPanel Orientation = "Horizontal"> <TextBox Text = "{Binding Path = FirstName, Mode = TwoWay}" Width = "100" Margin = "3 5 3 5"/> <TextBox Text = "{Binding Path = LastName, Mode = TwoWay}" Width = "100" Margin = "0 5 3 5"/> <TextBlock Text = "{Binding Path = FullName, Mode = OneWay}" Margin = "0 5 3 5"/> </StackPanel> </DataTemplate> </UserControl.Resources> <Grid> <StackPanel Orientation = "Horizontal"> <ListBox ItemsSource = "{Binding Students}" SelectedItem = "{Binding SelectedStudent}"/> <Button Content = "Delete" Command = "{Binding DeleteCommand}" HorizontalAlignment = "Left" VerticalAlignment = "Top" Width = "75" /> </StackPanel> </Grid> </UserControl>
编译并执行上述代码后,您将看到以下窗口.
您可以看到删除按钮被禁用.选择任何项目时都会启用它.
选择任何项目并按删除.您将看到所选项目列表被删除,删除按钮再次被禁用.
我们建议您逐步执行上述示例,以便更好地理解.