Przez ostatnie kilka tygodni (miesięcy), społeczność wrzała z powodu pogłosek na temat mizernej przyszłości technologii Silverlight.
Przyznam szczerze, że sam się lekko przestraszyłem ponieważ Silverlight okazał się dla mnie tym czymś w czym czuję się naprawdę dobrze, a perspektywa nie trafionej inwestycji w tą technologię była dla mnie co najmniej deprymująca.
Z lekkim przerażeniem czekałem na konferencję Microsoft BUILD na której miało okazać się czy firma z Redmond idzie w kierunku HTML5, CSS3 zapominając o starszych technologiach, czy też w całym ekosystemie Windows 8 znajdzie się miejsce dla Silverlight lub też bardziej ogólnie - technologii XAML-o podobnych.
Pierwszy dzień konferencji Microsoft BUILD na szczęście rozwiał moją niepewność dzięki czemu nadal będę mógł zgłębiać kolejne obszary Silverlight'a, które zapewne poszerzą się o dodatkowe funkcjonalności wraz z wyjściem wersji 5, jak również samego Windows 8.
Chętnych zapraszam do przeczytania artykułu:
"Microsoft to developers: Metro is your future" autorstwa Mary Jo Foley
(http://www.zdnet.com/blog/microsoft/microsoft-to-developers-metro-is-your-future/10611?tag=content;feature-roto)
Pod poniższym linkiem znajdziecie wykaz prezentacji, których wspólnym mianownikiem jest XAML
http://timheuer.com/blog/archive/2011/09/13/xaml-sessions-at-build.aspx
Samą konferencję Microsoft BUILD możecie śledzić pod adresem:
http://www.buildwindows.com/
DevSpark
Moje boje z technologiami Microsoft
Szukaj na tym blogu
wtorek, 13 września 2011
piątek, 13 maja 2011
Silverlight 5 Markup Extension and Commanding
Last time, my college from my job told me that creation of ICommand implementation for every required element is very annoying for him and he would be happy if he could bind command (for ex. Button Click) directly to specific method.
As we know, there is not possible customize Markup Extension in Silverlight 4 because the IMarkupExtension interface has only internal access in this version.
Version 5 of Silverlight, extends ours horizons.
Below, I will show how we can achive our goal.
Interface IServiceProvider gives us possibility to get specific service which allows us access to target element (where was used our custom Markup Extension) or root element which is our View with associated ViewModel by property DataContext.
IRootObjectProvider: provide a reference to the Root object of the VisualTree which the element is part of
IXamlTypeResolver : is able to resolve the name of the tags in the markup to the corresponding type.
IProvideValueTarget : gets a reference to the property and the elements which the markup extension is assigned
(Source: Andrea Boschin Blog)
As we see, there is no problem to using more complex methods because I use .NET Reflection which gives me many possibilities.
Usage:
and also our ViewModel:
As we know, there is not possible customize Markup Extension in Silverlight 4 because the IMarkupExtension interface has only internal access in this version.
Version 5 of Silverlight, extends ours horizons.
Below, I will show how we can achive our goal.
using System; using System.Windows; using System.Windows.Input; using System.Xaml; using Microsoft.Practices.Prism.Commands; namespace CommandingSL5 { public class Commanding : IMarkupExtension<ICommand> { public string MethodName { get; set; } public ICommand ProvideValue(IServiceProvider serviceProvider) { if (!String.IsNullOrEmpty(MethodName)) { if (serviceProvider == null) return null; var rootService = (IRootObjectProvider)serviceProvider.GetService(typeof(IRootObjectProvider)); var root = rootService.RootObject as FrameworkElement; if (root != null) { if (root.DataContext != null) { var method = root.DataContext.GetType().GetMethod(MethodName); if (method != null) { return new DelegateCommand(() => method.Invoke(root.DataContext, null)); } } } } return null; } } }
Interface IServiceProvider gives us possibility to get specific service which allows us access to target element (where was used our custom Markup Extension) or root element which is our View with associated ViewModel by property DataContext.
IRootObjectProvider: provide a reference to the Root object of the VisualTree which the element is part of
IXamlTypeResolver : is able to resolve the name of the tags in the markup to the corresponding type.
IProvideValueTarget : gets a reference to the property and the elements which the markup extension is assigned
(Source: Andrea Boschin Blog)
As we see, there is no problem to using more complex methods because I use .NET Reflection which gives me many possibilities.
Usage:
<UserControl x:Class="CommandingSL5.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:c="clr-namespace:CommandingSL5" mc:Ignorable="d"> <UserControl.DataContext> <c:MainPageViewModel /> </UserControl.DataContext> <Grid x:Name="LayoutRoot" Background="White"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="150" /> </Grid.ColumnDefinitions> <TextBox Text="{Binding MethodAText}" Margin="5,5" /> <Button Content="Execute TestMethod A" Command="{c:Commanding MethodName=TestMethodA}" Margin="5,5" Grid.Column="1"/> </Grid> </UserControl>
and also our ViewModel:
using System.ComponentModel; namespace CommandingSL5 { public class MainPageViewModel : INotifyPropertyChanged { #region Fields private string _methodAText; #endregion #region Properties public string MethodAText { get { return _methodAText; } set { _methodAText = value; RaisePropertyChanged("MethodAText"); } } #endregion #region Methods public void TestMethodA() { MethodAText = "TestMethodA executed !"; } #endregion #region INotifyPropertyChanged Implementation public event PropertyChangedEventHandler PropertyChanged; public void RaisePropertyChanged(string propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } #endregion } }
wtorek, 7 grudnia 2010
Przegląd PRISM 4 – część 5
Słowo wstępu
Na samym początku postawiłem sobie cel związany z przerobieniem całego ebooka dotyczącego PRISM 4. Nie ma co owijać w bawełnę- sprawa zero jedynkowa – cel nie został osiągnięty, choć jest to i tak o wiele więcej niż jakbym nie miał przed sobą wyzwania.
Do końca listopada udało mi się przeczytać 8 rozdziałów z 11, przy czym na podstawie pierwszych sześciu powstały 4 części dotyczące PRISM 4. Również rzeczy opisane w opublikowanych przeze mnie postach są zaimplementowane w moim codeplex’owym projekcie DevPrototype.
Oczywiście dokończę to co zacząłem, jednakże podejdę do tego na luzie (idą święta, obowiązki, muszę ulepić bałwana i takie tam :P )
PROŚBA: Mam nadzieję, że zdajecie sobie sprawę, że rzeczy, które opisuję to tylko wycinek pełnych możliwości. Jeśli są wśród was osoby, które rzyczyłby sobie uzupełnienia, któregoś z postów o dodatkowe szczegóły, proszę napiszcie o tym w komentarzach.
Zauważyłem, że blog także odwiedzają ludzie z poza naszego kraju. Z angielskim u mnie na bakier dlatego dodatkowy trening zawsze się przyda (mam nadzieję, że nie strzelę sobie tym w kolano ;-) ) więc,
If you want to read this post in English, please leave message in comments below interesting post.
O czym dzisiaj ?
• Rola regionów w PRISM 4
- View Discovery
- View Injection
• Trochę kodu
Rola regionów w PRISM 4
Mówiąc najprościej, region to obszar w którym będzie wyświetlana kontrolka z konkretnego modułu.
Wyobraźcie sobie, że posiadacie kontrolkę ContentControl osadzoną na widoku „ShellView.xaml” zarejestrowaną w menadżeże regionów jako „RegionA”. W momencie inicjalizacji przykładowego modułu „ModułA”, rejestrowany jest w kontenerze DI widok „ViewA” oraz w menadżeże regionów widok ten jest powiązany z regionem o nazwie „RegionA”. W momencie wyświetlania widoku „ShellView.xaml”, program wie, że potrzebuje ściągnąć „ModułA”, który podczas swojej inicjalizacji ustawia „ViewA” jako aktywny widok w „RegionA” dzięki czemu widzimy go w naszyej kontrolce ContentControl.
Po co takie zabiegi ? Otóż dzięki temu, możemy w regionie wyświetlić dowolne widoki z luźno powiązanych modułów. Oczywiście należy zaznaczyć, że relacja pomiędzy regionem a widokiem to 1 : N czyli jeden region może posiadać wiele powiązanych ze sobą widoków przy czym w regionie wyświetlany jest tylko aktywny widok.
Prism pozwala nam na rejestrację wielu regionów oraz na tworzenie zagnieżdżonych regionów. Rolę regionów mogą pełnić: ContentControl, ItemsControl, TabControl oraz w scenariuszu z nawigacją Frame.ContentLoader (ostatniego scenariusza nie znajdziecie w opisywanym ebook’u, jednakże pod tym adresem http://blogs.msdn.com/b/kashiffl można znaleźć bardzo ciekawy artykuł dotyczący łączenia funkcjonalności regionów z Silverlight Navigation Framework)
View Discovery
To strategia polegająca na sztywnym powiązaniu typu z regionem tzn. powiadamiamy menadżer regionów, że dla regionu „RegionA” ma być tworzony widok „ViewA” dzięki czemu w momencie wejścia na formatkę z powyższym regionem, automatycznie tworzona jest instancja widoku „ViewA”. W tym scenariuszu jesteśmy ograniczeni tylko do tej jednej pary region- widok oraz nie mamy żadnej kontroli nad cyklem życia widoku w regionie.
View Injection
Jak zapewne się domyślacie, to rozwiązanie jest w wielu miejscach przeciwieństwem powyższego tzn. z regionem może być skojarzone wiele widoków, które mogą być rejestrowane podczas działania aplikacji. Mamy tutaj pełną kontrolę nad cyklem życia widoków w regionie dzięki czemu możemy dodawać, usuwać bądź zmieniać ich aktywność. Pełnia swobody w zarządzaniu regionem pozwala nam dodatkowo na wyświetlanie w nim odmiennych widoków / danych. Sposób działania tej strategii został opisany na wstępie tego postu. Oczywistym jest, że to rozwiązanie o wiele lepiej nadaje się do aplikacji biznesowych.
Trochę kodu
Rejestracja regionu w widoku
Przykladowy modul wraz z kawalkiem kodu inicjalizujacego
Przykładowa klasa zarządzająca regionem "MainRegion"
Dosyć pomocną właściwością regionów, może okazać się
prism.RegionManager.RegionContext
pozwalający na współdzielenie kontekstu danych pomiędzy widokami.
Na samym początku postawiłem sobie cel związany z przerobieniem całego ebooka dotyczącego PRISM 4. Nie ma co owijać w bawełnę- sprawa zero jedynkowa – cel nie został osiągnięty, choć jest to i tak o wiele więcej niż jakbym nie miał przed sobą wyzwania.
Do końca listopada udało mi się przeczytać 8 rozdziałów z 11, przy czym na podstawie pierwszych sześciu powstały 4 części dotyczące PRISM 4. Również rzeczy opisane w opublikowanych przeze mnie postach są zaimplementowane w moim codeplex’owym projekcie DevPrototype.
Oczywiście dokończę to co zacząłem, jednakże podejdę do tego na luzie (idą święta, obowiązki, muszę ulepić bałwana i takie tam :P )
PROŚBA: Mam nadzieję, że zdajecie sobie sprawę, że rzeczy, które opisuję to tylko wycinek pełnych możliwości. Jeśli są wśród was osoby, które rzyczyłby sobie uzupełnienia, któregoś z postów o dodatkowe szczegóły, proszę napiszcie o tym w komentarzach.
Zauważyłem, że blog także odwiedzają ludzie z poza naszego kraju. Z angielskim u mnie na bakier dlatego dodatkowy trening zawsze się przyda (mam nadzieję, że nie strzelę sobie tym w kolano ;-) ) więc,
If you want to read this post in English, please leave message in comments below interesting post.
O czym dzisiaj ?
• Rola regionów w PRISM 4
- View Discovery
- View Injection
• Trochę kodu
Rola regionów w PRISM 4
Mówiąc najprościej, region to obszar w którym będzie wyświetlana kontrolka z konkretnego modułu.
Wyobraźcie sobie, że posiadacie kontrolkę ContentControl osadzoną na widoku „ShellView.xaml” zarejestrowaną w menadżeże regionów jako „RegionA”. W momencie inicjalizacji przykładowego modułu „ModułA”, rejestrowany jest w kontenerze DI widok „ViewA” oraz w menadżeże regionów widok ten jest powiązany z regionem o nazwie „RegionA”. W momencie wyświetlania widoku „ShellView.xaml”, program wie, że potrzebuje ściągnąć „ModułA”, który podczas swojej inicjalizacji ustawia „ViewA” jako aktywny widok w „RegionA” dzięki czemu widzimy go w naszyej kontrolce ContentControl.
Po co takie zabiegi ? Otóż dzięki temu, możemy w regionie wyświetlić dowolne widoki z luźno powiązanych modułów. Oczywiście należy zaznaczyć, że relacja pomiędzy regionem a widokiem to 1 : N czyli jeden region może posiadać wiele powiązanych ze sobą widoków przy czym w regionie wyświetlany jest tylko aktywny widok.
Prism pozwala nam na rejestrację wielu regionów oraz na tworzenie zagnieżdżonych regionów. Rolę regionów mogą pełnić: ContentControl, ItemsControl, TabControl oraz w scenariuszu z nawigacją Frame.ContentLoader (ostatniego scenariusza nie znajdziecie w opisywanym ebook’u, jednakże pod tym adresem http://blogs.msdn.com/b/kashiffl można znaleźć bardzo ciekawy artykuł dotyczący łączenia funkcjonalności regionów z Silverlight Navigation Framework)
View Discovery
To strategia polegająca na sztywnym powiązaniu typu z regionem tzn. powiadamiamy menadżer regionów, że dla regionu „RegionA” ma być tworzony widok „ViewA” dzięki czemu w momencie wejścia na formatkę z powyższym regionem, automatycznie tworzona jest instancja widoku „ViewA”. W tym scenariuszu jesteśmy ograniczeni tylko do tej jednej pary region- widok oraz nie mamy żadnej kontroli nad cyklem życia widoku w regionie.
View Injection
Jak zapewne się domyślacie, to rozwiązanie jest w wielu miejscach przeciwieństwem powyższego tzn. z regionem może być skojarzone wiele widoków, które mogą być rejestrowane podczas działania aplikacji. Mamy tutaj pełną kontrolę nad cyklem życia widoków w regionie dzięki czemu możemy dodawać, usuwać bądź zmieniać ich aktywność. Pełnia swobody w zarządzaniu regionem pozwala nam dodatkowo na wyświetlanie w nim odmiennych widoków / danych. Sposób działania tej strategii został opisany na wstępie tego postu. Oczywistym jest, że to rozwiązanie o wiele lepiej nadaje się do aplikacji biznesowych.
Trochę kodu
Rejestracja regionu w widoku
<UserControl x:Class="UIComposition.Shell.Views.ShellView" xmlns:prism="http://www.codeplex.com/prism" ...> <!-- Left Region --> <Border Grid.Row="1"> <ContentControl x:Name="LeftRegion" prism:RegionManager.RegionName="LeftRegion" /> </Border> <!-- Main Region --> <Border Grid.Column="2" Grid.Row="1" Grid.RowSpan="2"> <ContentControl x:Name="MainRegion" prism:RegionManager.RegionName="MainRegion" /> </Border> </UserControl>
Przykladowy modul wraz z kawalkiem kodu inicjalizujacego
public class ModuleInit : IModule { ... public void Initialize() { // View Discovery this.regionManager.RegisterViewWithRegion("LeftRegion", () => this.container.Resolve<EmployeeListView>()); // View Injection IRegion mainRegion = this.regionManager.Regions["MainRegion"]; if (mainRegion == null) return; EmployeeSummaryView view = mainRegion.GetView("EmployeeSummaryView") as EmployeeSummaryView; if (view == null) { // Tworzymy instancje nowego widoku view = this.container.Resolve<EmployeeSummaryView>(); // I dodajemy go do widoku. W tym momencie automatycznie widok ustawi sie jako aktywny mainRegion.Add(view, "EmployeeSummaryView"); } } }
Przykładowa klasa zarządzająca regionem "MainRegion"
public class AnotherClass { IRegion mainRegion = this.regionManager.Regions["MainRegion"]; if (mainRegion == null) return; EmployeeSummaryView view = mainRegion.GetView("EmployeeSummaryView") as EmployeeSummaryView; if (view != null) { // Aktywujemy widok, który zostal wczesniej dodany do regionu mainRegion.Activate(view); } }
Dosyć pomocną właściwością regionów, może okazać się
prism.RegionManager.RegionContext
pozwalający na współdzielenie kontekstu danych pomiędzy widokami.
czwartek, 2 grudnia 2010
Silverlight Firestarter na moim blogu
Już dzisiaj, o godzinie 18:00 rozpocznie się długo oczekiwana przez wszystkich zainteresowanych technologią srebrnych światełek, konferencja w całości poświęcona Silverlight.
Choć wiele sesji dotyczy głównie Windows Phone 7, mam nadzieję, że prelegenci zaskoczą czymś osoby, które wykorzystują Silverlight'a w aplikacjach biznesowych. Szczególnie ciekawą sesją może okazać się wystąpienie Scotta Guthrie, który opowie nam o przyszłości SL'a.
Osobiście liczę na twarde dowody potwierdzające, że Silverlight nadal będzie kluczową technologią w polityce Microsoftu'u (pragnę przypomnieć zamieszanie jakie powstało podczas tegorocznego PDC oraz wywiadu z Panem Bob'em Muglia).
Z bardziej technicznych zmian oczekuję zapowiedzi Silverlight 5, który mam nadzieję, że wprowadzi takie zmiany/ficzery jak:
- pełne wsparcie dla mechanizmu Bindingu (rodem z WPF),
- wsparcie dla większej ilości przeglądarek wraz z poprawą jakości działania Silverlight'a w obecnie wspieranych (np. Chrome),
- zwiększenie wydajności.
Liczba moich oczekiwań jest skromna, choć w pełni wystarczająca by usprawnić naszą pracę.
Zainteresowanych konferencją odsyłam pod wskazanych link : Silverlight Firestarter
sobota, 27 listopada 2010
Przegląd PRISM 4 – część 4
W tej części będzie trochę nietypowo.
Dla przypomnienia, zaznaczam, że cały przegląd PRISM 4 oparty jest o ebook’a pt. "Developers Guide to Microsoft Prism. Building MVVM and Modular Applications with WPF and Silverlight"
Część 4 dotyczy najbardziej oczekiwanych przeze mnie rozdziałów „Implementing the MVVM pattern” oraz „Advanced MVVM Scenarios”. Ponieważ w tej części książki, autor opisał założenia wzorca MVVM, które są opisane w moich wcześniejszych postach, posłużę się odwołaniem do nich przy opisie poszczególnych działów będących częścią wymienionych powyżej rozdziałów.
Implementing the MVVM pattern
Class Responsibilities and Characteristics
W tej części znajdziemy opis wzorca projektowego MVVM. Zainteresowanych odsyłam do posta „Dziennik Zdrowej Diety – Wzorzec MVVM – Koncepcja”
Class interactions
Część ta opisuje mechanizm Bindowania oraz interakcję pomiędzy View, a ViewModel’em za pomocą interfejsów INotifyPropertyChanged, INotifyCollectionChanged, ICollectionView, Commanding, IDataError, INotifyDataErrorInfo.
Sposób wykorzystania pierwszych dwóch interfejsów znajdziecie również w poście „Dziennik Zdrowej Diety – Wzorzec MVVM – Koncepcja” . Mechanizm Commandingu jest przedstawiony w poście „Dziennik Zdrowej Diety – Wzorzec MVVM – Commanding”
Poniżej przedstawię możliwości jakie daje nam interfejs ICollectionView oraz DelegateCommand będący PRISM’ową implementacją interfejsu ICommand.
ICollectionView to interfejs, który umożliwia nam sortowanie, filtrowanie, grupowanie oraz śledzenie aktualnie zaznaczonego elementu z kolekcji. Jest to bardzo pomocne z punktu widzenia MVVM ponieważ otrzymujemy dosyć bogatą funkcjonalność operacji na kolekcjach po stronie ViewModel.
PagedCollectionView jest klasą, która w Silverlight (w WPF jest to klasa ListCollectionView) implementuje omawiany interfejs.
Poniżej przedstawiam krótki przykład jak tego używać (implementacja w DevPrototype)
DelegateCommand implementuje interfejs ICommand (polecam post „Dziennik Zdrowej Diety – Wzorzec MVVM – Commanding” oraz dokłada dwie rzeczy. Pierwsza to możliwość wymuszenie ponownego sprawdzenia metody CanExecute przez metodę RaiseCanExecuteChanged(); Drugą rzeczą jest powiązanie wartości właściwości IsEnabled z wynikiem metody CanExecute. Te dwie rzeczy świetnie się uzupełniają w scenariuszach, gdzie np. mamy walidowane pola oraz przycisk „Zapisz”. Warto pamiętać, że podpinając DelegateCommand pod Button, nie mamy możliwości własnoręcznego zbindowania propercji IsEnabled. Klasa ta była także budowana dla kontrolek dziedziczących z ButtonBase. Ludzie od Prisma sami sugerują w książce, że dla innych kontrolek oraz innych zdarzeń warto przyjrzeć się Behavior’om z SDK Blenda. Głównie mają na myśli InvokeCommandAction oraz CallMethodAction. Po szczegóły na temat tych dwóch Behavior’ów odsyłam do posta „Przegląd Action (Blend SDK) w kontekście MVVM cz. 1„
Construction and Wire-Up
Dział ten opisuje starategie zarządzania View oraz ViewModel. Mamy tutaj do wybory:
- ViewModel First – najpierw inicjalizowany jest ViewModel, a następnie wewnątrz ViewModelu instancjonowana jest formatka View. Należy pamiętać by trzymać w takim ViewModelu bezparametrowy konstruktor w celu prawidłowego wyświetlania naszego View w designerze VS bądź Blend.
- View First – najpierw inicjalizowany jest View. Tworzenie instancji ViewModelu może się odbywać zarówno w kodzie XAML jak i w zdarzeniu Loaded. Po szczegóły odnośnie tej strategii odsyłam do posta „Dziennik Zdrowej Diety – Blendability MVVM”
MessageBox i PopUp
To w sumie jedyna oryginalna rzecz jaką stworzył team PRISM na potrzeby wzorca MVVM. Zestaw klas i interfejsów IInteractionRequest, InteractionRequestTrigger, Confirmation i Notification ma na celu wspomóc programistę przy tworzeniu wyskakujących okienek, okienek typu MessageBox w taki sposób by w ViewModelu nie było bezpośredniego odwołania do np. specyficznych rzeczy dostępnych tylko w Silverlight. Ułatwia to także testowanie, gdzie wywołanie tego typu komponentów odbywa się na zasadzie zdarzeń.
Prosty przykłąd
Odnośnie interakcji naszego ViewModel’u z logiką UI odsyłam do posta „Dziennik Zdrowej Diety – Logika UI w MVVM”, gdzie przedstawiłem własną koncepcję jak nasz ViewModel może operować z logiką dostępną po stronie widoku nie będąc bezpośrednio powiązanym z tym widokiem.
Dla przypomnienia, zaznaczam, że cały przegląd PRISM 4 oparty jest o ebook’a pt. "Developers Guide to Microsoft Prism. Building MVVM and Modular Applications with WPF and Silverlight"
Część 4 dotyczy najbardziej oczekiwanych przeze mnie rozdziałów „Implementing the MVVM pattern” oraz „Advanced MVVM Scenarios”. Ponieważ w tej części książki, autor opisał założenia wzorca MVVM, które są opisane w moich wcześniejszych postach, posłużę się odwołaniem do nich przy opisie poszczególnych działów będących częścią wymienionych powyżej rozdziałów.
Implementing the MVVM pattern
Class Responsibilities and Characteristics
W tej części znajdziemy opis wzorca projektowego MVVM. Zainteresowanych odsyłam do posta „Dziennik Zdrowej Diety – Wzorzec MVVM – Koncepcja”
Class interactions
Część ta opisuje mechanizm Bindowania oraz interakcję pomiędzy View, a ViewModel’em za pomocą interfejsów INotifyPropertyChanged, INotifyCollectionChanged, ICollectionView, Commanding, IDataError, INotifyDataErrorInfo.
Sposób wykorzystania pierwszych dwóch interfejsów znajdziecie również w poście „Dziennik Zdrowej Diety – Wzorzec MVVM – Koncepcja” . Mechanizm Commandingu jest przedstawiony w poście „Dziennik Zdrowej Diety – Wzorzec MVVM – Commanding”
Poniżej przedstawię możliwości jakie daje nam interfejs ICollectionView oraz DelegateCommand będący PRISM’ową implementacją interfejsu ICommand.
ICollectionView to interfejs, który umożliwia nam sortowanie, filtrowanie, grupowanie oraz śledzenie aktualnie zaznaczonego elementu z kolekcji. Jest to bardzo pomocne z punktu widzenia MVVM ponieważ otrzymujemy dosyć bogatą funkcjonalność operacji na kolekcjach po stronie ViewModel.
PagedCollectionView jest klasą, która w Silverlight (w WPF jest to klasa ListCollectionView) implementuje omawiany interfejs.
Poniżej przedstawiam krótki przykład jak tego używać (implementacja w DevPrototype)
public Dictionary SelectedDictionary { get; set; } public ObservableCollectionDictionariesCollection { get { return _dictionariesCollection; } set { _dictionariesCollection = value; Dictionaries = new PagedCollectionView(_dictionariesCollection); Dictionaries.CurrentChanged += SelectedItemChanged; NotifyOfPropertyChange(() => DictionariesCollection); } } public ICollectionView Dictionaries { get { return _dictionaries; } set { _dictionaries = value; NotifyOfPropertyChange(() => Dictionaries); } } private void SelectedItemChanged( object sender, EventArgs e ) { SelectedDictionary = Dictionaries.CurrentItem as Dictionary; } private void OnGroupCommand(object obj) { Dictionaries.GroupDescriptions.Add(new PropertyGroupDescription("Description")); } private void OnUngroupCommand(object obj) { Dictionaries.GroupDescriptions.Clear(); } private void OnSortCommand(object obj) { Dictionaries.SortDescriptions.Add(new SortDescription("Description",ListSortDirection.Ascending)); }
DelegateCommand implementuje interfejs ICommand (polecam post „Dziennik Zdrowej Diety – Wzorzec MVVM – Commanding” oraz dokłada dwie rzeczy. Pierwsza to możliwość wymuszenie ponownego sprawdzenia metody CanExecute przez metodę RaiseCanExecuteChanged(); Drugą rzeczą jest powiązanie wartości właściwości IsEnabled z wynikiem metody CanExecute. Te dwie rzeczy świetnie się uzupełniają w scenariuszach, gdzie np. mamy walidowane pola oraz przycisk „Zapisz”. Warto pamiętać, że podpinając DelegateCommand pod Button, nie mamy możliwości własnoręcznego zbindowania propercji IsEnabled. Klasa ta była także budowana dla kontrolek dziedziczących z ButtonBase. Ludzie od Prisma sami sugerują w książce, że dla innych kontrolek oraz innych zdarzeń warto przyjrzeć się Behavior’om z SDK Blenda. Głównie mają na myśli InvokeCommandAction oraz CallMethodAction. Po szczegóły na temat tych dwóch Behavior’ów odsyłam do posta „Przegląd Action (Blend SDK) w kontekście MVVM cz. 1„
Construction and Wire-Up
Dział ten opisuje starategie zarządzania View oraz ViewModel. Mamy tutaj do wybory:
- ViewModel First – najpierw inicjalizowany jest ViewModel, a następnie wewnątrz ViewModelu instancjonowana jest formatka View. Należy pamiętać by trzymać w takim ViewModelu bezparametrowy konstruktor w celu prawidłowego wyświetlania naszego View w designerze VS bądź Blend.
- View First – najpierw inicjalizowany jest View. Tworzenie instancji ViewModelu może się odbywać zarówno w kodzie XAML jak i w zdarzeniu Loaded. Po szczegóły odnośnie tej strategii odsyłam do posta „Dziennik Zdrowej Diety – Blendability MVVM”
MessageBox i PopUp
To w sumie jedyna oryginalna rzecz jaką stworzył team PRISM na potrzeby wzorca MVVM. Zestaw klas i interfejsów IInteractionRequest, InteractionRequestTrigger, Confirmation i Notification ma na celu wspomóc programistę przy tworzeniu wyskakujących okienek, okienek typu MessageBox w taki sposób by w ViewModelu nie było bezpośredniego odwołania do np. specyficznych rzeczy dostępnych tylko w Silverlight. Ułatwia to także testowanie, gdzie wywołanie tego typu komponentów odbywa się na zasadzie zdarzeń.
Prosty przykłąd
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK ); if (result == MessageBoxResult.Yes) { CancelRequest(); }
Odnośnie interakcji naszego ViewModel’u z logiką UI odsyłam do posta „Dziennik Zdrowej Diety – Logika UI w MVVM”, gdzie przedstawiłem własną koncepcję jak nasz ViewModel może operować z logiką dostępną po stronie widoku nie będąc bezpośrednio powiązanym z tym widokiem.
czwartek, 25 listopada 2010
Przegląd PRISM 4 – część 3
Agenda
• Czy jest aplikacja modułowa ?
• Inicjalizacja modułów
Czym jest moduł ?
Pozwoliłem sobie na przetłumaczenie pierwszego akapitu z 4 rozdziału ebooka o Prism 4. Oto on.
Aplikacja modułowa to aplikacja podzielona na zestaw funkcjonalnych jednostek (zwanych modułami), które mogę być zintegrowane w jedną większą aplikację.
Moduł jest swego rodzaju kontenerem części funkcjonalnościcałej aplikacji i zazwyczaj stanowi zbiór powiązanych ze sobą zagadnień. Moduł taki może zawierać zbirór elementów opisujących część logiki biznesowej, część infrastruktury aplikacji takiej jak serwisy odpowiedzialne za logowanie lub autentykację użytkownika. Moduły są niezależne względem siebie, ale potrafią komunikować się ze sobą opierając się na mechanizmie rozgłoszeniowym.
Aplikacja modułowa może ułatwić Ci jej rozwój, testowanie, wdrażanie czy też rozszerzanie.
To w jaki sposób podzielisz aplikację zależy tylko i wyłącznie od Ciebie. Może to być podział ze względu na odpowiednie tematy biznesowe np. moduł zamówień, moduł faktur. Może to być również podział ze względu na architekturę aplikacji np. moduł dostępu do danych, moduł UI, moduł narzędziowy. Warto skorzystać z obu rozwiązań i posiadać w aplikacji podział na moduły techniczne i moduły biznesowe. Z własnych doświadczeń nie polecam tworzenia osobnych modułów dla każdego widoku lub pary widoków (np. widok listy użytkowników wraz z widokiem szczegółów użytkownika). Wraz z rozwojem aplikacji, zwiększy się ilość takich modułów, a wasza praca z Visual Studio przy np. 50 modułach (projektach) stanie się koszmarem.
Inicjalizacja modułów
Cały proces ładowania modułu zaczyna się od wywołania funkcji:
Powyższy rysunek przedstawia praktycznie wszystko co powinieneś wiedzieć czyli:
Zarejestruj moduł w katalogu modułów
Dla Silverlight’a Prism dostarcza dwa sposoby rejestracji modułów (moduły rejestrujemy w bootstraperze)
Pierwszy to inicjalizacja modułu bezpośrednio w kodzie
Drugi sposób to załadowanie modułów do katalogu na podstawie pliku XAML
Gdzie plik XAML ma postać:
Odnajdywanie modułów
Krok ten dotyczy aplikacji WPF. Programista musi wskazać lokalizację katalogu w którym znajdują się moduły.
Pobież interesujący Cię modułu z odpowiedniej lokalizacji
Dla aplikacji Silverlight, w kroku tym pobierany jest moduł z odpowiedniej lokalizacji sieciowej (katalog /ClientBin w katalogu wirtualnym naszej hostowanej aplikacji) i przenoszony i załadowany do pamięci po stronie klienckiej.
Ponieważ pobranie modułu to ściągnięcie z serwera kolejnych paczek danych (które mogą być duże), mamy możliwość określenia w jaki sposób ma być pobierany moduł. Mówi nam o tym właściwość InitializationMode, która przyjmuje wartości:
• InitializationMode.OnDemand – pobiera moduł na żądanie,
• InitializationMode.WhenAvailable – pobiera moduł w tle w osobnym wątku.
Zainicjalizuj moduł
Po pobraniu modułu, następuje jego inicjalizacja, której zadaniem jest zintegrowanie tego modułu z naszą aplikacją. Inicjalizacja następuje poprzez wyszukanie w ściągniętym module typu określonego w katalogu modułów jako ModuleType.
Jest to klasa implementująca interfejs IModule.
Zupełnym minimum by zainicjalizować moduł jest implementacja metody Initialize. Jednakże nasz moduł składa się z zestawu widoków, które należy odpowiednio zarejestrować w odpowiednich kontenerach, menadżerach.
• Czy jest aplikacja modułowa ?
• Inicjalizacja modułów
Czym jest moduł ?
Pozwoliłem sobie na przetłumaczenie pierwszego akapitu z 4 rozdziału ebooka o Prism 4. Oto on.
Aplikacja modułowa to aplikacja podzielona na zestaw funkcjonalnych jednostek (zwanych modułami), które mogę być zintegrowane w jedną większą aplikację.
Moduł jest swego rodzaju kontenerem części funkcjonalnościcałej aplikacji i zazwyczaj stanowi zbiór powiązanych ze sobą zagadnień. Moduł taki może zawierać zbirór elementów opisujących część logiki biznesowej, część infrastruktury aplikacji takiej jak serwisy odpowiedzialne za logowanie lub autentykację użytkownika. Moduły są niezależne względem siebie, ale potrafią komunikować się ze sobą opierając się na mechanizmie rozgłoszeniowym.
Aplikacja modułowa może ułatwić Ci jej rozwój, testowanie, wdrażanie czy też rozszerzanie.
To w jaki sposób podzielisz aplikację zależy tylko i wyłącznie od Ciebie. Może to być podział ze względu na odpowiednie tematy biznesowe np. moduł zamówień, moduł faktur. Może to być również podział ze względu na architekturę aplikacji np. moduł dostępu do danych, moduł UI, moduł narzędziowy. Warto skorzystać z obu rozwiązań i posiadać w aplikacji podział na moduły techniczne i moduły biznesowe. Z własnych doświadczeń nie polecam tworzenia osobnych modułów dla każdego widoku lub pary widoków (np. widok listy użytkowników wraz z widokiem szczegółów użytkownika). Wraz z rozwojem aplikacji, zwiększy się ilość takich modułów, a wasza praca z Visual Studio przy np. 50 modułach (projektach) stanie się koszmarem.
Inicjalizacja modułów
Cały proces ładowania modułu zaczyna się od wywołania funkcji:
_moduleManager.LoadModule("CalendarModule");
Powyższy rysunek przedstawia praktycznie wszystko co powinieneś wiedzieć czyli:
Zarejestruj moduł w katalogu modułów
Dla Silverlight’a Prism dostarcza dwa sposoby rejestracji modułów (moduły rejestrujemy w bootstraperze)
Pierwszy to inicjalizacja modułu bezpośrednio w kodzie
protected override IModuleCatalog GetModuleCatalog() { var moduleCatalog = new ModuleCatalog(); var calendarModuleInfo = new ModuleInfo { ModuleName = "CalendarModule", ModuleType = "MVVMBasic.CalendarModule.Module, MVVMBasic.CalendarModule, Version=1.0.0.0", Ref = "MVVMBasic.CalendarModule.xap", InitializationMode = InitializationMode.WhenAvailable }; moduleCatalog.AddModule(waitWindowModuleInfo); return moduleCatalog; }
Drugi sposób to załadowanie modułów do katalogu na podstawie pliku XAML
private const string ModuleCatalogUri = "/MVVMBasic;component/ModulesCatalog.xaml"; protected override IModuleCatalog CreateModuleCatalog() { return Microsoft.Practices.Prism.Modularity.ModuleCatalog.CreateFromXaml(new Uri(ModuleCatalogUri, UriKind.Relative)); }
Gdzie plik XAML ma postać:
<Modularity:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:Modularity="clr-namespace:Microsoft.Practices.Prism.Modularity;assembly=Microsoft.Practices.Prism"> <Modularity:ModuleInfoGroup Ref="MVVMBasic.CalendarModule.xap" InitializationMode="WhenAvailable"> <Modularity:ModuleInfo ModuleName="MVVMBasic.CalendarModule" ModuleType = "MVVMBasic.CalendarModule.Module, MVVMBasic.CalendarModule, Version=1.0.0.0",/> </Modularity:ModuleInfoGroup> </Modularity:ModuleCatalog>
Odnajdywanie modułów
Krok ten dotyczy aplikacji WPF. Programista musi wskazać lokalizację katalogu w którym znajdują się moduły.
Pobież interesujący Cię modułu z odpowiedniej lokalizacji
Dla aplikacji Silverlight, w kroku tym pobierany jest moduł z odpowiedniej lokalizacji sieciowej (katalog /ClientBin w katalogu wirtualnym naszej hostowanej aplikacji) i przenoszony i załadowany do pamięci po stronie klienckiej.
Ponieważ pobranie modułu to ściągnięcie z serwera kolejnych paczek danych (które mogą być duże), mamy możliwość określenia w jaki sposób ma być pobierany moduł. Mówi nam o tym właściwość InitializationMode, która przyjmuje wartości:
• InitializationMode.OnDemand – pobiera moduł na żądanie,
• InitializationMode.WhenAvailable – pobiera moduł w tle w osobnym wątku.
Zainicjalizuj moduł
Po pobraniu modułu, następuje jego inicjalizacja, której zadaniem jest zintegrowanie tego modułu z naszą aplikacją. Inicjalizacja następuje poprzez wyszukanie w ściągniętym module typu określonego w katalogu modułów jako ModuleType.
Jest to klasa implementująca interfejs IModule.
namespace MVVMBasic.CalendarModule { public class Module : IModule { #region init private readonly IRegionManager _regionManager; readonly IUnityContainer _container; readonly IEventAggregator _eventAggregator; public Module(IRegionManager regionManager, IUnityContainer container, IEventAggregator eventAggregator) { _regionManager = regionManager; _container = container; _eventAggregator = eventAggregator; } #endregion #region IModule Members public void Initialize() { // zarejestruje nowe typy w kontenerze _container.RegisterType<ICalendarView, CalendarView>(); _container.RegisterType<ICalendarViewModel, CalendarViewModel>(); // Powiadom menadzer regionow, w ktorym regionie wyswietli sie dany widok _regionManager.RegisterViewWithRegion("CalendarView", () => new CalendarView()); // Powiadom wszystkich nasluchujacych o zakonczeniu inicjalizacji modulu _eventAggregator.GetEvent<ViewInitializedEvent>().Publish("CalendarModule"); } #endregion } }
Zupełnym minimum by zainicjalizować moduł jest implementacja metody Initialize. Jednakże nasz moduł składa się z zestawu widoków, które należy odpowiednio zarejestrować w odpowiednich kontenerach, menadżerach.
środa, 24 listopada 2010
Przegląd PRISM 4 – część 2
Agenda
• Kontener DI
• Rola kontenera DI w Prism
• Wykorzystanie kontenera DI
• ServiceLocator
Kontener DI
Kontener DI (ang. Dependency Injection) jest to wzorzec projektowy mający na celu usunąć z systemu zależności względem klas i wstrzykiwać obiekty implementujące dany interfejs. Słowno muzycznie: kontener stanowi swego rodzaju mapę, do której rejestrujemy powiązania pomiędzy interfejsem i klasą na zasadzie klucz, wartość. Jeśli potrzebujemy obiekt implementujący dany interfejs to odpytujemy się kontenera wskazując mu odpowiedni klucz (interfej), przez co w zamian otrzymujemy wartość (klasa).
Podejście to uniezależnia naszą aplikację od konkretnej implementacji przez co bardzo łatwo będzie można podmienić mechanizm funkcjonowania np.
Interfejs ten posiada deklarację metody zwracającej wszystkie klendarze.
Powyższe klasy implementują nasz interfejs pobierając kalendarze z różnych źródeł danych.
Ponieważ aplikacja bazuje na interfejsach, nie musi wiedzieć skąd są pobierane dane.
Podmiana źródła danych polega na rejestracji w kontenerze danych odpowiedniej klasy dla naszego interfejsu ICalendarDataService.
Benefity są interesujące (testowalność, prosta konfiguracja, niezależność etc.)
Rola kontenera DI w Prism
Koncepcją Prisma jest możliwość stworzenia aplikacji składającej się z modułów. Tak na prawdę nie interesuje nas jak dana funkcjonalność będzie zaimplementowana. Ważne by moduł implementował wskazany interfejs.
Prism dostarcza implementację dwóch kontenerów:
- MEF,
- Unity.
By z nich skorzystać, wystarczy by nasz Boostraper podziedziczył po odpowiedniej klasie. I tak dla MEF będzie to MefBootstraper, a dla Unity będzie to UnityBootstraper. Oczywiście jeśli ktoś lubi StructureMap, AutoFac albo inny kontener DI, nie stoi nic na przeszkodzie by napisać własną implementację Boostrapera rejestrującego wasz ulubiony konter.
Wykorzystanie kontenera DI
Najczęściej wykorzystywanymi operacjami przy pracy z kontenerem DI są:
- rejestracja typu,
- pobieranie instancji typu z kontenera,
- rejestracja zadanej instancji (Singleton),
- wstrzykiwanie obiektów do konstruktora
Rejestracja typu w kontenerze Unity
Rejestracja typu w kontenerze MEF
Pobieranie instancji typu z kontenera Unity
Pobieranie instancji typu z kontenera MEF
Rejestracja zadanej instancji (Singletone) w kontenerze Unity
Rejestracja zadanej instancji (Singletone) w kontenerze MEF
Wstrzykiwanie obiektów do konstruktora.
W Unity operacja ta dzieje się automatycznie czyli np.
Zakończy się zainicjalizowaniem _eventAggregator odpowiednim obiektem zarejestrowanym w kontenerze Unity.
W MEF działa to analogicznie z tym, że nad klasą musimy umieścić atrybut
Przedstawione przykłady to tylko szybka ściąga. Po szczegóły odsyłam pod adresy:
http://unity.codeplex.com
http://mef.codeplex.com
ServiceLocator
ServiceLocator to poprostu nakładka na nasze kontenery mająca na celu ułatwienie przy próbie zmiany kontenera podczas procesu tworzenia aplikacji.
Jedyną opcją jaką oferuje ServiceLocator jest możliwość pobrania typu obiektu i to w dodatku opakowanego w object przez co musimy to jeszcze rzutować na odpowiedni interfejs.
Fajną rzeczą z ServiceLocatorem jest fakt, że jest on klasą statyczną dostępną w całej aplikacji tak więc przydaje się w sytuacjach gdzie potrzebujemy coś pobrać z kontenera, a nie mamy do niego dostępu np. tworzymy nasze ViewModel’e w stylu Blendability, gdzie instancja jest tworzona przez View (View First strategy).
• Kontener DI
• Rola kontenera DI w Prism
• Wykorzystanie kontenera DI
• ServiceLocator
Kontener DI
Kontener DI (ang. Dependency Injection) jest to wzorzec projektowy mający na celu usunąć z systemu zależności względem klas i wstrzykiwać obiekty implementujące dany interfejs. Słowno muzycznie: kontener stanowi swego rodzaju mapę, do której rejestrujemy powiązania pomiędzy interfejsem i klasą na zasadzie klucz, wartość. Jeśli potrzebujemy obiekt implementujący dany interfejs to odpytujemy się kontenera wskazując mu odpowiedni klucz (interfej), przez co w zamian otrzymujemy wartość (klasa).
Podejście to uniezależnia naszą aplikację od konkretnej implementacji przez co bardzo łatwo będzie można podmienić mechanizm funkcjonowania np.
public interface ICalendarDataService { void GetAllCalendars(Action<Calendar> result, object state); }
Interfejs ten posiada deklarację metody zwracającej wszystkie klendarze.
public class AzureCalendarDataService : ICalendarDataService { public void GetAllCalendars(Action<Calendar> result, object state) { // pobierz kalendarze z SQL Azure } } public class XMLCalendarDataService : ICalendarDataService { public void GetAllCalendars(Action<Calendar> result, object state) { // pobierz kalendarze z pliku XML } } public class EFCalendarDataService : ICalendarDataService { public void GetAllCalendars(Action<Calendar> result, object state) { // pobierz kalendarze z DB za pomocą Entity Framework } }
Powyższe klasy implementują nasz interfejs pobierając kalendarze z różnych źródeł danych.
Ponieważ aplikacja bazuje na interfejsach, nie musi wiedzieć skąd są pobierane dane.
Podmiana źródła danych polega na rejestracji w kontenerze danych odpowiedniej klasy dla naszego interfejsu ICalendarDataService.
Benefity są interesujące (testowalność, prosta konfiguracja, niezależność etc.)
Rola kontenera DI w Prism
Koncepcją Prisma jest możliwość stworzenia aplikacji składającej się z modułów. Tak na prawdę nie interesuje nas jak dana funkcjonalność będzie zaimplementowana. Ważne by moduł implementował wskazany interfejs.
Prism dostarcza implementację dwóch kontenerów:
- MEF,
- Unity.
By z nich skorzystać, wystarczy by nasz Boostraper podziedziczył po odpowiedniej klasie. I tak dla MEF będzie to MefBootstraper, a dla Unity będzie to UnityBootstraper. Oczywiście jeśli ktoś lubi StructureMap, AutoFac albo inny kontener DI, nie stoi nic na przeszkodzie by napisać własną implementację Boostrapera rejestrującego wasz ulubiony konter.
Wykorzystanie kontenera DI
Najczęściej wykorzystywanymi operacjami przy pracy z kontenerem DI są:
- rejestracja typu,
- pobieranie instancji typu z kontenera,
- rejestracja zadanej instancji (Singleton),
- wstrzykiwanie obiektów do konstruktora
Rejestracja typu w kontenerze Unity
this.Container.RegisterType<ICalendarDataService, XMLCalendarDataService>();
Rejestracja typu w kontenerze MEF
[Export(typeof(ICalendarDataService))] public class XMLCalendarDataService: ICalendarDataService { }
Pobieranie instancji typu z kontenera Unity
var cal = this.Container.Resolve<ICalendarDataService>();
Pobieranie instancji typu z kontenera MEF
var cal = this.Container.GetExportedValue<ICalendarDataService>();
Rejestracja zadanej instancji (Singletone) w kontenerze Unity
container.RegisterInstance<ICalendarDataService>(new AzureCalendarDataService);
Rejestracja zadanej instancji (Singletone) w kontenerze MEF
this.Container.ComposeExportedValue<ICalendarDataService>(this.serwisKalendarza);
Wstrzykiwanie obiektów do konstruktora.
W Unity operacja ta dzieje się automatycznie czyli np.
public class CalendarViewModel { private IEventAggregator _eventAggregator; public CalendarViewModel(IEventAggregator eventAggregator) { _eventAggregator = eventAggregator; } }
Zakończy się zainicjalizowaniem _eventAggregator odpowiednim obiektem zarejestrowanym w kontenerze Unity.
W MEF działa to analogicznie z tym, że nad klasą musimy umieścić atrybut
[ImportingConstructor] public class CalendarViewModel
Przedstawione przykłady to tylko szybka ściąga. Po szczegóły odsyłam pod adresy:
http://unity.codeplex.com
http://mef.codeplex.com
ServiceLocator
ServiceLocator to poprostu nakładka na nasze kontenery mająca na celu ułatwienie przy próbie zmiany kontenera podczas procesu tworzenia aplikacji.
Jedyną opcją jaką oferuje ServiceLocator jest możliwość pobrania typu obiektu i to w dodatku opakowanego w object przez co musimy to jeszcze rzutować na odpowiedni interfejs.
ServiceLocator.Current.GetInstance<IRegionNavigationService>();
Fajną rzeczą z ServiceLocatorem jest fakt, że jest on klasą statyczną dostępną w całej aplikacji tak więc przydaje się w sytuacjach gdzie potrzebujemy coś pobrać z kontenera, a nie mamy do niego dostępu np. tworzymy nasze ViewModel’e w stylu Blendability, gdzie instancja jest tworzona przez View (View First strategy).
Subskrybuj:
Posty (Atom)