Artykuł

freeimages.com freeimages.com
lip 26 2015
0

Więcej MVVM w Universal Apps dzięki Behaviors SDK

Technologie typu WPF, czy Universal Apps aż proszą się o zastosowanie MVVM, jednak wsparcie dla tej architektury po stronie tych rozwiązań, jest umówmy się - co najwyżej średnie. Bardzo mało kontrolek posiada wbudowaną obsługę komend. I nawet jeśli się ona pojawia, to i tak nie dotyczy ona wszystkich dostępnych w kontrolce zdarzeń. Poza tym, nie ma jawnego wsparcia dla ViewModeli. Standardowo zbudowana aplikacja składa się ze stron wykorzystujących code-behind.

Na szczęście oba te problemy można w pewnym sensie dość mocno zmarginalizować. O tym drugim pisałem już trochę przy okazji tekstu poświęconego bibliotece MVVM Light. Ten pierwszy jest odrobinę bardziej skomplikowany, ale również można go stosunkowo łatwo obejść. Wystarczy tylko włączyć w projekcie rozszerzenie Behaviors SDK, a następnie dokonać odpowiedniej implementacji:-) Nad tym właśnie tematem skupię się w dzisiejszym tekście.

Na koniec wstępu, jeszcze mała uwaga techniczna. Przykłady zostały napisane pod kątem systemu Windows Phone 8.1 (UA), ale z zaprezentowanych tutaj rozwiązań można skorzystać również w aplikacjach pisanych dla Windows 8.1, czy WPF:-)

Instalacja

Behaviors SDK dostępny jest w postaci rozszerzenia Visual Studio, które możemy aktywować na ekranie Add Reference. Odpowiedniej wersji rozszerzenia powinniśmy szukać w sekcji dedykowanej docelowemu systemowi operacyjnemu. Jeśli tworzymy Universal Apps dla Windows Phone 8.1 oraz Windows 8.1, to musimy osobno aktywować dodatek w obu projektach.

Behaviors SDK - ogólna idea

Behavior (po polsku - zachowanie) to w pewnym sensie mechanizm, który nasłuchuje/oczekuje na realizację określonych zdarzeń, zachowań. Możemy np. stworzyć Behavior który będzie reagował na pewne zdarzenia (np. kliknięcie na element), albo też taki, który będzie w stanie zmienić określony fragment strony, na bazie wartości właściwości zdefiniowanej w ViewModelu. Zasadniczo mamy dostęp do trzech podstawowych grup behaviorów:

  • DataTriggerBehavior - reaguje na zmianę właściwości. Akcja dla niego zdefiniowana jest aktywowana wtedy, gdy właściwość przyjmuje określoną przez nas wartość
  • EventTriggerBehavior - nasłuchuje na konkretne zdarzenia - np. Tapped, DoubleTapped. Jeśli wykryje wystąpienie zdefiniowanego zdarzenia dla wskazanego elementu, to wtedy pozwoli na przeprowadzenie przypisanej do niego akcji
  • IncrementalUpdateBehavior - pozwala na obsłużenie sytuacji, w której treści są doładowywane (trochę na ten temat pisałem tutaj)

Oprócz wymienionych wyżej grup zachowań, mamy również dostęp do wspominanych wcześniej akcji. I tak w reakcji na określony behavior możemy skorzystać z następujących metod:

  • CallMethodAction - pozwala na wywołanie określonej metody - np. z ViewModelu. Standardowo wymaga metody bez argumentów. Jeśli argumenty są konieczne, muszą się one wpisać do maski (object sender, object params)
  • ChangePropertyAction - modyfikuje wartość właściwości wskazanego elementu
  • GoToStateAction - pozwala na przejście do określonego VisualState - znacznie ułatwia ich implementację:-)
  • InvokeCommandAction - chyba kluczowa akcja - pozwala na wywoływanie komend umieszczonych w ViewModelu. Obsługuje parametry zdefiniowane dla komend
  • NavigateToPageAction - pozwala na przeprowadzenie nawigacji do określonej strony

Przykład praktyczny - założenia

Zobrazowanie wszystkich możliwości Behaviors SDK zajęłoby naprawdę sporo miejsca i czasu, dlatego też przykład praktyczny będzie dość ograniczony. Punktem wyjścia będzie projekt przygotowany na potrzeby wpisu Biblioteki warte poznania - MVVM Light.

