INotifyPropertyChange – notifying about change of all properties at once

Today, working on a new feature for my pet project, I realized that I have to notify the view, that all properties in view model have changed. The most obvious way to achieve that would, of course be to rise PropertyChange event a bunch of times.

This is good solution for one time usage, however I was interested in something more general, something which could be extracted to base view model. Fortunately, it turns out that there is a simple trick to do that. All You have to do is use an empty string or null as a property name. So in my case this comes down to this one-liner

INotifyPropertyChange – notifying about change of all properties at once

Tworzenie bootstrappera aplikacji przy użyciu CaliburnMicro

Witam po długiej przerwie. W dzisiejszym wpisie postaram się krótko opisać w jaki sposób stworzyć bootstrapper aplikacji przy użyciu CaliburnMicro. Ponadto przedstawię w jaki sposób skonfigurować bootstrapper w taki sposób, aby Caliburn wykorzystywał nasz własny kontener IOC.

1. Wstęp

CaliburnMicro jest to framework MVVM, który w znacznym stopniu ułatwia i przyśpiesza pisanie aplikacji pod WPF,Silverlight,WindowsPhone oraz WinRT. Zdecydowałem się poznać ten framework z uwagi na jego przenośność na różne platformy. Wcześniej całkiem sporo czasu poświęciłem Prismowi, jednakże z powodu licznych problemów z jego działaniem pod WindowsPhonem chciałem spróbować czegoś nowego.

2. Bootstrapper – Silverlight

Bootstrapper jest to ogólnie rzecz biorąc klasa inicjalizująca całą aplikację. W trakcie odpalania bootstrappera na ogół konfiguruje się kontener IOC(rejestruje wszystkie potrzebne typu), inicjalizuje się połączenie z serverem, oraz odpala się główne okno aplikacji – tzw. Shella. Dlatego też musimy odchudzić plik App.xaml.cs i zostawić w nim jedynie konstruktor wraz z wywołaniem funkcji InitializeComponents()

Zacznijmy od utworzenia nowego projektu typu SilverlightApplication, do którego dodajemy referencje do dll-ek Cliburna. Możemy to zrobić poprzez NuGetta (niestety u mnie one nie działały :D), jak również możemy je ściągnąć z oficjalnej strony projektu http://caliburnmicro.codeplex.com/releases/view/81466. Następnie stwórzmy główne okno aplikacji(wspomnianego wcześniej Shella) oraz jego viewmodel. W tym celu dodajemy do projektu Silverlight User Control o nazwie ShellView
ShellView
oraz nową klasę ShellViewModel.
ShellViewModel
Dorzućmy jeszcze jakiegoś TextBox-a do naszego ShellView, tak aby mieć pewność, że rzeczywiście odpowiednie okno jest widoczne przy starcie aplikacji

W kolejnym kroku musimy stworzyć właściwy bootstrapper – dodajmy nową klasę o nazwie SilverlightBootstrapper, która dziedziczyć będzie po klasie Bootstrapper

Klasa Bootstrapper jest to klasa dostarczona przez CaliburnMicro, generyczny parametr T określa nam viewmodel na podstawie którego framework będzie wyszukiwał odpowiedni widok shella ze swojego wbudowanego kontenera IOC (Caliburn podczas uruchomienia aplikacji rejestruje dostępne typu w kontenerze). Zgodnie z domyślną konwencją, CaliburnMicro jako główne okno aplikacji ustawi widok, który nazywać się będzie ShellView. W celu odpalenia naszego bootstrappera musimy jeszcze umieścić go w zasobach aplikacji. Zmodyfikujmy zatem plik App.xaml aby wyglądał w następujący sposób

Kompilując, a następnie uruchamiając nasz projekt naszym oczom powinien ukazać się następujący widok
Client.Silverlight

