Windows Phone – wyświetlanie błędów walidacji

Kilka miesięcy temu w tym poście przedstawiałem różne sposoby walidacji jakie są dostępne na platformie Silverlight. Jako, że ostatnio mam więcej czasu aby pisać jakieś proste aplikacje pod Windows Phonem potrzebowałem mechanizmu walidacji działającego pod tą platformą. Według MSDN-u wszystkie metody walidacji przedstawione we wspomnianym wcześniej poście powinny działać. Niestety po przerzuceniu mechanizmu z Silverlighta na Windows Phona okazało się, że błędy walidacji nie zostają wyświetlone na interfejsie. Po przeszukaniu kilku for oraz blogów dowiedziałem się, że przyczyną takiego stanu rzeczy jest brak obsługi reakcji na błędy w templacie TextBoxów (i innych elementów typu input). Zatem aby wyświetlić błędy walidacji, pozostaje nam zmodyfikować istniejący template TextBox-a. Zacznijmy od wyciągnięcia standardowego templatu TextBox-a z Windows Phona. W tym celu możemy użyć aplikacji Expression Blend. Otwórzmy w niej jakikolwiek projekt typu Windows Phone Application, w którym na kontrolce mamy naniesionego Textboxa. W moim przypadku, po wczytaniu projektu do Blenda ekran wygląda w następujący sposób
blend
Następnie zaznaczamy w Blendzie TextBox-a i z opcji znajdujących się w lewym górnym rogu aktualnej zakładki wybieramy TextBox->Edit Template-> Edit Copy.
BlendTemplateExtraction
W oknie, które się pojawi klikamy OK. Następnie możemy przejść do widoku XAML-a, w sekcji phone:PhoneApplicationPage.Resources powinien pojawić się nam nowy styl wyglądający w ten sposób

W ten oto sposób udało nam się wydobyć domyślny styl oraz template TextBoxa w Windows Phonie. W kolejnym kroku musimy zmodyfikować ten template w taki sposób, aby zaczął reagować na zmiany stanu walidacji. Zacznijmy od sprawdzenia jakie stany wizualne może obsługiwać TextBox. W tym celu musimy przeglądnąć metadane TextBoxa – możemy to zrobić klikając PPM na klasie TextBox-a i wybierając Go To Definition. Naszym oczom powinno ukazać się coś takiego.
validationstates
Widzimy, że klasa TextBox jest opatrzona atrybutami TemplateVisualState, nas interesują najbardziej trzy z tych atrybutów:

  • [TemplateVisualState(GroupName = “ValidationStates”, Name = “Valid”)]
  • [TemplateVisualState(GroupName = “ValidationStates”, Name = “InvalidFocused”)]
  • [TemplateVisualState(GroupName = “ValidationStates”, Name = “InvalidUnfocused”)]

Mówią nam one, że w przypadku gdy wystąpią błędy walidacji nasza kontrolka przechodzi w stan InvalidFocused lub InvalidUnfocused. Natomiast, w przypadku gdy nie ma błędów znajduje się ona w stanie Valid. Analizując kod domyślnego templatu TextBoxa widzimy, że stany z grupy ValidationStates nie są obsługiwane – czas zatem to zmienić. Po pierwsze do templatu musimy dorzucić elementy, które będą wyświetlały błędy walidacji. Zmieńmy zatem opakowujący wszystko element Grid na StackPanel, a następnie dodajmy element typu TextBlock oraz Border. Elementy wyświetlające błędy walidacji musimy umieścić w jednym kontenerze z elementami odpowiadającymi za wyświetlanie tekstu. Zatem musimy je opakować w Grida. Po zastosowaniu powyższych wskazówek nasz template będzie wyglądał w następujący sposób

Dodanie elementów wyświetlających błędy to nie wszystko, musimy jeszcze określić kiedy nasze elementy mają być widoczne. W chwili obecnej ich właściwość Visibility jest ustawiona na Collapsed. Zatem elementy te nie będą widoczne, jak również nie będą zajmowały miejsca na interfejsie. Jak już wcześniej wspomniałem kontrolka TextBoxa w przypadku wystąpienia błędów walidacji przejdzie do stanów InvalidFocused lub InvalidUnfocused. Pozostaje nam zatem zareagować na przejście kontrolki w te stany i odpowiednio zmodyfikować właściwość Visibility bordera oraz textblocka. W tym celu posłużymy się VisualStateManagerem oraz animacjami. Do naszego templatu dodajmy następujący wpis