Do wcześniej utworzonego rozwiązania, dodamy przycisk Zmień kolor, który będzie reagować na zdarzenia Tapped oraz DoubleTapped (jak nie trudno się domyślić, przycisk ten będzie zmieniał kolor, a konkretniej kolor tła kontenera). Ponadto, dla każdego elementu typu ListViewItem dodamy obsługę zdarzenia Tapped. W wyniku aktywacji tego zdarzenia, zostanie wywołana komenda, która na ekranie wyświetli MessageDialog z nazwą aktualnie tapnietego elementu.

Przykład praktyczny - realizacja

Tak jak wspomniałem wcześniej, w przykładzie rozszerzymy istniejący kod. Zaczniemy od modyfikacji ViewModelu.

W tym przypadku dodajemy tylko obsługę nowej komendy. Dla zwiększenia czytelności, pominąłem fragmenty istniejącego kodu:

public class MainViewModel : ViewModelBase
{
	// ...

	public MainViewModel()
	{
		// ...
		this.SampleBehaviorCommand = new RelayCommand<string>(async (txt) =>
		{
			await SampleBehaviorActionAsync(txt);
		});
	}
	
	// ...
	
	public ICommand SampleBehaviorCommand
	{
		get;
		private set;
	}
	
	private async Task SampleBehaviorActionAsync(string txt)
	{
		MessageDialog msg = new MessageDialog(txt, "Tytuł");
		await msg.ShowAsync();
	}
}

Więcej modyfikacji czeka nas po stronie pliku XAML. Tutaj musimy przede wszystkim podpiąć dwie nowe przestrzenie nazw w sekcji deklaracji strony:

xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"

A następnie zmodyfikować ContentRoot. Również w tym przypadku pominę istniejące fragmenty, a zaakcentuję tylko zmiany i nowości:

<Grid Grid.Row="1" x:Name="ContentRoot" Margin="19,9.5,19,0">
	<Grid.RowDefinitions>
		<RowDefinition Height="Auto" />
		<RowDefinition Height="Auto" />
		<RowDefinition Height="Auto" />
		<RowDefinition Height="*" />
	</Grid.RowDefinitions>

	<!-- Istniejące buttony -->
	<Button Content="Zmień kolor" Grid.Row="2" HorizontalAlignment="Stretch">
		<i:Interaction.Behaviors>
			<core:EventTriggerBehavior EventName="Tapped">
				<core:ChangePropertyAction TargetObject="{Binding ElementName=ContentRoot}"
									   PropertyName="Background" Value="Black" />
			</core:EventTriggerBehavior>
			<core:EventTriggerBehavior EventName="DoubleTapped">
				<core:ChangePropertyAction TargetObject="{Binding ElementName=ContentRoot}"
									   PropertyName="Background" Value="Blue" />
			</core:EventTriggerBehavior>
		</i:Interaction.Behaviors>
	</Button>
	
	<ListView ItemsSource="{Binding Items}" Grid.Row="3">
		<ListView.ItemTemplate>
			<DataTemplate>
				<StackPanel Margin="0,0,0,27.5">
					<i:Interaction.Behaviors>
						<core:EventTriggerBehavior EventName="Tapped">
							<core:InvokeCommandAction Command="{Binding 
								DataContext.SampleBehaviorCommand, ElementName=ContentRoot}"
								CommandParameter="{Binding}" />
						</core:EventTriggerBehavior>
					</i:Interaction.Behaviors>
					<TextBlock Text="{Binding}" Style="{ThemeResource ListViewItemTextBlockStyle}" />
				</StackPanel>
			</DataTemplate>
		</ListView.ItemTemplate>
	</ListView>
</Grid>

Behaviory zawsze definiujemy wewnątrz określonego elementu. Zawsze też musimy wskazać rodzaj, a także przypiąć odpowiednią akcję. Behaviory dobrze współpracują z IntelliSense, dlatego też nie będę się więcej rozpisywał na temat - kod zdaje się być całkiem klarowny:-)

Jedno o czym warto pamiętać stosując te technologię to fakt, że nie wszystkie zdarzenia są obecnie obsługiwane przez ten mechanizm. Poza tym, jeśli odczuwacie jakiekolwiek ograniczenia, to zawsze możecie oprogramować swój własny Behavior:)

Projekt utworzony w niniejszym wpisie, znajdziecie w dziale download.

Materiały

Data ostatniej modyfikacji: 14.10.2015, 20:14.

Podoba Ci się ten wpis? Powiedz o tym innym!

Send to Kindle

Komentarze

blog comments powered by Disqus