MVVM - Hooking Up ViewModel

Trong chương này, chúng tôi sẽ trình bày cách kết nối ViewModel. Đây là phần tiếp theo của chương trước, trong đó chúng ta đã thảo luận về việc xây dựng View đầu tiên. Bây giờ, hình thức tiếp theo của công trình đầu tiên làmeta-pattern được gọi là ViewModelLocator. Nó là một mô hình giả và được xếp lớp trên mô hình MVVM.

  • Trong MVVM, mỗi View cần được nối với ViewModel của nó.

  • ViewModelLocator là một cách tiếp cận đơn giản để tập trung mã và phân tách chế độ xem nhiều hơn.

  • Nó có nghĩa là nó không cần phải biết rõ ràng về kiểu ViewModel và cách xây dựng nó.

  • Có một số cách tiếp cận khác nhau để sử dụng ViewModelLocator, nhưng ở đây chúng tôi sử dụng cách tương tự nhất với cách tiếp cận là một phần của khuôn khổ PRISM.

ViewModelLocator cung cấp một cách chuẩn, nhất quán, có tính khai báo và được kết hợp lỏng lẻo để thực hiện việc xây dựng chế độ xem đầu tiên tự động hóa quá trình kết nối ViewModel với Chế độ xem. Hình sau thể hiện quy trình cấp cao của ViewModelLocator.

Step 1 - Tìm ra kiểu View nào đang được xây dựng.

Step 2 - Xác định ViewModel cho loại View cụ thể đó.

Step 3 - Xây dựng ViewModel đó.

Step 4 - Đặt View DataContext thành ViewModel.

Để hiểu khái niệm cơ bản, chúng ta hãy xem ví dụ đơn giản của ViewModelLocator bằng cách tiếp tục ví dụ tương tự từ chương trước. Nếu bạn nhìn vào tệp StudentView.xaml, bạn sẽ thấy rằng chúng tôi đã kết nối tĩnh ViewModel.

Bây giờ như được hiển thị trong chương trình sau, nhận xét mã XAML này cũng xóa mã khỏi Code-phía sau.

<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" 
   mc:Ignorable = "d" d:DesignHeight = "300" d:DesignWidth = "300">
	
   <!--<UserControl.DataContext> 
      <viewModel:StudentViewModel/> 
   </UserControl.DataContext>-->
	
   <Grid> 
      <StackPanel HorizontalAlignment = "Left"> 
         <ItemsControl ItemsSource = "{Binding Path = Students}">
            <ItemsControl.ItemTemplate> 
               <DataTemplate> 
					
                  <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> 
            </ItemsControl.ItemTemplate> 
         </ItemsControl> 
      </StackPanel> 
   </Grid>
	
</UserControl>

Bây giờ, hãy tạo một thư mục mới VML và thêm một lớp công khai ViewModelLocator mới sẽ chứa một thuộc tính đính kèm duy nhất (thuộc tính phụ thuộc) AutoHookedUpViewModel như được hiển thị trong đoạn mã sau.

public static bool GetAutoHookedUpViewModel(DependencyObject obj) { 
   return (bool)obj.GetValue(AutoHookedUpViewModelProperty); 
}

public static void SetAutoHookedUpViewModel(DependencyObject obj, bool value) { 
   obj.SetValue(AutoHookedUpViewModelProperty, value); 
}

// Using a DependencyProperty as the backing store for AutoHookedUpViewModel. 
//This enables animation, styling, binding, etc...
 
public static readonly DependencyProperty AutoHookedUpViewModelProperty =
   DependencyProperty.RegisterAttached("AutoHookedUpViewModel",
   typeof(bool), typeof(ViewModelLocator), new PropertyMetadata(false,
   AutoHookedUpViewModelChanged));

Và bây giờ bạn có thể thấy một định nghĩa thuộc tính đính kèm cơ bản. Để thêm hành vi vào thuộc tính, chúng ta cần thêm một trình xử lý sự kiện đã thay đổi cho thuộc tính này, chứa quy trình tự động kết nối ViewModel cho Chế độ xem. Mã để thực hiện việc này như sau:

private static void AutoHookedUpViewModelChanged(DependencyObject d, 
   DependencyPropertyChangedEventArgs e) { 
   if (DesignerProperties.GetIsInDesignMode(d)) return; 
   var viewType = d.GetType(); 
   string str = viewType.FullName; 
   str = str.Replace(".Views.", ".ViewModel."); 
	
   var viewTypeName = str; 
   var viewModelTypeName = viewTypeName + "Model"; 
   var viewModelType = Type.GetType(viewModelTypeName); 
   var viewModel = Activator.CreateInstance(viewModelType);
   ((FrameworkElement)d).DataContext = viewModel; 
}

Sau đây là phần triển khai hoàn chỉnh của lớp ViewModelLocator.

using System; 
using System.ComponentModel; 
using System.Windows;

namespace MVVMDemo.VML { 

   public static class ViewModelLocator { 
	
      public static bool GetAutoHookedUpViewModel(DependencyObject obj) {
         return (bool)obj.GetValue(AutoHookedUpViewModelProperty); 
      }
		
      public static void SetAutoHookedUpViewModel(DependencyObject obj, bool value) { 
         obj.SetValue(AutoHookedUpViewModelProperty, value); 
      }
		
      // Using a DependencyProperty as the backing store for AutoHookedUpViewModel. 
		
      //This enables animation, styling, binding, etc...
      public static readonly DependencyProperty AutoHookedUpViewModelProperty =
         DependencyProperty.RegisterAttached("AutoHookedUpViewModel", 
         typeof(bool), typeof(ViewModelLocator), new
         PropertyMetadata(false, AutoHookedUpViewModelChanged));
		
      private static void AutoHookedUpViewModelChanged(DependencyObject d,
         DependencyPropertyChangedEventArgs e) { 
         if (DesignerProperties.GetIsInDesignMode(d)) return; 
         var viewType = d.GetType(); 
			
         string str = viewType.FullName; 
         str = str.Replace(".Views.", ".ViewModel."); 
			
         var viewTypeName = str; 
         var viewModelTypeName = viewTypeName + "Model";
         var viewModelType = Type.GetType(viewModelTypeName); 
         var viewModel = Activator.CreateInstance(viewModelType);
			
        ((FrameworkElement)d).DataContext = viewModel; 
      } 
   } 
}

Điều đầu tiên cần làm là thêm một không gian tên để chúng ta có thể truy cập loại ViewModelLocator đó trong thư mục gốc của dự án của chúng ta. Sau đó, trên phần tử định tuyến là một loại chế độ xem, hãy thêm thuộc tính AutoHookedUpViewModel và đặt nó thành true.

xmlns:vml = "clr-namespace:MVVMDemo.VML"
vml:ViewModelLocator.AutoHookedUpViewModel = "True"

Đây là phần triển khai hoàn chỉnh của tệp StudentView.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:vml = "clr-namespace:MVVMDemo.VML" 
   vml:ViewModelLocator.AutoHookedUpViewModel = "True" 
   mc:Ignorable = "d" d:DesignHeight = "300" d:DesignWidth = "300">
   
   <!--<UserControl.DataContext> 
      <viewModel:StudentViewModel/> 
   </UserControl.DataContext>-->

   <Grid> 
      <StackPanel HorizontalAlignment = "Left"> 
         <ItemsControl ItemsSource = "{Binding Path = Students}"> 
            <ItemsControl.ItemTemplate> 
               <DataTemplate> 
					
                  <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> 
            </ItemsControl.ItemTemplate> 
         </ItemsControl> 
      </StackPanel>
   </Grid> 
	
</UserControl>

Khi đoạn mã trên được biên dịch và thực thi, bạn sẽ thấy ViewModelLocator đang kết nối ViewModel cho View cụ thể đó.

Một điều quan trọng cần lưu ý về điều này là chế độ xem không còn được kết hợp theo cách với loại ViewModel của nó hoặc cách nó được xây dựng. Tất cả những thứ đó đã được chuyển đến vị trí trung tâm bên trong ViewModelLocator.