Jak napisać aplikację wykorzystującą biblioteki PRISM 5.0 w WPF cz. 2 nawigacja

W dzisiejszym poście kontynuacja pierwszego wpisu o PRISM. Ostatnio skonfigurowaliśmy podstawy aplikacji, a dziś dodamy kolejny moduł i będziemy się przełączać pomiędzy widokami tych modułów czyli zajmiemy się nawigacją. Aby specjalnie nie przedłużać przejdźmy od razu do działania.

Założenia są takie że nasza aplikacja powinna posiadać coś w rodzaju menu, będzie to nic innego jak dodatkowy widok w naszym module leczy wyświetlany w innym regionie. Aby mieć się pomiędzy czym przełączać powinniśmy posiadać inne widoki lub pójdźmy dalej i stwórzmy widoki w drugim module. Jak dodać drugi moduł? Patrz poprzedni post w którym dodałem moduł A, zrób to analogicznie dla drugiego modułu. Poglądowy rysunek przedstawia wygląd aplikacji.

 

 

 

 

 

Aby kontynuować programowanie wspólnie ze mną w tym poście pobierz aplikację z pierwszego posta.

1. Zaczniemy od edycji shella i dodaniu regionu w którym będziemy wyświetlać widoki przycisków przełączających widok w naszym głównym regionie – MainRegion.

<DockPanel LastChildFill="True" >
<ItemsControl DockPanel.Dock="Top" prism:RegionManager.RegionName="MenuRegion" />
<ContentControl DockPanel.Dock="Bottom" prism:RegionManager.RegionName="MainRegion" />
</DockPanel>

2. Następnie do projektu modułu A dodajmy widok (add user control) który będzie elementem naszego menu i nazwijmy go ButtonViewA, będzie to zwykły button pod który później podepniemy komendę, po jego dodaniu zarejestrujmy go w klasie modułu, tak aby był widoczny od razu po jego załadowaniu. Leczy tym razem ten widok rejestrujemy do regionu o nazwie MenuRegion, który to przed chwilą skonfigurowaliśmy w naszym shellu. Zmieniamy również sposób w jaki będziemy pokazywać nasz główny widok w regionie MainRegion. Zamiast rejestrować go od razu do region menadżera zarejestrujemy go do kontenera, którego instancje wcześniej musimy pobrać np w konstruktorze.

public class ModuleA : IModule
 {
 protected IRegionManager RegionManager { get; private set; }
 protected IUnityContainer Container { get; private set; }

 public ModuleA(IRegionManager regionManager, IUnityContainer container)
  {
   Container = container;
   RegionManager = regionManager;
  }

 public void Initialize()
  {
   Container.RegisterType<Object, ModuleAView>(typeof(ModuleAView).Name);
   RegionManager.RegisterViewWithRegion("MenuRegion", typeof(ButtonViewA));
  }
 }

3. Dodajmy teraz projekt drugiego modułu analogicznie jak dodany został moduł A w pierwszym poście, nazwijmy go ModuleB, i nie zapomnijmy dodać go do katalogu modułów w klasie bootstrapper-a. Dodajmy do niego również widoki podobnie jak mamy je zorganizowane w pierwszym module i tak samo zarejestrujemy w metodzie Initialize.

Tak skonfigurowaną aplikację mamy prawie gotową do nawigacji pomiędzy widokami. Pozostał jeszcze jeden bardzo ważny element czyli view modele do widoków gdzie będziemy bindować nasze komendy przełączające widoki.

Poniższe czynności wykonujemy dla każdego z modułów.

1. Dodajemy klasę view modelu. W klasie tej wykonujemy następujące czynności:

a) Pobieramy instancję region menadżera z pomocą ServiceLocatora

b) Tworzymy komendę typu DelegateCommand, i inicjalizujemy ją w konstruktorze view modelu

c) Do naszej komendy dodajemy metodę SwitchView, w ciele tej metody wykonujemy metodę region mendażera RequestNavigate w której określamy do jakiego regionu wstawiamy widok, oraz jaki widok, określany po jego nazwie użytej podczas rejestrowania widoku do kontenera.