Powyżej przedstawiłem w jaki sposób stworzyć najprostszą wersję bootstrapera z wykorzystaniem CaliburnMicro oraz jego domyślnego kontenera IOC. Jednakże najczęściej jest tak, że w aplikacji wykorzystujemy już jakiś bardziej zaawansowany kontener i po prostu nie chcemy wykorzystywać jednego kontenera do “rozwiązywania” widoków, a drugiego do pozostałych rzeczy. Dlatego też pokaże teraz w jaki sposób skonfigurować napisany wcześniej bootstrapper tak aby widoki były wyciągane z naszego kontenera IOC. Zacznijmy do dodania do naszego projektu referencji do NInjecta, a następnie utwórzmy klasę IOCContainer (będącą naszym customowym kontenerem), wyglądającą w następujący sposób

Następnie w klasie SilverlightBootstraper musimy przeciążyć funkcję Configure,GetAllInstances oraz GetInstance. Jako, że będziemy korzystali z naszego własnego kontenera w funkcji Configure rejestrujemy wszystkie potrzebne nam viewmodele oraz widoki. Zatem funkcja Configure powinna od teraz wyglądać tak

Następnie musimy “pokazać” Caliburn-owi gdzie powinien szukać widoków. Dlatego też przeciążamy funkcje GetInstance oraz GetAllInstances i zmieniamy ich postać na następującą

Od tej pory, za każdym razem gdy użyjemy mechanizmów Caliburna do bindowania viewmodelu z widokiem itp.,Caliburm będzie szukał widoków oraz viewmodeli w naszym kontenerze IOC.

2.2 Własna konwencja wyszukiwania widoków

W przedstawionym powyżej przykładzie bootstrappera, nasz bootstrapper korzystał z domyślnej konwencji rozwiązywania widoków na podstawie viewmodeli (do ShellViewModel został dopasowany widok ShellView). Czasem jednak konwencja ta, nie pasuje do konwencji przyjętej w danym projekcie. Sam biorę udział w projekcie, w którym widoki w kontenerze są rejestrowane następujący sposób

Czy zatem oznacza to, że nie mogę korzystać już mechanizmów Caliburna i musze zrezygnować chociażby z bootstrappera ?Oczywiście,że nie. Framework dostarcza nam możliwość zdefiniowania własnej konwencji wyszukiwania widoków na podstawie viewmodeli. W celu zastąpienia domyślnej konwencji musimy podpiąć się pod propercję LocateForModelType znajdującą się w klasie ViewLocator i zdefiniować własną funkcję wyszukującą widok na podstawie viewmodelu. W moim przypadku wygląda to w następujący sposób

Funkcja ViewLocator.LocateForModelType odpalana jest za każdym razem, gdy CaliburnMicro chce wyszukać widok na podstawie viewmodleu. Najważniejszym argumentem funkcji LocateForModelType jest modelType – czyli typ viewmodelu, który posłuży nam do znalezienia widoku – a właściwie jego typu. Po odnalezieniu typu widoku, wywołujemy funkcję

(które z kolei znajdzie nam w kontenerze pożądanym przez nas typ widoku) i zwracamy jej rezultat.

3. Bootstrapper – Windows Phone

W przypadku bootstrappera dla Windows Phona sytuacja wygląda odrobinę inaczej. W celu utworzenia własnego bootstrappera musimy rozszerzyć klasę PhoneBootstrapper. Utwórzmy zatem nowy projekt typu Windows Phone Application, oraz dodajmy do niego nową kontrolkę typu Windows Phone Portrait Page.
ShellViewPortrait
Podobnie jak w przypadku projektu Silverlightowego dodajemy referencję do CaliburnMicro ,dorzucamy klasę viewmodelu (którą nazywamy ShellViewModel) oraz dodajemy jakiegoś textboxa do ShellView. Następnie musimy utworzyć nasz bootstrapper, dodajmy zatem klasę WindowsPhoneBootstrapper rozszerzającą klasę PhoneBootstrapper. Podobnie jak w przypadku bootstrappera silverlightowego przeciążamy funkcje odpowiedzialne za konfigurację kontenera IOC.

Zwróćmy uwagę, że w kontenerze IOC zostały zarejestrowane usługi nawigacji Windows Phona, do których został przekazany RootFrame. RootFrame jest to główne okno aplikacji, zostaje ono utworzone przez CliburnMicro. Tak samo jak w przypadku projektu Silverlightowego w pliku App.xaml.cs zostawiamy jedynie konstruktor z wywołaniem funkcji InitializeComponents(). W celu ustawienia ShellView jako RootFram-a musimy zmodyfikować plik WMAppManifest.xml (znajdujący się w katalogu Properties). Odnajdujemy tam wpis