W ten oto sposób zdefiniwaliśmy w XAML-u grupę (VisualStateGroup ) o nazwie ValidationStates – zauważmy, że nazwa grupy jest taka sama jak nazwa grupy z atrybutów klasy TextBox. Dodaliśmy również trzy stany wizualne (VisualState), których nazwy również odpowiadają nazwą zdefiniowanym w atrubytach klasy TextBox. Od tej chwili gdy kontrolka TextBoxa będzie posiadała błędy walidacji, zostanie odpalona animacja zmieniająca właściwość Visibility bordera “ValidationBorder” oraz TextBlocka “ValidationMessage” na Visible. Ostatecznie cały gotowy template będzie wyglądał w następujący sposób

Tak natomiast przedstawiają się błędy walidacji wyświetlone na TextBoxach
wyświetlanie błędów walidacji

Windows Phone – wyświetlanie błędów walidacji

Windows Phone – własny DataTemplateSelector

Pisząc moją małą aplikację pod Windows Phone po raz kolejny natknąłem się na problem. Pod WP7 nie ma tak przydatnej rzeczy jaką jest DataTemplateSelector znany nam bardziej, lub mniej z Silverlighta oraz WPF-a.Na szczęście napisane własnego DataTemplateSelector-a nie jest specjalnie skomplikowane. Jak zwykle w takich przypadkach liczy się pomysł – jak dobrze, że jest Google. Nasz customowy DataTemplateSelector zostanie oparty o kontrolkę ContentControl. Po pierwsze stwórzmy klasę bazową DataTemplateSelectora dziedziczącą po ContentControl,w której przeciążamy funkcję OnContentChanged,

Klasa ta będzie klasą z której będą wywodzić się wszystkie nasze TemplateSelectory. Załóżmy, że chcemy zbudować DataTemplateSelector, który będzie obsługiwał aplikację chata. Powinien on zatem reagować na dwa typy elementów:

  • wiadomości przychodzące
  • wiadomości wychodzące

Stwórzmy zatem “wyspecjalizowany” DataTemplateSelector, który w zależności od typu wiadomości zwróci odpowiedni DataTemplate.

W klasie ChatMessageTemplateSelector przeciążyliśmy metodę SelectTemplate. W metodzie tej, na podstawie zbindowanego obiektu (parametr item) określamy jaki template powinniśmy zwrócić. Templaty do wiadomości przychodzących oraz wychodzących zostały zdefiniowane jako propertisy.

  • Out – template wiadomości wychodzącej
  • In – template wiadomości przychodzącej

Pozostaje nam tylko zdefiniować wymienione wyżej templaty. Nie będziemy robić tego w kodzie, ale w XAML-u. Nasz nowo stworzony DataTemplateSelector wykorzytamy w ListBox-ie

Jako, że nasz ChatMessageTemplateSelector jest tak naprawdę obiektem typu ContentControl, zatem możemy go zbindować do właściwości ItemTemplate ListBox-a. Zasób GeneralChatMessageTemplate wygląda w następujący sposób

Jak widać jest to obiekt typu ChatMessageTemplateSelector, którego właściwość Content jest zbindowana do aktualnego kontekstu (pojedyncze słowo kluczowe Binding). Widzimy również, że ustawiliśmy wartości właściwości odpowiadających za templaty poszczególnych wiadomości (In=”{StaticResource IncomingChatMessageTemplate}” Out=”{StaticResource OutcomingChatMessageTemplate}”). Wspomniane templaty wyglądają w następujący sposób

Od teraz gdy do ListBox-a zostanie dodany nowy item, pierwszą rzeczą jaka się odpali będzie funkcja OnContentChanged. W funkcji tej AbstractDataTemplateSelector na podstawie nowo przybyłego itemu ustawi właściwość ContentTemplate, na taki template jaki zwróci mu funkcja SelectTemplate. Wynik działania ChatMessageTemplateSelector wygląda w następujący sposób
DataTemplateSelector

Windows Phone – własny DataTemplateSelector