public class ButtonViewBViewModel
 {
 private readonly IRegionManager regionManager;
 public DelegateCommand SwitchViewCommand { get; set; }

  public ButtonViewBViewModel()
  {
   regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
   SwitchViewCommand = new DelegateCommand(SwitchView);
  }

  private void SwitchView()
  {
   regionManager.RequestNavigate("MainRegion", new Uri("ModuleBView", UriKind.Relative));
  }
 }

2. Łączymy nasz view model z DataContext-em naszego widoku …

public partial class ButtonViewB : UserControl
 {
 public ButtonViewB()
   {
    InitializeComponent();
    this.DataContext = new ButtonViewBViewModel();
   }
 }

i bindujemy naszą komendę w xamlu.

<Button Content="Przełącz na widok B" Command="{Binding SwitchViewCommand}" />

Jak pisałem wyżej te same czynności należy wykonać dla każdego modułu. Po poprawnym wykonaniu wszystkich kroków powinniśmy uzyskać efekt w postaci aplikacji PRISM przełączającej widoki z pomocą nawigacji. Efekt końcowy widoczny na poniższym gifie.

Animation 0

 

 

 

 

 

 

 

 

 

Kod źródłowy do pobrania z githuba

  • shaggy

    Świetny tutek, właśnie byłem na etapie kiedy klasyczne wykorzystanie wzorca MVVM okazało się zbyt uciążliwe i zacząłem interesować się pakietem Prism. Ten tutorial w bardzo szybki sposób wprowadził mnie w podstawy Prism a szczególnie w nawigację na czym mi najbardziej zależało. Za bardzo dziękuję autorowi i zachęcam do kontynuacji tutoriala bo na pewno pozwoli on wielu początkującym w bardzo szybki sposób zrozumieć jak to wszystko działa :-)

  • Dziękuje za słowa uznania! :) Jako że to pierwszy komentarz na Tym blogu i to w dodatku tak budujący to jeżeli chcesz to zapraszam do kontaktu w przypadku problemów z PRISM lub czymkolwiek o czym tutaj piszę. Postaram się pomóc jak tylko będę najlepiej potrafił.

  • shaggy

    W związku z oderwaną przez kolegę chęcią pomocy mam pytanie :-) W podanym przykładzie instancje okien modułów są tworzone podczas startu aplikacji a następnie przełączane za pomocą buttonów. Jak skonfigurować prism tak aby jakieś mało używane okno np Modułu C było tworzone po naciśnięciu buttonu a następnie po zamknięciu go np buttonem “Zamknij” jego instancja była niszczona i w razie potrzeby podczas ponownej próby użycia Modułu C byłą tworzona na nowo?

    • shaggy

      Sory źle napisałem, instancje Modelów tworzone są po nienacięciu buttona a nie startu aplikacji, a pytanie jak zamknąć (“usunąć”) instancje tak aby po przełączeniu widoku i otwarciu go ponownie utworzona została nowa instancja klasy danego modelu.

      • Cześć! Nie za bardzo rozumiem o co Ci dokładnie chodzi. W podanym przez mnie prostym przykładzie view model danego widoku jest tworzony za każdym razem podczas przełączania widoków. Można oczywiście wymuszać inne zachowanie, polecam Ci sprawdzić dokumentacje interfejsu z tej biblioteki o nazwie INavigationAware, dzięki temu interfejsowi można wymuszać zachowanie po dokonanej nawigacji. lub sprecyzuj może dokładnie o co chodzi.
        Pozdrawiam.

  • Daniel

    A ja mam pytanie może podobne co shaggy, mianowicie jak dynamicznie stworzyć takie moduły, chodzi mi dokładnie o to co tutaj tworzymy z tym ze np chciałbym mieć jeden moduł który jest wywoływany kilka razy po naciśnięciu buttona a nie na sztywno tworzyć ich nie wiadomo ile. Czyli mieć np jeden przycisk “wczytaj” i po każdym kliknięciu tworzyłby i otwierałby mi się kolejny moduł Jest w ogóle taka możliwość?