MVVM - View / ViewModel Communication
Trong chương này, chúng ta sẽ học cách thêm tính tương tác vào các ứng dụng MVVM và cách gọi logic rõ ràng. Bạn cũng sẽ thấy rằng tất cả những điều này được thực hiện bằng cách duy trì khớp nối lỏng lẻo và cấu trúc tốt là trung tâm của mô hình MVVM. Để hiểu tất cả điều này, trước tiên chúng ta hãy tìm hiểu về các lệnh.
View / ViewModel Communication thông qua Commands
Mẫu lệnh đã được ghi chép đầy đủ và thường xuyên sử dụng mẫu thiết kế trong một vài thập kỷ. Trong mô hình này có hai tác nhân chính, người gọi và người nhận.
Người tham gia
Kẻ xâm lược là một đoạn mã có thể thực thi một số logic mệnh lệnh.
Thông thường, nó là một phần tử giao diện người dùng mà người dùng tương tác với, trong ngữ cảnh của khung giao diện người dùng.
Nó chỉ có thể là một đoạn mã logic khác ở đâu đó trong ứng dụng.
Người nhận
Bộ nhận là logic được thiết kế để thực hiện khi kẻ gọi tên bắn ra.
Trong ngữ cảnh của MVVM, bộ thu thường là một phương thức trong ViewModel của bạn cần được gọi.
Ở giữa hai thứ này, bạn có một lớp vật cản, có nghĩa là người gọi và người nhận không cần phải biết rõ ràng về nhau. Điều này thường được biểu diễn dưới dạng một giao diện trừu tượng tiếp xúc với người gọi và một triển khai cụ thể của giao diện đó có khả năng gọi người nhận.
Hãy xem một ví dụ đơn giản, trong đó bạn sẽ học các lệnh và cách sử dụng chúng để giao tiếp giữa View và ViewModel. Trong chương này, chúng ta sẽ tiếp tục với ví dụ tương tự từ chương trước.
Trong tệp StudentView.xaml, chúng tôi có một ListBox kết nối dữ liệu sinh viên từ một ViewModel. Bây giờ, hãy thêm một nút để xóa một sinh viên khỏi ListBox.
Điều quan trọng là làm việc với các lệnh trên nút rất dễ dàng vì chúng có thuộc tính lệnh để kết nối với một ICommand.
Vì vậy, chúng tôi có thể hiển thị một thuộc tính trên ViewModel của chúng tôi có một ICommand và liên kết với nó từ thuộc tính lệnh của nút như được hiển thị trong đoạn mã sau.
<Button Content = "Delete"
Command = "{Binding DeleteCommand}"
HorizontalAlignment = "Left"
VerticalAlignment = "Top"
Width = "75" />
Hãy thêm một lớp mới trong dự án của bạn, lớp này sẽ triển khai giao diện ICommand. Sau đây là việc thực hiện giao diện 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();
}
}
}
}
Như bạn có thể thấy, đây là một triển khai ủy quyền đơn giản của ICommand, trong đó chúng ta có hai ủy quyền, một cho executeMethod và một cho canExecuteMethod có thể được chuyển vào khi xây dựng.
Trong cách triển khai ở trên, có hai hàm tạo được nạp chồng, một cho chỉ executeMethod và một cho cả executeMethod và tôi có thể canExecuteMethod.
Hãy thêm một thuộc tính kiểu MyICommand trong lớp StudentView Model. Bây giờ chúng ta cần tạo một thể hiện trong StudentViewModel. Chúng ta sẽ sử dụng phương thức khởi tạo được nạp chồng của MyICommand có hai tham số.
public MyICommand DeleteCommand { get; set;}
public StudentViewModel() {
LoadStudents();
DeleteCommand = new MyICommand(OnDelete, CanDelete);
}
Bây giờ thêm việc triển khai các phương thức OnDelete và CanDelete.
private void OnDelete() {
Students.Remove(SelectedStudent);
}
private bool CanDelete() {
return SelectedStudent != null;
}
Chúng tôi cũng cần thêm một Học viên được Chọn mới để người dùng có thể xóa Mục đã Chọn khỏi ListBox.
private Student _selectedStudent;
public Student SelectedStudent {
get {
return _selectedStudent;
}
set {
_selectedStudent = value;
DeleteCommand.RaiseCanExecuteChanged();
}
}
Sau đây là phần triển khai hoàn chỉnh của lớp 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;
}
}
}
Trong StudentView.xaml, chúng ta cần thêm thuộc tính SelectedItem trong ListBox sẽ liên kết với thuộc tính SelectStudent.
<ListBox ItemsSource = "{Binding Students}" SelectedItem = "{Binding SelectedStudent}"/>
Sau đây là tệp xaml hoàn chỉnh.
<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>
Khi đoạn mã trên được biên dịch và thực thi, bạn sẽ thấy cửa sổ sau.
Bạn có thể thấy rằng nút xóa đã bị tắt. Nó sẽ được bật khi bạn chọn bất kỳ mục nào.
Khi bạn chọn bất kỳ mục nào và nhấn xóa. Bạn sẽ thấy rằng danh sách mục đã chọn bị xóa và nút xóa lại bị vô hiệu hóa.
Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.