Szukaj na tym blogu

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.

SketchFlow - nowy projekt


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


SketchFlow - nowy projekt


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)

public Dictionary SelectedDictionary { get; set; }
public ObservableCollection DictionariesCollection
{
    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

SketchFlow - nowy projekt


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.

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).

wtorek, 23 listopada 2010

Przegląd PRISM 4 – część 1

Tematem części 1 będzie, krótki opis na temat tego czym jest Prism, oraz opis Boostrapera.

Co to jest Prism ?
Prism to framework zawierająy szereg narzędzi ułatwiających budowę aplikacji składających się z niezależnych modułów. Wbrew pozorom, tak jak wiele osób uważa, Prism nie jest kolejnym frameworkiem MVVM. Wiele z ficzerów (Unity, EventAggregator) wchodzących w skład Prisma jest wykorzystywanych we własnych implementacjach MVVM typu Cinch, Caliburn etc.

Dlaczego warto korzystać z Prism’a ?
Głównie po to by móc tworzyć niezależne od siebie moduły tzn. zespoły programistów skupiają się na tworzeniu tylko danego kawałka funkcjonalności całego projektu (może to być część biznesowa jak również techniczna- infrastrukturalna). Zabieg ten wprowadza łatwiejsze testowanie poszczególnych modułów, zarządzanie całym projektem czy też aktualizacje danych funkcjonalności. W środowisku Silverlight modułowość pozwala także na przesłanie do klienta tylko tych modułów, których aktualnie potrzebuje przez co transfer danych pomiędzy serwerem, a klientem jest mniejszy (= działa szybciej).

Co nowego w wersji 4 ?
- wsparcie dla MEF (Managed Extensibility Framework) – MEF pełni rolę kontenera DI oraz zarządzania modułami,
- wsparcie dla MVVM - o tym szerzej w kolejnych częściach,
- wsparcie dla nawigacji .
Poza dodatkowymi funkcjonalnościami w wersji czwartej, zmianom uległa licencja, namespace etc.

Założenia
Z racji osobistych powódek, założyłem, że wykorzystam:
- Unity jako kontener UI,
- modułowość,
- nawigację opartą o przełączanie widoków (view- switching navigation),
- Prism’owe rozszerzenia dla MVVM (o ile będą lepsze od tego co sobie wypracowałem do tej pory).
Będę także przekazywał wystarczające minimum informacji by zacząć pracę z Prism. Po resztę szczegółów dotyczących wykorzystywanych funkcjonalności odsyłam do dokumentacji.

Bootstraper - koncepcja

SketchFlow - nowy projekt


Kluczowym elementem dla modułowej aplikacji wykorzystująca Prism jest Bootstraper.
Zadaniem jego jest inicjalizacja narzędzi podstawowych tj.:
- kontener DI – Prism oferuje nam Unity oraz MEF (jeśli chcemy, możemy z łatwością zaimplementować inny kontener),
- agregatora zdarzeń – odpowiedzialny za komunikację pomiędzy „luźno powiązanymi” modułami,
- menadżerów regionów, logowania, wyjątków,
Dodatkowo rejestruje w kontenerze DI wszelkiego rodzaju interfejsy implementowane przez powyższe narzędzia.
Po wykonaniu powyższych zadań, Bootstraper inicjalizuje Shell, który jest niczym innym jak głównym widokiem aplikacji zawierającym regiony do których będą wczytywane widoki, style i zasoby wykorzystywane przez całą aplikację. Poprzez analogię, Shell można porównać do ASP.NET-owego MasterPage.

Kod warty więcej niż 1000 słów
Inicjalizacja Bootstrapera odbywa się w App.xaml.cs

private void Application_Startup(object sender, StartupEventArgs e)
{
    var bootstrapper = new Bootstrapper();
    bootstrapper.Run();
}

A tak wygląda kod Bootstrapera

public class Bootstrapper : UnityBootstrapper
{
    private readonly CallbackLogger callbackLogger = new CallbackLogger();
 
    /// 
    /// Utworzenie widoku Shell
    /// ServiceLocator przejmuje funkcję Container.Resolve<IType>()
    /// opakowując kontener DI
    /// 
    protected override DependencyObject CreateShell()
    {
        return ServiceLocator.Current.GetInstance<MainPage>();
    }
 
    /// 
    /// Zainicjalizowanie głównego widoku
    /// 
    protected override void InitializeShell()
    {
        base.InitializeShell();
        Application.Current.RootVisual = (UIElement)this.Shell;
    }
 
    /// 
    /// Utworzenie własnego loggera 
    /// 
    protected override ILoggerFacade CreateLogger()
    {
        return this.callbackLogger;
    }
 
    /// 
    /// Konfiguracja kontenera DI: inicjalizacja Unity oraz mapowanie interfejs- typ np. IEventAggregator
    /// 
    protected override void ConfigureContainer()
    {
        base.ConfigureContainer();
        this.Container.RegisterInstance(this.callbackLogger);
        this.Container.RegisterType<ICustomerDetailsView, CustomerDetailsView>();
        this.Container.RegisterType<ICustomerDetailsViewViewModel, CustomerDetailsViewViewModel>();
        this.Container.RegisterType<IErrorWindow, ErrorWindow>();
        this.Container.RegisterType<IUnitOfWorkFactory, BasicUnitOfWorkFactory>();
        this.Container.RegisterType(typeof(IRepository<>), typeof(BasicRepository<>));
    }
 
    /// 
    /// Utworzenie katalogu modułów na podstawie pliku XAML
    /// 
    protected override IModuleCatalog CreateModuleCatalog()
    {
        return ModuleCatalog.CreateFromXaml(new 
         Uri("/MVVMBasic;component/ModulesCatalog.xaml", UriKind.Relative));
    }
}