i zamieniamy go na

Jak widać WindowsPhone wymusza tutaj podejście ViewFirst. Przy uruchomieniu aplikacji mechanizm nawigacji WindowsPhona odpali widok ShellView (bez wiedzy Caliburn’a), natomiast CaliburnMicro dopasuje odpowiedni ViewModel do naszego widoku – na podstawie domyślnej konwencji. Jeżeli chcielibyśmy zmodyfikować sposób wyszukiwania ViewModeli do widoków musimy podpiąć się do propertisa LocateForViewType znajdującego się w klasie ViewModelLocator

W ostatnim kroku musimy jeszcze tylko dodać nasz bootstrapper do zasobów aplikacji

odpalając teraz program naszym oczom powinien ukazać się taki oto widok
CaliburnMicro

Tworzenie bootstrappera aplikacji przy użyciu CaliburnMicro

Lokalizowanie aplikacji WPF oraz Silverlight 5 przy użyciu MarkupExtension

W poprzednim wpisie przedstawiłem w jaki sposób można lokalizować aplikację napisaną w Silverlight 4 oraz Windows Phone, wykorzystując do tego ten sam mechanizm. Tym razem zademonstruje w jaki sposób można nieco uprościć składnie tłumaczenia wykorzystując do tego MarkupExtension.
Jeżeli kiedykolwiek pisałeś coś w Silverlighcie, Windows Phonie lub WPF-ie istnieje duża szansa, że używałeś już MarkupExtension. Do najpopularniejszych MarkupExtensions należą takie słowa kluczowe (używane w XAML-u) jak:

  • Binding
  • StaticResource
  • DynamicResource
  • TemplateBinding

Na potrzeby mechanizmu lokalizowania aplikacji WPF (ewentualnie Silverlight 5) stworzymy customowe MarkupExtenssion, które będzie odpowiedzialne za tłumaczenie elementów UI naszej aplikacji.
Zacznijmy od przygotowania plików zasobów. Podobnie jak w poprzednim wpisie utwórzmy trzy pliki:

  • Localization.resx
  • Localization.pl-PL.resx
  • Localization.en-US.resx

W plikach tych będziemy przechowywać nasze tłumaczenia. Następnie stwórzmy klasę Translator, która będzie dziedziczyła po klasie MarkupExtension.Do klasy tej dodajmy właściwość

,która będzie przechowywać klucz dzięki któremu z pliku zasobów wyciągniemy tekst w odpowiednim języku. W kolejnym kroku musimy przeciążyć funkcję

tak aby dostarczyła nam ona przetłumaczony tekst.W moim pierwszym podejściu funkcja ta wyglądała w następujący sposób

Wadami takiego rozwiązania było:

  • duża liczba tworzonych obiektów – przy każdym tłumaczeniu tworzyłem nowy obiekt Localization(), który prawdopodobnie może być dość ciężkim obiektem (zwłaszcza gdy będzie przechowywał dużo tłumaczonego tekstu)
  • brak możliwości dynamicznej zmiany języka

Ostatecznie zatem zrezygnowałem z przedstawionej wyżej opcji i zdecydowałem się na metodę odrobinę bardziej zaawansowaną. Po pierwsze utworzyłem klasę TranslationManager

która będzie zarządzała tłumaczeniami. Najważniejszą metodą tej klasy jest oczywiście funkcja

która to zwraca tekst w odpowiednim języku – w zależności od kultury, która zostanie ustawiona w TranslationManagerze. Ponadto TranslationManager posiada jedno zdarzenie

które ma za zadanie poinformować UI o potrzebie odświeżenia zbindowanych elementów. W jaki sposób się to odbywa ? Wszystko zawdzięczamy interfejsowi INotifyPropertyChanged oraz bindingom. Funkcja ProvideValue została zmodyfikowana w następujący sposób

