MVVM - Podłączanie modelu widoku

W tym rozdziale omówimy, jak podłączyć ViewModel. Jest to kontynuacja ostatniego rozdziału, w którym omówiliśmy pierwszą konstrukcję View. Teraz następną formą pierwszej konstrukcji jestmeta-pattern który jest znany jako ViewModelLocator. Jest to pseudo wzór i jest nałożony na wzór MVVM.

  • W MVVM każdy widok musi być podłączony do swojego ViewModel.

  • ViewModelLocator to proste podejście do scentralizowania kodu i oddzielenia widoku więcej.

  • Oznacza to, że nie musi jawnie wiedzieć o typie ViewModel i jak go zbudować.

  • Istnieje wiele różnych podejść do korzystania z ViewModelLocator, ale tutaj używamy najbardziej podobnego do tego, który jest częścią struktury PRISM.

ViewModelLocator zapewnia standardowy, spójny, deklaratywny i luźno powiązany sposób wykonywania pierwszej konstrukcji widoku, który automatyzuje proces podłączania ViewModel do widoku. Poniższy rysunek przedstawia proces wysokiego poziomu ViewModelLocator.

Step 1 - Dowiedz się, który typ widoku jest konstruowany.

Step 2 - Zidentyfikuj ViewModel dla tego konkretnego typu widoku.

Step 3 - Skonstruuj ten ViewModel.

Step 4 - Ustaw widoki DataContext na ViewModel.

Aby zrozumieć podstawową koncepcję, spójrzmy na prosty przykład ViewModelLocator, kontynuując ten sam przykład z poprzedniego rozdziału. Jeśli spojrzysz na plik StudentView.xaml, zobaczysz, że statycznie podłączyliśmy ViewModel.

Teraz, jak pokazano w poniższym programie, skomentuj ten kod XAML również usuń kod z kodu związanego z kodem.

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

Teraz utwórzmy nowy folder VML i dodajmy nową klasę publiczną ViewModelLocator, która będzie zawierać pojedynczą dołączoną właściwość (właściwość zależności) AutoHookedUpViewModel, jak pokazano w poniższym kodzie.

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

Teraz możesz zobaczyć podstawową definicję właściwości dołączania. Aby dodać zachowanie do właściwości, musimy dodać zmienioną procedurę obsługi zdarzeń dla tej właściwości, która zawiera automatyczny proces podłączania ViewModel for View. Kod, aby to zrobić, jest następujący -

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

Poniżej znajduje się pełna implementacja klasy 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; 
      } 
   } 
}

Pierwszą rzeczą do zrobienia jest dodanie przestrzeni nazw, abyśmy mogli dostać się do tego typu ViewModelLocator w katalogu głównym naszego projektu. Następnie w elemencie trasy, który jest typem widoku, dodaj właściwość AutoHookedUpViewModel i ustaw ją na true.

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

Oto pełna implementacja pliku 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>

Gdy powyższy kod zostanie skompilowany i wykonany, zobaczysz, że ViewModelLocator podłącza ViewModel dla tego konkretnego widoku.

Kluczową rzeczą, na którą należy zwrócić uwagę, jest to, że widok nie jest już powiązany w sposób z typem jego ViewModel lub w jaki sposób jest zbudowany. To wszystko zostało przeniesione do centralnej lokalizacji wewnątrz ViewModelLocator.