MVVM - Conectando ViewModel
Neste capítulo, vamos cobrir como conectar ViewModel. É uma continuação do último capítulo em que discutimos a primeira construção da View. Agora, a próxima forma da primeira construção é ummeta-pattern que é conhecido como ViewModelLocator. É um pseudo padrão e é colocado em camadas sobre o padrão MVVM.
No MVVM, cada View precisa ser conectado ao seu ViewModel.
O ViewModelLocator é uma abordagem simples para centralizar o código e desacoplar ainda mais a visualização.
Isso significa que ele não precisa saber explicitamente sobre o tipo de ViewModel e como construí-lo.
Existem várias abordagens diferentes para usar ViewModelLocator, mas aqui usamos a mais semelhante àquela que faz parte da estrutura PRISM.
ViewModelLocator fornece uma maneira padrão, consistente, declarativa e fracamente acoplada de fazer a construção da primeira visualização, que automatiza o processo de conectar o ViewModel ao View. A figura a seguir representa o processo de alto nível de ViewModelLocator.
Step 1 - Descobrir qual tipo de visualização está sendo construído.
Step 2 - Identifique o ViewModel para esse tipo de View específico.
Step 3 - Construa esse ViewModel.
Step 4 - Defina Views DataContext como ViewModel.
Para entender o conceito básico, vamos dar uma olhada no exemplo simples de ViewModelLocator continuando o mesmo exemplo do último capítulo. Se você olhar o arquivo StudentView.xaml, verá que conectamos estaticamente o ViewModel.
Agora, conforme mostrado no programa a seguir, comente esse código XAML e remova o código do 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>
Agora vamos criar uma nova pasta VML e adicionar uma nova classe pública ViewModelLocator que conterá uma única propriedade anexada (propriedade de dependência) AutoHookedUpViewModel conforme mostrado no código a seguir.
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 agora você pode ver uma definição básica de propriedade de anexação. Para adicionar comportamento à propriedade, precisamos adicionar um manipulador de eventos alterado para esta propriedade que contém o processo automático de conectar ViewModel para View. O código para fazer isso é o seguinte -
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;
}
A seguir está a implementação completa da 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;
}
}
}
A primeira coisa a fazer é adicionar um namespace para que possamos chegar a esse tipo de ViewModelLocator na raiz do nosso projeto. Em seguida, no elemento de rota que é um tipo de visualização, adicione a propriedade AutoHookedUpViewModel e defina-a como true.
xmlns:vml = "clr-namespace:MVVMDemo.VML"
vml:ViewModelLocator.AutoHookedUpViewModel = "True"
Aqui está a implementação completa do arquivo 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 o código acima for compilado e executado, você verá que ViewModelLocator está conectando o ViewModel para aquela View em particular.
Uma coisa importante a se notar sobre isso é que a visualização não está mais acoplada de forma a qual é o tipo de seu ViewModel ou como ele é construído. Tudo isso foi movido para o local central dentro do ViewModelLocator.