Kolejny artykuł z serii View Based Navigation in Prism. Tym razem kilka przydatnych wiadomości nt. dwóch bardzo przydatnych interfejsów z których na pewno będziemy korzystać nie raz, podczas implementacji nawigacji w naszej aplikacji tworzonej w WPF/Silverlight przy użyciu Prism-a.
W ostatnim poście opisałem w jaki sposób w Prism implementujemy nawigację. Z nawigacją związane są bardzo silnie dwa interfejsy:
Code:
public interface INavigationAware
{
void OnNavigatedTo(NavigationContext navigationContext);
bool IsNavigationTarget(NavigationContext navigationContext);
void OnNavigatedFrom(NavigationContext navigationContext);
}
Metody zawarte w tym interfejsie:
- IsNavigationTarget - decyduje czy dany widok uczestniczy w procesie nawigacji (domyślnie jeżeli widok/VM nie implementuje tego interfejsu jest przyjmowane true)
- OnNavigateFrom - metoda wywoływana jest przed przystąpieniem do zmiany widoku
- OnNavigateTo - metoda wywoływana jest po zmianie widoku (załadowaniu nowego widoku)
Cały proces nawigacji można prześledzić na diagramie zawartym w dokumentacji Prisma:
Jak zobaczyć w praktyce działanie powyższych metod? Skorzystajmy z stworzonego przez nas ostatnio przykładu i dołóżmy dodatkowe elementy
W widokach A i B dokładamy właściwość CurrentTime oraz Counter oraz implementujemy interfejs INavigationAware:
Code:
using System;
using Microsoft.Practices.Prism.Regions;
using System.ComponentModel;
namespace ModuleA
{
public class ModuleAViewModel : INotifyPropertyChanged, INavigationAware
{
public DateTime CurrentTime
{
get { return DateTime.Now; }
}
private int _counter;
public int Counter
{
get { return _counter; }
set
{
_counter = value;
RaisePropertyChanged("Counter");
}
}
public void OnNavigatedTo(NavigationContext navigationContext)
{
Counter++;
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
W VM B kod wygląda tak samo więc nie będę go tutaj dwa razy kopiować.
W widokach dokładamy dodatkowe TextBlock-i do wyświetlenia dodatkowych informacji:
Code:
<UserControl x:Class="ModuleB.ModuleBView"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="Moduł B" />
<TextBlock Text="{Binding CurrentTime, Mode=OneWay}" Grid.Row="1" />
<TextBox Text="{Binding Counter}" Grid.Row="2" />
</Grid>
</UserControl>
Odpalmy aplikację i poprzemieszczajmy się pomiędzy widokami:
Efekt jest taki, że data pozostaje cały czas taka sama, a licznik w obu modułach jest inkrementowany.
Jest to dla nas o tyle ważne, że już teraz widzimy iż nasze widoki nie są niszczone. Przechodząc pomiędzy kolejnymi gałęziami naszego meni nawigacyjnego widoki pod spodem pozostają te same (te same instancje klasy).
Korzystanie z tej samem instancji pomiędzy kolejnymi przemieszczeniami się, jest w większości przypadków dla nas korzystną opcją, jednak nie zawsze. Niekiedy wymagamy aby widok był tworzony za każdym razem od nowa. Aby osiągnąć taki rezultat, należy skorzystać z innego interfejsu:
Interfejs ten ma następującą definicję:
Code:
public interface IRegionMemberLifetime
{
bool KeepAlive { get; }
}
Występuje tutaj jedna właściwość KeepAlive, która definiuje czy dany widok w regionie ma być tworzony za każdym razem od nowa, czy też ma zostać użyta już raz stworzona instancja.
Aby zobrazować zasadę działania, zaimplementujemy ten interfejs w module B:
Code:
using System;
using System.ComponentModel;
using Microsoft.Practices.Prism.Regions;
namespace ModuleB
{
public class ModuleBViewModel : INotifyPropertyChanged, INavigationAware, IRegionMemberLifetime
{
public DateTime CurrentTime
{
get { return DateTime.Now; }
}
private int _counter;
public int Counter
{
get { return _counter; }
set
{
_counter = value;
RaisePropertyChanged("Counter");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public void OnNavigatedTo(NavigationContext navigationContext)
{
Counter++;
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
}
public bool KeepAlive
{
get { return false; }
}
}
}
Zwracamy wartość false więc nasz widok nie powinien być przetrzymywany w pamięci. Co to oznacza w przypadku rozważanego przykładu?
Zobaczmy efekt działania po kilkakrotnym przemieszczeniu się pomiędzy modułami A i B
Na pierwszym screenie widać Moduł A, na drugim B. Licznik pierwszego modułu ma wartość 9 a drugiego 1. Także czas na pierwszym screenie nie zmienił się pomimo upływu go podczas przemieszczania się między widokami jak to ma miejsce w module A.
Z tego wyciągamy wnioski, iż moduł A został raz zainicjowany i jego instancja była używana w dalszej nawigacji. Moduł B za każdym razem był inicjowany od nowa.
Omówione tutaj interfejsy z pewnością nie raz przydadzą się nam w naszych aplikacjach w przypadku nawigacji.
Brak komentarzy:
Prześlij komentarz