Tak jak zapowiadałem, DevPrototype zawiera wszystkie powyższe zmiany prezentowane w tym poście (http://devprototype.codeplex.com)

poniedziałek, 22 listopada 2010

Listopadowe wyzwanie

Już od kilku lat zauważyłem, że listopad jest dla mnie jałowym miesiącem. Nic mi się nie chce robić, najlepiej to poszedłbym spać (chociaż noc przespana), marudzę, jęczę, człapię, kszątam się z kąta w kąt .... mógłbym tak wymieniać.

Niestety moje zachowanie było widoczne również na blogu- albo jak kto woli brak zachowania, rekacji, "Halo !!! Jest tam kto ?"

Ileż tak można ?

Na szczęście są osoby, które w tym czasie pracują pełną parą.
Całkiem niedawno (12.11.2010) pojawiła się 4 część frameworku Prism.

Chłopaki się postarali i oprócz ciekawych funkcjonalności, stworzyli obszerną dokumentację w postaci ebooka pt. "Developers Guide to Microsoft Prism. Building MVVM and Modular Applications with WPF and Silverlight"
(chętnych odsyłam pod adres: http://compositewpf.codeplex.com/

Był konkurs "DAJ SIĘ POZNAĆ", teraz będzie konkurs "CZY ŁUKASZ DA RADĘ ?" aka "PRZEGLĄD PRIS 4"

Na czym on polega ?
Ebook składa się z 11 rozdziałów oraz kilku dodatków.
Daję sobie czas do końca miesiąca by:
- przerobić całego ebooka,
- streścić wam rozdziały w postaci postów na blogu
- zaimplementować kilka ciekawych ficzerów w moim projekcie DevPrototype (http://devprototype.codeplex.com/).

Myślę, że to całkiem fajne wyzwanie mające na celu wyciągnięcie mnie z listopadowego dreszczowca.
Nagroda ? Kupię sobie piwo :D albo i dwa :D (a co tam :P)

Zainteresowanych zapraszam do śledzenia mojego bloga.

sobota, 30 października 2010

Dziennik Zdrowej Diety – checkpoint i zmiany

Zapewne zauważyliście, że sposób prowadzenia mojego bloga oraz projektu odbiega od założeń postawionych kilka miesięcy temu. Przyznam się szczerze, że pomysł przerósł moje możliwości czasowe co nie oznacza, że się poddaję.

Przez te kilka tygodni starałem się wprowadzić was w sposób tworzenia aplikacji w technologii Silverlight wraz z wykorzystaniem wzorca projektowego MVVM. Ci którzy bawią się Silverlightem na co dzień, stykają się z większością rzeczy tutaj opisanych. Zamierzam nadal dzielić się moją wiedzą, spostrzeżeniami oraz wątpliwościami, jednakże będą to raczej elementy rzemiosła niż opis krok po kroku co dzieje się w projekcie.
Projekt „Dziennik Zdrowej Diety” nabieże także innego charakteru.
Posłuży mi on raczej jako prototyp przy tworzeniu kolejnych aplikacji. Tak więc wszelkiego rodzaju nowinki, eksperymenty będą zaimplementowane dokładnie w nim.
Co chcę osiągnąć ? O tym poniżej.

Założenia:
Aplikacja ma być napisana w Silverlight 4 z wykorzystaniem wzorca MVVM oraz frameworku Prism.
Wsparcie asynchroniczne będzie zrealizowane poprzez framework Async.
We wzorcu MVVM postaram się wykorzystać elementy takich frameworków MVVM jak Caliburn, Calcium, Cinch, MVVM Light itp.
Do komunikacji z bazą danych chcę wykorzystać WCF RIA Services przykryte dodatkową warstwą abstrakcji pozwalającą mi na uniezależnienie się ViewModelu od konkretnych serwisów.

Po co mi to ?
Na początku będzie to WCF RIA Services wraz z Entity Framework 4. Z czasem chcę udostępnić możliwość korzystania z projektu na platformie mobilnej Windows Phone 7, gdzie sposób wymiany danych będzie oparty o protokół OData. Ponieważ interesuje mnie temat chmur obliczeniowych, spróbuję udostępnić możliwość trzymania aplikacji na Windows Azure.
Mam nadzieję, że dużą część aplikacji, dzięki MVVM, będzie wspólna dla każdej z platform.
Do tej pory miałem także znikomą styczność z testami jednostkowymi, tak więc postaram się zgłębić także ten temat opierając się na nUnit.

Co mam ?
Przez ostatnich kilka tygodni rozijałem już swój prototyp, w którym możecie znaleźć:
- architektura oparta o wzorzec MVVM,
- WCF RIA Services jako komunikacja z serwerem,
- Entity Framework 4 pełniący rolę dostępu do danych,
- zaimplementowany wzorzec Repository dla Entity Framework 4,
- elementy frameworka Prism.

Nad czym chcę się skupić w najbliższym czasie ?
- próba implementacji wzorca Repository dla środowiska asynchronicznego,
- wykorzystanie frameworka Async,
- stworzenie warstwy abstrakcji pomiędzy ViewModelem a serwisem,
- autoryzacja i autentykacja w WCF RIA Services

Z niewiadomych przyczyn (wrong Repository UUID) nie udało mi się dorzucić projektu prototypu do istniejącego SVN dla projektu Dziennik Zdrowej Diety dlatego też stworzyłem nowy.
Zinteresowanych zapraszam pod adres: http://devprototype.codeplex.com
Zachęcam do śledzenia moich poczynań :)

środa, 20 października 2010

Przegląd Action (Blend SDK) w kontekście MVVM cz. 2

Trochę czasu minęło od ostatniego postu. Postaram się jednak dotrzymać słowa i dokończyć to co zacząłem ;)

ControlStoryboardAction – Action, który pozwala na uruchomienie StorayBoard czyli animacji w momencie wystąpienia zdarzenia na elemencie źródłowym.

MVVM: Przez pewien czas zastanawiałem się w jaki sposób mogę skorzystać z tego Action w kontekście wzorca MVVM. Rozwiązaniem jest zastosowanie wyzwalacza PropertyChangeTrigger.
Wykorzystując wspomniany wyzwalacz, możemy sprawić by nasza aplikacja uruchomiła się w momencie zmiany właściwości zawartej w naszym ViewModel’u.


SketchFlow - nowy projekt


Tak to wygląda w XAML’u (FirstName jest oczywiście właściwością ViewModel’u).
<Path x:Name="path" Data="…" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Bottom">
     <i:Interaction.Triggers>
        <ei:PropertyChangedTrigger Binding="{Binding FirstName}">
            <ei:ControlStoryboardAction Storyboard="{StaticResource SampleStoryboard}"/>
        </ei:PropertyChangedTrigger>
     </i:Interaction.Triggers>
</Path>

GoToStateAction – Action, który zmienia stan wizualny kontrolki w momencie wystąpienia zdarzenia na elemencie źródłowym.

MVVM: Przykładowy scenariusz, czysto hipotetyczny, wygląda tak:
Na naszym widoku posiadamy kontrolkę [elipsa] sygnalizującą dwoma kolorami informację o aktywności zadanego serwisu. Jeśli serwis jest online to kontrolka zmienia stan na wcześniej zdefiniowany „StateOnline”, który nadaje kontrolce kolor zielony. Jeśli serwis jest offline, ustawiany jest stan „StateOffline” zmieniający kolor na czerwony.
W naszym ViewModelu posiadamy właściwość bool IsOnline.
We właściwościach GoToStateAction ustawiamy TriggerType na TimerTrigger. Domyślne ustawienia sprawią, że co 1000 ms będzie sprawdzany stan naszej kontrolki.
Właściwość StateName bindujemy z właściwością IsOnline pamiętając o zastosowaniu konwertera, którego zadaniem będzie zwrócenie stringa „StateOnline” jeśli IsOnline = true oraz „StateOffline” w przeciwnym wypadku.


SketchFlow - nowy projekt


<Ellipse x:Name="ellipse" Height="80" Fill="Black">
    <i:Interaction.Triggers>
        <ei:TimerTrigger>
            <ei:GoToStateAction StateName="{Binding IsOnline, Converter={StaticResource myConverter}}"/>
        </ei:TimerTrigger>
    </i:Interaction.Triggers>
</Ellipse>

HyperlinkAction – Action, który pozwala przekierować użytkownika pod wskazany adres internetowy w momencie wystąpienia zadanego zdarzenia.

MVVM: Jedyne co mi przychodzi do głowy w kontekście tego Action to możliwość odsyłania użytkownika do różnych adresów na podstawie tego co się stanie w ViewModelu. Oczywiście, trzymanie w ViewModelu adresów internetowych nie jest najlepszym rozwiązaniem dlatego też warto byłoby skupić się na trzymaniu jakiegoś enumeryka do którego zbindowana jest właściwość NavigateUriprzy wykorzystaniu konwertera, który tłumaczy daną wartość enumeryka na bardziej odpowiedni adres internetowy. Z racji formy aplikacji, Action ten ma sens tylko dla aplikacji Silverlight’owych.

Dodatkową ciekawostką jest możliwość otworzenia zadanej strony w zadanej ramce:
• _blank - nowe okno/zakładka przeglądarki
• _parent - w oknie rodzica ramki, a jeśli taki nie istnieje to w aktualnym oknie
• _self - w aktualnym oknie


SketchFlow - nowy projekt


<Ellipse x:Name="ellipse" Height="80" RenderTransformOrigin="0.5,0.5" Fill="Black">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="MouseEnter">
            <ei:HyperlinkAction NavigateUri="http://devgroup.com.pl" TargetWindow="_blank"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Ellipse>

Na dzisiaj tyle.
Kolejny wpis będzie zawierał Action:
• PlaySoundAction
• RemoveElementAction
• SetDataStoreValueAction
Na sam koniec postaram się także wgłębić w kod i pokazać wam jak Action działa od środka.

sobota, 9 października 2010

Przegląd Action (Blend SDK) w kontekście MVVM cz. 1

Dzisiaj chciałbym opisać akcje (Action) jakie znajdują się w Expression Blend w zakładce Assets -> Behaviors. Wiele z nich ułatwi wam życie pisząc aplikację Silverlight opartą o wzorzec projektowy MVVM.
Dla tych którzy nie posiadają Expression Blend, dostępne jest darmowe BlendSDK, które zawiera wszystkie funkcjonalności opisane poniżej. Link do Blend SDK -> LINK

CallMethodAction – Action, który pozwala na wywołanie dowolnej metody bezparametrowej na wskazanym obiekcie w momencie, gdy wystąpi zdarzenie na obiekcie źródłowym.
Poniżej przykład pokazujący jak wywołać metodę OnAddCommand() z ViewModel’u przy zmianie zaznaczonego elementu na kontrolce dataGrid.

MVVM: zachowanie te może posłużyć do wykonania logiki biznesowej interfejsu graficznego przy wystąpieniu odpowiedniego zdarzenia np. kontrolka DevExpress agDataGrid posiada metodę UnGroup(), która usówa wszystkie grupowania kolumn z grida. Podpinając to zachowanie do przycisku i ustawiając wyzwalacz na zdarzenie Click, możemy w całkiem zgrabny sposób wykorzystać powyższą metodę UnGroup() nie pisząc linijki kodu w CodeBehind naszego widoku.

UWAGA: Nie sugerujcie się możliwością bindowania MethodName ponieważ wskazanie metody spowoduje wystąpienie błędu. W MethodName należy wpisać nazwę metody jaką implementuje obiekt wskazany w TargetObject.


SketchFlow - nowy projekt


<sdk:DataGrid x:Name="dataGrid"
                 ItemsSource="{Binding Customers}"
                 AutoGenerateColumns="False">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="SelectionChanged">
      <ei:CallMethodAction TargetObject="{Binding Mode=OneWay}"
                              MethodName="OnAddCommand" />
    </i:EventTrigger>
  </i:Interaction.Triggers>

ChangePropertyAction – Action, który pozwala na zmianę właściwości docelowego elementu w momencie wystąpienia zdarzenia na elemencie źródłowym.

MVVM: Na poniższym przykładzie pokazałem jak zmieniać wartości właściwości SelectedValue zawartej w ViewModel’u. Właściwość ta przechowuje aktualnie zaznaczony wiersz na kontrolce dataGrid.

UWAGA: Przy niektórych kombinacjach (np. Opacity) nie zawierzajcie w 100% Blend’owi i zerknijcie do pliku z XAML’em, ponieważ czasami źle zapisuje ustawione wartości i aplikacja się wywala (zamiast 0.55 ustawił wartość 0,55).


SketchFlow - nowy projekt


<sdk:DataGrid x:Name="dataGrid"
                 ItemsSource="{Binding Customers}"
                 AutoGenerateColumns="False">
  <i:Interaction.Triggers>
    <i:EventTrigger SourceObject="{Binding ElementName=dataGrid}"
                                EventName="SelectionChanged"
                                SourceName="dataGrid">
      <ei:ChangePropertyAction TargetObject="{Binding Mode=OneWay}"
                                  PropertyName="SelectedValue"
                                  TargetName="button"
                                  Value="{Binding SelectedItem,                        ElementName=dataGrid}">
     <ei:ChangePropertyAction.Ease>
      <BackEase EasingMode="EaseInOut"/>
     </ei:ChangePropertyAction.Ease>
      </ei:ChangePropertyAction>
    </i:EventTrigger>
  </i:Interaction.Triggers>

InvokeCommandAction – Action, który pozwala na uruchomienie komenty ICommand dla dowolnej kontrolki i na podstawie dowolnego zdarzenia.

MVVM: Moim zdaniem jest to jedna z najważniejszych Action patrząc pod kątem MVVM. Jak wiemy, Silverlight 4 wprowadził tzw. Commanding do podstaw frameworka (wcześniej można była skorzystać np. z Prism, który rozszerzał kontrolkę Button o możliwość korzystania z tej funkcjonalności). Niestety Commanding jest zaimplementowany tylko dla kontrolki Button (właściwości Commandi CommandParameter). Implementacja dla innych kontrolek oraz innych zdarzeń wymagała napisania odpowiednich Behaviors. InvokeCommandAction robi to z automatu i bez pisania nawet linijki kodu.

Poniższy przykład przedstawia wywołanie metody OnEditCommand(object obj) poprzez EditCommand typu ICommand wraz z przekazaniem do metody aktualnie zaznaczonego wiersza w dataGrid. Metoda jest wywoływana w momencie zaznaczenia wiersza w dataGrid.

Po zabawie z Reflectorem, zauważyłem, że posiadamy dwie opcje dowiązywania do ICommand:
1. Właściwość Command- związujemy się bezpośrednio z właściwością typu ICommand w naszym ViewModel'u,
2. Właściwość CommandName- związuje się do właściwości typu ICommand, wyszukując ją poprzez refleksję w skojarzonej z Action klasie.


SketchFlow - nowy projekt


<sdk:DataGrid x:Name="dataGrid"
                 ItemsSource="{Binding Customers}"
                 AutoGenerateColumns="False">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="SelectionChanged">
      <i:InvokeCommandAction Command="{Binding EditCommand, Mode=OneWay}"
                                           CommandParameter="{Binding SelectedItem, ElementName=dataGrid}" />
    </i:EventTrigger>
  </i:Interaction.Triggers>

W kolejnej części opiszę pozostałe Actions:
• ControlStoryboardAction
• GoToStateAction
• HyperlinkAction
• PlaySoundAction
• RemoveElementAction
• SetDataStoreValueAction

sobota, 2 października 2010

Dziennik Zdrowej Diety - Silverlight i zasoby na serwerze

Jak wiemy, aplikacje Silverlight działają po stronie klienta tak więc całość ściągana jest na komputer użytkownika. Z tego powodu głównym celem jaki nam przyświeca jest stworzenie w taki sposób aplikacji by plik wynikowy XAP był jak najmniejszy w celu wydajnego załadowania programu.

Dziś chciałbym przedstawić sposób dostępu aplikacji Silverlight (klient) do zasobów umieszczonych na serwerze.
Za to zadanie odpowiada klasa WebClient znajdująca się w przestrzeni nazw System.Net

Oto przykład:

private void LoadImageFromServer()
{
    this.wcImg = new WebClient();
    this.wcImg.OpenReadAsync(new Uri("../AppData/picture.jpg"),UriKind.RelativeOrAbsolute));
    this.wcImg.OpenReadCompleted += delegate(object sender, OpenReadCompletedEventArgs e)
    {
         if (e.Error == null)
         {
             using (Stream stream = e.Result)
             {
                 var image = new BitmapImage();
                 image.SetSource(stream);
                 Dispatcher.BeginInvoke(() =>
                 {
                      this.picPhoto.Source = image;
                      this.picPhoto.Width = image.PixelWidth;
                      this.picPhoto.Height = image.PixelHeight;
                 });
             }
         }
    };
    this.wcImg.DownloadProgressChanged += delegate(object sender,DownloadProgressChangedEventArgs e)
    {
         this.txtPercentageLoaded.Text = e.ProgressPercentage + "%";
    };
}

