Jak napisać aplikację wykorzystującą biblioteki PRISM 5.0 w WPF cz. 3 (VIewModelLocator)

W tej części zmagań z biblioteką PRISM 5.0 dla WPF chciałbym opisać czym jest ViewModelLocator. Nasz bohater wpisu jest jednym z wielu sposobów wiązania widoków do klasy View Modelu jaki można zastosować programując aplikacje MVVM. Moim zdaniem po poznaniu jego wszystkich nietrudnych założeń na pewno będziesz chciał go stosować w swoich “prismowych” aplikacjach.

Nie będę się skupiał po co wiązać te dwie klasy (widoku i view modelu), bo nie jest to wpis o wzorcu MVVM. Bardziej skupię się na tym jak wykorzystać tą klasę w bibliotece PRISM 5.0 dla WPF. A więc po koleji.

ViewModelLocator opiera swoje działanie na tzw. konwencjach, co to oznacza w praktyce, że aby wiązać DataContext widoku do ViewModelu musimy przyjąć konwencje założone przez programistów PRISM-a. Te założenia opierają się na szczególnym nazewnictwie i położeniu naszych widoków i viewmodeli (plus parę dodatków, ale o tym zaraz). I tak aby móc dokonać wiązania musimy przyjąć że widoki w naszym projekcie zawsze będą w folderze Views, a viewmodele w folderze ViewModels. Co do nazewnictwa nasze widoki mogą się nazywać dowolnie np. Customer, natomiast aby viewmodellocator powiązał go z odpowiednim viewmodelem, jego nazwa powinna zaczynać się od nazwy widoku, a kończyć viewmodel. Dla uzupełnienia naszego przykładu poprawnie nazwany viewmodel to np. CustomerViewModel. Trzeba też wspomnieć że implementacja view model locatora w bibliotece PRISM umożliwia zdefiniowanie własnych konwencji.

Stwórzmy przykładowy projekt w którym skonfigurujemy ViewModelLocator-a.

1. Tworzymy nowy projekt WPF i dodajemy z pomocą Nuget-a referencje do Prism.Mvvm

vimodelLocatorNuget

2. Tworzymy foldery Views i ViewModels dla założeń nazewnictwa.

ViewModelLocatorfolders

3. Dodajemy, lub przenosimy widok do folderu Views, a w folderze ViewModels tworzymy klasę viewmodelu.

3.1 Uwaga: Jeżeli przenośmy wcześniej utworzony widok do folderu Views, pamiętaj aby zmienić namespace w codebhind, oraz w XAMLu na odpowiadający jego położeniu. Ponadto każdy widok musi implementować interfejs IView.

namespace ViewModelLocatorDemo.Views
{
 public partial class MainWindow : Window, IView
 {
   public MainWindow()
   {
     InitializeComponent();
   }
 }
}

3.2 Nasz VewModel poniżej

namespace ViewModelLocatorDemo.ViewModels
{
 public class MainWindowViewModel : BindableBase
 {
 string test;
 public string Test
 {
 get { return test; }
 set { SetProperty<string>(ref test, value); }
 }

 public MainWindowViewModel ()
 {
 Test = "Test ViewModelLocatora";
 }
 }
}

3.3 W App.xaml Zmieńmy nasz StartupUri na wskazujący na nasz widok w StartupUri=”Views/MainWindow.xaml”

4. Do pełni szczęścia brakuje nam jeszcze dodać w Xamlu, namespace PRISM-a, oraz ustawienia na true attached property ViewModelLocatora (zauważ zaznaczone linie w kodzie).


<Window x:Class="ViewModelLocatorDemo.Views.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:prism="clr-namespace:Microsoft.Practices.Prism.Mvvm;assembly=Microsoft.Practices.Prism.Mvvm.Desktop"
 prism:ViewModelLocator.AutoWireViewModel="True"
 Title="MainWindow" Height="350" Width="525">
 <Grid>
 <TextBlock Text="{Binding Test}"/>
 </Grid>
</Window>

Co jeśli używamy kontenera DI ? (a zapewne używamy) W klasie startującej naszą aplikację dodajemy metodę ViewModelLocationProvider.SetDefaultViewModelFactory. Na przykładzie Unity.


public partial class App : Application
{
 protected override void OnStartup(StartupEventArgs e)
 {
 base.OnStartup(e);

 IUnityContainer _container = new UnityContainer();

 ViewModelLocationProvider.SetDefaultViewModelFactory((type) =>
  {
   return _container.Resolve(type);
  });
 }
}

I to w sumie wszystko w temacie ViewModelLocatora, w każdym bądź razie na ten post wystarczy. Źródło programu pobierzecie stąd