Szukaj na tym blogu

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