Program pobiera zdjęcie z katalogu AppData zamieszczonego w katalogu wirtualnym aplikacji.

Nic nie zostai na przeszkodzie by zamieścić wszystkie pliki graficzne bezpośrednio w projekcie Silverlight, jednakże należy pamiętać, że postępując w ten sposób, zwiększamy rozmiar naszego pliku XAP czego chcieliśmy uniknąć.

Powyższy sposób pozwala nam na utrzymanie optymalnego rozmiaru pliku XAP co jest równoważne z szybszym ściągnięciem go na maszynę kliencką oraz pozwala nam na swego rodzaju dynamikę tzn. możemy upload'ować pliki na serwer i mieć od razu do nich dostęp z poziomu aplikacji Silverlight.

poniedziałek, 27 września 2010

Licencjonowanie produktów Express Edition

Witam,

Ponieważ nie wszyscy zdają sobie sprawę z możliwości oraz wymagań jakie stawia przed nami firma Microsoft odnośnie korzystania z produktów w wersji Express Edition, postanowiłem zamieścić fragment listu jaki otrzymałem z Centrum Obsługi Klienta Microsoft.

Mam nadzieję, że rozwieje on wszystkie wątpliwości .

Umowa Licencyjna dołączona do narzędzi programistycznych Visual Studio Express Editions daje możliwość komercyjnego wytwarzania aplikacji i ich dystrybucji przy zachowaniu wytycznych zawartych w Umowie Użytkownika Końcowego oprogramowania Microsoft Visual Studio Express.
SQL Server Express można bez dodatkowych opłat redystrybuować, wymagana jest tylko rejestracja na stronie internetowej: http://www.microsoft.com/sql/editions/express/redistregister.mspx
SQL Server Express może być używany wewnątrz firmy komercyjnie, jednakże wykorzystanie komercyjne polegające na hostowaniu SQL Server Express i pobieraniu opłat nie jest możliwe.
Oprogramowanie pozyskane w ramach subskrypcji MSDN AA można wykorzystywać do celów edukacyjnych i badawczych.Nie ma możliwości wykorzystywania oprogramowania z MSDN AA w celach komercyjnych. Informacje na ten temat można znaleźć na stronie: http://www.codeguru.pl/faq.aspx