W funkcji tej tworze binding, który binduje się do właściwości Value obiektu TranslationItem. TranslationItem jest to prosty obiekt który udostępnia właściwość Value zwracającą przetłumaczony tekst. Dodatkowo obiekt ten podpina się do zdarzenia LanguageChanged z klasy TranslationManager. W przypadku gdy ktoś zmieni język aplikacji, odpalone zostanie zdarzenie NotifyPropertyChanged, które poinformuje widok o potrzebie odświeżenia odpowiednich elementów. Klasa TranslationItem wygląda zatem w następujący sposób

Mając gotowy mechanizm tłumaczący możemy wykorzystać go w następujący sposób w XAML-u

Zauważmy, że tekst do labelki jest przypisywany z wykorzystywaniem naszego customoweog MarkupExtension – WPFMarkupExtension:Translator (WPFMarkupExtension – jest to alias na namespace, w którym znajduje się nasza klasa Translator). W składni przekazujemy do właściwości Key klucz do tekstu (znajdującego się w resourcach), który chcemy tłumaczyć. W celu zmiany języka wystarczy, że ustawimy interesującą nas kulturę w klasie TranslationManager

Przykładowy widok wykorzystujący napisany translator, oraz pokazujący dynamiczną zmianę języka może wyglądać w następujący sposób (dorzucamy następujące linijki do MainWindow.xaml)

Następnie tworzymy ViewModel do naszego okna

Ostatecznie przypisujemy obiekt klasy MainPageViewModel do DataContextu MainWindow.xaml

MarkupExtension
Kod do projektu można znaleźć pod tym linkiem
http://www.4shared.com/rar/SwDXay7V/WPFMarkupExtension.html

Lokalizowanie aplikacji WPF oraz Silverlight 5 przy użyciu MarkupExtension

Silverlight – koncepcja logowania z użyciem MembershipProviders oraz WCF RIA cz.2

W poprzednim wpisie przedstawiłem w jaki sposób zaimplementować mechanizm logowania po stronie serwera. Wykorzystałem do tego celu WCF RIA oraz znane z ASP MembershipProvidery. Tym razem przedstawię jak wymusić logowanie po stronie klienta, oraz w jaki sposób można dynamicznie zmieniać providerów, którzy walidują usera.
Poprzednim razem stworzyliśmy już szkielet aplikacji, zatem mamy projekt klienta oraz projekt serwera. Zacznijmy od “włączenia” FormsAuthentication po stronie klienta. W pliku App.xaml.cs , w konstruktorze dorzućmy następujące linijki

Następnie zanim pokażemy treść naszej strony, musimy sprawdzić czy użytkownik jest zalogowany.Jeżeli nie, należy pokazać okno logowania. Możemy to zrobić w następujący sposób.

WebContext jest to klasa generowana automatycznie przy buildowaniu solucji.Jej kod powinniśmy odnaleźć w katalogu Generated_Code po stronie klienta. Klasa ta posiada właściwość Current, która zwraca obecny context zarejestrowany w kolekcji ApplicationLifetimeObjects.
Okno logowania zostało zaimplementowane jako ChildWindow. Jego kod może wyglądać w następujący sposób

Kontrolka logowania jest bardzo prosta, składa się ona z dwóch labelek, textboxa oraz passwordBoxa. Powyższy kod powinien wygenerować mniej więcej takie oto okno
WCF RIA
Mechanizm wywoływania operacji logowania znajduje się natomiast w ViewModelu kontrolki logowania. Po naciśnięciu przycisku OK, dzięki zastosowaniu DelegateCommand uruchamiany następującą funkcję

W funkcji tej najpierw sprawdzamy, czy użytkownik podał dane do logowania, a następnie wywołujemy kolejną funkcję PerfomrLoginOperation. Przedstawia się w następujący sposób

Widzimy zatem, że wywołujemy funkcję logowania z serwisu WCF, w parametrach natomiast podajemy login oraz hasło przechwycone z okna. Dodatkowo w parametrze podajemy delegata do funkcji, która zostanie wykonana po zakończeniu operacji logowania.

