MVVM - Collegamento di ViewModel

In questo capitolo vedremo come collegare ViewModel. È una continuazione dell'ultimo capitolo in cui abbiamo discusso la prima costruzione della Vista. Ora, la forma successiva della prima costruzione è ameta-pattern che è noto come ViewModelLocator. È uno pseudo pattern ed è sovrapposto al pattern MVVM.

  • In MVVM ogni vista deve essere collegata al suo ViewModel.

  • ViewModelLocator è un approccio semplice per centralizzare il codice e disaccoppiare maggiormente la visualizzazione.

  • Significa che non deve conoscere esplicitamente il tipo di ViewModel e come costruirlo.

  • Esistono diversi approcci per utilizzare ViewModelLocator, ma qui usiamo il più simile a quello che fa parte del framework PRISM.

ViewModelLocator fornisce un modo standard, coerente, dichiarativo e vagamente accoppiato per eseguire la prima costruzione della vista che automatizza il processo di collegamento di ViewModel alla vista. La figura seguente rappresenta il processo di alto livello di ViewModelLocator.

Step 1 - Scopri quale tipo di visualizzazione è stato costruito.

Step 2 - Identifica il ViewModel per quel particolare tipo di vista.

Step 3 - Costruisci quel ViewModel.

Step 4 - Imposta Views DataContext su ViewModel.

Per comprendere il concetto di base, diamo uno sguardo al semplice esempio di ViewModelLocator continuando lo stesso esempio dell'ultimo capitolo. Se guardi il file StudentView.xaml, vedrai che abbiamo collegato staticamente il ViewModel.

Ora, come mostrato nel programma seguente, commenta questo codice XAML e rimuovi anche il codice da 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>

Ora creiamo una nuova cartella VML e aggiungiamo una nuova classe pubblica ViewModelLocator che conterrà una singola proprietà allegata (proprietà di dipendenza) AutoHookedUpViewModel come mostrato nel codice seguente.

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

E ora puoi vedere una definizione di proprietà di collegamento di base. Per aggiungere un comportamento alla proprietà, dobbiamo aggiungere un gestore di eventi modificato per questa proprietà che contiene il processo automatico di collegamento del ViewModel per la visualizzazione. Il codice per farlo è il seguente:

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

Di seguito è riportata l'implementazione completa della classe 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; 
      } 
   } 
}

La prima cosa da fare è aggiungere uno spazio dei nomi in modo da poter accedere a quel tipo ViewModelLocator nella radice del nostro progetto. Quindi sull'elemento route che è un tipo di visualizzazione, aggiungi la proprietà AutoHookedUpViewModel e impostala su true.

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

Ecco l'implementazione completa del file 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>

Quando il codice precedente viene compilato ed eseguito, vedrai che ViewModelLocator sta collegando il ViewModel per quella particolare vista.

Una cosa fondamentale da notare in questo caso è che la vista non è più accoppiata in un modo al tipo di ViewModel o al modo in cui viene costruita. È stato tutto spostato nella posizione centrale all'interno di ViewModelLocator.