Wersje testowe oprogramowania służą do zapoznania się z możliwościami i funkcjami oprogramowania. Jeżeli umowa licencyjna nie stanowi inaczej można testować oprogramowanie na sprzęcie, który służy do prowadzenia działalności gospodarczej. Natomiast komercyjne prowadzenie działalności gospodarczej w oparciu o wersje testowe nie jest dopuszczalne, między innymi dlatego, że licencja taka ma ograniczenia czasowe.

WireFrames - szybki sposób na przekazanie wiedzy biznesowej

Dzisiaj zaskoczyła mnie informacja o rezygnacji przez Microsoft z Windows Live Space czyli blogów z pod znaczka MS. Z tego względu postanowiłem zajrzeć co się znajduje na moim starym i zapomnianym blogu. Efektem tego przeglądu jest przeklejenie kilku wpisów.

Ostatnio pracuję razem z kumplami nad rozwojem aplikacji typu ERP.
Ponieważ tak się zdarzyło, że z naszej czwórki tylko ja i kolega Tomek posiadaliśmy największą wiedzę biznesową apropo naszego rozwiązania, padł pomysł by stworzyć diagramy przypadków użycia wraz z opisami, które miały być czymś co pozwoli całej grupie na zaznajomienie się z tematem.
Szybko jednak okazało się, że każdy z nas ma własny sposób tworzenia przypadków użycia co spowodowało wiele niejasności.
Kolega Przemek zaproponował WireFrames.

WireFrames to obraz formatek aplikacji wraz z opisem funkcjonalności pozwalający na szybkie zapoznanie się z zadanym tematem. Ponieważ w naszym przypadku WireFrames tworzone jest przy pomocy PowerPoint, w każdej chwili można dodać/usunąć/przestawić odpowiedni element na slajdzie formatki.
Dodatkowo PowerPoint udostępnia szereg funkcjonalności typu hiperlinki, animacje przez co można stworzyć w bardzo szybki sposób szkielet aplikacji (warstwa prezentacji) wraz ze wszystkimi przejściami pomiędzy oknami.
Bardzo fajnie sprawdza się to w grupie ponieważ wymusza na projektantach spójny styl tworzenia danej partii projektu.
Po kilku spotkaniach i omówieniu naszej pracy bardzo szybko doszliśmy do wspólnych wniosków dzięki czemu możemy wkroczyć w kolejny etap procesu tworzenia naszej aplikacji - Domain Model..

Poniżej przykładowy screen WireFrames.


WireFrames- BudCon


Ten sposób podejścia do tworzenia UI może być alternatywą do SketchFlow.
Efekt końcowy jest podobny czyli możliwość pokazania klientowi interfejsu użytkownika aplikacji wraz z przejściami oraz możliwością wprowadzenia wszystkich poprawek na bieżąco na długo przed rozpoczęciem developmentu.

środa, 22 września 2010

Sekwencja wywołań w asynchronicznym środowisku Silverlight cz. 2 - CoRutines

W dzisiejszej części przedstawię sposób wykorzystania corutines w Silverlight.
Corutines to mechanizm pozwalający na wykonywanie sekwencji kodu tzn. do puki nie skończy się działanie danego bloku kodu, program nie przejdzie dalej. W Silverlight, gdzie wszystko musi być wywoływane asynchronicznie tego typu mechanizm jest wręcz zbawieniem. Nie oznacza to, że od teraz wszelkiego rodzaju operacje będziesz wykonywał przy wykorzystaniu corutines. To by zabiło całą koncepcję technologii Silverlight, jednakże zdarzają się sytuacje gdzie do wykonania pewnego bloku kodu potrzebujesz dane będące wynikiem operacji poprzedzającego bloku kodu.
Domyślnie język C# nie wspiera corutines. Mechanizm ten można natomiast zbudować z elementów jakie dostarcza nam .NET.
Na rozwiązanie, które chcę zaproponować składa się:
- klasa wątków BackgroundWorker,
- interfejs IEnumerable,
- słówko yield.

Ponieważ większość z nas zetknęła się ze słówkiem yield na samym początku przygody z językiem C# (zapewne czytając jakieś opasłe tomisko) po czym przez resztę swojego komercyjnego / niekomercyjnego doświadczenia nie miała sposobności wykorzystać go (tak, generalizuję), postaram się na prostym przykładzie pokazać jego działanie.

class Program
{
    static void Main(string[] args)
    {
        foreach(var msg in Teksty())
        {
            Console.WriteLine(msg);
        }
        Console.ReadKey();
    }
 
    public static IEnumerable<string> Teksty()
    {
        yield return "Start";
 
        Console.WriteLine("Tutaj sobie coś wykonuję");
        Console.ReadKey();
 
        yield return "Drugi krok";
 
        Console.WriteLine("Kolejne czynności");
        Console.ReadKey();
        yield return "Zakończenie";
    }
}

Wyobraźmy sobie, że iterujemy pętlą foreach po tablicy string’ów { „A”, „B”, „C” }. Czyli każda kolejna iteracja bieże kolejny element tablicy. Dla przedstawionego poniżej przykładu tablica string’ów jest równoważna YieldTab() :

class Program
{
    static void Main(string[] args)
    {
        string[] normalTab = {"A", "B", "C"};
 
        foreach(var msg in normalTab)
        {
            Console.WriteLine(msg);
        }
        Console.ReadKey();
 
        foreach (var msg in YieldTab())
        {
            Console.WriteLine(msg);
        }
        Console.ReadKey();
    }
 
    public static IEnumerable YieldTab()
    {
        yield return "A";
        yield return "B";
        yield return "C";
    }
}


Analizując przykład pierwszy bardzo łatwo wychwycić różnicę. Przy kolejnej iteracji po IEnumerable, program wykonuje wszystko co się znajduje pomiędzy słówkami yield oraz zwraca wartość elementu.
Myślę, że koncepcja działania jest mniej więcej zrozumiała.

Zważając na powyższy przykład opiszę słowno muzycznie jak dziła mechanizm corutines.
1. Tworzymy interfejs pomocniczy IResult

public interface IResult
{
    void Execute();
    event EventHandler Completed;
}

Zadaniem metody Execute() będzie wywołanie dowolnej metody przekazanej to mechanizmu Corutines, natomiast zdarzenie Completed będzie informowało o zakończeniu działania przekazanej metody.

2. Tworzymy klasę implementującą powyższy mechanizm zintegrowany z klasą BackgroundResult

public class BackgroundResult : IResult
{
    private readonly Action action;
    public BackgroundResult(Action action)
    {
        this.action = action;
    }
 
    public void Execute()
    {
        var backgroundWorker = new BackgroundWorker();
        backgroundWorker.DoWork += (e, sender) => action();
        backgroundWorker.RunWorkerCompleted += (e, sender) => Completed(this, EventArgs.Empty);
        backgroundWorker.RunWorkerAsync();
    }
    public event EventHandler Completed;
}


Klasa ta w konstruktorze przyjmuje Action będący delegatem, który nie przyjmuje żadnych parametrów i nie zwraca, żadnej wartości. W skrócie, w konstruktorze przekazujemy referencję do metody odpowiadającej wymaganiom powyższego delegata.

Metoda Execute() uruchamia przekazaną metodę w trybie asynchronicznym i nasłuchuje moment jej zakończenia poprzez zdarzenie RunWorkerCompleted, który następnie wzbudza zdarzenie Completed.

3. Tworzymy klasę, której zadaniem będzie iteracja po IEnumerable w sposób jaki jest właściwy dla opisywanego mechanizmu Corutines.

public class ResultEnumerator
{
    private readonly IEnumerator _enumerator;
    public ResultEnumerator(IEnumerable children)
    {
        _enumerator = children.GetEnumerator();
    }
    public void Enumerate()
    {
        ChildCompleted(null, EventArgs.Empty);
    }
    private void ChildCompleted(object sender, EventArgs args)
    {
        var previous = sender as IResult;
        if (previous != null)
            previous.Completed -= ChildCompleted;
        if (!_enumerator.MoveNext())
            return;
        var next = _enumerator.Current;
        next.Completed += ChildCompleted;
        next.Execute();
    }
}


Klasa przyjmuje w konstruktorze kolekcję elementów implementujących interfejs IResult, a następnie pobiera enumerator tej kolekcji.
MoveNext() enumeratora można przyrównać do pojedyńczego odczytania z pętli np. foreach, gdzie wskazany element znajduje się we właściwości Current. Kolejne wywołanie metody MoveNext() powoduje przesunięcie obecnego elementu o jeden dalej.
Wywołując metodę Enumerate() uruchamiamy swoistą funkcję rekurencyjną ChildCompleted(object sender, EventArgs args), gdzie zakończone zadanie wywołuje ponownie tą samą funkcję.
Proces kończy się w momencie osiągnięcia kresu przekazanej kolekcji.