Jeżeli odpowiedź z serwisu WCF zawiera błędy to wiemy, że operacja logowania się nie powiodła. Wyświetlamy zatem stosowny komunikat. Jeżeli natomiast wszystko jest Ok, pole loginOperation.Error będzie miało wartość null. Możemy wtedy pokazać naszą wiadomość dostępną jedynie dla zalogowanych użytkowników.
Mamy zatem ochronę przeciwko nieautoryzowanemu dostępowi do klienta. Jednak co w przypadku gdy nasz klient musi w calach np. zassania danych pobrać je przy wykorzystaniu jakiegoś WebServicu. Na ogól wystawiany jest wtedy drugi WebService do pobierania danych z jakiejś bazy. Do takiego WebServicu w teorii może dostać się każdy – my natomiast chcemy żeby dane można było pobierać jedynie po zalogowaniu się do aplikacji. Na szczęście nasz dodatkowy WebServise można w prosty sposób zabezpieczyć przed dostępem osób trzecich. Stwórzmy zatem sobie nowy WebService służący do pobierania danych. Do projektu Webowego dodajmy nową klasę typu “Domain Service” oraz dopiszmy do nie jedną metodę symulującą pobieranie danych.

Do takiego Servicu może dostać się każdy. Jeżeli natomiast naszą klasę udekorujemy atrybutem [RequiresAuthentication()] tylko użytkownicy, którzy pomyślnie przeszli autentykację przez nasz CustomAuthenticationService będą w stanie pobrać dane. W przeciwnym wypadku zostanie rzucony wyjątek.
debugger

Zastanówmy się teraz w jaki sposób można byłoby logować się do naszej aplikacji np. przy pomocy konta Google. Zacznijmy od tego, że musimy stworzyć własny MembershipProvider. W tym celu dodajmy do projektu Webowego nową klasę “GoogleMembershipProvider“, która będzie rozszerzać klasę MembershipProvider. Klasa MembershipProvider jest klasą abstrakcyjną zatem musimy zoverridować wszystkie jej funkcje (nie musimy wrzucać tam logiki, narazie po prostu wpiszmy tam cokolwiek aby przeszła kompilacja). Mając stworzony szkielet providera musimy go dorzucić do listy wszystkich providerów (tak jak to zrobiliśmy z SqlMembershipProvider-em). Modyfikujemy zatem sekcję podsekcję providers z sekcji membership na następującą.

Mając w kolekcji wszystkie nasze providery pozostaje nam się zastanowić, w jaki sposób raz używać GoogleMembershpProvidera, a innym razem SqlMembershipProvidera. Ja zrobiłem to w następujący sposób. Najpierw utworzyłem enuma, w którym przechowuje “nazwy” moich providerów

Następnie wykorzystałem parametr customData interfejsu IAuthentication. Po stronie klienta w funkcji logowania po prostu przesyłam typ konta do którego się loguję. Wygląda to w ten sposób:

Następnie po stronie serwera wybieram tego providera, przez którego loguje się użytkownik.

Zauważmy, że linijka

została zastąpiona przez

zatem teraz mamy wpływ na to, którego MembershipProvidera użyjemy. Jeżeli chodzi o samo sprawdzenie czy dany użytkownik podał poprawne dane do konta google, to sprawa jest trochę kłopotliwa(tak mi się wydaje :D). Nie udało mi się użyć biblioteki dotNetOpenAuth, gdyż z tego co widzę logowanie poprzez tą bibliotekę wymaga przekierowania na stronę Googla. Niestety taka operacja nie jest dozwolona w Silverlighcie (Silverlight nie wspiera cross-domain policy). Ponadto wykorzystanie tej biblioteki po stronie serwera również nie przyniosło oczekiwanych rezultatów. Ostatecznie zatem skorzystałem z opisu znajdującego się na stronie Googla http://code.google.com/intl/pl-PL/apis/accounts/docs/AuthForInstalledApps.html#Using. Czyli po prostu spreparowałem odpowiedni HTTPS Post Request. Funkcja ValidateUser z GoogleMembershipProvidera wygląda zatem następująco

