WPF – wprowadzenie do MVVM

Od pewnego czasu wszystkie nowe aplikację okienkowe tworzę w WPFie. Próba napisania czegoś w Winformsach sprawia mi ból. A przecież gdy zaczynałem to Winformsy były moją pierwotną technologią (bo php litościwie pomine). To właśnie WinFormsy wraz z prostotą C# (o naiwności!) były przyczyną mojego zainteresowania platformą .net. Pamiętam jak przez mgłę mój pierwszy projekt w WinFormsach. Jakieś matematyczne obliczenia (średnia, mediana itp.), ale istotny był graficzny interfejs (jego brak powodował automatyczną ocenę w dół). Ponieważ program robił to co do niego należało projekt został zaliczony. A ponieważ prowadzący nie miał pojęcia o C# to nie mógł mieć większych zastrzeżeń co do kodu (wraz z awarią dysku projekt przepadł więc nikt nie będzie mnie nim szantażował). Więc co takiego się stało, że odwróciłem się od swojej macierzystej technologii? Trzy główne powody: deklaratywny sposób tworzenia interfejsu, lepszy DataBinding oraz MVVM.

Deklaratywny sposób tworzenia interfejsu

Każdy kto pisał choćby najprostszą aplikację w Formsach musi to pamiętać. Z Toolboxa przeciągamy interesujące nas elementy np TextBox, Button a następnie podpinamy odpowiednie zdarzenia (zdarzenie domyślne przez dwuklik albo wybieramy zdarzenie z listy). I mimo tego co napisałem wcześniej (o moim bólu) czasami nadal tak robię. Np. prosta aplikacja konwertująca pliki z jednego formatu w drugi i przeznaczona wyłącznie do użytku wewnętrznego. Szkoda czasu na bardziej wyrafinowane zabawy (chyba, że jako zadanie dla stażysty). A wracając do sedna w WPFie też tak można tylko, że ja nie pamiętam kiedy ostatnio tak robiłem (a z moich obserwacji wynika, że reszta zespołu również nie używa toolboxa). Wszystko piszemy z ręki i mimo naszych początkowych obaw jest to niesamowicie wygodne i szybkie. Wpisując kontrolkę w odpowiedni panel zapewniamy sobie jej odpowiednie ułożenie (później trzeba to oczywiście zmodyfikować tak aby spełniało wymagania). Kolejnym plusem jest to, że teoretycznie wyglądem aplikacji może zajmować się grafik, ale często to sam programista pełni rolę grafika (ja mam zakaz samodzielnego wybierania kolorów – pewnie ktoś kiedyś zobaczył kombinację kolorów jaką zaprezentowałem we wczesnej wersji aplikacji wraz z tłem głównego okna, które zawsze jest wynikiem moich muzycznych fascynacji – żółty+czerwony i tło z “Wolfheart” Moonspella).

DataBinding

Ciekawszą sprawą jest pewnie binding danych w WPFie (jeśli ktoś szykuje się na rozmowę kwalifikacyjną to może się to mu przydać). Wyróżniamy następujące rodzaje bindingu:

  • One Way
  • Two Way
  • One Way to Source
  • One Time

żeby nie być gołosłowny przykład został zamieszczony na githubie.

One Way

Jak nazwa wskazuje binding występuje tylko w jedną stronę (konkretnie od źródła do celu)

<TextBox x:Name="OneWaySource" Grid.Column="1" Grid.Row="1"/>
<TextBox x:Name="OneWayTarget" Grid.Column="2" Grid.Row="1" Text="{Binding ElementName=OneWaySource, Path=Text, Mode=OneWay}"/>

Jakakolwiek zmiana tekstu w obrębie Textboxa OnewWaySource powoduje automatycznie taką samą zmianę w OneWayTarget

Two Way

Tuataj z premedytacją rozbiłem na dwa przypadki. W obu przypadkach efekt będzie identyczny. Binding odbywa się w dwie strony.

<TextBox x:Name="TwoWaySource" Grid.Column="1" Grid.Row="2" Grid.RowSpan="2" />
<TextBox x:Name="TwoWayTarget" Grid.Column="2" Grid.Row="2" Text="{Binding ElementName=TwoWaySource, Path=Text, Mode=TwoWay}"/>
<TextBox x:Name="TwoWayTargetPropertyChanged" Grid.Column="2" Grid.Row="3" Text="{Binding ElementName=TwoWaySource, Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

