Szukaj na tym blogu

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