Учебники

MVVM — Подключение ViewModel

В этой главе мы расскажем, как подключить ViewModel. Это продолжение последней главы, в которой мы обсуждали первую конструкцию View. Теперь следующая форма первой конструкции — это мета-шаблон, известный как ViewModelLocator . Это псевдо паттерн и накладывается поверх паттерна MVVM.

  • В MVVM каждый View должен быть подключен к его ViewModel.

  • ViewModelLocator — это простой подход для централизации кода и дополнительной развязки представления.

  • Это означает, что он не должен явно знать о типе ViewModel и как его конструировать.

  • Существует несколько различных подходов к использованию ViewModelLocator, но здесь мы используем наиболее похожий на тот, который является частью инфраструктуры PRISM.

В MVVM каждый View должен быть подключен к его ViewModel.

ViewModelLocator — это простой подход для централизации кода и дополнительной развязки представления.

Это означает, что он не должен явно знать о типе ViewModel и как его конструировать.

Существует несколько различных подходов к использованию ViewModelLocator, но здесь мы используем наиболее похожий на тот, который является частью инфраструктуры PRISM.

ViewModelLocator предоставляет стандартный, согласованный, декларативный и слабо связанный способ создания первой конструкции View, которая автоматизирует процесс подключения ViewModel к View. На следующем рисунке представлен процесс высокого уровня ViewModelLocator.

Подключение ViewModel

Шаг 1 — выяснить, какой тип представления создается.

Шаг 2 — Определите ViewModel для этого конкретного типа View.

Шаг 3 — Построить эту ViewModel.

Шаг 4 — Установите для DataContext Views значение ViewModel.

Чтобы понять основную концепцию, давайте посмотрим на простой пример ViewModelLocator, продолжая тот же пример из предыдущей главы. Если вы посмотрите на файл StudentView.xaml, вы увидите, что мы статически подключили ViewModel.

Теперь, как показано в следующей программе, прокомментируйте этот код XAML, также удалите код из Code-behind.

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

Теперь давайте создадим новую папку VML и добавим новый открытый класс ViewModelLocator, который будет содержать одно прикрепленное свойство (свойство зависимости) AutoHookedUpViewModel, как показано в следующем коде.

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

И теперь вы можете увидеть базовое определение свойства присоединения. Чтобы добавить поведение к свойству, нам нужно добавить измененный обработчик событий для этого свойства, который содержит автоматический процесс подключения ViewModel для View. Код для этого следующий:

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

Ниже приводится полная реализация класса 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; 
      } 
   } 
}

Первое, что нужно сделать, это добавить пространство имен, чтобы мы могли получить этот тип ViewModelLocator в корне нашего проекта. Затем в элементе маршрута, который является типом представления, добавьте свойство AutoHookedUpViewModel и установите для него значение true.

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

Вот полная реализация файла 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>

Когда приведенный выше код скомпилирован и выполнен, вы увидите, что ViewModelLocator подключает ViewModel для этого конкретного View.

Подключение ViewModel Главное окно

Ключевым моментом, на который следует обратить внимание, является то, что представление больше не связано с тем, каков его тип ViewModel или как он создается. Это все было перемещено в центральное место внутри ViewModelLocator.