A tak to działa w praktyce:

public partial class MainPage : UserControl
{
    private AutoResetEvent trigger = new AutoResetEvent(false);
 
    public MainPage()
    {
        InitializeComponent();
        this.Loaded += MainPage_Loaded;
    }
 
    void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        var enumerator = new ResultEnumerator(CallWebServiceMethods());
        enumerator.Enumerate();
    }
    public IEnumerable CallWebServiceMethods()
    {
        yield return new BackgroundResult(() =>
        {
            var serwis = new LocalSerwis.Service1SoapClient();
            serwis.MetodaPierwszaCompleted += (sender, e) =>
            {
                Dispatcher.BeginInvoke(() => MessageBox.Show(e.Result));
                trigger.Set();
            };
            serwis.MetodaPierwszaAsync();
            trigger.WaitOne();
        });
        yield return new BackgroundResult(() =>
        {
            var serwis = new LocalSerwis.Service1SoapClient();
            serwis.MetodaDrugaCompleted += (sender, e) =>
            {
                Dispatcher.BeginInvoke(() => MessageBox.Show(e.Result));
                trigger.Set();
            };
            serwis.MetodaDrugaAsync("Ala ma kota");
            trigger.WaitOne();
        });
    }
}

Gdzie mój serwis ma postać:
public class Service1 : System.Web.Services.WebService
{
    [WebMethod]
    public string MetodaPierwsza()
    {
        return "Wynik Metody Pierwszej";
    }
    [WebMethod]
    public string MetodaDruga(string msg)
    {
        char[] chars = msg.ToCharArray();
        Array.Reverse(chars);
        return new string(chars);
    }
}

Ponieważ zapytania do webserwisu są również asynchroniczne, skorzystałem z klasy AutoResetEvent, która potrafi zastopować aktualny wątek (metoda WaitOne())aż do momentu uruchomienia metody Set(), która go wzbudza.
Wywołanie postaci Dispatcher.BeginInvoke(Action a) pozwala na wykonanie kodu w wątku głównym UI aplikacji Silverlight.

Źródła do przykładu znajdują się pod tym adresem: http://dhd.codeplex.com/releases/view/52747
Na zakończenie chciałbym wspomnieć, że powyższe rozwiązanie nie jest mojego autorstwa tylko Pana Roba Eisenberga, który jest twórcą frameworka MVVM Caliburn (http://caliburn.codeplex.com/).

czwartek, 16 września 2010

Sekwencja wywołań w asynchronicznym środowisku Silverlight cz. 1

Przes ostatnie kilka dni męczyłem się niemiłosiernie z asynchronicznością Silverlighta. Pomimo wielkiego entuzjazmu jaki żywię do tej technologii, pojawiły się we mnie pierwsze wątpliwości. Zacząłem się zastanawiać czy, aby na pewno Silverlight nadaje się do biznesowych rozwiązań czy też obiekt Session z XPO DevExpressa (ORM firmy DevExpress) jest w pełni dopracowany do wymagań jakie stawia Silverlight. A może to ja jestem w tym wszystkim najsłabszym ogniwem ? To ostatnie jest wielce prawdopodobne. Dosyć tego bajdużenia.

O co w tym wszystkim chodzi?

Jeśli Twój proces biznesowy wymaga byś odczytał obiekt ze źródła danych, a następnie wykorzystał informacje w nim zawarte do odczytania kolejnych danych, będziesz potrzebował sekwencyjnych wywołań asynchronicznych.
Rozwiązanie tego typu (metoda Initialize()):

public class Sample
{
    private FirstObject firstObj;
    private SecondObject secondObj;
 
    public void GetFirstObjectAsync(int id)
    {
        var service = new DummyWebService.ServiceSoapClient();
        service.GetFirstObjectCompleted += (sender, e) =>
        {
            firstObj = new FirstObject
                           {
                                Id = e.Result.Id,
                                ChildId = e.Result.ChildId
                           };
        };
        service.GetFirstObjectAsync(id);
    }
 
    public void GetSecondObjectAsync(int id)
    {
        var service = new DummyWebService.ServiceSoapClient();
        service.GetSecondObjectCompleted += (sender, e) =>
        {
            secondObj = new SecondObject
            {
                Id = e.Result.Id,
            };
        };
        service.GetSecondObjectAsync(id);
    }
 
    public void Initialize()
    {
        GetFirstObjectAsync(1);
        GetSecondObjectAsync(firstObj.Id);
    }
}


nie zda egzaminu ponieważ metody są wykonywane asynchronicznie co oznacza, że nie wiesz, która z nich wykona się pierwsza. W naszym przypadku wymogiem jest by najpierw wykonała się metoda GetFirstObjectAsync(1), a po otrzymania rezultatu, wykonała się metoda GetSecondObjectAsync(firstObj.Id).
Wywołanie tego w synchroniczny sposób spowoduje zablokowanie aplikacji.

Najprostszym rozwiązaniem jest coś takiego:


public class Sample
{
    private FirstObject firstObj;
    private SecondObject secondObj;
 
    public void GetFirstObjectAsync(int id)
    {
        var service = new DummyWebService.ServiceSoapClient();
        service.GetFirstObjectCompleted += (sender, e) =>
        {
            firstObj = new FirstObject
                           {
                                Id = e.Result.Id,
                                ChildId = e.Result.ChildId
                           };
                           GetSecondObjectAsync(firstObj.Id);
        };
        service.GetFirstObjectAsync(id);
    }
 
    public void GetSecondObjectAsync(int id)
    {
        var service = new DummyWebService.ServiceSoapClient();
        service.GetSecondObjectCompleted += (sender, e) =>
        {
            secondObj = new SecondObject
            {
                Id = e.Result.Id,
            };
        };
        service.GetSecondObjectAsync(id);
    }
 
    public void Initialize()
    {
        GetFirstObjectAsync(1);
    }
}


W ten sposób osiągniemy zamierzony efekt.
Problem pojawia się w momencie jak takich metod jest więcej. Zarządzanie takim kodem pozostawia wiele do życzenia – blah :/ ).

UWAGA:
Próba wywoływania asynchronicznie metody, wykorzystując delegata oraz jego metodę BeginInvoke() w Silverlight zakończy się wystąpieniem wyjątku NotSupportedException. Nazwa wyjątku mówi sama za siebie.