Ciężko mi jednak stwierdzić czy takie logowanie jest bezpieczne. Co prawda nie udało mi się wyłapać nic konkretnego poprzez Wiresharka lub Fiddlera, jednakże ekspertem od zabezpieczeń niestety nie jestem.
W następnym wpisie postaram się króciutko przedstawić bibliotekę Microsoft Enterprise Library, a właściwie jeden z jej bloków – Security Block.Wykorzystam go do zezwalania użytkownikom do dostępu do funkcji serwisu, w zależności od ich roli w systemie.

Silverlight – koncepcja logowania z użyciem MembershipProviders oraz WCF RIA cz.2

Interactivity oraz Interaction

Interactions oraz Interactivity są to dwie dll-ki, które poszerzają sposób komunikacji pomiędzy widokiem oraz ViewModelem. Wprowadzają one nowy typ EventTriggerów(w Silverlighcie nie ma triggerów, natomiast w WPF-ie event triggery można praktycznie używać jedynie do animacji) oraz dodatkowo wprowadzają ciekawe sposoby(funkcje) na interakcję widoku z ViewModelem. Pierwszym z tych sposobów jest użycie właściwości InvokeCommandAction.Jak sama nazwa wskazuje można wywołać komendę z ViewModelu. Jaka jest różnica między wywołaniem komendy poprzez InvokeCommandAction, a Command=”{Binding CommandName}” ? InvokeCommandAction ma tą przewagę nad normalnm bindowaniem do property Command,że pozwala wywołać daną komendę w odpowiedzi na zajście jakiegoś zdarzenia na widoku. Przykładowe użycie może wyglądać następująco:

Powyższy przykład działa w następujący sposób. Za każdym razem gdy zostanie wywołane zdarzenie MouseMove na przycisku, odpalona zostanie komenda SubmitCommand. Wadą używania InvokeCommandAction jest to, że sami musimy się martwić o wygaszenie kontrolki w przypadku gdy dana komenda nie może być wykonana. Dlatego też w pierwszej linii mamy bindowanie właściwości IsEnabled przycisku do właściwości CanSubmit z ViewModelu. Alias i: jest aliasen na namespace interactivity z dll-ki

Drugim sposobem powiadomiania ViewModelu o jakimś zdarzeniu w widoku jest użycie InvokeMethodAction. Dzięki tej właściwości możemy bezpośrednio z widoku (z XAML-a) wywołać funkcję w ViewModelu. Składnia przedstawia się w następujący sposób:

TargetObject jest zbindowany do DataContextu, to właśnie tam będzie szukana funkcja Submit. Należy pamiętać, że właściwość CallMethodAction nie wspiera parametrów funkcji. Zatem jeżeli musimy wywołać jakąś funkcję z parametrami, należy opakować ją w funkcję bezparametrową, natomiast wszystkie parametry przekazać z właściwości ViewModelu.

Kolejnym ciekawym featurem Interactivity jest klasa Behavior. Jest niezastąpiona jeżeli potrzebujemy ingerować w działanie widoku po jakimś zdarzeniu. Nie musimy wówczas tworzyć obsługi eventów w code behind. Możemy wyłuskać daną funkcjonalność i zamknąć ją w klasie Behavior a następnie doczepić do wszystkich widoków danego typu. Załóżmy, że sterujemy ListBoxem z poziomu ViewModelu (przełączamy SelectedItem). Możemy zaznaczyć jakiś Item, co jednak jeżeli nasza lista jest bardzo długa,a zaznaczamy ostatni element. Na widoku element rzeczywiście się zaznaczył jednak lista się nie przewinęła. Przez co cały czas wiszą elementy z początku i nie widać czy któryś element jest zaznaczony. Dopiero gdy przeskrolujemy ręcznie ListBoxa widzimy, że rzeczywiście prawidłowo zareagował na zmianę property w ViewModelu. Jest to idealna sytuacja, aby stworzyć własny Behavior, który będziemy mogli podczepić do wszystkich listboxów w każdym widoku.

Metoda OnAttached odpala się w momencie, gdy behavior jest podpinany do kontrolki. W metodzie tej powinniśmy podpiąć się pod wszystkie eventy, które są dla nas istotne. W momencie, gdy widok jest niszczony wywołuje się metoda OnDetaching, w której powinniśmy odpiąć wszystkie metody od eventów.

Interactivity oraz Interaction