Każda zmiana w TwoWaySource jest automatycznie odwzorowana w TwoWayTarget i TwoWayTargetPropertyChanged. W drugą stronę działa to “prawie” tak samo. A jak wiemy z reklamy prawie robi różnicę. Czyli zmiana w TwoWayTarget jest odwzorowana w TwoWaySource, ale dopiero na LostFocus. Dopiero ustawienie property UpdateSourceTrigger na PropertyChanged (czyli tak jak w TwoWayTargetPropertyChanged) powoduje update na “żywo”. Ten pierwszy sposób jest domyślny (z tego co się orientuje to kwestia wydajności).

One Way to Source

Tak jak w przypadku One Way binding występuje tylko w jedną stronę (tylko, że tym razem od celu do źródła)

<TextBox x:Name="OneWayToSourceSource" Grid.Column="1" Grid.Row="4"/>
<TextBox x:Name="OneWayToSourceTarget" Grid.Column="2" Grid.Row="4" Text="{Binding ElementName=OneWayToSourceSource, Path=Text, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"/>

Jakakolwiek zmiana tekstu w obrębie Textboxa OneWayToSourceTarget powoduje automatycznie taką samą zmianę w OneWayToSourceSource

One Time

Binding występuje tylko raz na samym początku

<TextBox  x:Name="OneTimeSource"  Grid.Column="1" Grid.Row="5" Text = "At app start" />
<TextBox  x:Name="OneTimeTarget"  Grid.Column="2" Grid.Row="5" Text="{Binding ElementName=OneTimeSource, Path=Text, Mode=OneTime}"/>

Dane są pobierane raz i to koniec.

Przykłady, które zamieściłem powyżej to przykłady gdzie bindujemy z TextBoxa to TextBoxa a to przecież nie koniec możliwości Bindingu. Możemy bindować z właściwości jednej konrtolki do właściwości kontrolki zupełnie innego typu np.:
(kod na githubie)

<Button x:Name="Test" Content="Test Button" IsEnabled="{Binding ElementName=CheckBoxTest, Path=IsChecked}"/>
<CheckBox x:Name="CheckBoxTest" Content="Are you sure?" />

Przyzwyczajonych do WinForms od razu informuje, że niestety ale firma Microsoft nie pomogła nam i teraz np. zamiast Checked mamy IsChecked, zamiast Enabled mamy IsEnabled a zamiast Visible mamy trójstanową Visibility.
Zastanówmy się przez chwilę co się stanie gdy ktoś dla kogo Formsy to chleb powszechni pomyli się i zamiast

Path=IsChecked

napisałby

Path=Checked

Otóż nic złego się nie stanie(choć to kwestia dyskusyjna). Aplikacja się uruchomi a program nie rzuci wyjątkiem (tylko, że bindowanie nie będzie działać no ale to chyba oczywiste).
Bindować możemy znacznie więcej (np do klasy). Istnieje znacznie więcej ciekawych mechanizmów konwertery i multivalue-konwertery, ale to temat na całkiem inną serię postów.

W następnej części omówię o co chodzi z tym całym MVVM (i dlaczego tak bardzo mi się on podoba).

6 thoughts on “WPF – wprowadzenie do MVVM

  1. Pingback: dotnetomaniak.pl
  2. Warto dodać że błędy bindingu są widoczne w runtime w konsoli (albo output albo debug). A co do kolorów to polecam aplikację Adobe kuler zestaw kolorów który na nim znajdziesz warto przepuścić przez kreator ze strony projektu bootstrap. Jak każdy rasowy programista nie znam się na kolorach ale zestawy tak uzyskane są przyjemne do użytkowania.

    1. Dzięki za przypomnienie o błędach bindingu, zapomniałem o tym napisać. Co do kolorów to przetestuje Adobe Kuler w którymś ze swoich projektów (choć przyznaje wygląda sympatycznie). W firmie na szczęście są osoby o wyrobionym zmyśle estetycznym więc problem dobrania kolorów odpada.

    1. Zgadza się więc do wczesnych wersji jak najbardziej mogę użyć Adobe Kuler

  3. Dobry wpis, przyjemnie się czyta :). Chciałbym abyś w którymś z przyszłych rozwinął cześć dotyczącą tworzenia własnych kontrolek/modyfikacji istniejących.

    ” Wszystko piszemy z ręki i mimo naszych początkowych obaw jest to niesamowicie wygodne i szybkie.” – dla mnie to cały czas magia np. przerobienie standardowego buttona na okrągły…

Comments are closed.