W kolejnej części postaram się pokazać jak wykonywać sekwencyjnie metody asynchroniczne przy wykorzystaniu corutines (http://pl.wikipedia.org/wiki/Wsp%C3%B3%C5%82program).

poniedziałek, 13 września 2010

Dziennik Zdrowej Diety – Logika UI w MVVM

Korzystając z wzorca MVVM cały czas skupiamy się na zachowaniu koncepcji czyli wszelkiego rodzaju logika biznesowa udostępniana jest widokowi poprzez ViewModel. Z drugiej strony nasz widok (View) powinien składać się tylko ze znaczników XAML. W sieci można znaleźć mnóstwo video tutoriali gdzie oglądając odnosimy wrażenie, że punktem honoru prelegenta jest pokazanie w pełni działającej aplikacji przy zachowaniu czystego CodeBehind.

Ostatnio jednak uświadomiłem sobie (a raczej wymaganie biznesowe postawiło mnie przed tym faktem), że to tylko teoria i w pewnym momencie choć byśmy bardzo chcieli musimy dodać coś do CodeBehind naszego widoku, bo przecież gdzie możemy wywołać metody udostępnione przez kontrolki (np. DevExpress AgGrid.Ungroup()) jak nie w CodeBehind widoku, na którym jest umieszczona ta kontrolka.
Tak więc schowałem swoje rządze związane z zachowaniem czystego CodeBehind do kieszenie i wymyśliłem rozwiązanie, które według mnie zachowuje założenia koncepcji MVVM.
Poniżej przykład.

ViewModel:

public class MainPageViewModel : IMainViewModel, INotifyPropertyChanged
{
    public event EventHandler SampleEvent;
    public MainPageViewModel()
    {
        if (!DesignerProperties.IsInDesignTool)
        {
            LoadData();
        }
    }

    public void LoadData()
    {
        WorkingItem = new Customer
        {
            Age = 28,
            FirstName = "Lukas"
        };
    }

    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            _firstName = value;
            RaisePropertyChanged("FirstName");
        }
    }
    private int _age;
    public int Age
    {
        get { return _age; }
        set
        {
            _age = value;
            RaisePropertyChanged("Age");
        }
    }
    public Customer WorkingItem { get; private set; }
    private ICommand _clickCommand;
    public ICommand ClickCommand
    {
        get
        {
            if (_clickCommand == null)
                _clickCommand = new MyCommand(OnClickCommand, null);
            return _clickCommand;
        }
    }
    private void OnClickCommand(object obj)
    {
        if (_age == 0)
            Age = 28;
        else
        {
            Age += 2;
            if (Age == 4 && SampleEvent != null)
                SampleEvent(this, new EventArgs());
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(string propertyName)
    {
        if(PropertyChanged != null)
           PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Interfejs IMainViewModel:
public interface IMainViewModel
{
    event EventHandler SampleEvent;
    string FirstName { get; set; }
    int Age { get; set; }
    void LoadData();
}

CodeBehind widoku:
public partial class MainPage : UserControl
{
    public MainPage()
    {
        InitializeComponent();
        this.Loaded += (sender, ea) =>
        {
            if (this.DataContext != null && this.DataContext is IMainViewModel)
            {
                var vm = DataContext as IMainViewModel;
                vm.SampleEvent += vm_SampleEvent;
            }
        };
    }
    void vm_SampleEvent(object sender, EventArgs e)
    {
        MessageBox.Show("To jest tylko przykład");
    }
}

Nasz ViewModel implementuje zdarzenie SampleEvent, które jest subskrybowane przez widok. W celu wywołania logiki interfejsu graficznego ze strony naszego ViewModel’u, odpalamy zdarzenie i w metodzie obsługującej zdarzenie w CodeBehind widoku uruchamiamy odpowiednią logikę UI. W podanym przykładzie jest to zwykły MessageBox, ale możemy to zastąpić bardziej złożonymi metodami (np. wspomniany AgGrid.Ungroup()).

Dzięki powyższemu zabiegowi nasz widok operuje tylko na logice interfejsu graficznego oraz ViewModel nadal nie wie o istnieniu widoku przez co łatwo możemy przeportować naszą aplikację Silverligh MVVM do np. WPF MVVM.

Dla wyjaśnienia nieścisłości zaznaczam, że korzystam z MVVM dostosowanego do pracy z Expresssion Blend (ViewModel dołączony w XAML’u widoku), dlatego też podpinam się pod zdarzenia ViewModel’u w zdarzeniu Loaded widoku.

czwartek, 9 września 2010

Dziennik Zdrowej Diety – Blendability MVVM

Bawiąc się Silverlightem bardzo polubiłem narzędzie Expression Blend. Pomimo braków w talencie graficznym dzięki temu programowi potrafię coś zrobić z UI co wywołuje we mnie emocje typu WOW! Choć daleko mi do perfekcji to nie ukrywam, że Blend bardzo usprawnia pracę podczas tworzenia aplikacji Silverlight.

Ponieważ wzorzec MVVM w aplikacjach Silverlight jest na topie, warto byłoby zapewnić mu współpracę z Blend’em. Oczywiście miejscem najbardziej do tego nadającym się jest ViewModel. To właśnie z jego właściwościami chcemy zbindować kontrolki z naszego widoku, wykorzystując do tego wspomniany powyżej program.

Istnieje kilka strategii łączenia ViewModel z View, których efektem końcowym jest przypisanie ViewModel do właściwości DataContext naszego widoku.
Możemy np.:
- wstrzykiwać ViewModel do View np. w konstruktorze przy wykorzystaniu narzędzia DI,
- wstrzykiwać View do ViewModel,
- stworzyć instancję ViewModel w statycznych zasobach widoku,
- stworzyć instancję ViewModel bezpośrednio w DataContext naszego widoku.

Ważnym ograniczeniem jakie narzuca nam budowa i mechanizm działania XAML oraz sam Blend jest fakt, że instancja ViewModel umieszczona w postaci StaticResource bądź UserControl.DataContext musi posiadać konstruktor bezparametrowy co może bardzo utrudnić życie korzystając z wszelkiego rodzaju kontenerów DI (przygoda z tą funkcjonalnością w połączeniu z Prism zajęła mi ostatnio trochę czasu).

W sieci można znaleźć przykłady pokazując jak stworzyć tzw. ViewModelLocator pozwalający na zbudowanie fasady wokół naszego docelowego ViewModel i udostępnieniu go w postaci właściwości.
Niestety jeśli nasz ViewModel posiada konstruktor parametrowy, który jest wymagany do uruchomienia byspełnić swoje zadanie, Blend może odmówić posłuszeństwa (funkcjonalnością, która by mnie uradowała było by wsparcie dla kontenerów DI wtrybie projektowania, ale to chyba tylko moje mżonki).

Poniżej zamieszczam moją implementację rozwiązania.
public class MainViewModel : ViewModelBase
{
    private IEventAggregator _eventAggregator;
    private IUnityContainer _container;

    //Wcześniej korzystałem z kontenera DI i Unity Constructor Injection:
    //public MainViewModel(IUnityContainer ua, IEventAggregator ea)
    //{
    //    LoadData();
    //}
    //co się nie sprawdziło przy korzystaniu z Blend'a

    public MainViewModel()
    {
        if(!DesignerProperties.IsInDesignTool)
        {
            _container = ServiceUnityContainer.Container;
            _eventAggregator = _container.Resolve<IEventAggregator>();
            LoadData();
        }
    }

    public void LoadData()
    {
        WorkingItem = new Customer
        {
            Age = 28,
            FirstName = "Lukas"
        };
    }
        
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set { _firstName = value;
            RaisePropertyChanged("FirstName");}
    }

    private int _age;
    public int Age
    {
        get{ return _age; }
        set { _age = value;
            RaisePropertyChanged("Age"); }
    }
    public Customer WorkingItem { get; private set; }

    private ICommand _clickCommand;
    public ICommand ClickCommand
    {
        get
        {
            if(_clickCommand == null)
               _clickCommand = new RelayCommand<object>(OnClickCommand);
            return _clickCommand;
        }
    }

    private void OnClickCommand(object obj)
    {
        if(_age != 0)
            Age = 28;
        else
        {
            Age = _age + 2;
        }
    }
}


Właściwość InDesignMode przechowuje informacje na temat obecnego trybu (swoją drogą nigdy za bardzo nie rozumiałem jak VisualStudio czy też Blend potrafi sobie skompilować (w trybie projektowania aplikacji) klasę i zainicjować ją wartościami).
Ponieważ wykorzystuję Unity Container, którego instancja dla całej aplikacji jest ustawiana w bootstraperze mojego programu, stworzyłem klasę ServiceUnityContainer, której głównym zadaniem jest przechowywanie referencji do wspomnianego kontenera.
Wykorzystując powyższą implementację otrzymujemy wsparcie MVVM dla Expression Blend, jak również nie tracimy nic z funkcjonalności naszego ViewModel podczas działania aplikacji.
Poniżej efekt w Blend.


SketchFlow - nowy projekt


Spostrzeżenie: W sieci można znaleźć bindowanie ViewModel w taki oto sposób:

<UserControl.Resources>
    <ViewModels:MyViewModel x:Key="vM"  />
</UserControl.Resources>

<UserControl.DataContext>
    <Binding Source="{StaticResource vM}"/>
</UserControl.DataContext>
Dla Silverlight w wersji 4, powyższe rozwiązanie powodowało u mnie wystąpienie wyjątku JavaScript.

niedziela, 5 września 2010

Dziennik Zdrowej Diety – Wzorzec MVVM – Commanding

W poprzedni wpisie przedstawiłem podstawową koncepcję wzorca projektowego MVVM oraz pokazałem jak złączyć widok (View), będący graficzną reprezentacją naszej logiki biznesowej wraz modelem (Model) przy wykorzystaniu klasy pośredniczącej (ViewModel).
Brakującym elementem w przedstawionym przykładzie była implementacja zdarzeń jakie oferują nam kontrolki.
Z pomocą przychodzi nam wzorzec projektowy Command, który doczekał się swojej implenetacji w Silverlight 4.
Ogólna koncepcja działania polega na tym by udostępnić możliwość obsługi zdarzeń w ViewModel’u. W obecnej chwili zaimplementowane są tylko zdarzenia Click dla kontrolek będących implementacją/rozszerzeniem klasy ButtonBase oraz kontrolki HyperLink. Ograniczenia te jednak można przekroczyć poprzez własną implementację tego wzorca.
By wykorzystać tę nową funkcjonalność należy stworzyć klasę dziedziczącą po interfejsie ICommand (namespace System.Windows.Input)
namespace System.Windows.Input
{
    public interface ICommand
    {
        bool CanExecute(object parameter);
        void Execute(object parameter);
        event EventHandler CanExecuteChanged;
    }
}

CanExecute – metoda określająca status czy podpięta komenda może być wykonana,
Execute – komenda wywoływana w wyniku wywołania zdarzenia (np. wspomniany Click),
CanExecuteChanged – zdarzenie wywoływane w momencie zmiany statusu wywołania komency. Mechanizm bindujący kod XAML podpina się do tego zdarzenia tak więc możecie zauważyć dziwną zależność w postaci : nie podpinacie nic do zdarzenia, a jednak metody Execute oraz CanExecute oddziaływują na siebie.
Poniżej przedstawiam moją własną implementację wykorzystania wzorca Command.
public class MyCommand : ICommand
{
    private readonly Predicate<object> canExecute;
    private readonly Action<object> executeAction;
    
    public MyCommand(Action<object> executeAction, Predicate<object> canExecute)
    {
         this.canExecute = canExecute;
         this.executeAction = executeAction;
    }
   
    public bool CanExecute(object parameter)
    {
         if (canExecute != null)
             return canExecute(parameter);
    
         return true;    
    }
    
    public void Execute(object parameter)
    {
         if (executeAction != null)
             executeAction(parameter);
    }
    
    public event EventHandler CanExecuteChanged;
}

Klasa implemencująca interfejs ICommand korzysta z :
- delegatu Action<object> pozwalającego mi na oddelegowanie wykonania komendy do własnej implementacji metody Execute znajdującej się w ViewModel,
- delegatu Predicate<object> pozwalającego na oddelegowanie sprawdzenia testu możliwości wykonania komendy (CanExecute) do własnej implementacji metody znajdującej się w ViewModel.
Poniżej znajduje się część klasy ViewModel wykorzystującej powyższą komendę MyCommand
public class MyViewModel : INotifyPropertyChanged
{
    private readonly MyModel myModel;
    
    public MyViewModel()
    {
        myModel = new MyModel();
        ButtonCommandClick = new MyCommand(ButtonClickExecute, CanExecuteButtonClick);
    }
    
    private bool CanExecuteButtonClick(object obj)
    {
        if (!string.IsNullOrEmpty(Nickname) && Nickname == "Lukas")
            return false;
    
        return true;
    }
    
    private void ButtonClickExecute(object obj)
    {
        Description = String.Format("Witaj {0} ! To jest implementacja interfejsu ICommand", Nickname);
    }
    
    public string Nickname
    {
        get { return myModel.NickName ?? string.Empty; }
        set
        {
           if (myModel.ValidateNickName(value))
           {
               myModel.NickName = value;
               FirePropertyChanged("Nickname");
           }
        }
   }
    
   public ICommand ButtonCommandClick { get; set; }

By dopełnić dzieła wystarczy tylko zbindować właściwość Command klasy Button z właściwością ButtonCommandClick znajdującą się w naszy ViewModel’u.

<Button Content="Say Hello!" 
           Command="{Binding 
                     Source={StaticResource myViewModel},
                     Path=ButtonCommandClick}" />

Powyższy przykład jest najprostszą postacią wykorzystania możliwości jakie daje nam wzorzezc Command. Pozwala on jednak na postawienie pierwszych kroków przy zaznajamianiu się z możliwościami MVVM.
Przykładową aplikację możecie pobrać TUTAJ

wtorek, 17 sierpnia 2010

Dziennik Zdrowej Diety – Wzorzec MVVM – Koncepcja

Aplikacja Dziennik Zdrowej Diety jest udostępniona na codeplex, przez co nad rozwojem projektu może pracować wiele osób. Wśród tych osób znajdą się zapewne programiści oraz osoby odpowiedzialne za projektowanie interfejsu użytkownika. By usprawnić pracę pomiędzy te dwie profesje, zastosowałem wzorzec projektowy MVVM (Model-View-ViewModel).
Czym jest wzorzec MVVM ? To wzorzec, stworzony na potrzeby technologii WPF oraz Silverlight, którego zadaniem jest odspearowanie od siebie kolejnych warstwy aplikacji tj.



View – warstwa prezentacji, czyli plik xaml przedstawiający widok formatki z umieszczonymi na niej kontrolkami. Widokiem może być również pojedyńcza kontrolka użytkownika (UserControl).
Rzeczą charakterystyczną takiej formatki w porównaniu do zwykłaej aplikacji nie wykorzystującej wzorca MVVM (lub pokrewnych) jest fakt, że w code behind formatki nie znajduje się żaden kod związany z logiką biznesową. Dopuszczalne jest posiadanie w code behind kodu operującego na interfejsie użytkownika (nie mającego nic wspólnego z logiką biznesową).
Model – model opisuje naszą logikę biznesową. Składa się on obiektów biznesowych przybierających najczęściej postać obiektów POCO ang. Plain Old CLR Object, czyli klas zawierających dane (w postaci property) oraz logikę biznesową (walidatory danych). Model jak i View nie wiedzą o swoim istnieniu.
ViewModel – to obiekt będący łącznikiem pomiędzy modelem (Model), a widokiem (View). To właśnie ten obiekt odpowiada za pobranie właściwych informacji ze źródła danych, udostępnienia ich w odpowiedniej postaci do widoku (Converter) oraz przekazania zaktualizowanych informacji spowrotem do źródła danych. Interakcja widoku z ViewModel odbywa się za pomocą funkcjonalności Bindowania oraz wsparcia wzorca projektowego Command.


Przykład:

Na potrzeby przykładu stworzyłem prosty model zawierający 2 właściwości i jedną walidację.
namespace SimpleMVVM
{
    public class MyModel
    {
        public string NickName { get; set; }
        public string Description { get; set; }
        
        public bool ValidateNickName(string nickname)
        {
            return string.IsNullOrWhiteSpace(nickname);
        }
    }
}

Kolejnym elementem odgrywającym ważną rolę jest ViewModel, który pobiera dane z naszego modelu i udostępnia je na zewnątrz

public class MyViewModel : INotifyPropertyChanged  
    {
        private readonly MyModel myModel;

        public MyViewModel()
        {
            myModel = new MyModel();
        }

        public string Nickname
        {
            get { return myModel.NickName ?? string.Empty; }
            set 
            {
                if (myModel.ValidateNickName(value))
                {
                    myModel.NickName = value;
                    FirePropertyChanged("Nickname");
                }
            }
        }

        public string Description
        {
            get { return myModel.Description; }
            set
            {
                myModel.Description = value;
                myModel.NickName += " Hello!";
                FirePropertyChanged("Nickname");
                FirePropertyChanged("Description");
            }
        }

        protected void FirePropertyChanged(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

W View deklarujemy nasz ViewModel w następujący sposób
<UserControl.Resources>
        <vm:MyViewModel x:Key="myViewModel" />
        <vm:MyConverter x:Key="myConverter" />
    </UserControl.Resources>

Dodatkowo zadeklarowaliśmy konwerter MyConverter, który ma postać

public class MyConverter : IValueConverter
    {
        public MyConverter()
        {
            
        }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var msg = value as string;
            if (!string.IsNullOrWhiteSpace(msg) && msg.Contains("Hello!"))
                msg = msg.Replace("Hello!", "Hello World!");
            return msg ?? String.Empty;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value;
        }
    }

Na koniec bindujemy kontrolki z View z naszym ViewModel
<TextBox Text="{Binding 
                            Source={StaticResource myViewModel}, 
                            Path=Nickname, 
                            Mode=OneWay}"
                 Grid.Column="1"
                 Grid.Row="0"
                 VerticalAlignment="Center"" />

<TextBox Text="{Binding 
                            Source={StaticResource myViewModel}, 
                            Path=Description, 
                            Mode=TwoWay, 
                            Converter={StaticResource myConverter}}"
                 Grid.Column="1"
                 Grid.Row="1"
                 VerticalAlignment="Center" />


Działanie:
Pierwszy TextBox jest zbindowany z właściwością Nickname w MyViewModel. Jest to bindowanie typu OneWay co oznacza, że widok pobiera wartości z ViewModel, a ViewModel nic nie wie o ewentualnych zmianach wartości tego TextBox’a.

Drugi TextBox jest zbindowany z właściwością Description w MyViewModel. Jest to bindowanie typu TwoWay co oznacza, że zarówno widok, jak i ViewModel są informowane o zmianie wartości zbindowanej właściwości. Dodatkowo wartość wysyłana do TextBox’a podlega konwersji w klasie MyConverter.

Efekt:
• wpisujemy w pierwszego TextBox’a tekst „Ala ma kota” – nic się nie dzieje,
• wpisujemy w drugiego TextBox’a tekst „Hello!”
• ustawia się właściwość Description instancji klasy myModel na „Hello!”
• ustawia się właściwość NickName instancji klasy myModel na „ Hello!” (zauważ, że pomimo wpisania w pierwszego TextBox’a wartości „Ala ma kota”, nie ma jej po stronie ViewModel)
• odpalane jest powiadomienie o zmianie właściwości „Description”
• pobierana jest wartość właściwości „Description” z uwzględnieniem konwertera,
• pobierana jest wartość właściwości „Nickname”

Kod źródłowy

Ktoś pewno zada pytanie „Poco to wszystko ?”. Otóż korzystając ze wzorca MVVM poza separacją widoku (projektant) od całej reszty (programista) mamy szereg dodatkowych plusów tj. możliwość stosowania testów jednostkowych, uproszczone zarządzanie kodem, schemat działania pozwalający na systematyczny rozwój aplikacji oraz łatwe wdrożenie nowych programistów do projektu.

Powyższy opis ma na celu ogólne przedstawienie koncepcji tworzenia oprogramowania przy wykorzystaniu wzorca MVVM. Ma to posłużyć większemu zrozumieniu kolejnych kroków związanych z rozwojem aplikacji „Dziennik Zdrowej Diety”.
Po więcej szczegółów odsyłam do artykułu zamieszczonego w MSDN Magazine (http://msdn.microsoft.com/en-us/magazine/dd419663.aspx).

Wartym wspomnienia jest fakt, że wzorzec MVVM zyskał na popularności dzięki czemu wielu programistów zaczęło tworzyć własne rozszerzenia tego wzorca.
I tak np. Calcium wspiera funkcjonalność Undo/Redo, ale jest opracowany tylko dla WPF podczas gdy Cinch nadaje się zarówno do WPF jak i Silverlight i dodatkowo współpracuje z MEF (Managed Extensibility Framework).
By przyjrzeć się zestawieniu frameworków MVVM odysłam pod poniższy adres:
(http://www.japf.fr/silverlight/mvvm/index.html)

czwartek, 12 sierpnia 2010

LinkedIn Silverlight PL Users Group

Założyłem na LinkedIn nową grupę Polskich użytkowników Silverlight.

Chciałbym by w tej grupie można było znaleźć takie rzeczy jak:
- wasze pytania odnośnie napotkanych problemów,
- sugestie związane z ciekawym odkryciem wykorzystania "czegoś",
- informacje na temat spotkań grup Silverlight'owych,
- informacje na temat zbliżających się konferencji, prezentacji (również internetowych),
- linki do ciekawych źródeł dotyczących Silverlight jak np. kolejny odcinek Silverlight TV,
- inne równie interesujące tematy.

Dlaczego akurat na LinkedIn ? Ponieważ serwis ma zasięg globalny dzięki czemu być może któremuś z was uda się znaleźć jakiś ciekawy projekt poza granicami naszego kraju.

Gorąco zapraszam.

środa, 11 sierpnia 2010

Dodatki do Visual Studio - objawienie

Z Visual Studio korzystam już kilka lat, ale dopiero wczoraj w mojej przeglądarce pojawił się link do Visual Studio Gallery. Szkoda, że tak późno.
Poniżej przedstawiam kilka dodatków, które posiadają funkcje jakich do tej pory brakowało mi w Visual Studio.

Search References - dodaje textbox'a pomocnego przy filtrowaniu assembly w okienku do dodawania referencji.


VSGesture.v2 for VS2010
- gesty myszy (cofnij się o dokument, przejdź do następnego dokumenu, zbuduj projekt, uruchom projekt).

Go To Definition - przechodzi do definicji klasy, właściwości, etc. za pomocą skrótu CTRL + MouseClick (tego brakowało mi najbardziej- w Borland Developer Studio można było zbindować Mouse3 do tego typu rzeczy i to było nieocenione).

Open Folder in Windows Explorer - rozszerza menu kontekstowe o możliwość przejścia do folderu, w którym znajduje się dany plik, projekt.

Numbered Bookmarks - numerowane bookmarki z bardzo fajnym sposobem bindowania, odszukiwania (fajny bo taki ale StarCraft-owy ;) ).

Highlight all occurrences of selected word - po zaznaczeniu szukanej frazy w tekście, podświetla wszystkie jej wystąpienia (podobnie jak w Notepad++)

Snipper - dosyć zgrabny tool do zapisywania własnych snippet-ów.

CodeCompare - dosyć rozbudowany comparer kodu z możliwością integracji z TFS, SVN.

Productivity Power Tools - zestaw narzędzi udostępniających wiele ciekawych funkcji tj. Solution Navigator, Ctrl + Click Go to Definition, kopiowanie HTML i wiele innych.

Resource Refactoring Tools 2010 - dzięki temu narzędziu, łatwiej Ci będzie przenieść zahardkodowane stringi do pliku .resx

wtorek, 3 sierpnia 2010

Dziennik Zdrowej Diety – Zarządzanie zasobami

Budując aplikację wielo- modułową (np. Przy wykorzystaniu Prism, MEF) musimy zadbać o zgrabne zarządzanie plikiem przechowującym nasze style, szblony.
Poniżej przedstawiam krótki przykład jak sobie z tym poradzić w Silverlight.

1) Załóżmy, że posiadamy solucję składającą się z kilku projektów.



Każdy z widoków wykorzystuje zestaw kontrolek, na których chcielibyśmy skorzystać z tego samego stylu.

2) Tworzymy nowy projekt Silverlight Class Library, a następnie dodajemy nowy element Silverlight Resource Dictionary (nasz będzie się nazywał DictStyles.xaml)













