MVVM - Komunikasi View / ViewModel

Dalam bab ini, kita akan mempelajari cara menambahkan interaktivitas ke aplikasi MVVM dan cara memanggil logika dengan rapi. Anda juga akan melihat bahwa semua ini dilakukan dengan mempertahankan kopling yang longgar dan penataan yang baik yang merupakan jantung dari pola MVVM. Untuk memahami semua ini, pertama mari kita pelajari tentang perintah.

Komunikasi View / ViewModel melalui Perintah

Pola perintah telah didokumentasikan dengan baik dan sering menggunakan pola desain selama beberapa dekade. Dalam pola ini terdapat dua aktor utama yaitu invoker dan receiver.

Invoker

  • Invoker adalah bagian dari kode yang dapat mengeksekusi beberapa logika imperatif.

  • Biasanya, ini adalah elemen UI yang berinteraksi dengan pengguna, dalam konteks framework UI.

  • Itu hanya bisa menjadi potongan kode logika di tempat lain dalam aplikasi.

Penerima

  • Penerima adalah logika yang dimaksudkan untuk dieksekusi ketika pemicu diaktifkan.

  • Dalam konteks MVVM, penerima biasanya adalah metode dalam ViewModel Anda yang perlu dipanggil.

Di antara keduanya, Anda memiliki lapisan penghalang, yang menyiratkan pemanggil dan penerima tidak harus secara eksplisit mengetahui satu sama lain. Ini biasanya direpresentasikan sebagai abstraksi antarmuka yang diekspos ke invoker dan implementasi konkret dari antarmuka itu mampu memanggil penerima.

Mari kita lihat contoh sederhana di mana Anda akan mempelajari perintah dan cara menggunakannya untuk berkomunikasi antara View dan ViewModel. Dalam bab ini, kami akan melanjutkan dengan contoh yang sama dari bab sebelumnya.

Dalam file StudentView.xaml, kami memiliki ListBox yang menghubungkan data siswa dari ViewModel. Sekarang mari tambahkan tombol untuk menghapus siswa dari ListBox.

Yang penting adalah bekerja dengan perintah pada tombol sangat mudah karena mereka memiliki properti perintah untuk dihubungkan ke ICommand.

Jadi, kita dapat mengekspos properti di ViewModel yang memiliki ICommand dan mengikatnya dari properti perintah tombol seperti yang ditunjukkan pada kode berikut.

<Button Content = "Delete" 
   Command = "{Binding DeleteCommand}" 
   HorizontalAlignment = "Left" 
   VerticalAlignment = "Top" 
   Width = "75" />

Mari tambahkan kelas baru dalam proyek Anda, yang akan mengimplementasikan antarmuka ICommand. Berikut adalah implementasi antarmuka 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(); 
         } 
      } 
   } 
}

Seperti yang Anda lihat, ini adalah implementasi pendelegasian sederhana dari ICommand di mana kami memiliki dua delegasi, satu untuk executeMethod dan satu lagi untuk canExecuteMethod yang dapat diteruskan saat konstruksi.

Dalam penerapan di atas, ada dua konstruktor yang kelebihan beban, satu hanya untuk executeMethod dan satu lagi untuk executeMethod dan I canExecuteMethod.

Mari tambahkan properti tipe MyICommand di kelas Model StudentView. Sekarang kita perlu membangun sebuah instance di StudentViewModel. Kami akan menggunakan konstruktor MyICommand yang kelebihan beban yang membutuhkan dua parameter.

public MyICommand DeleteCommand { get; set;} 

public StudentViewModel() { 
   LoadStudents(); 
   DeleteCommand = new MyICommand(OnDelete, CanDelete); 
}

Sekarang tambahkan implementasi metode OnDelete dan CanDelete.

private void OnDelete() { 
   Students.Remove(SelectedStudent); 
}

private bool CanDelete() { 
   return SelectedStudent != null; 
}

Kita juga perlu menambahkan SelectedStudent baru sehingga pengguna dapat menghapus Item yang Dipilih dari ListBox.

private Student _selectedStudent;
 
public Student SelectedStudent { 
   get { 
      return _selectedStudent; 
   } 
	
   set { 
      _selectedStudent = value;
      DeleteCommand.RaiseCanExecuteChanged(); 
   } 
}

Berikut adalah implementasi lengkap kelas 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; 
      }
   } 
}

Di StudentView.xaml, kita perlu menambahkan properti SelectedItem dalam ListBox yang akan mengikat ke properti SelectStudent.

<ListBox ItemsSource = "{Binding Students}" SelectedItem = "{Binding SelectedStudent}"/>

Berikut ini adalah file xaml lengkapnya.

<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>

Ketika kode di atas dikompilasi dan dijalankan, Anda akan melihat jendela berikut.

Anda dapat melihat bahwa tombol hapus dinonaktifkan. Ini akan diaktifkan ketika Anda memilih item apa pun.

Saat Anda memilih item apa pun dan menekan hapus. Anda akan melihat bahwa daftar item yang dipilih dihapus dan tombol hapus kembali menjadi nonaktif.

Kami menyarankan Anda untuk menjalankan contoh di atas dengan cara langkah demi langkah untuk pemahaman yang lebih baik.