3) Wszystkie style zapisujemy w tym pliku, a jeśli chcemy z nich skorzystać to do każdego widoku dodajemy sekcję:



gdzie ResourcesStore to nazwa assembly w której znajduje się nasz plik ze stylami, component to słówko stałe, a DictStyles.xaml to nazwa naszego słownika
(po szczegóły na temat odwoływania się do zasobów odsyłam pod ten link: Channel9- Mike Taulty )

4) Teraz tylko wystarczy dodać do każdego projektu referencję do naszej dll’ki i cieszyć się z efektu.

(UWAGA: bardzo ważne by dodać referencję do dll’ki ze stylami w głównym projekci – nawet jeśli nie posiadacie tam nic poza Shell.xaml, App.xaml. Ja zmarnowałem kilka godzin zanim rozgryzłem dlaczego moje moduły w runtime nie widzą styli :/ ).

Edycja 15-08-2010.
By nie rozbijać wątku na dwa tematy dopiszę coś więcej na temat zarządzania zasobami.

Istnieje kilka sposobów dostępu do zasobów:

"myImage.jpg" - aplikacja poszukuje obrazek w assembly umieszczonym w pliku XAP. Jeśli go nie znajdzie to przeszukuje katalog ClientBin. Plik musi mieć ustawioną właściwość:
Build Action = Resource

"/myImage.jpg" - aplikacja poszukuje obrazka w pliku XAP. Jeśli go nie znajdzie to przeszukuje katalog ClientBin. Plik musi mieć ustawioną właściwość:
Build Action = Content

"/MyAssembly;component/Images/myImage.jpg" - aplikacja poszukuje obrazka w katalogu Images znajdującym się w assembly MyAssembly osadzonym w pliku XAP.

"/MyAssembly;component/Fonts/MyFont.ttf#My Font" - aplikacja poszukuje czcionkę w katalogu Fonts znajdującym się w assembly MyAssembly osadzonym w pliku XAP.

"/MyAssembly;component/Fonts/Fonty.zip#My Font" - analogicznie jak powyżej z tym, że przeszukiwane jest archiwum ZIP pod kątem wystąpienia czcionki o nazwie "My Font" (nie mylić z nazwą pliku czcionki).