Professional Documents
Culture Documents
Doskonały podręcznik
do nauki praktycznego
programowania
w C#, XAML i .NET
Zarządzaj swoimi
obiektami dzięki
wykorzystaniu wszystkie
abstrakcji tajniki wzorca
i dziedziczenia Model-Widok
-Widok-Modelu
Napisz w pełni
funkcjonalną
staromodną
7
Autorzy
Jenny i Andrew tw orzą wspo'/nie oprogramowanie i p isz ą o nżynierii oprogramowania od czasu poznania
W ro ^ : lCh7 ierWSZ»'' *3 ^
. Applied
A ' h ' Softw
co ftw —
are Project
n' M anagem ent, zo sta ła wydana przez 0 ' Rei//y
w 2005 roku. Inną książką napisaną przez S te //mana i Gree n d/a wy dawnictwa 0 ’Rei//y była Be au t,fu / 7eams
(wydana w 2009 roku), a p ierw szą z serii Head First. Head R r s t pMp, °p u b/ik° wa/i w roku 2007:
W 2 003 roku założy/i Ste//m an & Greene Consu/ting w ce/u t ^ r r o r n a nap rawdę starannych projektów
do badania wpływu herbicydów na weteranów z W ietnamu. Gdy m e p iszą p rogramów ani książek. to można
ich spotkać na meetingach i konferencjach inżynierów oprogramowania, architektów i kierowników projektów.
S prawdź ich b/og Building B etter Softw are: h ttp ://w w w s te llm a n -greene-c° m ,
a/bo ś/edź na Twitterze: @AndrewSte//man i' (aO en n y O ^ n .
8
Spis treści
W Przygotuj się na C#. Właśnie sobie siedzisz i próbujesz się czegoś nauczyć, ale mózg wciąż
9
Spis treści
do ręki potężny język program ow ania i wartościowe narzędzie. Dzięki Visual Studio IDE
do historii przejdą sytuacje, w których musiałeś pisać jakiś nędzny kod, by ponownie zapewnić
prawidłowe działanie przycisku. I to nie wszystko. Dodatkowo będziesz mógł skupić się na
faktycznym w y ko n yw a n iu napraw dę fajn ych program ów , zamiast starać się zapamiętać,
który parametr metody odpowiadał za nazwę przycisku, a który za wyświetlany na nim tekst.
Brzmi zachęcająco? Przewróć zatem stronę i przystąpmy do programowania.
Dlaczego powinieneś uczyć się C # 44
Ich unikaj.
C # oraz Visual Studio ułatwiają wiele czynności 45
Co robić w Visual Studio... 46
Co Visual Studio robi w naszym im ie n iu . 46
Obcy atakują! 50
Tylko Ty możesz uratować Ziemię 51
Oto co masz zamiar napisać 52
Zacznij od pustej aplikacji 54
Określ wymiary siatki na stronie 60
Dodaj kontrolki do siatki 62
Używaj właściwości, by zmieniać wygląd kontrolek 64
To kontrolki sprawiają, że gra działa 66
Stworzyłeś scenę, na której będzie prowadzona gra 71
Och! Kosmici Czym zajmiesz się teraz? 72
I wciągają, ludzi'.
Niedobrze! Dodaj metody, które coś zrobią
Podaj kod metody
73
74
Dokończ metodę i uruchom program 76
Oto co zrobiłeś do tej pory 78
Dodaj liczniki czasu zarządzające rozgrywką 80
Popraw działanie przycisku Start 82
Uruchom program, by zobaczyć postępy w pracy 83
Dodaj kod obsługujący interakcję użytkownika z kontrolkami 84
Dotknięcie człowiekiem wroga kończy grę 86
% Teraz już można bawić się grą 87
Zadbaj, by wrogowie wyglądali jak obcy 88
Dodaj ekran startowy i tytuł 89
na innym komputerze 91
Rozpocznij zdalne debugowanie 92
/ O
10
H
Spis treści
To tylko kod
Pod maską
Jesteś programistą, nie jedynie użytkownikiem IDE. IDE może wykonać za Ciebie
wiele pracy, ale na razie jest to wszystko, co może dla Ciebie zrobić. Oczywiście istnieje wiele
po w tarza lnych czynności podczas pisania aplikacji i IDE okazuje się tu bardzo pomocne.
Praca z nim to jednak dopiero poczgtek. Możesz wycisnąć ze swoich programów znacznie
więcej — pisanie kodu C# to właśnie droga, która doprowadzi Cię do tego celu. Kiedy
osiągniesz mistrzowski poziom w kodowaniu, nie będzie żadnej rzeczy, której Twój program
nie umiałby zrobić.
12
Spis treści
Typy i referencje
lucky = nuli; W /
^ p y k !-
Do9
/ V \
13
Spis treści
Laboratorium C# numer 1
Dzień na wyicięach
Janek, Bartek i Arek uwielbiają chodzić na tor wyścigowy, ale ciągła utrata pieniędzy powoduje u nich
frustrację. Potrzebują symulatora, aby mogli określić zwycięzcę, zanim wyłożą pieniądze na zakłady.
Jeśli dobrze wywiążesz się z zadania, będziesz miał procenty z ich wygranych.
Hermetyzacja
,Tak7j
Ilo ś ć gości
Jed zen ie
b
(2 5 z ł od osoby)
O p cja zd ro w a?
_ N i£ ^
15
Spis treści
Dziedziczenie
16
Spis treści
17
Spis treści
8 Z deszczu pod rynnę. W rzeczywistym świecie nie musisz się zwykle zajmować danymi
w małych ilościach i w niewielkich fragmentach. Nie, Twoje dane przychodzą do Ciebie
w grupach, stosach, pękach, kopach. Potrzebujesz jakiegoś potężnego narzędzia do ich
zorganizowania. Nadszedł czas, aby przedstawić kolekcje. Pozwalają one przechowywać
i sortow ać dane, a także zarządzać tymi z nich, które Twój program musi przeanalizować.
Dzięki temu możesz myśleć o pisaniu programów do pracy z danymi, a samo ich
przechowywanie zostawić kolekcjom.
18
Spis treści
9 Czasem opłaca się być trwałym. Do tej pory wszystkie programy były krótkotrwałe.
Uruchamiały się, działały przez chwilę i były zamykane. Czasami nie jest to wystarczające,
zwłaszcza jeżeli zajmujesz się ważnymi danymi. Musisz mieć możliwość zapisania swojej
pracy. W tym rozdziale pokażemy sposób zapisyw ania danych do pliku, a następnie
w czytyw a n ia tych in fo rm acji z po w ro te m do programu. Dowiesz się co nieco o klasach
strum ie ni .NET i zetkniesz się z tajemnicami systemów szesnastkowego i dw ójkow ego.
69 1 17 1 1 4 1 0 1 107 97 33
Kiedy obiekt jest serializowany, serializowane są także wszystkie obiekty
z nim powiązane... 475
E u re k a! -L 1 i '/ M ..
Serializacja pozwala Ci zapisywać lub odczytywać całe grafy obiektów naraz 476
.NET automatycznie konwertuje tekst do postaci Unicode 481
C # może użyć tablicy bajtów do przesyłania danych 482
Do zapisywania danych binarnych używaj klasy BinaryWriter 483
Pliki utworzone dzięki serializacji można także zapisywać i odczytywać ręcznie 485
Sprawdź, gdzie pliki się różnią, i użyj tej informacji do ich zmiany 486
Praca z plikami binarnymi może być skomplikowana 487
6 9 1 1 7 7 1 4 707 7079
■— «07 973, fc, Użyj strumieni plików do utworzenia widoku w postaci szesnastkowej 488
19
Spis treści
Laboratorium C# numer 2
Wyprawa
Twoim zadaniem jest stworzenie gry przygodowej, w której potężny wojownik wyrusza na
wyprawę i dzielnie walczy, poziom za poziomem, ze śmiertelnie niebezpiecznymi wrogami.
Stworzysz system turowy. Oznacza to, że najpierw gracz wykonuje jeden ruch, a następnie
ruch wykonuje przeciwnik. Gracz może przesunąć się lub zaatakować; potem możliwość
ruchu i ataku dostaje każdy z wrogów. Gra toczy się do czasu, aż gracz pokona wszystkich
przeciwników na wszystkich siedmiu poziomach lub zginie.
Siatka składa się z kwadratów o wielkości Każda jednostka jest podzielona na Damian używa Windows 8 518
20 pikseli, nazywanych jednostkami. podjednostki o wielkości 5 pikseli.
Technologia Windows Forms korzysta z grafu obiektów
I \ stworzonego przez IDE
Użyj IDE do przejrzenia grafu obiektów
524
527
Aplikacje dla Sklepu Windows używają XAML do tworzenia obiektów
interfejsu użytkownika 528
POWIĄZANIE
ItemsSource="{Binding}"
O , ek t \) ^
k
21
Spis treści
Przepraszam, że przerywam
Nikt nie lubi być zmuszanym do oczekiwania... zwłaszcza użytkownicy.
Komputery są doskonałe w wykonywaniu wielu rzeczy jednocześnie, nie ma zatem żadnego
powodu, aby Twoje aplikacje nie mogły tego robić. W tym rozdziale dowiesz się, jak sprawić,
by dzięki zastosow aniu m etod asynchronicznych Twoje aplikacje reagowały błyskawicznie.
Nauczysz się także korzystać z w b ud ow a nych narzędzi do w yb ie ra n ia p likó w , wyświetlać
okienka z kom unikatam i oraz asynchronicznie zapisywać i odczytyw ać dane z p likó w
bez „zawieszania" aplikacji. Połączysz te wszystkie możliwości z serializacją kontraktu danych
i opanujesz tworzenie bardzo nowoczesnych aplikacji.
22
Spis treści
Obsługa wyjątków
23
Spis treści
K a p ita n W sp a n ia ły
ŚmieTlobiektu
24
Spis treści
25
Spis treści
Zdarzenia i delegaty
O OQ
26
^ p ir e ° 6 /ek* *
Spis treści
27
Spis treści
Laboratorium C# numer 3
Invaders
Dzięki temu laboratorium oddasz hołd jednej z najbardziej popularnych, czczonych
i powielanych ikon w historii gier komputerowych. Nie potrzebuje ona żadnego wprowadzenia.
Czas utworzyć grę Invaders.
* * * * * *
* * * * * *
M A . j £ *■*
i/ sz JL '
\
Spis treści
Projekt dodatkowy!
29
Spis treści
Pozostałości
c :\ U s e r s \ P u b lic\Documents>type HelloMorld.es
us i n g System;
class Hel l o W orld {
p u b l i c s tatic void Main(string[] args) {
C o n s o l e . MriteLine("Witaj, t’wieciel “
}
c :\ U s e r s \ P u bl ic\Documents>csc H e l l oWorld.cs
K o m pilator M i crosoft (R) Visual C # w wersji 4 . 0 . 30319.33440
dla prog r a m u Microsoft (R) .NET Framework 4.5
C opyright (C) Microsoft Corporation. Wszelkie prawa zastrzeżor
c :\ U s e r s \ P u b l i c \ D o c u m e n t s > H e l l o W o r ld .t
Witaj, świeciel
c :\ U s e r s \ P u b lic\Documents>
Skorowidz 905
30
Jak korzystać z tej książki?
Wstęp
- c -
31
Jak korzystać z tej książki?
© Czy jesteś zapalonym p ro g ra m istą C + + lub Java, k tóry szuka książki Jeśli ta k , to w ie d z, że
w stylu encyklopedycznym ? w iele osób podobnych do
Ciebie skorzystało z tej
© Czy b o isz się sp ró b o w ać czegoś now ego? W olałbyś raczej p o d d a ć się książki dokładnie w tych
leczeniu k anałow em u, niż połączyć paski z k ratk ą? Czy w ydaje Ci się, samych celach!
że książka tech n iczn a o C # nie m oże być pow ażna, jeżeli zagadnienia
są opisan e p o ludzku?
Korzystanie z tej książki nie
wymaga żadnych doświadczeń
to nie je st to książka dla Ciebie. programistycznych... a jedynie
ciekawości i zainteresowania!
Tysiące początkujących
programistów, którzy nie
posiadali doświadczenia,
skorzystało ju ż z książki
[Komentarz dziatu m arketingu: C#. R usz głową, by nauczyć się
ta ks iążka j e s t dla każdego pisania kodu. I Ty m o żesz do
z kartą kredytową]. nich dołączyć!
C'
32 Wstęp
Wstęp
jesteś tutaj ► 33
Jak korzystać z tej książki?
T r a k t u j e m y c z y t e l n i k a „ R u s z g ł o w ą !” ja k u c z n i a ^
c T y te ln ic y ^ o z ^ ą ^ z opisywanymi zagadnieniami,
będzie nawet dwukrotnie większe. n<:tatnie
!b s s s s ^ ś sbbss
E S S S $ 5£S£# *» — ^ obicd" podaas
a y t f n S s f b y ć zainspirowany,
rozwiązywania P ^ 16™ ^ 5' ^ n i a prowokacyjne pytania i czynności,
Potrzebne są do tego wyzwarla móza0wych oraz wielu zmysłów.
które wymagają uruchomienia obu półkul mo g następujące uczucie: „naprawdę
P rz y c ią g n ij - i u t n * ™ 1- « « A ™ k l6 [c
chcę się tego nauczyć, ale po jednej s ro wnaciajaCe w oko niespodziewane. Uczenie się
S ó re p S c h o T z ip o rozwiązaniu
: działu inżynierii
.....
oprogramowania tego nie wie.
34
Wstęp
W iększość z nas nie przech o d ziła kursów m etap o z n a n ia i teo rii n auki podczas dorastania.
O czekiw ano od nas u czen ia się, ale nie nauczono nas tego.
Przypuszczam y, że skoro trzym asz tę książkę w ręk u , to n ap raw d ę chciałbyś nauczyć się
tw orzenia program ów w C # . P raw d o p o d o b n ie nie chcesz n a to tracić dużo czasu. Jeżeli
m asz zam iar używać tego, co znajdziesz w tej książce, m usisz p am iętać, co przeczytałeś.
A by stało się to łatw iejsze, pow inieneś wszystko zrozum ieć, a aby odnieść najw iększe
korzyści z lektury tej lub jakiejkolw iek innej książki czy źródła, musisz
przejąć odpow iedzialność za swój m ózg. T en p o d p u n k t dotyczy mózgu.
Szybszy sposób poleg a n a tym , aby zrobić wszystko, co zw iększa aktyw n o ść m ózgu, korzystając p rzed e
wszystkim z różnych jej rodzajów . E lem en ty znajdujące się n a p o p rzed n iej stro n ie stanow ią dużą część
rozw iązania. Skuteczność stosow ania tych wszystkich uro zm aiceń w stym ulacji m ózgu je st udow odniona.
N a przykład badania , k tó re polegały n a u m ieszczaniu słów wewnątrz obrazków (w przeciw ieństw ie
do w pisyw ania ich w innych m iejscach, takich ja k tytuły i zwykły tek st), wykazały, że m ózg pró b u je
znaleźć pow iązania pom iędzy tymi słowam i i o brazkam i, p o b u d zają c jed n o cześn ie do pracy w iększą
liczbę neuronów . W ięcej pobudzonych k o m ó re k nerw owych to w iększa szansa, że m ózg zakw alifikuje
inform ację jako w artą zap am iętan ia i z większym praw d o p o d o b ień stw em ją zachow a.
Styl rozm ow y jest pom ocny, p o niew aż ludzie zw racają w iększą uw agę n a szczegóły podczas konw ersacji.
Są bow iem zm uszeni do podtrzym yw ania w ym iany zdań i jej zakończenia. N iesam ow ite jest to, że m ózg
nie interesuje się faktem , że „konw ersacja” odbyw a się p om iędzy T o b ą i książką! Z drugiej strony,
jeżeli styl książki jest suchy i form alny, Twój m ózg p o strzeg a to w taki sam sposób, jakbyś był n a sali
konferencyjnej i siedział razem z pasywnymi słuchaczam i. N ie d a się nie zasnąć.
jesteś tutaj ► 35
Jak korzystać z tej książki?
Kiedy defin iujesz klasę,
d efin iujesz także j e j met° d y
Oto co zrobiliśmy: podobnie ja k projekt d e f ilu je
układ pom ieszczeń w dom u.
Użyliśmy ilustracji, ponieważ Twój mózg jest zaprogramowany do odbioru obrazów, nie s*
tekstu. Biorąc to pod uwagę, możemy powiedzieć, że jeden obrazek jest wart tyle, co tysiąc
słów. Kiedy ilustracje z nimi współpracują, to dopiero się dzieje! Umieściliśmy tekst wewnątrz
obrazków, ponieważ mózg działa efektywniej, gdy jest wewnątrz czegoś, nad czym pracuje.
Może s2 użyć jednego
Jest to podejście zgoła odmienne od wypisywania tekstu w tytule lub umieszczania go gdzieś projektu do zbudowania
v dowo/nej /ic2by domów.
w innym miejscu. Możesz również przy
uży c iu je d n e j k/asy
ufwcirzyć każdą /iczbę
obiektów.
Użyliśmy redundancji, powtarzając tę samą rzecz na kilka różnych sposobów,
z wykorzystaniem odmiennych form przekazu działających na wiele zmysłów, aby zwiększyć
szansę, że zawartość książki zostanie zakodowana w więcej niż jednym obszarze Twojego mózgu.
Użyliśmy stylu spersonifikowanego, przyjmującego postać rozmowy, gdyż mózg przystosowany jest
do większej aktywności wtedy, gdy wierzy, że z kimś rozmawiasz, niż wtedy, gdy pasywnie słuchasz
prezentacji. Zachowuje się tak samo nawet podczas czytania.
Zamieściliśmy tu dziesiątki zadań, ponieważ mózg jest zaprojektowany do nauki i zapamiętywania większej ilości
danych, jeżeli nad tym pracujesz, a nie, gdy tylko o tym czytasz. Ćwiczenia są w stylu trudne-ale-do-zrobienia, takie
zadania preferuje bowiem większość osób.
CELNE SPOSTRZEŻENIA
Użyliśmy wielu stylów nauczania, ponieważ Ty mógłbyś preferować procedury z rodzaju
krok po kroku, ktoś inny chciałby najpierw zobaczyć ogólny zarys, a jeszcze inny czytelnik —
przykład. Bez względu na preferencje, każdy odniesie pewne korzyści, zapoznając się z tym
samym materiałem prezentowanym w różny sposób. Pogawędki przy kominku
Umieściliśmy w książce treści przeznaczone dla obu półkul mózgowych. Im bardziej wykorzystujemy
potencjał mózgu, tym większa jest jego zdolność do zapamiętywania oraz nauki. Poza tym możesz
się na niej dłużej skupić. W związku z tym, że praca z wykorzystaniem jednej półkuli często wiąże się
z odpoczynkiem drugiej, możesz być bardziej produktywny, ucząc się przez dłuższy czas.
Wykorzystaliśmy historie i ćwiczenia, które prezentują więcej niż jeden punkt widzenia, ponieważ Twój umysł może być
bardziej skupiony i uczy się dokładniej podczas wyrażania ocen i sądów.
Zamieściliśmy tu wyzwania i ćwiczenia, zadawaliśmy pytania, które nie zawsze mają prostą odpowiedź, ponieważ
mózg bardziej się skupia, pracując nad problemem. Pomyśl nad tym — kształt Twojego ciała nie poprawi się
od samego obserwowania ludzi na siłowni. Zrobiliśmy, co w naszej mocy, abyś podczas ciężkiej nauki pracował
nad rzeczami właściwymi. W ten sposób nie będziesz zajmował dendrytów przetwarzaniem przykładu trudnego
do zrozumienia lub tłumaczeniem trudnego, żargonowego lub nadmiernie zwięzłego tekstu.
Skorzystaliśmy z ludzi. W opowiadaniach, przykładach, obrazkach, bo... bo jesteś człowiekiem. Twój umysł
zwraca większą uwagę na ludzi, którzy wykonują różne czynności.
36 Wstęp
Wstęp
jesteś tutaj ► 37
Jak korzystać z tej książki? Zrzuty ekranów zamieszczone w tej książce zostały zrobione
w Visual Studio Express, najnowszej darmowej wersji środowiska
dostępnej w czasie pisania tej książki. Kolejne wydania będą
aktualizowane, jednak firma Microsoft zazwyczaj zapewnia
Czego potrzebujesz do tej książki? możliwość pobierania starszych wersji Visual Studio.
N apisaliśm y tę książkę, korzystając z V isual S tudio E x press 2012 fo r W indow s 8 o raz V isual Studio E x p ress 2012
for W indow s D esktop. W szystkie zrzuty ekranów , jakie zam ieściliśm y w tej książce, zostały zro b io n e przy użyciu tych
dwóch w ersji V isual S tudio, dlatego zalecam y, abyś tak że Ty ich używał. Jeśli używasz innej w ersji V isual S tudio 2010 —
Proffesional, P rem ium , U ltim ate lub T e st P ro fessio n al — zauważysz pew ne d ro b n e różnice (nie spraw ią Ci o n e je d n ak
żadnych problem ów podczas pracy n ad kodem przedstaw ionym w książce).
★ Visual Studio Express 2012 fo r W ind ow s 8 można pobrać bezpłatnie z witryny firmy Microsoft. Środowisko instaluje się
bezproblemowo, także gdy na komputerze są już zainstalowane inne wersje Visual Studio 2012, jak również starsze wersje
Visual Studio: h ttp://w w w .visualstudio.com /dow nloads/dow n load-visual-studio-vs.
Dodatkowo będziesz
także m usiał
wygenerować
klucz produktu,
co w przypadku
korzystania
z wersji Express
nic nie kosztuje
(choć wymaga
utworzenia konta
Microsoft.com).
★ Po zainstalowaniu tej wersji Visual Studio w podobny sposób będziesz musiał zainstalować Visual Studio Express 2012 fo r
W ind ow s Desktop.
Co zrobić, je ś li nie masz Windows 8 lub nie możesz uruchomić Visual Studio 2 0 1 2 ?
W iele ćwiczeń zam ieszczonych w tej książce w ym aga p o siad an ia system u W indow s 8. Oczywiście rozum iem y, że niektórzy
z Czytelników m ogą go nie używać — n a przykład w ielu p rofesjonalnych p rogram istów używ a w pracy k om puterów
z ta k starym system em operacyjnym jak W indow s 2003, bądź m ają zainstalow ane V isual Studio 2010 i nie m ogą go
zaktualizow ać. Jeśli należysz do tej g ru p y Czytelników, to nie m u sisz się przejm ow ać — w ciąż będziesz m ógł w ykonać
niem al wszystkie ćw iczenia zam ieszczone w książce. O to ja k to zrobić:
★ Ćw iczenia zam ieszczone w rozdziałach o d 3. do 9. i w pierw szych dwóch lab o rato riach w ogóle nie w ym agają system u
W indows 8. B ędziesz naw et w stanie w ykonać je, używając V isual Studio 2010 (a naw et 2008), choć w tym przypadku to,
co zobaczysz n a ek ran ie, będzie się nieco różnić o d zrzutów zam ieszczonych w książce.
★ W p rzypadku pozostałych rozdziałów będziesz m u siał tworzyć trad y cy jn e a p lik acje W indow s P re se n ta tio n F o u n d atio n
(W PF), a nie aplikacje dla system u W indow s 8. W ięcej inform acji n a te n te m a t m ożesz tak że znaleźć w p u n k cie 11.
d o d atk u „P ozostałości”.
38 Wstęp
Wstęp
Z a d a n ia i ćw iczenia nie są tylko d o d atk iem , są głów ną częścią tej książki. N iek tó re
z nich p o m agają w zapam iętyw aniu, n iek tó re u łatw iają zrozum ienie, in n e pozw alają
zastosow ać zdobytą w iedzę. N ie p o m ija j ćwiczeń. Z agadkow y b asen to jedyne
ćw iczenia, których nie m usisz robić, ale stanow ią o n e d o sk o n ałą szansę dla Tw ojego
m ózgu, aby pom yślał o słow ach w innym kontekście.
U m ieściliśm y także rozw iązania wszystkich zad ań , abyś m ógł je p o b rać. Z najdziesz je Gdy Widzisz logo Z agadk ow ego
n a ftp://ftp.helion.pl/przyklady/cshru3.zip. basenu", zadanie m e j e s t
obowiązkowe. Jeżeli m e lubjsz
pokrętnej logiki, takie ćw iczenia
Ćwiczenia „W ytęż umysł” nie mają rozwiązań. m ogą Ci s i ę nie spodobać.
D la jednych z nich nie m a właściwych odpow iedzi, dla innych ćwiczeń „W ytęż
um ysł” częścią n auki je st ok reślen ie, k tó re z odpow iedzi są pop raw n e. W niektórych
ćw iczeniach tego typu znajdziesz w skazówki, k tó re nap ro w ad zą Cię n a właściwy trop.
jesteś tutaj ► 39
Jak korzystać z tej książki?
K iedy napisaliśm y tę książkę p o raz pierwszy, zaw ierała o n a m nóstw o pom yłek, problem ów , literów ek, nieścisłości
i okropnych błędów obliczeniow ych. D o b ra , nie było w cale ta k źle. Jesteśm y je d n a k niezm iern ie w dzięczni za pracę,
k tó rą korekto rzy techniczni w ykonali dla tej książki. M ogliśm y pójść do d ru k arn i z błędam i (w łączając w to je d e n lub dwa
napraw dę pow ażne), gdyby nie n ajlepsza g ru p a k o rek to ró w technicznych, jak a K IE D Y K O L W IE K istn iała...
P rzed e w szystkim n apraw d ę chcem y podziękow ać L isie K e lln e r — to ju ż d z iew ią ta (!) k siążk a, k tó r ą d la n a s p o p raw ia,
a jej p ra c a w o g ro m n y m s to p n iu p o p ra w iła c zy te ln o ść k o ń c o w eg o p ro d u k tu . D z ię k u je m y C i, Liso! S p ec ja ln e
p o d z ię k o w a n ia chcieliśm y ta k ż e p rz e k a z a ć C h riso w i B u rro w so w i, R eb ece D u n n - K r a h n o ra z D avid o w i S terlin g o w i
z a n ie z lic z o n ą liczbę w sk a z ó w e k te c h n ic z n y c h o ra z J o e m u A lb a h a ri i Jo n o w i S k eet z a ic h u w a ż n ą i d b a łą re c e n z ję
p ierw szeg o w y d a n ia k siążk i, a ta k ż e N ickow i P a la d in o , k tó ry w p o d o b n y sp o só b z a d b a ł o jej d ru g ie w yd an ie.
C h ris B u rro w s jest p ro g ram istą w firm ie M icrosoft, zatru d n io n y m w zespole pracującym n ad k o m p ilato rem C # ,
który zajm ow ał się głów nie p ro jek to w an iem i im p lem en tacją nowych (zwłaszcza dynam icznych) aspektów C # 4.0.
R eb eca D u n n - K r a h n je s t zało ż y c ie lk ą S e m a p h o re S tu d io s, sk le p u z o p ro g ra m o w a n ie m w V ic to rii w K a n a d z ie ,
k tó ry sp ecjalizu je się w ap lik a c ja c h .N ET. M ie sz k a w V ic to rii w ra z ze sw ym m ę ż e m T o b ia se m , dziećm i: S o p h ią
i S e b a stia n e m , k o te m o ra z tr z e m a k u rc z a k a m i.
D av id S te rlin g przez n iem al trzy la ta praco w ał w zespole zajm ującym się tw orzeniem k o m p ilato ra V isual C # .
J o h n n y H alife je s t głów nym a rc h ite k te m i w spółzało ży cielem M u ra l.ly (http://m urally.com ) — in te rn e to w e g o sta rtu p u
p ozw alającego u ży tko w n ik o m n a tw o rz e n ie m u rali: g ro m a d z e n ie n a n ic h d ow olnych tre śc i i o rg an izo w an ie ich
w elastyczny i organiczn y sp o só b w je d n e j dużej p rz e strz en i. J o h n n y je s t sp ecjalistą o d tech n o lo g ii c h m u r i ro zw iązań
zap ew niających d u żą skalow alność. J e s t ta k ż e p a s jo n a te m b ie g a n ia i w ielk im fa n e m sp o rtu .
40 Wstęp
Wstęp
Podziękowania
N a sz redaktor:
Courtney Nash
Z espół O ’Reilly:
jesteś tutaj ► 41
42 Wstęp
1. Zacznij pisać proqramv w C #
z
py^ isknieco
h'ego \a °fo Z kÎlar
w ie ^ y D°danie
J £ ? t-' nZyS°WdoaĆ
wizualnych może spow od y e!ementów
wynikowy kod bP e Z ed7 ać‘.
Co otrzymujesz razem £ 2 2 / d łuższy. ę e
z Visual Studio oraz C # ?
Z językiem C # , przystosow anym do prog ram o w an ia
W indow s, o raz V isual Studio ID E m ożesz natychm iast
skupić się n a tym , co pow inien ro b ić Twój p rogram .
r
® No to zaczynajmy!
Czyje« dobrze7
p o s tę p
do danych
44 Rozdział 1.
Zacznij pisać programy w C#
Tworzenia aplikacji SZYBKO. Pro g ram y w C # pisze się błyskawicznie. Język jest
p otężn y i łatw y do o panow ania, a V isual Studio ID E p rzejm u je o g ro m n ą część pracy
i w ykonuje ją za C iebie autom atycznie. M ożesz p o m in ąć przyziem ne spraw y zw iązane
z kodow aniem ID E , skupiając się n a tym , co Twój k o d pow inien wykonywać.
★ kontro lo w an ie grafiki, dźwięków, ikon o raz innych zasobów w T w oich p ro jek tach ,
O znacza to , że cały ten czas, jaki m usiałbyś spędzić, w ykonując rutynow e zadania,
A k a c je
m ożesz przeznaczyć n a tw o rz e n ie zab ó jc z y c h p ro g ram ó w .
jesteś tuta
Zatem zaczynajm y
Jeśli nie w idzisz tej opcji, to najprawdopodobniej
uruchom iłeś V isual Stu d io 2012 for W M ow s D esktop.
W takim przypadku będziesz m usiał j e _zam knąć
Co robić w Visual Studio... i uruchomić V isual Studio 2012 for wi’ndows 8.
A zatem , jeśli jeszcze tego nie zrobiłeś, to u ru ch o m V isual S tudio 2012 fo r W indow s 8. P o m iń stro n ę startow ą
i w ybierz z m en u F IL E opcję N ew Project. D o w yboru m am y kilka różnych rodzajów projektów . N a liście z lewej
strony rozw iń opcję V isual C # o ra z W indow s Store, a n a stęp n ie w ybierz szablon p ro je k tu aplikacji dla S klepu
W indow s — B lan k A pp (XAM L). W rezu ltacie ID E utw orzy fo ld er Visual Studio 2012 w Tw oim folderze
M oje dokum enty, n astęp n ie um ieści w nim fo ld er Projects, a w ew nątrz niego utw orzy now ą aplikację
(m iejsce, w którym zostanie u tw o rzo n a aplikacja, m o żn a zm ienić, p o d ając je w p o lu L ocation).
B r o i ........................................
G L a r Opcje dostępne w Twojej
' , . wersji IDE mogą wyglądać
Obejru/i łoi nieco inaczej.
K iedy tylko zapiszem y nowy p ro jek t, ID E tw orzy g ru p ę plików, do której, t y C t T p l i k , nad którym aktualnie
m iędzy innym i, b ęd ą należały pliki: M ainPage.xaml, MainPage.xaml.cs pracujesz.
o raz A pp.xam l.cs. Z o sta n ą o n e w yśw ietlone w o knie Solution Explorer,
a ID E um ieści je w folderze Projects\App1\App1.
t umieszczony Teni plik zaw iera kod C#
Ten plik zaw iera kod XAML ^ J ^ ^ k o n t r o l o j ą c y zachowań wykonywany w momencie
definiujący interfejs użytkownika K°Awnej strony aplikacji.
strony gtównej aplikacji- * _
<yid>
</yid>
</p*y>
MainPage.Xaml.cs
App.xaml.cs
MainPage.xaml
V isual Stu d io autom atycznie tw orzy te
w szy stk ie pliki. Oprócz nich tw ó rz/ ta.kie
kilka innych plików! Można j e wszy stk ie
zobaczyć w oknie Solution Explorer.
46 Rozdział 1.
Zacznij pisać programy w C#
Zaostrz ołówek
Wystarczy kilka prostych kroków i okno Visual Studio będzie wyglądało tak jak na poniższym rysunku. Przede
wszystkim musisz się upewnić, że są wyświetlone okna Toolbox (nazywane także przybornikiem) oraz Error
List, w tym celu należy je w ybrać na liście w menu VIEW. Następnie wybierz z menu głównego opcję TOOLS/
Options i w oknie dia lo g o w ym Options w yb ie rz opcję Light z lis ty Color theme. Powinieneś być w stanie
samemu domyślić się przeznaczenia wielu spośród tych okien, na podstawie posiadanej już wiedzy. A zatem,
w pustych miejscach obok rysunku zapisz, jakie jest przeznaczenie poszczególnych elementów IDE. Aby Ci
ułatwić zadanie, wpisaliśmy jedną z odpowiedzi. Sprawdźmy, czy będziesz w stanie omówić znaczenie wszystkich
wskazanych elementów.
U dołu strony um ieściliśm y
en pasek narzędzi pow iększenie t ego okna.,
zawiera przyciski abyś miał w ięcej m iejs ca
odpowiadające na opis jego elementów .
Czynnościom,
które aktualnie
wykonujesz
w Visual Studio
W ybraliśm y ja sn y sc h em at
kolorów (opcja Light), poniew aż
takie lepiej w yglądały w książce.
Je śli Ci się podoba, to w ybierz
z m enu głów nego opcję TOOLS,
n astęp n ie „ O p tio n s...”, po czym
rozwiń opcję E nvironm ent i kliknij
General', sc h e m a t kolorów m ożesz
w ybrać z listy Color them e
(w każdej chwili m ożesz w rócić
do poprzedniego).
jesteś tutaj ► 47
Poznaj swoje IDE
aostrz ołówek
Rozwiązanie Poniżej podaliśmy opisy poszczególnych elementów Visual Studio C# IDE.
Być może samemu podałeś nieco inne opisy, niemniej jednak sądzimy, że potrafiłeś
domyślić się, jakie jest podstawowe przeznaczenie każdego z okien IDE.
P ^ y c is k lko d p 7 ^ d a j ą c J era
czynnościom, które akt,, i ■
wykonujesz w
C zy w idzisz tę niewielką
ikonę pinezki? Możesz ją
Łdotyczące
i Ł i y aplikacji. S olution Explorer
klikać, by w łączać i w yłączać
automatyczne ukrywanie okna.
is T© - ? a a m
Na przykład okno Toolbox
Search Solution Explorer (Ctrl-*-;)
ma domyślnie włączoną opcję
^ y ś i 7 eet/aneUsa°n ^ P h r *r 5 1 Solution 'App1' (1 project) automatycznego ukrywania.
XAWL oraz Z Tn A [c # ] Appl
autom atycznie ' J Óre ID^
P f* Properties
P o d c z a Z o T jn T a erUje P References
^ ¡ J ° k r Z ^ ° We9° - \ P l i Assets ^ S I r jm o z Z kn\ So,uti'ion
do roz*<ązmtia. Rl'k' należ<lce t> H C om m on 1 ^ ia tia ó ró ż T p T Z
P rJ App.xamll
*E3 App1_Tem poraryKey.pfx
P ^ M ainPagejiam l
la p ackage.appxm anifest
48 Rozdział 1.
Zacznij pisać programy w C#
LNie istnieją. .
głupie pytania
jesteś tutaj * 49
Gdyby tylko ludzie nie byli ta k sm akowici..
Obcy atakują!
Cóż, m am y niespodziankę: źli obcy rozpoczęli zm asow any a ta k n a naszą
koch an ą Z iem ię, poryw ając ludzi, by p rzep ro w ad zać sw oje nikczem ne
gastronom iczne eksperym enty. N ik t się tego nie spodziewał!
N Och! Kosmici
wciągają ludzi-
Niedobrze!
Zacznij pisać programy w C#
Ratuj ludzi
Coraz więcej złych obcy ch będzie
wypełniać ekran. J e ś li_przeciągn'e sz
swojego człowieka. do jednego
z n ic h - „Gra skończona!"
Nie przeciągaj
człowieka zb y t
szybko, bo go
stra cisz.
To do CIEBIE należy
URATOWANIE LUDZI, musisz
ich bezpiecznie przeprowadzić
do docelowych portali.
jesteś tutaj * 51
O to Twój cel
^Pp c^ iK 7J ontro/^
moz/iu,oś£ g^zytkoLunikoiv,
Licznik czasu
dotarcia do
portalu sprawdza
właściwości
kontrolki
ProgressBar,
by dowiedzieć się,
czy gracz zdążył
uciec na czas.
Każdy ratowany
człow iek będzie
rysowany przy
użyciu komponentu
StackPanel,
za w ierającego
elip sę oraz
prostokąt.
- ^ y jr s Ä r Ä
nnantu Canvas.
52 Rozdział 1.
Zacznij pisać programy w C#
Zdarza
zai w biurze j e s t
j ak Windows 2003.
Tw orząc aplikację, będziesz pisał dwa różne rodzaje
przypadkach
kodu. W pierwszej kolejności zaprojektujesz tej książce.
4
interfejs użytkownika, używając w tym celu języka
XA M L (Extensible Application Markup Language) N ie m asz W indow s 8? N ie m a p ro b le m u .
— naprawdę elastycznego języka do projektowania. Spokojnie W dwóch pierwszych oraz kilku ostatnich rozdziałach
tej książki zostało przedstawionych wiele projektów
Później zajm iesz się kodem C # , dzięki któremu gra
wymagających użycia Visual Studio 2012 for Windows 8. Jednak
faktycznie będzie działać. Znacznie więcej na temat wiele osób nie dysponuje jeszcze tym systemem operacyjnym.
ję zyka XA M L dowiesz się w drugiej części książki. Na szczęście większość aplikacji przeznaczonych dla Sklepu Windows
można także tworzyć przy wykorzystaniu technologii Windows Presentation
Foundation (WPF), zgodnej z wcześniejszymi wersjami systemu
operacyjnego Windows. Szczegółowe informacje i instrukcje z tym związane
można znaleźć w dokumencie PDF, dostępnym do pobrania na stronie
N a p iszesz kod O
http://www.headfirstlabs.com/hfcsharp. Więcej informacji na ten temat
znajdziesz w dodatku Pozostałości, w poradzie num er 11.
na prowadzenie gry.
Pakiet wdrożeniowy
Kod C#
^ P rocedura o b s łu g i z d a rz e ń ^
U żyjesz dwóch
liczników czasu,
by dodawać obcych
oraz zakończyć
grę, gdy g raczowi
/ « U c is k i skończy s ię czas
_ na ucieczkę.
^Procedura^obsługrźdarzeńCNck^
StartGame() |
AddEnemy() ] ^sk^J& w T T
| AnimateEnemy() \
aP
p hkt7 ania ' dyStrybU
] EndTheGame() ^
jesteś tutaj ► 53
W ypełnij puste miejsca
o4 Ratuj ludzi - Microsoft Visual Studio Express 2012 for Windows 8 Quick Launch (Ctrl+Q) p - □ X
FILE EDIT VIEW PROJECT BUILD DEBUG TEAM DESIGN FORMAT TOOLS STORE TEST WINDOW HELP
Q -O U ffllilb i* “P - ę * - ► Local Machine - Debug - Any CPU - _ § fcj
Appjtaml.ci
Okno Desig
pokazuje
podgląd s tr
nad którą
pracujesz
Początkowo
wygląda on
p u s tą stro n
z domyślni
czarnym t
54 Rozdział 1.
Zacznij pisać programy w C#
Jesteś tutaj!
Główna strona
XAM L i kontenery
* Kontrolki interfejsu
50% - 1
Cl Design H E XAML Cl
Ę iP age
x:Class=”Ratuj l u d z i . M a i n P a g e ”
xmlns="http://schemas.microsoft.com/winfx/ 2 006 /xaml/presentation"
x m ln s:x="h t t p ://schenas.microsoft.com/winfx/ 2006 /xaml"
x m ln s:local=”using:Ratuj_ludzi”
x m In s:d=”http://schemas.microsoft.com/expression/blend/ 2008 ”
x m ln s:mc=”http://schemas.openxmlformats.org/markup-compatibility/ 2 006 ”
mc :Ignorable="d">
< / 5r id > To
lo jje
e s t znacznik otwierający
o tw ierajm y i< ¿.umy
zam ykający
^ a jm y —siatki ----------—
z awierającej
j —^ t r d k i .
</Page> 1^ . ... J, do
Kiedy . siatki
. , i •_______
będziem y dodawać kolumny, l ai, minrcTo
w iersze nr/iT
oraz inne
inne kontrolki
ttonfrM h
Kiedy do sia
odpowiednie znaczniki XAML będą um ies z czane pomiędzy ty mi dwoma
odpowiednie
znacznikami.
LOO %
© T w oja stro n a będzie m usiała m ieć jakiś tytuł, n iepraw daż? I pew nie przyda się jej
Na kilku następnych stronach
także m argines. O bydw a elem en ty m o żn a w ykonać ręcznie w kodzie X A M L , istnieje poznasz wiele różnych możliwości
je d n a k łatwiejszy sposób, by zapew nić swojej aplikacji taki wygląd, jaki m ają n orm alne Visual Studio IDE, gdyż będziemy
aplikacje d o stęp n e w Sklepie W indows. go używali jako potężnego
narzędzia do nauki. Ty sam
Przejdź do okna Solution Explorer i odszukaj w nim pozycję ^ MamPagexami będziesz używał IDE podczas
lektury tej książki do poznawania
Kliknij ją praw ym przyciskiem myszy i w ybierz opcję Delete, aby u s u n ą ć s tro n ę
możliwości języka C#. To jest
M ainP age.xam l. naprawdę efektywny sposób,
by wbić Ci tę wiedzę do głowy!
o (h o - ? (i m [?■
MainPage.xaml
Search Solution Explorer (Ctrl*;} P' :\Users\Public\D(
*
Solution 'Ratuj ludzi' (1 project}
H§ Ratuj ludzi Tworząc aplikacje dla
> A* Properties
1» *-■ References
> Assets Sklepu Windows, często
l> i i Common
D App.xi
będziesz zastępował
I n Packag c* Open
£0 Save the Open With...
Open in Blend...
domyślnie wygenerowaną
O View Code Ctrl+Alt+0
C* View Designer Shift+R stronę główną innymi
Scope to This
Cut Ctrl+X
przez Visual Studio.
&
je iwyśm etlić, wybierając di Copy Ctrl+C
© T eraz m usisz dodać now ą stro n ę głów ną. Ponow nie w oknie Solution Explorer kliknij praw ym
przyciskiem myszy pozycję j 0 Ratuj ludzi (pow inna być druga o d góry), aby w ybrać pro jek t.
N astęp n ie z w yśw ietlonego m en u w ybierz opcję A d d /N e w Item ...
56 Rozdział 1.
Zacznij pisać programy w C#
N a ek ran ie zostanie w yśw ietlone o k n o dialogow e A d d N ew Item . W ybierz w nim opcję B a sic P a g e
i zm ień nazw ę n a M a in P a g e .x a m l. N astęp n ie kliknij przycisk A d d , aby d odać now ą stro n ę do projektu.
i
M ainPage.xaml, gdyż m usi ona m ieć tę sam ą
f- nazwę, co strona, którą w cześniej u su n ą łeś.
ID E przypom ni o konieczności d o d an ia nowych plików — k lik n ij p rz y c isk Tak, a b y je d o d a ć . T eraz m usisz trochę
poczekać, aż stro n a zostanie w yśw ietlona w oknie Designer. M oże się w nim pojaw ić napis lr™!'d Mark|iF>
Buiidthe project to update Design »¡ów. ^ zaktualizow ać p ro jek t, w ybierz z m en u B U IL D opcję R ebuild Solution .
T eraz wszystko już b ędzie działać śpiewająco!
Spraw dźm y, jak w ygląda nasz nowy p lik MainPage.xaml. Przew iń p a n e l z kodem X A M L wyświetlony poniżej o k n a Designer,
aż pojaw i się w nim nowy k o d strony. O to siatka, któ rej będziesz używał jak o p u n k tu wyjścia do tw orzenia swojej aplikacji:
40.1% - [iiip M l^ l I ►
□ Design E XAM L d m0ai
T h is g r i d a c t s a s a r o o t p a n e l f o r t h e page t h a t d e f in e s tw o row s
* Row 0 c o n t a in s t h e b a c k b u t t o n and page t i t l e
* Row 1 c o n t a in s t h e r e s t o f t h e page la y o u t Użyje sz IDE do określenia wyglądu aplikacji, Twoja strona
V. modyfikując w tym celiu tę sia tk ę .
powinna zostać
a P< gir i ^ ^ t y l e = ” { S t a tic R e s o u r c e L a y o u tR o o t S t y le } " > wyświetlona
a < G r id . R ow D e fin i t io n s >
< RowDefi n i t i o n H e ig h t = " 1 4 0 " />
w oknie Designer.
< RowDefi n i t i o n H e ig h t = " * ” / >
Zw róciłeś uwagę, że pojawiła s ię zupełnie Jeśli jednak tak
< / G r i d . R o w D e fin itio n s > nowa siatka, ze swoim własnym znacznikiem s ię nie stało,
otwierającym <Grid> oraz zam ykającym </Grid>? to kliknij pozycję
< ! - - B ack b u t t o n and page t i t l e -->
< G rid >
To j e s t nagłówek strony, zaw ierający je j tytuł. MainPage.xaml
< G r id . C o lu m n D e fin itio n s > Ta siatka j e s t jednocześnie um ieszczona w oknie Solution
< C o lum nD efi n i t io n W id t h = " Auto™ /> wewnątrz sia tki głównej, do której będziesz Explorer.
< C o lu m n D e fin itio n W id t h = " * ” / >
dodawał kontrolki.
< / G r i d . Colum n D e fi n i t i o n s >
< B u tto n x:N am e= ” b a c k B u tto n ” C lick= "G o B a ck™ I s E n a b le d = ” { B in d in g Frame .C anG oB ack, E lem e n tN a m e = p a g e R o o t}” 5 t y le = " { S i
< T e x tB lo c k x:N am e= '’ p a g e T itle ™ G rid .C o lu m n = 1' l " T e x t = " { S t a t ic R e s o u r c e AppNam e}1’ S t y l e = " { S t a t i c R e so u rce PageHeaderTr
< / G r id >
jesteś tutaj ► 57
Nie do końca taka pusta
T w oja aplikacja będzie używ ała siatki składającej się z dw óch wierszy Aplikacje dla Sklepu
i trzech kolum n (o raz dodatkow ego w iersza nagłów ka, k tóry w chodzi
w skład szablonu pustej strony). Środkow a k o m ó rk a tego u k ład u będzie Windows muszą
b ardzo duża i to o n a będzie zaw ierać o b szar gry. D efiniow anie wierszy
zacznij od um ieszczenia w skaźnika myszy n a kraw ędzi strony, ta k by wyglądać prawidłowo
pojaw iła się linia i m ały trójkąt:
na dowolnym ekranie,
Jeśli na Umieść wskaźnik na tabletach, laptopach
krawędzi m yszy nad
strony kraw ędzią strony,
nie w idzisz t ak by pojawił
oraz ogromnych
ani liczby & My Application się trójkąt oraz
140, ani 1*, pomarańczowa monitorach,
to kliknij linia...
gdzieś poza
stroną. "/
wyświetlane w układzie
...a następnie
kliknij, aby
poziomym i pionowym.
utworzyć dolny
w iersz siatki.
Po dodaniu w iersza,
linia zm ieni kolor
na niebieski, a na
Określanie układu strony przy krawędzi strony
zo sta n ie wyświetlona
ego wysokość.
użyciu kolumn i wierszy siatki W''ysokość środkowego
w iersza zm ieni się
sprawia, że aplikacja będzie mogła z 1* na inną liczbę
ze znakiem gwiazdki
na końcu.
automatycznie dostosowywać się
do wymiarów ekranu.
Jupie ania ----------------------------------------------------
P : Ale wygląda na to, że w mojej siatce już jest wiele P : Chwileczkę. Ale ja chce się uczyć C#. Dlaczego tracę
wierszy i kolumn. Czym są te szare linie? czas na te wszystkie informacje o języku XAML?
O: Te szare linie są jedynie pomocą wyświetlaną przez Visual O: Ponieważ pisanie aplikacji dla Sklepu Windows w C# niemal
Studio, by ułatwić odpowiednie określanie rozmieszczenia zawsze zaczyna się od utworzenia interfejsu użytkownika, który
elementów na stronie. Można je wyłączyć klikając przycisk UHL jest definiowany w języku XAML. To także powód dla którego
Żadna z linii wyświetlanych w oknie Designer nie będzie Visual Studio dysponuje tak dobrym edytorem XAML — by
widoczna po uruchomieniu aplikacji poza Visual Studio. Jednak zapewnić programistom narzędzia, których potrzebują do tworzenia
kiedy klinkąłeś stronę i utworzyłeś nowy wiersz, zmieniłeś olśniewających interfejsów użytkownika. W tej książce dowiesz
tym samym jej kod XAML, co sprawi, że po skompilowaniu się także, jak pisać w C# dwa inne typy programów — klasyczne
i uruchomieniu aplikacja będzie działała inaczej. aplikacje Windows oraz aplikacje konsolowe, które w ogóle nie
korzystają z XAML. Poznanie tych wszystkich trzech rodzajów
aplikacji pozwoli Ci lepiej zrozumieć pisanie programów w języku C#.
58 Rozdział 1.
Zacznij pisać programy w C#
D o kładnie w ten sam sposób m ożna użyć górnej kraw ędzi stro n y — z tym że w tym przy p ad k u pow inieneś
utw orzyć dwie d o datkow e kolum ny, je d n ą w ąską z lewej strony o raz d ru g ą w ąską z praw ej strony. N a razie
nie przejm uj się ani w ysokością wierszy, ani szerokością kolum n — b ę d ą o n e zależeć o d m iejsca, w którym
klikniesz. P opraw im y je o d pow iednio już za chwilę.
\.
© K1y A p p lica tio n
K iedy już zrobisz co trzeb a, zerknij n a k o d X A M L , a n a stęp n ie n a k o d siatki p o k azan y n a p o p rzed n iej kartce.
Ja k widać, szerokości kolum n i wysokości wierszy w kodzie od p o w iad ają tym wyświetlonym n a górnej i lewej
kraw ędzi strony.
jesteś tutaj ► 59
Określmy obszar pola bitwy
60 Rozdział 1.
Zacznij pisać programy w C#
This grid acts as a root panel for the page that: defines two rows
* Row 9 contains the back button and page title
* Row 1 contains the rest of the page layout Umieszczony na sam ej górze wiersz
ze znacznikiem <Grid ...> oznacza,
że w szystfco' co j e s t poniżej, będzie
< G rid S t y l e = " { S t a t i c R e s o u r c e L a y o u tR o o t5 ty ie } " > o ^ ś t a t o p o stać i zaw artość siatki.
< G r id ,C o l u m n D e f i n i t i o n s >
cCo l u t r n D e f i n i t i o n Wi d t h = " 1 6 0 " /> To w łaśnie w taki sposób s ą defini°wane k°/umny
siatki w kodzie XAML. Utworzyłeś siałłtę składającą
< C o lu m n D e f i n i ti o n /> <----------------- s ię z trzech w ierszy i trzech ko^iwi, f^ łe g o
~ < L o l u n n u e T i n i t i o ń w i d t h = ', 1 6 0 1V> w kodzie zostały um ieszczone trzy znaczniki _
ColumnOefinition oraz trzy znaczniki R°wDefiniti° n.
< /G rid ,C o lu m n D e f in itio n s >
< G rid , R o w D e f i n i t i o n s :>
<RowDefi n i t i o n H e i g h t = " 1 4 0 '7 > Ten górny w iersz o wysokości 140 p ik se l
pochodzi z szablonu B asic Page.
<R o w D e f i n i t i o n / ;
cRow D ef i n i t i i ^ T l e i g h t = " 1 6 0 lV
W artości właściwości Width oraz Height
< / G r i d . R o w D e f in i t io r określiłeś, korzystając z r°zwij aneg°
menu w oknie Designer.
Ju ż za chwilkę dodasz do tej siatki
kontrolki, które będą um ieszczane tu taj
— poniżej definicji wierszy i kolumn.
jesteś tutaj ► 61
Przejm ij kontrolę nad program em
‘v/ m i j e wyśi
Dodaj kontrolki do siatki korzystając z menu VIEW.
S korzystaj z ,kony ppinezki,
^ ezk i,
aby okno m e było ukrywane.
Z w róciłeś kiedyś uw agę n a to , że aplikacje są w ypełnione przyciskam i, tekstam i,
paskam i p o stęp u , pask am i przew ijania, rozw ijanym i m enu, nie w spom inając o m en u
\
głów nym ? T o wszystko są k o n tro lk i, a te ra z n ad szed ł czas, abyś d o d ał ich tro ch ę do T oolbox T ł X
swojej aplikacji — wewnątrz k o m ó rek zdefiniow anych przez w iersze i kolum ny siatki. Search Toolbox p ’
J C om m on XAML Controls
Button
El CheckBox
m ComboBox
hEP FlipView
Ul Grid
3 ■ Button ■
— =— -i GridView
m Image
m ListView
© RadioButton
□ Rectangle
N astęp n ie spójrz n a okno z k odem X A M L i zobacz, jaki z n a c z n ik StackPanel
H
X A M L w ygenerow ało ID E . Z obaczysz k o d p o d o b n y do teg o poniżej TextBlock
m
— w artości m arginesów b ę d ą nieco inne, zależnie o d m iejsca, w którym EhD TextBox
upuściłeś przycisk, a poszczególne właściwości m ogą być zapisane w nieco
innej kolejności.
To s ą właś c iwości. Każda z nich ma
Kod XAML przycisku rozpoczyna s ,ę nazwę, za którą je s t umieszczony
w tym m iejscu, od znacznika otw ierającego. znak równości i w artość.
62 Rozdział 1.
Zacznij pisać programy w C#
-1601--— r '4------- I r # 16 0 -|
© Riatuj ludzi
B- m
tg
P>o dodaniu komponen t
Canvas będzie wyglądał ja k
niewielki p u sty prostokąt. _
Ju ż niebawem za jmiemy się 1
nim dokładniej.
■ * p
i— B — ■ • ■ 0B
« "■ A to j e s t kontrolka
TextBlock dodana
w kroku 2. W tej sam ej
komórce um ieściłeś
także kontrolkę
Oto przycisk ContentControl. ■>
iitw Uroku 1. . .
--- U O O H U g i—w a _________________ _____
V
\ Dodateś także ten pasek postępu
| B u t to n j
4 -
- komponent ProgressBar.
To j e s t kontrolka ContentControl Contort
-
-
A k tu aln ie w ybrana je st k o n tro lk a C anvas, gdyż dodałeś ją jak o o sta tn ią (jeśli nie jest, Kiedy przeciągasz
to kliknij ją myszką, aby ją w ybrać i zaznaczyć). Spójrz n a o k n o X A M L:
kontrolkę
<C anvas G r i d . C o lu m n = " l" G r id .R o w = " l" H o r iz o n t a lA lig n m e n t = " L e 'f t " H e ig h t = " 1 0 0 ” z przybornika
i umieszczasz
Z o stał w nim w yróżniony znacznik kontro lk i C anvas. R o zpoczyna się on o d <Canvas
i kończy sekw encją znaków /> , a p om iędzy nim i zn ajdują się tak ie właściwości jak na stronie,
G rid .C o lu m n = "1 " (dzięki której k o m p o n e n t ten je st um ieszczony w środkow ej ID E automatycznie
kolum nie) o raz G rid.R ow = "1" (dzięki któ rej k o m p o n e n t znajduje się w środkowym
generuje kod XAM L,
w ierszu). S próbuj klikać zarów no w oknie Designer, ja k i różne zna czn iki w oknie
prezentującym k o d X A M L . aby umieścić
Spróbuj kliknąć ten przycisk, powoduje kontrolkę tam,
57.33% ’'|! ! ! || ESI 14 -| i on w yśw ietlenie okna Document
gdzie ją upuściłeś.
« ¡ a «
o nim znacznie w ięcej w dalszej
części rozdziału. jesteś tutaj ► 63
W artości właściw ości w a plika cji m ają znaczenie
Podczas edycji te k s tu w y ^ e t h n e g o na
^ ------------------- IDE odpowiednio zm ienia z awai-to ść właściwośc
Content w kodzie XAML.
Użyj pola Name, by zm ienić
nazwę kontrolki na startB utton.
Użyj okna Properties, aby zmodyfikować przycisk.
U pew nij się, że przycisk je st w ybrany, a n a stęp n ie spójrz n a okno
Properties w yśw ietlone w praw ym dolnym ro g u ID E . Użyj go,
by zm ienić nazw ę k o n tro lk i n a s t a r t B u t t o n i w yśrodkow ać ją
w kom órce. K iedy przycisk będzie ju ż w yglądał odpow iednio,
k lik n ij go p raw y m p rz y c isk ie m m yszy i w y b ierz o p c ję View
Source, by p rzejść bezp o śred n io do znacznika < B u tto n >
w kodzie X A M L.
Być może
będziesz Te niew ielkie kwadraty informują, czy wartość właściwości
m u siał z ° s t ata określona. J e śli' wtaściw ość została określona,
to kwadracik będzie wyp e łniony; je ś li będzie p u sty,
rozwinąć
sekcje będzie to oznacz ać, że właściw ość ma wartość domyślną.
Common Krecy u żyłeś opcj i Edit Text wybranej z menu podręcznego,
oraz Layout. IDE au tom atycznie zaktualizowało wartość właściwości
Content.
Skorzystaj z przycisków @ oraz ES,
aby okre ślić w łaściw ości HorizontalAUgnment oraz
VerticalAlignm ent i w yśrodkow ać kontrolkę w komórce.
Text="{StaticResource AppName}"
Ale zaraz! To wcale nie jest widoczny na stronie tekst „My A pplication” — co jest grane?
Otóż szablon Blank Page używa zasobu statycznego o nazwie AppName do określania tekstu wyświetlanego w górnej części strony.
Przewiń okno kodu XAM L ku górze, aż pojawi się w nim sekqa <P age.R esources> zawierająca następujący wiersz kodu:
< x :S trin g x:Key="AppName">My A p p lic a tio n < /x :S trin g >
StackPanel jest dosyć podobna do kontrolek G rid i Canvas — jej zadaniem jest grupowanie innych
kontrolek (dlatego jest określana mianem „kontenera”) i dlatego nie jest widoczna n a stronie. Jednak Ich unikaj
C n n f^ n tf”n n tm l J
ponieważ kontrolka TextBlo ck była umieszczona u góry komórki, a ContentControl u dołu, zatem
utworzony kontener StackPanel wypełnił niemal cały jej obszar. Kliknij pośrodku konentera S tackPan el ,
aby go zaznaczyć; następnie kliknij go prawym przyciskiem myszy i wybierz z menu podręcznego opcję
IReset Layout >| i | a» |. Spowoduje to zastosowanie domyślnych wartości właściwości kontenera, co sprawi,
że właściwości określające wyrównanie w pionie i poziomie przyjmą wartość S tre tc h . W końcu kliknij
także kontrolki TextBlo ck i C ontentControl, i w podobny sposób przywróć domyślne wartości ich układu.
Zaznacz kontrolkę ContentControl i zmień jej właściwości HorizontalAlignm ent i V e rtica lA lig n m e n t ,
przypisując im wartość Center .
jesteś tutaj ► ÓS
Chcesz, by Twoja gra coś robiła, prawda?
A ::: [Grid] ® o
Pam iętasz k o n tro lk ę C anvas, k tó rą um ieściłeś w środkow ej k o m ó rce siatki? T eraz
^ backButton ® o
î naw et tru d n o ją zobaczyć, gdyż kontro lk i tego typu są początkow o — p o przeciągnięciu ffl pageTitle ® o
Okno Document Outline można także otworzyć,
klikając kartę widoczną przy lewej krawędzi okna IDE.
*3 startButton ® o
z okna Toolbox — niew idoczne. N a szczęście istnieje pro sty sposób, by ją odnaleźć. a M [StackPanel] ® o
Kliknij niew ielki przycisk ES w yświetlony powyżej o kna z kodem X A M L ; spow oduje to U [TextBlock] ® o
SI [ContentControl] ® o
w yświetlenie o kna D o cu m en t O utline. Kliknij elem en t by zaznaczyć k o ntrolkę. I Ul [Canvas] °
[ProgressBar] ® o
s ■ a 1 E3 o P o o k reślen iu nazwy kontro lk i C anvas m ożesz już zam knąć o k n o D ocum ent
Color resources O utline. N astęp n ie użyj przycisków [SI i CU] dostępnych w oknie Properties,
Kliknij su w a k z lew ej m R 166 by nad ać w łaściwościom w yrów nania w poziom ie i p ionie w artość S t r e t c h ,
s trony, a n a stęp n ie - przyw róć dom yślne w artości m arginesów i kliknij przycisk , by przypisać
kliknij^początkow y kolor
i w łaściwościom W idth o raz H e ig h t w artość A uto. T e ra z przypisz właściwości
g ra d im tu . N a stęp n ie I
Winkijl suw ak z prawej A ' 00%
Column w arto ść 0, a właściwości ColumnSpan (um ieszczonej o b o k Column)
strony, po czym wybierz
końcowy kolor gradientu. w arto ść 3.
W końcu otw órz sekcję B rush w oknie Properties i kliknij przycisk d , by określić
g rad ien t. O kreśl początkowy i końcowy kolor gradientu, klikając, odpowiednio:
lewy i prawy suwak u dołu edytora kolorów, i klikając wybrany kolor.
66 Rozdział 1.
Zacznij pisać programy w C#
W o knie D ocu m en t O utline kliknij praw ym przyciskiem myszy ele m e n t C ontentControl. W ybierz z m en u opcję E d it
Tem plate, a n a stęp n ie w ybierz opcję Create E m p ty .... T w o rzo n em u szablonow i nadaj nazw ę Enem yTem plate. ID E
d o d a nowy szablon do k o d u X A M L.
Już prawie udało Ci się zakończyć rozmieszczanie elementów strony! Na następnej stronie znajdziesz kilka ostatnich kroków .. . ►
Sprawdź stronę, którą stworzyłeś
Człowieka m ożesz dodać na dwa sposoby. Pierwszy z nich został opisany w kolejnych trzech akapitach. Drugi, nieco
szybszy, polega na bezpośrednim wpisaniu w ID E odpowiedniego k o d u X A M L . W ybór należy do Ciebie!
Z aznacz kontrolkę Canvas, następnie wyświetl przybornik i rozwiń w nim sekcję A ll X A M L Control. D w ukrotnie kliknij
elem ent Ellipse, by dodać elipsę. N astępnie ponow nie wybierz kontrolkę Canvas i dw ukrotnie kliknij elem ent Rectangle,
by dodać prostokąt. P rostokąt zostanie wyświetlony bezpośrednio n a elipsie, dlatego przeciągnij go nieco w dół.
W ciśnij klawisz Sh ift i kliknij elipsę, aby zaznaczyć obie k ontrolki. Kliknij elipsę praw ym przyciskiem myszy
i z w yśw ietlonego m en u w ybierz opcję G roup In to i S tackP anel. Z azn acz elipsę, zm ień jej tło n a jednolicie białe,
a w łaściwościom W idth i H e ig h t przypisz w artość 10. N a stęp n ie w ybierz k o n tro lk ę Rectangle, zm ień jej tło na
jednolicie białe, a właściwościom W idth i H e ig h t nadaj w artości, odpow iednio: 10 i 25.
Skorzystaj z o kn a D o cu m en t Outline, by zaznaczyć ko n tro lk ę S ta c k P a n e l (upew nij się, że na sam ej górze okna
Properties m ożna zobaczyć inform ację Type StackPanel y Kliknij przyciski ? by przypisać w łaściwościom Wi d th o raz
H e ig h t w artość A uto. N astęp n ie, korzystając z p o la N a m e , zm ień nazw ę k o n tro lk i n a human. O to ja k pow inien
w yglądać w ygenerow any k o d X A M L:
J eśli zd ecyd u jesz s ię wpisać ten kod sam emu
< S t a c k P a n e l x ^ a m e ^ ' h u m a n " O r i e n t a t i o n = ”V e r t i c a l " > w oknie XAML, to upewnij s ię , że u m ieszcza sz go
< E l l i p s e F i l l = “W h i t e " H e i g h t = ” 2 5 ” W i d t h = ”1 0"/> bezpośrednio przed zamykającym znacznikiem </
Canvas>. W taki sposób zdecydujesz, że panel
< R e c t a n g l e Fill='’W h i t e ” Height='’190'' W i d t h = " 1 0 ”/>
człowieka będzie um ieszczony wewnątrz kontrolki
</5tackPanel> Canvas.
Ponow nie wyświetl o k no D o cum en t Outline i spraw dź, ja k w nim p re z en tu je się nowy kod:
T„dj kod
Podczas przeciągania
Dodaj tekst Koniec gry.
K iedy rozgryw ka się zakończy, T w oja g ra pow in n a wyświetlić stosow ny bloku tekstowego
kom unikat. Z ro b isz to , do d ając do strony k o lejn ą k o n tro lk ę T e x tB lo c k ,
w obszarze kontrolki
której n ad asz od pow iednią nazw ę i zm ienisz czcionkę:
68 Rozdział 1.
Zacznij pisać programy w C#
D o d o d an ia została Ci jeszcze je d n a kon tro lk a: będzie o n a rep rezen to w ać docelow y p o rtal, do k tórego
będziesz przeciągał człow ieka. (N ie m a znaczenia, w którym m iejscu k o n tro lk i C anvas ją um ieścisz).
W ybierz kontrolkę C anvas, następnie przeciągnij i upuść na nią kontrolkę R e c ta n g le . Użyj przycisku
dostępnego w sekcji Brush o k n a Properties, by w ypełnić ją g rad ien tem . Przypisz w łaściwościom H e ig h t
oraz W idht w arto ść 50.
N astęp n ie przekształć p ro sto k ą t w rom b, o b racając go o 45 stopni. W tym celu rozw iń sekcję Transform
w oknie Properties i o b ró ć p ro sto k ąt, klikając przycisk ** i w pisując w artość 45 w polu^4«g/e.
® Ratuj ludzi
Koniec gry
Ich unikaj
jesteś tutaj ► 69
Przejąłeś kontrolę
CO DO CZEGO SŁUŻY?
* • *
T eraz, skoro już utw orzyłeś interfejs użytkow nika swojej aplikacji, pow inieneś już m niej więcej w iedzieć,
do czego służą poszczególne kontro lk i, użyłeś też w ielu właściwości, by określić ich wygląd i dostosow ać go
do p o trzeb gry. P rzek o n ajm y się, czy po trafisz określić, do czego służą poszczególne w łaściwości i w których
sekcjach o kna Properties m o żn a je znaleźć.
t> Appearance
R o ta tio n U żywasz jej w kodzie C #
do korzystania z kontrolki.
t> C o m m o n
O k reśla k o lo r kontrolki.
F ill
> Layout
70 Rozdział 1.
Zacznij pisać programy w C#
Jesteś tutaj!
\
Kontrolki interfejsu
P akiet wdrożeniowy
Kod C#
| StartGanw<) }
f - | A ddEnem yO |
S m i
jesteś tutaj ► 71
Dalsze prace nad grą
A b y gra zaczęła
działać, p asek postępu
musi' odliczać czas,
człowiek m usi się
poruszać, a gra musi
s ię kończyć, kiedy
j akiś wróg go dotknie
lub człowiek ucieknie
do portalu.
72 Rozdział 1.
Zacznij pisać programy w C#
p r i v a t e v o i d s t a r t B u t t o n _ C l i c k ( o b j e c t sender, R o u t e d E v e n t A r g s e)
{
y Click="startButton_Click"
V dt C, także tą w taściwość do
U ż y j ID E d o s tw o r z e n ia w ła s n e j m e to d y ą d d n a ^ S l t T ^ a £ y2 U Z iZ m I
dow iesz się, co to j e s t. ej
Kliknij pom iędzy naw iasam i { } i w pisz przedstaw iony poniżej frag m en t kodu,
w łącznie z naw iasam i i średnikiem :
Czy zauw ażyłeś czerw oną falistą linię w yśw ietloną tu ż poniżej w pisanego p rz e d chw ilą tek stu ? W te n sposób ID E
sygnalizuje, że coś jest nie tak. K iedy klikniesz tę linię, pojaw i się m ały niebieski p ro sto k ą t — za jego p o m o cą ID E
inform uje, że być m oże będzie w stan ie Ci p o m ó c w rozw iązaniu problem u.
Um ieść wskaźnik myszy nad niebieskim prostokątem i kliknij ikonę a ' , która zostanie wyświetlona p o d nim. N a ekranie pojawi
się okienko umożliwiające utw orzenie szkieletu metody. Jak sądzisz, co się stanie, kiedy je klikniesz? N o dalej, przekonaj się!
iNie jstnieią,
--------------------------------------------- głupie pytania-----------------------------------------------------
O : M etoda jest blokiem kodu, który ma nazwę. Będziemy się O : Tak... jak na razie. Metody są podstawowymi elementami
nimi zajmowali szczegółowo w rozdziale 2. konstrukcyjnymi programów — będziesz ich pisał bardzo dużo
i wkrótce ich tworzenie nie będzie sprawiało Ci problemów.
jesteś tutaj ► 73
Inteligentny i rozsądny
© Zacznij dodaw ać kod. W ew nątrz m eto d y w pisz słowo C o n te n t. ID E wyświetli o k ien k o z sugestiam i;
nosi ono nazw ę o k ie n k a IntelliSense. Z wyświetlonej w nim listy w ybierz opcję C ontentControl.
^ it _contentLoaded *
f t Content
ContentControl
« *1^ ________________________
* i j ContentPresenter
f t ContentProperty
f t HorizontalContentAlignm ent
f t HorizontalContentAlignm entProperty
* i j ScrollContentPresenter
f t VerticalContentAlignm ent
© D okończ wpisyw anie pierw szego w iersza kodu. P o w pisaniu słowa new kolejny raz zostanie
w yśw ietlone okien k o IntelliSense.
private void A d d E n e m y Q
{
ContentControl enemy = new ContentControl ().;
74 Rozdział 1.
Zacznij pisać programy w C#
Z an im zakończysz p isanie m eto d y A ddEnem y(), m usisz d odać je d e n w iersz k o d u w górnej części
pliku. O dszukaj w iersz rozpoczynający się o d public sealed p artial c la s s MainPage i dodaj
nowy w iersz za otw ierającym naw iasem klam row ym ( { ):
/// <summary>
I I I A basic page that provides characteristics common to most applications.
I l l </summary>
public sealed partial class MainPage : Ratuj_ludzi.Common.LayoutAwarePage
D okończ wpisyw anie k o d u m etody. Pojaw ią się w nim dwie faliste czerw one linie.
ID E wyróżni w ten sposób w yw ołania m etody A n im ateE nem y(); o bie linie znikną,
kiedy w ygenerujesz szkielet tej m etody.
Ten w iersz kodu P'ayArea^Wri^do P°dljreśleniepo
dodaje utworzoną private void AddEnemy()
1 “P w n lj s le ¿e „ Hy! ° ra X A /*L
przed chwilą {
kontrolkę wroga Convas nazwę p/ayArea W r ° /ce
ContentControl enemy = new ContentControl();
do kolekcji enemy.Template = Resources["EnemyTemplate"] as ControlTemplate; *
o nazwie Children.
AnimateEnemy(enemy, 0, playArea.ActualWidth - 100, "(Canvas.Left)”);
O kolekcjach
AnimateEnemy(enemy, random.Next((int)playArea.ActualHeight - 100),
dow iesz s ię
więcej random.Next((int)playArea.ActualHeight - 100), "(Canvas.Top)");
w rozdziale 8. playArea.Children.A d d (enemy);
Jeśli m u sisz przełączać s ię pomiędzy kodem XAML i edy torem C# , M ainP age.xam l M a in P a g ejta m l.e s -o X
to skorzystaj z tych kart um ieszczonych w górnej części okna.
® Skorzystaj z niebieskiego p ro sto k ącik a i przycisku ® ' , by w ygenerow ać szkielet m eto d y A nim ateEnem yO
(zrób to ta k sam o, ja k w cześniej w ygenerow ałeś m e to d ę AddEnem y()). Tym razem ID E d o dało do m etody
cztery parametry, o nazw ach enemy, p1, p2 o ra z p3. T e ra z będziesz m usiał ręcznie zm odyfikow ać ostatn ie
trzy param etry . P ara m etr p1 zm ień n a from, p a ra m e tr p2 n a to , a p a ra m e tr p3 n a propertyToAnimate .
N astęp n ie zm ień typ i n t n a double .
0 metodach
1 parametrach
dow iesz
s ię więcej
w rozdziale 2.
"....... ’ _ .’ _ ” ” ” ”_ ~ _ ~ ” _ ’......
IDE zapew ne wygeneruje szk ie le t metody
zaw ierający param etry ty p u J n f . Zm ień je
na „double . Typy poznasz w rozdziale 4.
Przewróć kartkę, by zobaczyć, jak Twój program działa!
jesteś tutaj ► 75
W porządku, jest naprawdę fajnie
private void AnimateEnemy(ContentControl enemy, double from, double to, string propertyToAnimate)
{
Storyboard storyboard = new Storyboard() { AutoReverse = true, RepeatBehavior = RepeatBehavior.Forever };
DoubleAnimation animation = new DoubleAnimation()
{
From - from,
Ten kod sprawia, że utworzony wróg
O animacjach i będzie s ię p rzesu w a ł w obszarze
dow iesz I To = to,
s ię w ięcej <. kontrolki playArea. Zm ieniając liczby
Duration - new Duration(TimeSpan.FromSeconds(random.Next(4, 6))) 4 i 6, m o żesz sprawić, że wróg będzie
w rozdziale 16. }J s ię p rzesu w a ł wolniej lub szybciej.
Storyboard.SetTarget(animation, enemy);
Storyboard.SetTargetProperty(animation, propertyToAnimate);
storyboard.Children.Add(animation);
storyboard.Begin();
N ajpierw , przez kilka sek u n d , n a ek ran ie będzie w idoczny duży „X ”, a p o tem pojaw i się głów na stro n a Twojej
aplikacji. K ilka razy kliknij przycisk „S tart!” . K ażde kliknięcie spow oduje w yśw ietlenie now ego koła, które
będzie się poru szało p o ek ran ie, odbijając się o d kraw ędzi o b szaru gry.
e
kod programu. Może z ^ m m a ^ ś
o jakichś nawiasach lub słowie
kluczowym?
[4 Zatrzymaj program.
Naciśnij kombinację klawiszyA lt+ T ab, aby wrócić do Visual Studio ID E . W idoczny wcześniej na pasku narzędzi przycisk
► został zastąpiony przez przyciski 11 ■ © , służące do, odpow iednio: wstrzymywania program u, zatrzymywania go oraz
do jego ponow nego urucham iania. Kliknij przycisk z czerwonym kw adratem , aby zatrzymać program .
jesteś tutaj ► 77
Co zrobiłeś, dokąd zmierzasz
Jesteś tutaj!
Główna strona
XAM L i kontenery
t Kontrolki interfejsu
użytkownika Windows
\
Kod C#
78 Rozdział 1.
Zacznij pisać programy w C#
:z e g < SŁUŻY?
CO DO CZEGO
*
*
R O Z W IĄ Z A N IE
T eraz, skoro ju ż utw orzyłeś in terfejs użytkow nika swojej aplikacji, pow inieneś m niej więcej w iedzieć,
do czego służą poszczególne k ontrolki, użyłeś też w ielu właściwości, by określić ich w ygląd i dostosow ać go
do p o trzeb gry. P rzekonajm y się, czy po trafisz określić, do czego służą poszczególne właściwości i w których
sekcjach o k n a Properties m o żn a je znaleźć.
O k reśla k ą t o b ro tu kontrolki.
O k reśla k o lo r kontrolki.
jesteś tutaj ► 79
Tik, tak, tik
DODAJ KOLEJNE WIERSZE NA SAMYM POCZĄTKU KODU C#. Plik M ainpage.xam l.cs, który
edytuje s z , zawiera kod klasy
Przejdź n a p o cząte k pliku, tam gdzie dodałeś w iersz k o d u rozpoczynający się o nazw ie MainPage. O klasach
dowies z s ię w rozdziale 3 .
od Random. Poniżej niego dodaj kolejne trzy wiersze:
/// <summary>
/// A basic page that provides characteristics common to most applications.
I l l </summary>
public sealed partial class MainPage : Ratuj_ludzi.Common.LayoutAwarePage
S do daj m e t o d ę d l a je d n e g o ze s w o ic h l ic z n ik ó w c z a s u
U m ieść k u rso r bezp o śred n io za śred n ik iem i dw ukrotnie naciśnij klawisz E nter, n astęp n ie
wpisz enem yT im er. (z k ro p k ą n a końcu). K iedy tylko w piszesz kro p k ę, zostanie
w yśw ietlone okien k o IntelliSense. W ybierz z niego opcję T ic k i w pisz dalszą część kodu.
G dy tylko w piszesz znaki +=, V isual Studio ID E wyświetli niew ielkie okienko:
n e m y T ira e r .T ic k +®j
Liczniki czasu
e n e m y T im e r T ic k ; (P ress T A B to in sert)
odmierzają go,
N aciśnij klawisz Tab. ID E wyświetli kolejne okienko: wywołując
r T ic k ;
e n e m y T im e r. T i c k +=
określoną metodę
Press TA B to gene ra te h a n d le r le n e m y T im e r_ T ic k l in th is class
za każdym razem,
Jeszcze raz naciśnij klawisz Tab. O to kod, który w ygeneruje ID E : gdy minie zadany okres
public MainPage() czasu. Użyjesz jednego
{ IDE wygenerowało
this.InitializeComponent(); m etodę nazywaną licznika czasu, by co
procedurą obsługi
enemyTimer.Tick += enemyTimer_Tick i zdarzeń ■ Procedury kilka sekund dodawać do
} tego rodzaju
poznasz dokładniej gry nowego wroga oraz
void enemyTimer_Tick(object sender, object e) w rozdziale 15.
{ drugiego, by zakończyć
throw new MotImplementedException();
} grę po określonym
80 Rozdział 1. czasie.
Do nazw m etod j e s t Zacznij pisać programy w C#
dołączana para maw iasów -
\
[4 DOKOŃCZ PISANIE METODY MAINPAGE()
K olejną p ro ce d u rę obsługi zd arzeń T ic k d odasz do drugiego
WYSIL
licznika czasu, dopiszesz także dw a kolejne w iersze kodu. SZARE KOMÓRKI
T a k pow inien w yglądać k o m pletny k o d m eto d y M ain P ag e()
Obecnie kliknięcie przycisku Start powoduje
o raz dw óch m e to d w ygenerow anych p rzez ID E :
dodanie nowego wroga do obszaru gry
public MainPage() Jak sądzisz, co powinieneś zrobić, żeby
{ zamiast tego powodow ało ono faktyczne
this.InitializeComponent(); rozpoczęcie gry?
enemyTimer.Tick += enemyTimer_Tick;
enemyTimer.Interval = TimeSpan.FromSeconds(2); 1
Po zakończeniu g ry
targetTimer.Tick += targetTimer_Tick; spróbuj zm ienić te liczby.
targetTimer.Interval = TimeSpan.FromSeconds(.1); W jaki sp osób wpft/nie to
} na ro zg ryw ką ?
void targetTimer_Tick(object sender, object e)
Gotowy
Popraw działanie przycisku Start kod
Czy pam iętasz, ja k spraw iłeś, że kliknięcie przycisku Start pow oduje wyświetlenie
Zmuszamy Cię do w p isyw a nia
w obszarze gry czerw onego kółka? T e ra z popraw isz jego działanie, tak by jego całkiem długich fra g m e n tó w kodu.
kliknięcie pow odow ało faktyczne rozpoczęcie gry.
Po zakończeniu lektury tej książki
będziesz wiedział, co robi cały ten
Zadbaj o to, by kliknięcie przycisku Start powodowało kod — w rzeczywistości będziesz
rozpoczęcie gry. w stanie pisać kod taki jak ten,
zupełnie samodzielnie.
O dszukaj dodany w cześniej kod, dzięki k tó re m u kliknięcie przycisku
Start pow odow ało d o d an ie w roga. Z m ień go, by w yglądał ta k ja k ten Jak na razie jednak Twoje zadanie
p rzedstaw iony poniżej: polega na uważnym przepisywaniu
każdego wiersza kodu oraz na
private void startButton_Click(object sender, RoutedEventArgs e) wykonywaniu wszystkich poleceń.
{ To pozwoli Ci przyzwyczaić się do
StartGame():
WVWVWW' ' # wpisywania kodu i poznać tajniki Visual
Zm ienlają c ten w iersz kodu
} s p raw isz, że przycisk S ta r t Studio IDE.
będz ie ' fakty cznie rozpoczynał grę,
a n <e jedy nie ¡wyświetlał wroga. Jeśli jednak utkniesz, to możesz pobrać
kompletną, działającą wersję pliku
MalnPage.xaml oraz MainPage.xaml.cs
[4 Dodaj metodę StartGameO. lub skopiować i wkleić wybrane
metody C# lub fragmenty kodu XAML;
W ygeneruj szkielet m eto d y S ta r tG a m e ( ). Poniżej zam ieściliśm y kod, znajdziesz je na stronie http://www.
który należy w niej um ieścić: headfirstlabs.com/hfcsharp.
Ratuj ludzi
Ich unikaj
' •
Kiedy pasek p ostęp u w yśw ietlany u dołu
strony wypełni się, gra zo sta n ie zakończona,
a na e kranie zostanie w yśw ietlony napis
m WYSIL ___
„Koniec gry". fyd SZARE KOMÓRKI
*) .. . Jak sądzisz, co musisz zrobić, by dokończyć pisanie gry
Licznik czasu dotarcia do celu powm ien s ię
i zapewnić jej prawidłowe działanie?
wypełniać powoli, a nowi wrogowie mają s ię
pojawiać co dwie sekundy. Jeśli pomiar upływu
czasu nie działa, to sprawdź, czy dodałeś
do m etody MainPage() w szystk ie w iersze kodu.
Przewróć kartkę, by zobaczyć, jak Twój proyram działa! - - - - - - - - - - - - ►
jesteś tutaj ► 83
W razie jakiegokolwiek zdarzenia..
| Properties
' 0
X 1
r
Okno Document Outline
mogło zwinąć w iersze [Grid],
H
Type StackPanel playArea i inne. W takim
Dwukrotnie kliknij to pole. przypadku rozwiń je by
PointerMoved *
w yśw ietlić w iersz human.
Pnint#»rPr#»ccpH ^
PointerReleased
© T eraz wyświetl o k n o z k odem X A M L i spraw dź co now ego pojaw iło się w kodzie reprezentującym
k o ntrolkę S ta c k P a n e l:
Możesz skorzystać
© U zupełnij kod m etody: z tych dwóch przycisków,
private void humanPointerPressed(object sender, PointerRoutedEventArgs e) by przełączać okno
{ Properties pomiędzy
if (enemyTimer.IsEnabled) trybem wyświetlania
{ właściwości i zdarzeń.
humanCaptured = true;
human.IsHitTestVisible = false;
}
}
Properties . J L
Jeśli wrócisz z powrotem do okna Designer i ponownie klikniesz
kontrolkę StackPanel reprezentującą człowieka, to przekonasz M
Name human
Type StackPanel
c t)
się, że IDE wyśw ietli nazwę nowej procedury obsługi zdarzeń )interMoved
w oknie Properties. W ten sam sposób będziesz jeszcze PointerPressed human PointerPressed
dodawał wiele innych procedur obsługi zdarzeń. PointerReleased
84 Rozdział 1.
Zacznij pisać programy w C#
Upewnij się, że dodajesz w łaściw ą procedurę obsług i
z darzeń! Dodałeś procedurę obsługi zdarzeń poM erPres sed
do kontrolki human, a teraz dodaje s z p rocedurę obsługi Jeśli okno Properties
zdarzeń PointerEntered do k°n trolki ta rg et. prezentuje procedury
obsługi zdarzeń, możesz
dw ukrotnie kliknąć puste
Użyj o k n a D o cu m en t Outline, aby w ybrać k o n tro lk ę R e c ta n g le o nazwie
pole obok w ybranego
t a r g e t , a n astęp n ie skorzystaj z o k n a Properties działającego w trybie prezen tacji
zdarzenia, aby IDE
zdarzeń, by d odać p ro ce d u rę obsługi zd arzen ia P o in t e r E n te r e d . Poniżej dodało odpow iedni
przedstaw iliśm y kod, k tóry pow inieneś w niej um ieścić: szkielet metody.
private void target_PointerEntered(object sender, PointerRoutedEventArgs e)
{
if (targetTimer.IsEnabled && humanCaptured)
{
progressBar.Value = 0;
C a n v a s . SetLeft(target, random.Next(100, (int)playArea.ActualWidth - 100));
C a n v a s . SetTop(target, random.Next(100, (int)playArea.ActualHeight - 100));
C a n v a s . SetLeft(human, random.Next(100, (int)playArea.ActualWidth - 100));
C a n v a s . SetTop(human, random.Next(100, (int)playArea.ActualHeight - 100));
humanCaptured = false;
human.IsHitTestVisible = true;
}
© T e ra z będziesz m usiał d odać jeszcze dwie kolejne p ro ced u ry obsługi zdarzeń. Tym razem b ę d ą o n e zw iązane
z k o n tro lk ą C anvas o nazw ie p la y A re a . M usisz zatem o d naleźć odp o w ied n ią k o n tro lk ę [G rid ] w o knie D ocum ent
O utline (są dwie tak ie k on tro lk i — w ybierz tę w ew nętrzną, w idoczną tro c h ę niżej i p rz e su n iętą nieco w praw o
w zględem głównej siatki określającej u k ład całej strony) i przypisz jej nazw ę g r i d . T e ra z m ożesz d o d ać do
k ontrolki p la y A re a dwie, p rzed staw io n e poniżej, p ro ced u ry obsługi zdarzeń:
PointerEntered
Upewnij się , że w pisałeś odpowiedni
Pointę rExited playArea_PointerExited
kod do odpowiedniej procedury
obsługi zdarzeń. PointerMoved playArea_ Pointe rMoved
Poi nterPressed
jesteś tutaj ► 85
Nie możesz uratować wszystkich
Z wyświetlonej listy w ybierz P o in t e r E n te r e d . (N ie przejm uj się, jeśli w ybierzesz niewłaściwy e le m e n t listy — p o p ro stu
u suń wszystkie znaki w łącznie z k ropką. N a stęp n ie znow u w pisz k ro p k ę, a ID E wyświetli o k ienko IntelliSense.)
N astęp n ie, w taki sam sposób ja k w cześniej, dodaj p ro ce d u rę obsługi zdarzeń. W pisz +=, p o czym naciśnij klawisz Tab:
N astęp n ie ponow nie naciśnij klawisz Tab, co spow oduje w ygenerow anie szkieletu p ro ced u ry obsługi zdarzeń:
T eraz m ożesz przejść do nowej m etody w ygenerow anej przez ID E i uzu p ełn ić jej kod:
86 Rozdział 1.
Zacznij pisać programy w C#
\
Przeglądnij kod i poszukaj w m’m
m iejsca, w którym określana j e s t \wartość
właściwości Is H itT e stV isible. Gdy j e s t ona
włączona, człowiek przechw ytuje zdatrzenm
PointerEntered, gdyż reprezen.tu jąca go
kontrolka StackPanel znajduje s ię p omiętJzy
wskaźnikiem m yszy i konM Irą woga..
jesteś tutaj ► 87
Ozdóbki, wodotryski i obcy
W yśw ietl okno D o cu m en t O utline, kliknij praw ym przyciskiem myszy wyświetlony Korzystając z przycisku klucza
w nim elem en t C ontentControl, a z w yśw ietlonego m en u p o d ręczn eg o wybierz i błyskawicy możesz zmieniać
opcję E d it Tem plate, a n a stęp n ie E dit Current. W ybrany szablon zostanie zawartość prezentowaną
w oknie Properties, * *
wyświetlony w oknie k o d u X A M L . Z m odyfikuj k od X A M L elipsy w tak i sposób,
by m iała szerokość (W idth) 75 pikseli i była w ypełniona ko lo rem szarym (G ray). wyświetlając, odpowiednio,
N astęp n ie dodaj do jej k o d u kolejną właściwość, s t™ ite = ,lB ia c k " (aby dodać właściwościlub zdarzenia.
obram ow anie), i przyw róć dom yślne w artości w łaściwościom w yrów nania w
pionie i poziom ie. T a k pow inien w yglądać ten k o d (m ożesz z niego u su n ąć
wszystkie po zo stałe właściwości, k tó re mogły zostać do niego d o d an e podczas
w prow adzania m odyfikacji):
Użyj przycisku x d o stęp n eg o w sekcji Transform okna Properties, aby nieco przekrzyw ić elipsę
(dodając do niej przekształcenie Skew).
z n a 1 y ©
X 10 V o
[4 Przeciągnij z o kna Toolbox jeszcze je d n ą elipsę i ją także um ieść w obszarze starej elipsy. Z m ień kolor w ypełnienia na
B lack , ustaw szerokość na 25, a wysokość na 35. O pcje w yrów nania i m arginesy ustaw zgodnie z poniższym rysunkiem:
88 Rozdział 1.
Zacznij pisać programy w C#
.i * B ez tytułu - P a in t ” D
□ ; w = ■ ■ ■ ■ ■ ■ ■ " ■ ■ ■
V«H| ' Z im a , M * O 1*04 R O I0»0i «0*0ł l<Wuj
• • • • 1 J .010,r
kM w rt Oer»r N>i7<<ni (u M y (otory
</ElIipse>
JEST JESZCZE JEDNA RZECZ, KTÓRĄc POWINIENEŚ ZROBIĆ...
ZAGRAĆ W SWOJĄ GRĘ!
</ControlTemplate> ...
I nie zapomnij zatrzym ać s ię nc° chwij k ę i d c ™ 6 t o
S p rawdźmy, czy m ożesz wykazać s ię co w ła ś n i udało O s ię zrobić. a ro
kreatyw nością i zm ienić wygląd człowieka,
docelowego portalu oraz wroga. jesteś tutaj ► 89
Twoja aplikacja trafia do wszystkich
Jesteś tutaj!
Opublikuj swoją aplikację
t
Pow inieneś być n apraw d ę zadow olony ze swojej
aplikacji! T e ra z n ad szed ł czas, żeby ją w drożyć. K iedy ją
opublikujesz w Sklepie W indow s, u d o stęp n isz ją m ilionom
0
potencjalnych użytkow ników . ID E m oże Ci w tym pom óc,
przeprow adzając Cię p rzez wszystkie czynności zw iązane
z publikow aniem aplikacji w Sklepie W indows.
r
W niektórych wersjach V isual Stu d io opcje
zw iązane ze Sklepem Windows s ą um ieszczone
w menu PROJECT, a nie w odrębnym menu
gtównego poziomu — STORE.
W tej książce będziem y Ci pokazy wać, gdzie może sz
znajdować dodatkowe informacje ma. w itrynie MSDN
— M icrosoft Developer Network. To naprąwdę
bardzo cenne źródto informacjit które może Ci pomóc
w poszerzaniu wiedzy.
Więcej inform acji na tem at publikow ania aplikacji w Sklepie W indows można
znaleźć na stronie http://msdn.microsoft.com/pl-pl/library/windows/apps/jj657972.aspx.
90 Rozdział 1.
Zacznij pisać programy w C#
★ P o b ierz p rogram odpow iadający a rch itek tu rze k o m p u te ra (x 86, x64, A R M ) i u ru ch o m program
instalacyjny.
★ Jeśli konieczne je st w p row adzenie zm ian w konfiguracji sieci, to n a ek ran ie m oże zostać wyświetlony
k re a to r, który p o m o że Ci to zrobić. P o u ru ch o m ien iu deb u g g era n a ek ran ie zostanie w yświetlone
okno p ro g ram u V isual S tu d io R em o te D ebugging M onitor:
File lo o ls Help
14/6/2013 2:37:34 PM M svsm o n started a new server nam ed 'MY-SURFACE:4016'. W a itin g fo r new conn...
'f
Ten program zo sta t u ru c h o m m y na _
Zanotuj tę nazw ę kom putera gdyż j u ż niebawem Ci s ię Rrrzyoa
★ T e ra z n a zdalnym k o m p u terze działa już p ro g ram V isual Studio R e m o te D ebugging M o n ito r, oczekujący na
p ołączen ia inicjow ane przez V isual Studio działające n a Tw oim roboczym k o m p u terze.
jesteś tutaj ► 91
Na razie ludzie są uratowani
P> R em ote M a ch in e -
Nie zapomnij zm ienić wybranej tu opcji z powrotem na
► R em ote M achín e Sim ulator przed rozpoczęciem le ktury następnego rozd zia łu -
S im u la to r
N apiszesz w nim sporo pr°gramów, więc ten przycisk
będzie Ci potrzebny, by je uruchamiać-
Local M a chin e
□ R em ote M a ch in e
92 Rozdział 1.
Zacznij pisać programy w C#
jesteś tutaj ► 93
94 Rozdział 1.
2. To tylko kod
. +
Pod maską
PRZECIĄGASZ KONTROLKĘ
Z O KNA TOOLBOX N A STRONĘ,
A NASTĘPNIE KLIKASZ JĄ DWUKROTNIE...
K o n tro lk i um ieszczan e n a stro n a c h um ożliw iają
p o d ejm o w an ie akcji. W tym ro zd ziale będ ziesz używał
k o n tro le k B u tto n , by p o zn aw ać ró żn e elem en ty
języka C # .
96 Rozdział 2.
To tylko kod
...ID E robi to
Z a każdym razem , kiedy d okonasz zm iany w o knie ID E , p rzen o szo n a
je st o n a także n a kod. O znacza to , że zm ieniane są rów nież pliki z tym
kodem . C zasam i m odyfikow anych je st tylko kilka wierszy, innym razem
dodaw ane są całkiem now e pliki.
Te pliki s ą tworzone na podstawie
w cześniej zdefiniowanego
wzorca. Zaw iera on podstawową
[1 „.IDE TW ORZY PLIKI I KATALOGI DLA kod niezbędny do utworzenia
i w yśw ietlenia formularza
PROJEKTU.
<page>*
<grid>
</grid>
</page>
M ainPage.xam l
p riv a te void sta rtB u tto n _C lick (o b je ct sender, RoutedEventArgs e)
<Button x:Name="startButton"
Content="Start!"
HorizontalAlignment="Center" <page>*
<grid>
VerticalAlignm ent="Center" C lick= "startB u tto n _C lick"/>
</grid>
:/page>
M ainPage.xam l
...i zmodyfikowało ten w iersz kodu.
jesteś tutaj ► 97
Wspaniale, pogadanka
98 Rozdział 2.
To tylko kod
Wszystkie kontrolki, których używałeś, są elem entam i .NET for W indows Store — biblioteki
zawierającej siatki, przyciski, strony oraz wszelkie inne narzędzia służące do tw orzenia aplikacji
przeznaczonych dla Sklepu W indows. Jed n ak w trakcie lektury pierwszych kilku rozdziałów,
zaczynając od rozdziału 3., będziesz się uczył pisać klasyczne aplikacje systemu W indows, tworzone
przy użyciu narzędzi wchodzących w skład biblioteki .NET for W indows D esktop (nazywanych także
czasami „W inForm s”). D oskonale nadają się one do tw orzenia klasycznych aplikacji, mających postać okien
zawierających form ularze z polam i wyboru, przyciskami, listami itd. M ogą one wyświetlać obrazy, odczytywać A P I, czyli
i zapisywać pliki, zarządzać kolekcjami obiektów ... stanowią zbiór narzędzi służących do realizacji bardzo wielu interfejs
programowania
zadań, które program iści muszą wykonywać każdego dnia. Zabaw ne jest to, że dokładnie te sam e czynności aplikacji
m uszą realizować aplikacje przeznaczone dla Sklepu Windows! Jed n ą z rzeczy, których dowiesz się w tej książce, (od angielskich
słów: Application
są różnice w sposobie realizacji różnych zadań w aplikacjach dla Sklepu W indows oraz klasycznych aplikacjach
Programming
systemu Windows. T o właśnie taka w iedza sprawia, że dobrzy program iści m ogą się stać świetnymi program istam i. Interface), j e s t
kolekcją narzędzi
N arzęd zia d o stęp n e w ram ach W indow s R u n tim e o raz p latfo rm y .N E T p o d zielo n e są za po m o cą programowych,
p r z e s trz e n i nazw . W idziałeś je już w cześniej n a p o czątk u k o d u w po staci linijek „using”. Jed n ą których możemy
używać, by
z p rzestrzeni nazw je st W in d o w s.U I.X a m l.C o n tro ls . T o m iejsce, sk ąd b io rą się w szystkie przyciski, p o la u zyskać dostęp
w yboru i form ularze. Z a każdym razem , kiedy w ybierasz utw o rzen ie p ro je k tu typu W indow s S to re, ID E lub kontrolować
sy ste m . Wiele
dodaje wszystkie p o trz eb n e pliki. D zięki te m u p ro jek t m oże zaw ierać form ularz, a wszystkie pliki p o siad ają system ów
n a górze w iersz using W in d o w s .U I.X a m l.C o n tro ls ;. udostępnia
A P I, lecz dla
system ów
Ogólne informacje na tem at .NET Framework używanej w aplikacjach dla Sklepu W indows możesz operacyjnych
znaleźć tutaj: http ://m sd n .m icro so ft.co m /p l-p l/lib ra ry/w in d o w s/a p p s/b r2 3 0 3 0 2 .a sp x. takich jak
Windows mają
one szczególne
znaczenie.
< śr )
Okno Solution
Explorer
pokazuje pliki
z katalogu
programu.
M ainPagejtam l MainPagej<aml.cs -0 X
100 Rozdział 2.
To tylko kod
Kliknij dw ukrotnie błąd, a ID E n atychm iast p rzen iesie k u rso r w o d pow iednie m iejsce w kodzie:
using System;
using System .Collections.Generic; Te wiers z e using znajdują s ię na początku
using System.IO; każd eg o pliku z kodem. Z a ich pośrednictwem
mów imy C#, aby używ a ł danych klas .NET. Każdy
using System.Linq; z nich informuj e , że klasy w tym pliku .c s będą
mogły u ży wać w szystkich klas, które znajdują się
using Windows.Foundation; w podanej przestrzeni nazw platformy .NET lub
using Windows.Foundation.Collections; Windows S to re A P I.
using Windows.UI.Xaml;
N ależy p am ięta ć o jed n ej rzeczy: tak n ap raw d ę w cale nie trzeba używać instrukcji u s in g . M o żn a użyć pełnej
nazwy. N a przykład w aplikacji R a tu j ludzi użyłeś następującej instrukcji u sin g :
Spróbuj um ieścić n a p o czątku tego w iersza znaki k o m en tarza ( / / ) , a następnie spójrz n a błędy wyświetlone w oknie
E rrorList. Jednego z nich m ożesz się pozbyć. O dszukaj typ S to ry b o a rd , który w edług inform acji prezentow anych
przez ID E pow oduje błędy i zm ień go n a W in d o w s.U I.X a m l.M e d ia .A n im a tio n .S to ry b o a rd (choć żeby program
z pow rotem zaczął działać, będziesz m usiał u sunąć dodany wcześniej kom entarz).
102 Rozdział 2.
To tylko kod
P : Jak to jest z tymi nawiasami klamrowymi? P : Skąd się biorą błędy wyświetlane w oknie Error List
podczas prób uruchamiania programu? Myślałem, że to się
O : C# używa nawiasów klamrowych do grupowania zdarza tylko w przypadku wyboru „Build Solution” .
instrukcji w b lo ki. Występują one zawsze w parach,
a więc nawias zamykający można zobaczyć tylko wtedy, O : Dzieje się tak, ponieważ pierwszą czynnością wykonywaną
gdy istnieje otwierający. IDE pomaga układać pary — kliknij po wyborze opcji StartDebugging z menu lub naciśnięciu
jeden z nawiasów, a zarówno on, jak i odpowiadający mu przycisku uruchamiającego program na pasku narzędzi jest
drugi nawias z pary zostaną wyróżnione ciemniejszym tłem zapisanie wszystkich plików w rozwiązaniu i próba ich kompilacji.
Kiedy kompilujesz napisany przez Ciebie kod — czy to podczas
uruchamiania, czy w trakcie budowania — IDE, zamiast uruchamiać
program, wyświetla ewentualne błędy w oknie ErrorList.
104 Rozdział 2.
To tylko kod
myGrid.Background =
new S o lid C o lo rB ru sh (C o lo rs.V io le t); Ustawia właściwości dla kontrolki T e x tB lo c k .
h ello La b el.T ext = "Cześć"; k Specjalny rodzaj komentarza, którego używa IDE
h ello Lab el.Fon tS ize = 24; w celu wyjaśnienia działania bloku kodu.
/ / / <summary>
/ / / Wyświetl obrazek Rovera po Zmienia kolor tła kontrolki G rid o nazwie
I I I n aciśn ięciu przycisku
/ / / </summary> m yG rid .
* '*■
JAKIE JEST MO
MOJE ZADANIE?- .
Rozwiązanie
myGrid.Background =
new S o lid C o lo rB ru sh (C o lo rs.V io le t); Ustawia właściwości dla kontrolki T ex tB lo c k .
r
Chwileczkę, okno? A nie strona?
Zaczn ie sz od poznania klasycznych
aplikacji sy ste m u Windows, które
mają okna i formularze.
/ / / <summary>
/ / / Wyświetl obrazek Rovera po Zmienia kolor tła kontrolki G rid o nazwie
I I I n a ciśn ię ciu przycisku
/ / / </summary> myGrid.
106 Rozdział 2.
To tylko kod
« " r t iS S
u ż y w a ć j e j m e to d .
MoreClasses.cs
f
Klasę m ożesz podzielić m iędzy kilka plików
wyłącznie wtedy, gdy u ży je sz słowa kluczowego
„partial". Najprawdopodobniej nie sko rzy sta sz z tej
możliwości w programach, które n a p iszesz podczas
lektury tej książki, jednak IDE robi to, by podzielić
kod strony i um ieścić go w dwóch plikach:
MainPage.xaml oraz MainPage.xaml.cs.
” ° że Pr ź e i t w y Zw a ! m a
i n t m yH eight = 63;
tekstem, wartościami
Z a każdym razem , gdy m yH eight pojaw i się w kodzie, C # zam ieni
prawda/fałsz lub
to w yrażenie n a w artość 63. Jeśli zm ienisz ją p o te m n a 12: innego rodzaju
m yH eight = 12; danymi, będziesz
C # zastąpi m yH eight w arto ścią 12 — nazw a zm iennej m yH eight
pozo stan ie bez zmian.
używał zmiennych
do przechowywania
tych wartości.
108 Rozdział 2.
To tylko kod
s t r i n g z; Jeśli w swoim
s t r i n g message = "Odpowiedź brzmi" + z;
kodzie spróbujesz
A te ra z spróbuj go u ruchom ić. Z o stan ie wyświetlony b łąd i ID E
odm ów i kom pilacji T w ojego kodu. D zieje się ta k dlatego, że ID E użyć zmiennej,
spraw dza stan każdej zm iennej, aby upew nić się, że została jej
przypisana jakaś w artość, zanim spróbujesz jej użyć. N ajłatw iejszy
Te wartości są której wartość nie
przypisyw ane
sposób na uniknięcie teg o typu p ro b lem ó w to p o łączen ie deklaracji do zmiennych. została wcześniej
zm iennych z instrukcjam i, k tó re u staw iają ich w artość początkow ą.
określona, to IDE
go nie skompiluje.
int maxWeight = Można w łatw y
stringAmessage sposób uniknąć
bool boxChecked tego typu
błędów poprzez
Każda deklaracja ma typ, połączenie
dokładnie tak samo jak
w cześniej. deklaracji zmiennej
i przypisania
Kilka użytecznych typów jej wartości
K ażda zm ienna p o sia d a typ, k tóry o k reśla rodzaj danych, jakie m oże w jednej instrukcji.
o n a przechow ywać. W iele szczegółowych inform acji dotyczących
typów danych dostępnych w języku C # p o d am y w ro zdziale 4. T eraz
skoncentrujem y się n a trzech najbardziej p opularnych. Liczby całkow ite
przechow uje i n t , s t r i n g zaw iera tekst, bool n a to m ia st przechow uje Juz P o p is a ł e ś
w artości logiczne t r u e / f a l s e (praw da/fałsz). w artość do sw ojej zm iennej,
m c m e sto i na przeszkodzie,
f y M zm ienić. Nie ma zatem
żadnych efektów ubocznych
zmienna, rzeczownik. w daw ania wartości zmiennym
podczas deklaracji.
e łe m e n t; łu b c e c h a , k t ó r a m o ż e u le g a ć z m i a n o m .
110 Rozdział 2.
To tylko kod
r
U żyjem y go, by p rzek o n ać się, ja k działa k o d przedstaw iony n a p o p rzed n iej stronie.
1
możliwość ta
number (na 15),
zostanie ona
n um ber 1 15 H H B H H E E H I
będzie naprawdę
odpowiednio
zaktualizowana
bardzo przydatna.
Locals W atch 1
w oknie Watch.
Podczas debugowania m ożesz
także w skazać zm ienną my sz k ą
[7 W ZN Ó W REALIZACJĘ PROGRAMU a je j wartość zo sta n ie w yśw ietlona
K iedy będziesz chciał kontynuow ać działanie p ro g ram u , naciśnij klawisz F 5 w formie podpowiedzi... B ęd ziesz
mógł j ą przypiąć, by ciągle byta
(lub w ybierz opcję D E B U G /C ontinue). widoczna.
112 Rozdział 2. ^
To tylko kod
T
To główny powód tego, że wartości
błędów. Na szczęście IDE może Ci pomóc ich
uniknąć! Wystarczy, że umieścisz kursor na
jednym nawiasie klamrowym, a IDE wyróżni
while (x > 5) typu logicznego s ą takie ważne.
Pętle p rzeprowadzają te s t, aby odpowiadający mu drugi nawias z pary:
s p rawdzić, czy powinny być dalej
{ wykonywane.
x = x - 3;
W szystkie instrukcje
pom iędzy nawiasami
klamrowymi pętli Każda pętla for ma trzy instrukcje. Pie rwsza je j wartości
while s ą wykonywane początkowe. Pętla będzie wykonywana tak (ttogo,^ ja k długo druga
tak długo, jak długo in strukcja będzie prawdziwa. Trzecia z instrukcji wykonywana j e s t
warunek w nawiasach po każdym kolejnym przebiegtu pęW .
okrągłych j e s t
prawdziwy.
for (int
{
// Wszystkie instrukcje pomiędzy nawiasami klamrowymi
// zostaną wykonane 4 razy.
}
Użyj schematu kodu do pisania prostych pętli
D osłow nie za m inutę będziesz pisał w łasne p ętle f o r , a ID E może Naciśnij Tab, a kursor
przeskoczy do pola
C i pom óc nieznacznie przyspieszyć i ułatwić kodow anie. W pisz tength. Od tej wartości
f o r i dw ukrotnie naciśnij Tab, a wstawi ono kod autom atycznie. zależy liczba przebiegów
G dy wpiszesz nową zm ienną, ID E sam o zam ieni resztę szablonu. pętli. M ożesz zm ienić
j ą na dowolną liczbę
Naciśnij Tab jeszcze raz, a k u rso r przem ieści się do p o la kod lub zmienną.
program u.
Jeżeli zm odyfikujesz
nazwę zm iennej, szablon
autom atycznie zm ieni
je j pozostałe dwa
w ystąpienia.
Każda instrukcja
if rozpoczyna się
String message = 1,1 od warunku.
if (someValue == 24)
Instrukcje znajdujące s ię
{ pom iędzy naw iasami klamrowymi
wykonywane s ą ty lko
message = "Wartość jest równa 24."; w przypadku spełnienia warunku-
Instrukcje {
typ u if/e ls e są
dość proste. // pomiędzy nawiasami możesz wstawić
Je śli warunek
j e s t spełniony, // dowolną liczbę instrukcji
wykonywane są
operacje zapisane
u; pierw szej
message = "Wartość jest równa 24.";
parze nawiasów
klamrowych. } else {
W przeciwnym
razie wykonywane message = 'Wartość nie jest równa 24.";
są, instrukcje
z drugiej pary
}
U — __________________________________________________________________________________________________
u I Nie myl operatorów ze znakiem równości!
lluiaqal Znaku równości (=) będziesz używał do ustawiania wartości zmiennych, natomiast dwóch znaków równości (==)
do ich porównywania. Nie uwierzysz, jak wiele błędów w programach — nawet takich, które zostały napisane przez
doświadczonych programistów — zostało spowodowanych użyciem = zamiast ==. Jeśli IDE skarży się komunikatem w stylu „cannot
implicitly convert type 'int' to 'bool"', to prawdopodobnie jest to problem tego typu.
114 Rozdział 2.
To tylko kod
Wybierz se n sowną nazwę projektu, gdyż w dalszej części
Kiedy zobaczysz takie adidasy,
książki będziesz je s zc ze do niego wracał.
to będzie znak, że nadsze d ł czas,
że byś sam em u napisał trochę kodu
Strona zawiera siatkę składającą się z trzech wierszy Na stronie zostały umieszczone cztery kontrolki
i dwóch kolumn. Wysokości wszystkich wierszy są B u tto n , po jednej w każdym wierszu. Użyj
określone jako 1*, czyli ich definicje mogą mieć postać właściwości C o n te n t , by wyświetlić w nich teksty:
< R o w D e fin itio n /> — bez żadnych właściwości. Pokaż komunikat, If/Else, Sprawdzenie kolejnego
Dokładnie tak samo wyglądają definicje kolumn. warunku oraz Pętla.
Ir - - y - .. ...
■ S p ra w d z e ń » lu A jn e g o w aru n k u
W dolnej komórce znajduje się kontrolka T e x tB lo c k Użyj właściwości x:N am e , by nadać przyciskom nazwy
o nazwie m y L ab el . W jej właściwości S t y l e została b u t t o n l , b u tt o n 2 , b u tto n 3 oraz b u tt o n 4 .
zapisana wartość B o d y T e x tS ty le . Po określeniu nazw dwukrotnie kliknij każdy z nich,
by wygenerować procedury obsługi zdarzeń.
Jeśli chcesz użyć opcji Edit S ty le dostępnej w menu^
podręcznym, lecz m asz problemy z wy braniem k° n tr°lki, to
kliknij j ą prawym przyciskiem m yszy w oknie Docum ent O M m e
i wybierz opcję Edit S ty le z wy świetlonego m enu. jesteś tutaj ► 115
Uwaga, gotowi, kodujemy!
Ten przycisk j e s t
<Button x :Name="button4" Content="Pętla" HorizontalAlignment="Center"
um ieszczony w drugiej
. Grid.Column="l" Grid.Row="l" Click="button4 Click"/>
kolumnie i w drugim
wiersz u , dlatego te
<TextBlock x:Name="myLabel" HorizontalAlignment="Center" VerticalAlignment="Center"
właściw ości mają
Grid.Row="2" Grid.ColumnSpan="2" Style="{StaticResource BodyTextStyle}"/>
wartości 1.
</Grid>
</Page>
I
100% - 4
WYSIL ______
SZARE KOMÓRKI
Jak sądzisz, dlaczego górny wiersz i lewa kolumna mają numer 0, a nie 1?
Dlaczego można pominąć ustawianie właściwości G rid.R ow oraz
G rid .C o lu m n kontrolek umieszczanych w lewej górnej komórce?
116 Rozdział 2.
To tylko kod
p r i v a t e voi d b u t t o n 2 _ C l i c k ( o b j e c t s e n d e r , EventArgs e)
{
Najpierw i n t x = 5;
ustaw iam y
wartość if (x == 10)
z miennej x
i przypisujem y {
do niej 5, myLabel . Text = 'x musi być równe 10"; x nie jest równe 10
a potem
sprawdzamy, }
czy j e s t ona i
else
równa 10.
{
myLabel . Text = "x n i e j e s t równe 10";
/
} Oto w ynik działania programu. Sprawdź, czy jesteś w stanie,
zmieniając jedną linię, spowodować wyświetlenie zamiast
tego komunikatu „ x musi być równe 10” .
118 Rozdział 2.
To tylko kod
p r i v a t e voi d b u t t o n 3 _ C l i c k ( o b j e c t s e n d e r , EventArgs e)
{
i n t someValue = 4;
s t r i n g name = "Kr z y s i ek " ;
i f ((someValue == 3) && ( n a m e . E q u a l s ( " J a n e k " ) ) )
{
myLabel . Text = "someValue j e s t równe 3 i name j e s t równe J a ne k" ;
}
myLabel . Text = " t en w i e r s z j e s t wykonywany bez względu na wa r u n k i " ;
}
p r i v a t e voi d b u t t o n 4 _ C l i c k ( o b j e c t s e n d e r , RoutedEventArgs e)
{
i n t count = 0;
Ta pętla j e s t Druga część instrukcji for to s prawdzenie e
wykonywana, warunku. Oznacza ona: „d°póki i j e s t mniejsze
dopóki zmienna w h i l e (count < 10) niż 5, pętla powinna by ć wy kony wana . .
count j e s t nWiża r5u,nek j e s t sprawdzany przed wyko nbalonkiem
mniejsza niż 10. { bloku kodu um ieszczonego w pętli, a blok ek
zo sta n ie wykonany tylko wtedy, gdy warunek
count = count + 1;
j e s t spetniony.
Ta instrukcja j e s t ^ konj f
pod koniec każdej iteracji. W tym
en fragment u sta la przypadku zw iększa ona w artość
wartości początkowe zmiennej i o 1. T e n fragm ent pętli
Ha pętli. Przypisuje for je s t nazywany iteratorem, je
artość początkową on wykonywany hęzpośredn . o ę ^
count = count - 1
do liczby całkow itej, zakończeniu realizacji bloku kodu_
która będzie w pę t l< umieszczonego wewnątrz nawiasów
używana.
klamrowych pętli-
myLabel . Text = "Odpowiedź brzmi c ount ;
}
Zanim naciśniesz przycisk, spróbuj wczytać się w kod i odgadnąć, jaki będzie
w ynik działania programu. Potem użyj go i sprawdź, czy miałeś rację!
jesteś tutaj ► 119
Jeszcze jeden i jeszcze raz...
Zaostrz ołówek —
Potrzeba nam trochę ćwiczeń związanych ze sprawdzaniem warunków i z pętlami.
Rzuć okiem na poniższy kod. Zakreśl miejsca sprawdzania warunków i uzupełnij puste pola,
tak aby komentarze poprawnie go wyjaśniały
while (x > 3)
{
// wykonuj te instrukcje, dopóki
result = result + x; // dodaj x
x = x - 1; // odejmij ......................................
}
for (int z = l ; z<3; z = z + l )
{
// rozpocznij pętlę poprzez _
// wykonuj pętlę, dopóki ...................................
// po wykonaniu jednego przebiegu
result = result + z; //
}
// następna instrukcja aktualizuje tekst w kontrolce TextBlock
// ...........................................................
myLabel.Text = "Zmienna result jest równa " + result;
x < y ( mn i e j sz e ni ż)
x > y (większe n i ż )
x == y (równe - t a k j e s t , dwa znaki równości)
120 Rozdział 2.
To tylko kod
ZACZEKAJ!
JEST JAKAŚ NIESPÓJNOŚĆ W TWOJEJ
LOGICE. CO SIĘ STANIE Z MOJĄ PĘTLĄ,
JEŚLI NAPISZĘ WARUNEK, KTÓ RY NIGDY NIE
PRZYJMIE WARTOŚCI FALSE?
C zasam i fira a
K 3 1 * * * * chcesz
Z a s to s o w a ć w p ro g ra m ie .
Zaostrz
Z.UU3U ołówek
Zaprezentowano tu kilka pętli. Zanotuj przy każdej z nich, czy będzie wykonywana
w nieskończoność, czy kiedyś się zatrzyma. Jeżeli dojdzie do końca, to ile razy się
przedtem wykona?
\
Pamię taj, że w pętlach
WYSIL
for warunek za w sze j e s t
sprawdzany przed wykonaniem
SZARE KOMÓRKI
bloku kodu um ieszczonego
wewnątrz pętli, natom ia s t Czy możesz znaleźć jakiś powód, dla którego pisanie
iterator u ruchamiany j e s t pętli nieskończonych ma sens? (Wskazówka: będziesz
po wykonaniu tego bloku. z nich korzystał w rozdziale 13.).
^Zaostrz ołówek
Potrzeba nam trochę ćwiczeń związanych ze sprawdzaniem warunków i z pętlami.
Rozwiązanie Rzuć okiem na poniższy kod. Zakreśl miejsca sprawdzania warunków i uzupełnij
puste pola, tak aby komentarze poprawnie go wyjaśniały.
x = x - 1; // odejmij I od wartości x
} Ta pętla wykonywana j e s t dwukrotnie — za pierwszym
z je s t na 1, a za drugim na 2.
for (int z = 1; 6 < 3; z = z + 1) Po o siqgnię ciu wartości 3 nie j e s t ju ż m niejsze niż 3,
więc pętla s ię kończy.
r- «^Zaostrz ołówek
Zaprezentowano tu kilka pętli. Zanotuj przy każdej z nich, czy będzie wykonywana
R O Z W ia Z a n ie w nieskończoność, czy się kiedyś zatrzyma. Jeżeli dojdzie do końca, to ile razy się
przedtem wykona?
122 Rozdział 2.
To tylko kod
■ Nie .istnieją.
głupie pytania
P : Czy każdy kawałek kodu musi być O : Istnieje doskonały sposób, abyś się o tym przekonał
umieszczony w klasie? — po prostu spróbuj! Zrób coś w miejscu, gdzie IDE tworzy
za Ciebie kod. Przeciągnij przycisk na stronę, zmień jego
O : Tak. Wszystko, co robią programy napisane w języku C#, właściwości. Spróbuj potem to cofnąć przyciskiem Undo.
sprowadza się do wykonywania instrukcji. Instrukcje te są częścią Co się stało? Generalnie przy prostych rzeczach można zauważyć,
klas, a te z kolei wchodzą w skład przestrzeni nazw. Nawet jeśli że IDE potrafi wycofać zmiany, które samo wprowadziło.
początkowo wydaje się, że coś nie jest częścią klasy — na przykład (W przypadku bardziej skomplikowanych, takich jak dodanie nowej
wtedy, gdy używasz okna Designer do ustawiania właściwości bazy danych SQL do projektu, zostanie wyświetlone ostrzeżenie.
kontrolek — to okazuje się, że dokładniejsze poszukiwania W tej książce nie znajdziesz jednak przykładów tak złożonych
pozwalają znaleźć odpowiednie fragmenty dodane przez IDE operacji).
i że jest to część jednej z klas.
O : Tak. Istnieje kilka przestrzeni nazw, których nie powinieneś O : W zasadzie powinieneś być dość ostrożny. Możesz odnieść
używać, choć z technicznego punktu widzenia będą działać. spore korzyści, gdy zrozumiesz, co IDE robi z Twoim kodem.
Zauważyłeś może, że wszystkie wiersze u s in g na początku Przyjdzie na pewno taki moment, gdy wiedza na temat tych
zagadnień przyda się w rozwiązywaniu naprawdę poważnych
plików klas C# zawsze zawierają System? To dlatego, że istnieje
problemów. W większości przypadków wszystko, co będziesz chciał
przestrzeń nazw System, która jest używana przez Windows
zrobić, możesz wykonać za pośrednictwem IDE.
Store API i platformę .NET. To właśnie tam możesz znaleźć
większość ważnych narzędzi dających prawdziwą moc programom
CELNE SPOSTRZEŻENIA --------------------
Zaliczyć można do nich przestrzeń nazw S yste m .L in q , która
pozwala pracować z sekwencjami danych, oraz przestrzeń nazw
System .IO zawierającą narzędzia do korzystania z plików ♦ Działanie programu określasz poprzez pisanie instrukcji.
i strumieni. W większości przypadków możesz wybrać dowolną Instrukcje zawsze są częścią klas, a każda klasa jest
nazwę dla swojej przestrzeni nazw (o ile składa się ona z liter, składową przestrzeni nazw.
cyfr i znaków podkreślenia). Kiedy tworzysz nową aplikację, ♦ Każda instrukcja kończy się średnikiem (;).
IDE automatycznie wybiera ją na podstawie nazwy programu. ♦ Kiedy używasz wizualnych narzędzi Visual Studio
Magnesy z kodem
C z ę ś c i programu C # zo stały pom ieszane i poprzypinane na lodówce. C zy p o tra fis z
p op rzestaw iać fra gm en ty kodu ta k , a b y utw orzyły prawidłowy program C # , który
w yśw ietla pokazany komunikat? N ie k tó re z nawiasów klamrowych spadły na podłogą
i s ą z b y t małe, ab y j e podnieść. W każdej chwili m ożesz dodać dowolną ich liczbą.
(Rada: nie ulega wątpliwości, że nie o b e jd zie sią bez dodania kilku nawiasów
klamrowych. Koniecznie j e dopisz!).
Wynik:
To j e s t kontrolka
TextBlock o nazwie
„output", której
zaw artość program
modyfikuje za pomocą
właściwości Text.
Teraz czas na poćwiczenie stosowania instrukcji i f / e l s e . Czy potrafisz napisać taki program?
Jeśli utw o rzysz dwa Dodaj tu p rzycisk i pole w yboru.
Ćwiczenie w iersze i jednem u K o n tro lk ę CheckBox m ożesz znaleźć w o knie Toolbox,
z nich p rzy p isze sz w IDE
w ysokość 1*, to w iersz tu ż poniżej k o n tro lk i B u tto n . K o n tro lce B u tto n nadaj
U tw ó rz tak ą stronę.
ten pozornie zniknie, gdyż nazw ę c h a n g e T e x t, a k o n tro lce CheckBox nazwę
Z aw iera siatkę sk ładającą się z o s tanie zm niejszony do
e n a b le C h ec k b o x . T ek st n a k o n tro lk ach zm ień, klikając
z dw óch wierszy i dw óch kolum n. bardzo mały ch rozmi'arów.
P rzypisz drugiemu je praw ym przyciskiem myszy i w ybierając z m en u opcję
w ierszowi wysokość 1 E d it Text (po w pisaniu tek stu naciśnij klawisz E sc).
a oba znów będą
widoczne. K liknij k ażd ą z k o n tro le k praw ym przyciskiem myszy
i w ybierz opcję R eset L ayout/A ll, a n astęp n ie upew nij
się, że ich właściwości V e r tic a lA lig n m e n t oraz
K liknięcie /m ie n ia e ty k ietę Q W łąc /a /m w n ę etykiety H o r iz o n ta lA lig n m e n t m ają w artość C e n te r.
To je st kontrolka TextBlock.
Jest n iem al ta k a sam a ja k ta, k tó rą um ieściłeś
Naciśnij przycisk, aby zmienić tekst
n a sam ym dole strony w ostatnim projekcie.
N adaj jej nazw ę la b e lT o C h a n g e ,
a właściwości G rid.R ow przypisz w artość 1.
enableCheckbox.IsChecked == true
Możliwość zmiany tekstu została wyłączona
Jeśli ten w aru n ek N IE będzie spełniony, to p ro g ram pow inien w ykonać n astęp u jące dwie instrukcje:
Jeżeli użytkow nik naciśnie p rzycisk i pole w yboru B ĘD Z IE zazn aczo ne, zm ień tekst w kontrolce
TextBlock ta k , by za w ie ra ł gdy tekst będzie w yśw ietlany z lewej strony, i I Z prawej
gdy będzie w idoczny po praw ej.
Jeśli właściwość T e x t etykiety m a w artość "Z p ra w e j" , to pro g ram pow inien zm ienić tekst n a "Z le w e j"
i zmienić w artość właściwości H o riz o n ta lA lig n m e n t n a H o riz o n ta lA lig n m e n t.L e f t. W przeciwnym razie tekst m a
być zm ieniony na "Z p ra w e j" , a wartość właściwości H o riz o n ta lA lig n m e n t n a H o riz o n ta lA lig n m e n t.R ig h t.
W ten sposób klikanie przycisku będzie pow odow ało zm ienianie położenia etykiety — jed n ak wyłącznie
w przypadku, gdy p ole w yboru będzie zaznaczone.
Zagadkowy basen
T w oim za d a n iem je st p o b ra n ie kolejnych
i n t x = 0;
fragm entów k o d u z b asen u i w staw ienie
s t r i n g poem
ich w o d pow iednie p u ste m iejsca
w kodzie. N ie m ożesz używać tego
w hile ( ) {
sam ego frag m en tu k ilkakrotnie
i nie m usisz w ykorzystać wszystkich
fragm entów . Twój cel to utw orzenie if ( x < 1 ) {
klasy, k tó ra się skom piluje i będzie
działała. N ie daj się zwieść — to zadanie
je st trud n iejsze, niż się wydaje.
if ( ) {
W ynik
if ( ) {
Z am ieściliśm y tu ćwiczenia w stylu „Puzzle z basenu“, aby dać
Twojemu um ysłowi znacznie am bitniejsze zadania. J eśli je s te ś
osobą, która choć trochę lubi zakręcone logiczne układanki,
to takie ćw iczenie przypadnie Ci do g u stu . Jeżeli za czym ś
takim nie przepadasz, nic nie szkodzi — spróbuj! Nie bój się
zerknąć na odpowiedź, aby coś sprawdzić. Jeśli pogubisz s ię
przy rozwiązywaniu „Puzzli z basenu“, przejdź dalej.
Przypominamy: każdy
fragment może być użyty
tylko raz!
126 Rozdział 2.
To tylko kod
Teraz czas na poćwiczenie stosowania instrukcji i f / e l s e . Czy potrafisz napisać taki program?
Zagadkowy basen.
Rozwiązanie
Magnesy z kodem.
Rozwiązanie
i n t x = 0;
Ten magnes
nie spadł z lodówki... s t r i n g poem = " " ;
while ( x < 4) {
poem = poem + "a";
if (x < 1) {
if ( x > 1) {
poem = poem + " oyster";
Ta instrukcja x = x + 2;
powoduje
przypisanie do }
x 2 podczas if (x == 1) {
pierwszego
j 1 podczas poem = poem + "noys ";
drugiego
przebiegu pętli. }
if ( x < 1) {
poem = poem + "oise ";
}
x = x + 1;
}
output.Text = poem;
T
Jeśli s zu k a sz p rawdziwego wyzwania, to sprawdź, czy
potra fis z 'znaleźć roz wiązanie alternatywne. Oto podpowiedź:
I s tnieje inna p ostać kodu, która daje identyczne wyniki.
Jeśli podałeś inne n zw ią za n ie niż to powyżej, to spróbuj
zrozumieć, dlaczego to działa.
128 Rozdział 2.
To tylko kod
Start
m , .
* ¡Hfc e a
tttop 10
Twitter
Wfrtt i t OTI_
m A
o > S*>Ow*
Podczas lektury kilku najbliższych rozdziałów będziesz p isał aplikacje, korzystając Kolejnym ważnym
z V isual S tu d io for W indow s D esk to p , a d o p iero p o te m zajm iesz się tw orzeniem powodem do nauki
tworzenia klasycznych
aplikacji dla S klepu W indows. W ynika to z faktu, że p o d w ielom a w zględam i aplikacji Windows je s t
klasyczne aplikacje W indow s są prostsze. Być m oże nie w yglądają ta k efektow nie, możliwość zobaczenia,
a co w ażniejsze, nie in teg ru ją się rów nie dobrze z system em W indow s 8 i nie ja k można tę sam ą
rzecz zrobić na dwa
ud o stęp n iają tego w spaniałego, spójnego in terfe jsu użytkow nika cechującego różne sposoby.
aplikacje dla S klepu W indow s. N iem niej je d n a k w ydajne tw orzenie aplikacji A to naprawdę szybki
sposób utrwalania
dla S klepu W indow s w ym aga zro zu m ien ia w ielu w ażnych, podstaw ow ych pojęć. w mózgu nowych
A tw orzenie klasycznych aplikacji W indow s je st rew elacyjnym sposobem ich pojęć. Przewróć kartkę
poznaw ania. K iedy ju ż zdobędziesz o d pow iednie podstaw y, w rócim y do tw orzenia by przekonać się,
co mamy na myśli...
aplikacji dla S klepu W indows.
New Project
Visual C++
m Kiedy tw orzysz nowy
projekt w V isual Z azw yczaj
Samples
ss Stu d io 2012 Express powinieneś
for Windows Desktop, nadawać projektom
m asz do wyboru lepsze nazwy niż
te opcje. Wybierz „Rozdział 2 -
Windows Forms Program 4“, jednak
Application. celowo użyliśm y
nazwy zawierającej
Name Rozdział 2 - Program 4 znaki odstępu
Locatior c:\users\p8r\documents\visual studio 2012\Projects
i myślnik, żebyś
mógł przekonać
Solutior Rozdział 2 - Program 4 [s/l Create directory fo r solution
I I Add to source control
s ię ja k IDE określi
p rzestrzeń nazw
dia tego programu.
130 Rozdział 2.
To tylko kod
Properties ▼ □ X
Jeśli używasz Visual Studio 2012 Express
Form l System.Windows.Forms.Form ▼
Edition, to będziesz musiał zainstalowaćjego
dwie wersje. Do tworzenia aplikacji dla Sklepu
i» ]? * \ & \ f *
Windows używałeś Visual Studio 2012 for
RightToLeft No A
Windows 8. Terazjednak będziesz potrzebował
RightT oLeftLayout False
| Text 1 Moja aplikacja Windows Visual Studio 2012 for Windows Desktop.
1 UseWaitCursor False Na szczęście obie te wersje Visual Studio można
bezpłatnie pobrać z witryny firmy Microsoft.
T oolbtM
M ożesz tak że uniem ożliw ić chow anie tego okna, klikając ikonę pinezki
(E3) w idoczną w jego praw ym górnym rogu. M ożesz też przeciągnąć
całe o k n o Toolbox, ta k że będzie cały czas w idoczne p o n a d oknem
całego ID E .
Z m ień w artość właściwości T e x t k o n tro le k CheckBox o raz Label tak , by pasow ały do zrzutów e k ran u
przedstaw ionych n a n astęp n ej stro n ie; oprócz tego ustaw właściwość C hecked k o n tro lk i CheckBox n a w arto ść T rue.
N astęp n ie zaznacz k o n tro lk ę Label i ustaw jej w łaściwość T e x tA lig n n a w artość M id d le C e n te r. U żyw ając okna
Properties, o k reśl nazwy k o n tro lek . Przyciskowi nadaj nazw ę c h a n g e T e x t,
p o lu w yboru nazw ę e n ab le C h e c k b o x , a etykiecie lab elT o C h an g e .
Przyjrzyj się dokład n ie kodow i p rzed staw io n em u u dołu strony,
by p rzek o n ać się, w jaki sposób te nazwy są używ ane w kodzie.
I :: - :: s -
* i; R o z d z i a ł 2 P r o g r a m 4 ,F o r m 1 * c h a n g e T e ) it_ C f tc k (o b je c t s e n d e r , E v e n tA r g s e) ___ -
132 Rozdział 2.
To tylko kod
Kliknij pole
wyboru, by
włączyć lub
wyłączyć
Kiedy j e s t włączona opcja zm ienianie
zmieniania ety k ie ty i prezen tu je etyk iety.
ona te k s t „Z lewej“ lub
„Z prawej“ zależnie od te g0i
po której stronie okna z o s ta ć
w yświetlona. Po w yłcą:zenis
opcji etyk ieta prezentuje
stosow ny te k st, wyśrodkowany
w oknie programu.
Zaostrz ołówek
Uzupełnij komentarze tak, aby opisywały one poszczególne wiersze
kodu C#, których dotyczą. Uzupełniliśmy pierwsze pole za Ciebie.
Czy potrafisz odgadnąć, co powinno się znaleźć w ostatnim opisie?
namespace SomeNamespace
{
c la s s MyClass {
p u b lic s t a t i c v oid DoSomething() {
MessageBox.Show("To j e s t wiadom ość.");
}
}
} A oto podpowiedź: jeszcze nie używałeś klasy MessageBox, lecz korzysta
z niej bardzo wiele klasycznych aplikacji Windows. Analogicznie do wielu
innych klas i metod, także ona ma sensowne nazwy.
using System; i
using System.Collections.Generic; IDE wygenerowało tę przestrzeń naziu na
p o s ta w ie nazwy projektu. N a d a jm y
using System.Linq; ^Rozdzial 2 - Program 4", a z atem to dlate.go .IDE
using System.Threading.Task; ^ g e n e ro w a ło ta ką przestrzeń ^ ¿ J j T m a S M k
wybraliśmy nazw ę zaw ierają cą o d stępy i myślnik'
using System.Windows.Forms;A by pokazać, że IDE zam ienia te znaki iw
podkreślenia.
namespace Rozdzial 2 Program 4
W iersze, które rozpoczynają s ię od dwóch lub
static class Program w iększej liczby ukośników, s ą traktowane jako
komentarze i mogą być wstaw iane, gdzie tylko
{
I I I <summary> ir chcesz. Ukośniki nakazują C# zignorować je.
Deklai-uję!
'U
Pamiętaj , _to dop iero początkowy etap
Pierwszy wiersz każdej klasy i metody p oznawania kodu. Z anim jednak przejdziem y
nazywamy deklaracją. daiej, m u sisz wiedzieć, na co patrzysz.
134 Rozdział 2.
To tylko kod
T w orzenie klasycznych aplikacji W indow s m a swoje tajniki. Z ajm iesz się nim i n a kilku następnych
stronach, dzięki czem u będziesz w iedział, co dzieje się za kulisam i. N iem niej je d n a k znaczna część
p racy n ad aplikacjam i tego typu b ędzie w ykonyw ana p o p rzez przeciąg an ie k o n tro le k z o k n a T oolbox
i um ieszczanie ich n a form ularzu.
P rzestrzeń nazw zaw iera klasy, a te zaw ierają m etody. W ew nątrz Każdy program C#
każdej z tych ostatn ich zn ajduje się zb ió r instrukcji. W tym
p ro g ram ie instrukcje odpow iedzialne są za u ru ch am ian ie musi mieć dokładnie
fo rm ularza listy kontaktow ej. Ju ż wiesz, że ro lą m e to d jest
w ykonywanie czynności — k ażd a coś robi.
jedną metodę Main().
Jest ona punktem
KAŻDA APLIKACJA POSIADA SPECYFICZNĄ wejścia w Twoim
METODĘ N A ZYW A N Ą PUNKTEM WEJŚCIA.
K ażda aplikacja C # musi m ieć d okładnie je d n ą m etodę
kodzie.
o nazw ie Main. P om im o tego, że w skład Tw ojego p ro g ram u
m oże w chodzić w iele m eto d , tylko je d n a z nich m oże być Kiedy uruchamiasz
w ykonyw ana jak o pierw sza. Je st to w łaśnie m e to d a Main. C #
spraw dza wszystkie klasy w Tw oim kodzie w poszukiw aniu swój kod, instrukcje
m etody zadeklarow anej jak o s t a t i c v o i d M a i n ( ) . Później,
podczas u ru ch am ian ia p ro g ram u , w ykonyw ana je st pierw sza
w metodzie Main()
instrukcja w tej m eto d zie, a w dalszej kolejności instrukcje wykonywane są jako
um ieszczone poniżej.
PIERWSZE.
jesteś tutaj ► 135
Klasowe rzeczy
r
klasie została u m ieszczona m e to d a p u n k tu wyjścia ani co robi. W jej
d z ia ła n iu n ie m a n iczeg o ta je m n ic z e g o a n i m ag ic zn e g o , p o d o b n ie ja k Z r ó b to !
w d z ia ła n iu sam ej aplik acji. M o ż esz to so b ie u d o w o d n ić , z m ie n iają c
p u n k t w ejścia p ro g ra m u .
D odaj now ą m e to d ę do klasy AnotherClass , w pisując ją p om iędzy naw iasam i klam row ym i:
c l a s s Anot herCl as s
{
MeSsageBox to klasa um ieszczona
p u b l i c s t a t i c voi d Main()
w przestrzeni nazw S y s tem .Windows-f° rm s.
To dlatego m usiałeś dodać w iersz u s 'ng —v {
w punkcie 3. SShow () to met
h ow() m e M a , która Je s t
częścią klasy M essageBo*. MessageBox.Show("Bum!")
}
}
C# zwraca uwagę na wielkość lite r! Upewnij się, że wielkość lite r w Twoim kodzie
jest dokładnie taka sama ja k w przykładzie.
136 Rozdział 2.
To tylko kod
Co się stało ?
Z am ia st u ru ch o m ien ia aplikacji, k tó rą napisałeś, pojaw iło się
o k ienko z k o m u n ik atem . Pisząc m e to d ę M a i n ( ) , wyposażyłeś
p ro g ram w now y p u n k t w ejścia. T e ra z pierw szą rzeczą, k tó rą robi,
je st w ykonanie instrukcji w tej m eto d zie — oznacza to , że zostanie
w ykonana instru k cja Me s sa g e B o x . Sh o w( ) . N ie m a w tej m etodzie
nic innego, zatem kliknięcie przycisku O K spow oduje w yczerpanie
się listy instrukcji i zakończenie p ro g ram u .
Wymyśl sposób n a nap raw ien ie p ro g ram u , aby znów wyświetlał się Podpowiedź: wys t arczy ,
form ularz. że zm ien isz dwa w iersze
„Zaostrz ołówek
Rozwiązanie
Uzupełnij komentarze tak, aby opisywały one poszczególne wiersze
kodu C#. Uzupełniliśmy pierwsze pole za Ciebie.
}
To j e s t instrukcja .
} Jej wykonanie
spow oduje w yśw ietlenie
} n iewielkiego okienka
z komunikatem.
Kliknij + p o lewej stronie wiersza, aby rozw inąć kod. Przew iń ek ran w dół i odszukaj następujący fragm ent:
// J eśli dwukrotnie klikniesz Forml.resx w oknie Solution Explorer, zobaczysz
'' zaimportowany obrazek. IDE zaimportowało go i nadało mu nazwę „pictureBoxl.Image"
/ / pictureBoxl — poniżej przedstaw iliśm y kod, który zo s ta ł wygenerowany w celu wczytania
// i wyśw ietlenia tego obrazka w kontrolce PictureBox.
t h i s .p i c tu re B o x l .I m a g e = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image")));
t h i s . p i c t u r e B o x l . L o c a t i o n = new S y s t e m. D r a w i n g . P o i n t ( 2 7 6 , 28);
Nie zwracaj uwagi na to,
this.pictureBoxl.Name = "pictureBoxl"; że liczby w wierszach b ize
t h i s . p i c t u r e B o x l . S i z e = new S y s t e m . D r a w i n g . S i z e ( l 0 0 , 50 ) ; i Location Twojego k° duQ ,
s ą nieco inne od tych. B ędą
t h i s . p i c t u r e B o x l . T a b l n d e x = 0; s ię one zmieniać zaleznie od
miejsca, w którym umieści feś
this.pictureBoxl.TabStop = false;
kontrolką PictureBox.
138 Rozdział 2.
To tylko kod
Zawsze jest ła tw ie j modyfikować kod generowany przez narzędzie Form Designer przy użyciu IDE,
jednak w szystkie zm iany wprowadzane w ten sposób i ta k stają się w efekcie zmianami w kodzie
źródłowym programu.
iNie .istnieją. .
- głupie pytania -------------------------
P: Nie do końca rozumiem, czym jest punkt wejścia. pogrupowane i należą do różnych klas. A zatem skąd wiadomo,
od której instrukcji zacząć wykonywanie programu?
Czy możecie mi to jeszcze raz wytłumaczyć?
Właśnie do tego celu służy punkt wejścia do programu. Kompilator
O: Twój program składa się z bardzo wielu instrukcji, jednak nie skompilowałby kodu programu, gdyby nie było w nim
nie wszystkie są wykonywane jednocześnie. Realizacja dokładnie jednej metody M a in ( ), to właśnie ona jest nazywana
programu rozpoczyna się od jego pierwszej instrukcji, która jest punktem wejścia. Działanie programu rozpoczyna się
wykonywana, następnie wykonywana jest druga instrukcja, od wykonania pierwszej instrukcji metody M a in ( ).
potem trzecia i tak dalej. Wszystkie te instrukcje są zazwyczaj
Tworzenie animacji w klasycznych aplikacjach Windows jest znacznie trudniejsze niż w aplikacjach dla Sklepu
Windows. Aby się o tym przekonać, zróbmy coś błyskotliw eg o! Zacznij od utworzenia w IDE nowego
Ćwi czenie projektu typu W ind ow s Forms A pplication.
A a -t'-
wyglądu formularza, sprawdzenie Z J ■ J odśw ieżenie V om M m ó ' 5* ’
czegoś m yszką, itd. Spróbuj go ^ s u n a ó ^ nfa klikn^
co S ię Stanie. Wygląd formularza n Z h J ? r09rarnu <zobacz, „ p po® * j ak
program, za m ia st odpowiadać na z d n ^ zrnienic,t- gdyż
zakończenie pętli. ° zdarzeń,a, będzie czeka t na
^ ZW OLNIJ NIECO.
Z m niejsz nieco te m p o błyskania, do d ając taki oto w iersz
po A p p l i c a t i o n . D o E v e n t s ( ) :
System.Threading.Thread.Sleep(3);
Ta instrukcja P ° ^ J % ^ S u n d y .
O
’ X & . & v s s s 'j S '
140 Rozdział 2.
To tylko kod
Uruchom swoją aplikację w IDE i rozpocznij wykonywanie pętli. Teraz zamknij okno.
Poczekaj minutę — IDE nie przechodzi w tryb edycji kodu! Zachowuje się tak, jakby
program cały czas działał. Musisz zatrzymać go, klikając przycisk stop z kwadratem
(lub wybrać opcję Stop Debugging z menu DEBUG).
6) ZATRZYMAJ GO.
Spraw , aby p ę tla d o d a n a w p u n k cie 5. kończyła się p o zam knięciu
p rogram u . Z m ień zew nętrzną p ę tlę n a następującą:
/
Podczas pracy z formularzem
lub kontrolką wartość Visible
decyduje o tym, czy s ą one
widoczne. Jeśli u sta w isz tę Czy domyślasz się, co może powodować takie opóźnienie?
wtaściwość na false, kontrolka Czy możesz poprawić program w ta k i sposób, aby kończył
lub formularz znikną.
się natychmiast po zamknięciu okna?
\
p r i v a te void b u tto n l C lic k (o b je c t sender, EventArgs e ) M
/ whi l e (Visible) {
Zew nętrzna
pęt/a cbiata, for (int c = 0; c <= 2 5 4 && V i s i b l e ; c++)l {
dopóki formu/arz
je s t w
Zaraz t h i s . B a c k C o l o r = Colo r.From A rgb(c, 255 - c , c);
z amknięciu
właści A pplication.D oEvents();
V isib/e zm ienia Pierw sza pętla zmienia
wartość na fa/se kolory w jedną stronę, druga
i pęt/a System .Threading.Thread.Sleep(3); odwraca kolejność. Dzięki temu
p rzestaje być animacj a j e s t płynna.
wykon
}
Z astosow
wyrażenie for (int c = 254; c >= 0 && V i s i b l e ; c--) {
Visible, a nie
&& V isib t h i s . B a c k C o l o r = Colo r.From A rgb(c, 255 - c , c);
true. To taka
różnica, j
za m ia st p A pplication.D oEvents(); Naprawiliśmy problem dodatkowego
„Czy to widać?“ opóźnienia za pomocą o p i o r ą
dzięki któremu w każdej p ętli for
zadać py System .Threading.Thread.Sleep(3);
„Czy to prawda, sprawdzana j e s t wtaściw ość y i^ b to .
że to je s W ten sposób pętla isończy s ^ zriraz
widoczne } gdy wtaściwość ta p rzyjm ą wartość fa lse .
w obu przypadkach
znaczeni } Czy domyślasz się, co może powodować takie opóźnienie?
takie samo.
Czy możesz poprawić program w ta k i sposób, aby kończył
} się natychmiast po zamknięciu okna?
Czy Twój
i kod ubył
y i nieco iinny
n n y niż nasz? Istnieje więcej
v v i ę v _ c | niż jeden sposób na
iii
142 Rozdział 2.
3. Obiekty: zorientuj się!
PRZEJADĘ PRZEZ
MOST PONIATOWSKIEGO, POTEM
SKIERUJĘ SIĘ N A ULICĘ WOLNOŚCI
I PRZEJADĘ PRZEZ ALEJĘ LOTNIKÓW.
Maciek wyzn a czyt cel
■' znalazł drogę.
144 Rozdział 3.
Obiekty: zorientuj się!
S y s tem nawigacyjny
pobiera dodatkową
informację o miejscach
których m usi unikać.
ModifyRouteToAvoid("ulica W olności");
string route;
route = GetRouteQ ;
M aciek zaprojektow ał klasę naw ig ato ra w taki sposób, aby łatw o było tworzyć i m odyfikow ać
trasy. A by p o b rać drogę, M aciek wywołuje m e to d ę S e t D e s t i n a t i o n ( ) służącą do u stalan ia
celu, a n astęp n ie używa m eto d y G e t R o u t e ( ) do uzyskania trasy w po staci tekstow ej. Jeśli
p o trzeb u je ją zm ienić, jego p ro g ram wywołuje m e to d ę Mo d i f y Ro u t e To Av o i d ( ) i M aciek
p o d aje m u ulicę, k tó rą trze b a om inąć. P o tem m e to d a G e t R o u t e ( ) je st w ykonywana Maciek tak dobrał nazwy
metod, aby miały one sen s
ponow nie i p o b ie ra n a jest now a trasa. dla kogoś, kto będzie chciał
korzystać z jego klas y do
public class Navigator { nawigacji po m ieście.
p u b l i c i n t M u l t i p l y T wo N u m b e r s ( i n t f i r s t N u m b e r , i n t secondNumber) {
i n t r e s u l t = f i r s t N u m b e r * secondNumber; Ta instrukcja return przekazuje
return result; wartość z powrotem do instrukcji
która wywołała metodę.
}
W wywołaniu metody można
A tu jest w yrażenie, k tó re wywołuje m eto d ę do m n o żen ia dwóch liczb. Z w raca ona przekazać liczby 3 1 5 . Można
w artość typu int: także przekazywać do m ej
w artości, używając zmiennych.
i n t my Re s u l t = Mu l t i pl y T wo N u mb e r s( 3 , 5 ) ;
146 Rozdział 3.
Obiekty: zorientuj się!
CELNE SPOSTRZEŻENIA
■ Klasy zawierają metody, a te z kolei zawierają instrukcje wykonujące pewne operacje. Dobierając sensowne i opisowe nazwy
metod, możesz tworzyć klasy, które będą łatwe w użyciu.
■ Niektóre metody mają określony ty p w a rto ści w y n ik o w e j . Jest on określany w deklaracji metody. Na przykład metoda,
której deklaracja zaczyna się od p u b l i c i n t , zwraca wartość całkowitą typu i n t . A to przykład instrukcji zwracającej
wartość całkowitą: r e t u r n 37.
■ Jeśli w deklaracji metody określono jakiś typ wartości wynikowej, to musi ona zawierać instrukcję r e t u r n , a co więcej
- instrukcja ta musi zwracać wartość, której typ odpowiada typowi użytemu w deklaracji. A zatem jeśli deklaracja
metody zaczyna się od p u b l i c s t r i n g , to w kodzie metody będziesz musiał umieścić instrukcję r e t u r n zwracającą
łańcuch znaków.
■ Nie wszystkie metody muszą zawierać określenie typu wartości wynikowej. Metoda, której deklaracja zaczyna się
od p u b l i c v o id , niczego nie zwraca. W takim przypadku także można użyć instrukcji r e t u r n , by bezzwłocznie
zakończyć metodę; oto przykład: i f ( c h c e S k o n c z y c ) { r e t u r n ; }.
U tw órz w ID E nowy p ro jek t W indow s F o rm s A p plication. N a stęp n ie dodaj do niego p lik klasy i nazwij go
T a l k e r . c s . W tym celu kliknij praw ym przyciskiem myszy p ro je k t w o knie Solution Explorer i w ybierz Class...
z m en u A d d . P o tym kiedy w piszesz nazw ę „T alker.cs”, ID E autom atycznie utw orzy nowy plik o p o d an ej nazwie,
um ieści w nim początkow y k o d klasy T a l k e r i otw orzy go w nowej zakładce.
c la s s Talker {
p u b lic s ta tic in t BlahBlahBlah(string thingToSay, in t numberOfTimes) {
W tym m iejscu
deklarujemy s trin g fin a lS trin g =
z mienną finalString fo r ( in t count = 1; count <= numberOfTimes; count++) {
i ustaw iam y
je j wartość fin a lS tr in g = fin a lS tr in g + thingToSay + M\n ";
Ten w iersz kodu dodaje do
początkową na
} tańcucha znakóiw przechowywanego
p u sty łańcuch
w zm iennej f ina/String zaw artość
znaków. MessageBox.Show(finalString); zm iennej thing To S a y oraz znak
return fin a lS trin g .L e n g th ; nowego w iersza.
+
Co właśnie stworzyłeś
N ow a klasa zaw iera je d n ą m e to d ę B l a h B l a h B l a h ( ) , k tó ra p o b ie ra dwa param etry.
*
A by w yłączyć
Pierwszy je st typu s t r i n g i decyduje o tym , co zostanie pow iedziane. D ru g i określa, ile
przycisk m inim alizacji
razy trzeb a to pow iedzieć. W m o m en cie w ywołania w yśw ietlane je st o k n o MessageBox i m aksym alizacji
z w iadom ością p o w tó rzo n ą o k reślo n ą liczbę razy. W arto ścią wynikow ą jest długość fo rm u la rza , przypisz
w yśw ietlanego tekstu. M eto d a w ym aga p o d a n ia w artości tekstow ej dla p a ra m e tru w artość False jego
t h i n g T o S a y i liczby całkow itej jak o p a ra m e tru number Of Ti mes. O b a b ę d ą p o b ieran e właściw ościom
z form ularza. W ykorzystam y k o n tro lk ę TextBox, k tó ra pozw ala n a w pisanie tekstu, M axim izeBox oraz
o raz k o n trolkę NumericUpDown do u sta le n ia liczby pow tórzeń. M inim izeB ox.
N astęp n ie kliknij dw ukrotnie m yszką przycisk i wpisz poniższy kod, który wywoła m etodę
B l a h B l a h B l a h ( ) i zapisze zw róconą przez n ią w artość w zm iennej l e n : To j e s t kontrolka
NtxmericUpDown. Ustaw
p r i v a t e voi d b u t t o n l _ C l i c k ( o b j e c t s e n d e r , EventArgs e) je j wtaściw ość Minimum
na h Maximum na 10,
{ natom ia st Value na 3.
i n t l en = T a l k e r . B l a h B l a h B l a h ( t e x t B o x l . T e x t , ( i n t ) numericllpDownl.Value);
MessageBox.Show("Długość wiadomości t o " + l e n ) ;
T eraz uru ch o m swój program ! Kliknij przycisk i przyjrzyj się dwóm Długość wynosi 21, gdyż
wyświetlonym okien k o m MessageBox. Pierw sze z nich, zaw ierające tekst, „Wit aj!" ma długość 6 znaków,
\ n to kolejny jeden znak;
zostało w yśw ietlone p rzez klasę, n a to m ia st drugie — przez form ularz. to w su m ie daje 7x3 = 21.-
Kiedy metoda
z wraca wartość,
Metoda BlahBlahBlah() formularz wyśw ietla
wyśw ietla to okno takie oto okno O *
M essageBox na p odstawie z komunikatem.
przekazanych jej
parametrów.
148 Rozdział 3.
Obiekty: zorientuj się!
TO BY BYŁO GENIALNE,
Maciek ma pewien pomysł GDYBYM MÓGŁ
PORÓWNYWAĆ KILKA TRAS
R ozm ow a kw alifikacyjna poszła świetnie! P oranny
I ROZSTRZYGAĆ, KTÓRA
k o rek zm usił je d n a k M aćka do zastanow ienia się
JEST NAJSZYBSZA.
n ad ulep szeniem naw igatora.
i
SetCurrentLocation()
N a v ig a to r 2
SetDestination()
ModifyRouteToAvoid() SetCurrentLocation()
N a v ig a to r 3
ModifyRouteToInclude() SetDestination()
GetRoute() ModifyRouteToAvoid() SetCurrentLocation()
GetTimeToDestination() ModifyRouteToInclude() SetDestination()
TotalDistance() GetRoute() ModifyRouteToAvoid()
GetTimeToDestination() ModifyRouteToInclude()
TotalDistance() GetRoute()
GetTimeToDestination()
TotalDistance()
2 L -
Teraz możesz użyć obiektu! Gdy tw orzysz
obiekt jakiejś klasy, ma on wszystkie je j metody.
150 Rozdział 3.
Obiekty: zorientuj się!
H o u se
Przed'. To j e s t obraz
pam ięci komputera
na początku programu.
Twój program
wykonuje
r
instrukcję new.
H ouse m a p l e D r i v e 1 1 5 = n e w House();
152 Rozdział 3.
Obiekty: zorientuj się!
GUI j e s t skrótem
Lepsze rozwiązanie... uzyskane dzięki obiektom! ° d Graphical User Interface
i oznacza graficzny
M aciek nap isał nowy p ro g ram porów nujący trasy, k tóry używa obiektów do interfejs użytkownika.
J e s t to w szystko, co
odnalezienia najkrótszej sp o śró d trzech dró g prow adzących do tego sam ego celu. tw orzys z podczas budowania
O to sposób, w jaki zbudow ał swoją aplikację. formularza w projektancie
formularzy.
M aciek utw orzył G U I z p o lem tekstow ym t e x t B o x l , k tó re zaw iera cel wszystkich trzech tras.
r N astęp n ie d o d ał ele m e n t t e x t B o x 2 zaw ierający ulicę, k tó rą je d n a z w yznaczonych tras m a omijać .
W staw ił także p o le t e x t B o x 3 z nazw ą ulicy, k tó rą trzecia z w yznaczonych tra s m usi zawierać .
SetCurrentl_ocation() _____)
SetDestination()
ModifyRouteToAvoid()
ModifyRouteToInclude() string destination = textB oxl.T ext;
GetRoute()
GetTimeToDestination() N a v i g a t o r n a v ig a to r l = new N a v i g a t o r Q ;
TotalDistance()
n a v ig a to r l . S e t D e s t i n a t i o n ( d e s t i n a t i o n ) ;
string r o u t e = n a v ig a to r l . G e t R o u t e Q ;
Za każdy razem,
gdy tworzysz
nowy obiekt
na podstawie
T eraz M aciek m oże wywołać m eto d ę T o t a l D i s t a n c e ( ) każdego klasy, nazywamy
obiektu, by znaleźć n ajk ró tszą trasę. N ajw ażniejsze jest to , że m usiał
napisać kod nie trzy razy, a tylko raz! to tworzeniem
instancji klasy.
jesteś tutaj ► 153
Kilka tajem nych porad „rusz g ło w ą "
Teoria i praktyka
S koro już m ow a o w zorcach, to zaraz przedstaw im y w zorzec, k tóry b ędzie w ielokrotnie
używany w tej książce. N ow e pojęcia lub koncepcje (takie ja k obiekty) będziem y opisywali
n a kilku kolejnych stro n ach , d em o n stru jąc je przy użyciu rysunków i niew ielkich fragm entów
kodu. D zięki tem u będziesz m iał możliw ość cofnąć się i spróbow ać zrozum ieć, o co chodzi,
bez konieczności zap rzątan ia sobie głowy p isaniem i u ru ch am ian iem program u.
Zaostrz ołówek
Przejdź przez te same etapy, przez które musiał przejść Maciek
podczas pisania kodu tworzącego klasę N a v ig a to r i jej metody
r — — — — — — — — — — — — — — — — — i
1. Stwórz obiekt navigator2, ustaw jego cel, wywołaj jego metodę M odifyR outeT oA void() Iuzyj metody
| TotalD istance() do ustawienia zmiennej całkowitej distance2. |
I navigator2........................................................... I
I navigator2........................................................... I
*~2. Stwórz obiekt navigator3, ustaw jego cel, wywołaj jego metodę M odifyR outeToInclude() i uzyj metody
I TotalD istance() do ustawienia zmiennej całkowitej distance3. |
in t s h o r t e s t D is t a n c e = M a t h . M in ( d i s t a n c e l , M a t h . M in ( d is t a n c e 2 , d is ta n c e 3 ) ) ;
Zaostrz ołówek
V
Sb- Z .U U 91I
Przejdź przez te same etapy, przez które musiał przejść Maciek
Rozwiązanie podczas pisania kodu tworzącego klasę N a v ig a to r i jej metody.
s tr in g d e s t in a t io n = t e x t B o x l. T e x t ;
P n a d su m U śm y tu ta j początek
s tr in g ro u te 2 S tr e e tT o A v o id = te x tB o x 2 .T e x t; za dania. Tak wy g ląda kod, który
nap isat M aciek, by pobrać nazwę
s tr in g ro u te 3 S tr e e tT o In c lu d e = te x tB o x 3 .T e x t; celu i ulicy z pól tekstow ych.
1. Stwórz obiekt navigator2, ustaw jego cel, wywołaj jego metodę M odifyR outeT oA void() I użyj metody
TotalD istance() do ustawienia zmiennej całkowitej distance2.
navigator2. SetDestination(destination);
navigator2. ModifyRouteToAvoid(route2StreetToAvoid);
2. Stwórz obiekt navigator3, ustaw jego cel, wywołaj jego metodę M odifyR outeToInclude() I użyj metody
TotalD istance() do ustawienia zmiennej całkowitej distance3.
navigator3.SetDestination (destination);
Wbudowana matada C # ^
z nich. Maciek u żył je) do ndnaiezierna najkrótszej g i
156 Rozdział 3.
Obiekty: zorientuj się!
Ii Nie.istnieią.
Nie .istniej .
głupie pytania
P: : Kiedy myślę o czymś „static” , wydaje mi się, P : Do czego są mi potrzebne metody, które wymagają
że jest to coś, co się nie zmienia. Czy to oznacza, utworzenia instancji? Dlaczego nie mogę zrobić samych
że metody bez static mogą się zmieniać, a te ze static metod statycznych?
nie mogą? Czy zachowują się one inaczej?
O : Ponieważ gdy posiadasz obiekty przechowujące pewne
O : Nie. Zarówno metody zadeklarowane ze słowem dane — także jak instancje klasy N a v ig a to r Maćka
kluczowym static, jak i te bez takiej deklaracji zachowują się przechowujące dane dotyczące różnych tras — możesz używać
dokładnie tak samo. Jedyna różnica polega na tym, że te metod instancji do pracy z nimi. Tak więc gdy Maciek wywołuje
pierwsze nie potrzebują instancji klasy, natomiast metody bez metodę M o d ify R o u te T o A v o id () instancji n a v ig a to r2 ,
static jej potrzebują. Wielu ludzi ma problemy z zapamiętaniem to zmiany dotyczą tylko trasy umieszczonej właśnie w niej.
tego, ponieważ słowo „static" nie jest dobrane intuicyjnie w tym Nie ma to wpływu na dane w obiektach n a v ig a t o r l
kontekście. i n a v ig a to r3 . To właśnie w ten sposób mogliśmy pracować
z trzema różnymi trasami w tym samym czasie i program mógł
P : Czy w takim razie nie mogę używać klasy, przechowywać jednocześnie wszystkie dane.
dopóki nie utworzę instancji obiektu?
metody, które nie są statyczne, ich użycie wymaga O : Przewróć stronę i sam się o tym przekonaj.
wcześniejszego utworzenia instancji tej klasy.
To wtaśnie tutaj
diagram klasy
pokazuje pola..
Każda instancja
klasy przechowuje Dodaj tę linię,
tu sw ój stan. aby oddzielić pola
od metod.
158 Rozdział 3.
Obiekty: zorientuj się!
Zaostrz ołówek
Clown a n o t h e r C l o w n = new C l o w n ( ) ;
anotherClown.Name = " B i f f " ;
a n o th erC lo w n.H eig ht = 40;
anotherC low n.T alkA boutY ourself(); „Nazywam s i ę ___________im a m ____________centym etrów w zrostu.”
Clown c l o w n 3 = new C l o w n ( ) ;
c l o w n 3 . N a me = a n o t h e r C l o w n . N a m e ;
clow n 3.H eigh t = oneClown.H eight - 3;
clow n3.T alkA boutY ourself(); „Nazywam s i ę ___________im a m ____________centym etrów w zrostu.”
a n o t h e r C l o w n . H e i g h t *= 2 ;
anotherC low n.T alkA boutY ourself(); „Nazywam s i ę ___________ im a m ____________centym etrów w zrostu.”
Dzięki za pamięć
G dy Twój p rog ram tw orzy obiekty, są o n e przechow yw ane
w specjalnym obszarze pam ięci k o m p u te ra zwanym stertą .
G dy tylko Twój kod utw orzy o b iek t za p o m o cą w yrażenia new,
C # natychm iast rezerw uje n a stercie p rzestrzeń n iezb ę d n ą do
przechow yw ania jego danych.
To j e s t ilustracja s te r ty przed uruchomieniem
projektu. Z auw aż, że j e s t ona. piasfo.
ę. Naostrz ołówek
Rozwiązanie' Uzupełnij treść każdego komunikatu wyświetlanego w oknie
MessageBox po wywołaniu wyróżnionej instrukcji.
Clown a n o t h e r C l o w n
anotherClown.Name = " B i t t " ;
a n o t h e r C l o w n . H e i g h t = 40;
anotherC low n.T alkA boutY ourself();
N azywam się Bi'ff i m am 40 centym etrów w zro stu .”
I"
Clown c l o w n 3 = ( n e w C l o w n ( ) ;
c l o w n 3 . Na me = a n o t n e r u o w n T N a m e ;
clow n 3.H eig ht = oneClown.Height - 3;
clow n3.T alkA boutY ourself();
„Nazywam się B iff i m am 28 centym etrów w zro stu .”
a n o t h e r C l o w n . H e i g h t *= 2 ;
anotherC low n.T alkA boutY ourself(); B iff 80
„Nazywam się i m am _centym etrów w zro stu .”
160 Rozdział 3.
Obiekty: zorientuj się!
Ten obiekt j e s t instancją
klasy Clown.
Co Twój program ma na myśli
N ow a instancja klasy Clown w Tw oim p ro g ram ie tw orzona jest
w następujący sposób:
Clown myl ns t an c e = new Clown();
Są to w zasadzie dwie instrukcje p o łączo n e w jed n ą. Pierw sza z nich
d ek laruje zm ienną typu Clown (Clown m y l n s t a n c e ; ), a druga
tw orzy nowy o b iek t i przypisuje go do w łaśnie utw orzonej zm iennej
(m y l n s t a n c e = new C l o w n ( ) ; ). W ygląd sterty p o każdej z tych
instrukcji będzie przed staw iał się następująco:
oneClown.TalkAboutYourselfO;
° 6 /e k tC \° '
Clown a n o t h e r C l o w n = new C l o w n ( ) ;
anotherClown.Name = " B i f f " ; Te . instrukcje tw orzą drug i
obie k t i wypełniają go
an oth erC lo w n .H e ig h t = 40; danymi.
anotherClown.TalkAboutYourself();
Clown c l o w n 3 = new C l o w n ( ) ;
c l o w n 3 . N a me = a n o t h e r C l o w n . N a m e ;
clow n 3.H eig ht = oneClown.Height
N astępnie tworzony '/e /c t CN0
clown3.TalkAboutYourself();
i uzupetniany j e s t
trzeci obiekt.
a n o t h e r C l o w n . H e i g h t *= 2 ;
anotherClown.TalkAboutYourself();
Popatrz przez chwilę na ten kod. Czy jesteś w stanie odgadnąć, do czego on służy?
T akie instrukcje n ie d ają Ci żadnej w skazówki odn o śn ie do tego, co p ro g ram ro b i i w jaki sposób. W tym
przypadku p ro g ram ista był n iezm iern ie zadow olony z wyników, bo wszystko u d ało m u się zaw rzeć w jed n ej
m etodzie. T rzeb a je d n a k w iedzieć, że m aksym alizacja zwięzłości k o d u ta k n ap raw d ę nie je st użyteczna! Podzielm y
zatem ten frag m en t n a kilka m eto d , aby ułatw ić jego późniejszą analizę, i zadbajm y przy tym , by klasy i m etody
otrzym ały bardziej sensow ne nazwy. N ajpierw je d n a k sp róbujem y dow iedzieć się, co k o d pow inien robić.
162 Rozdział 3.
Obiekty: zorientuj się!
T a stro n a z instrukcji znacznie ułatw ia zrozum ienie kodu. D aje nam dodatkow o w spaniałe wskazówki,
ja k popraw ić kod, by ułatw ić jego analizę. T eraz ju ż wiemy, do czego służy w aru n ek porów nujący zm ienną
t z w artością 160. W instrukcji je st n ap isan e, co oznacza te m p e ra tu ra powyżej 160°C — n u g at je st zbyt gorący.
D ochodzim y do w niosku, że mm usi być klasą k o n tro lu jącą m aszynę do pro d u k cji słodyczy. P o siad a o n a statyczne
m etody odpow iedzialne za spraw dzanie te m p eratu ry n u g atu i system u k o n tro li pow ietrza. U m ieśćm y zatem
spraw dzanie te m p e ra tu ry w osobnej m eto d zie i nadajm y klasom i m eto d o m tak ie nazwy, aby o k reślen ie ich
p rzeznaczenia było p roste.
^NougatTooHot() {
Typ wartości w ynikow y i n t temp = Ma ker . CheckNougatTemperatur e();
m etody IsNougatT°oHot- i f (temp > 160)
,N azywając klasę Maker, a je j metod. \
return
} else {
return
true;
false;
t
CheikNougatTemperature, czynim y kod
łatw iejszym do zrozum ie'n ia-
}
i W artość wynikowa tej metody
j e s t typ u Boolean, co oznacza,
ż e będzie to true lub fa lse .
3 C o specyfikacja każe zrobić w przy p ad k u p rzek ro czen ia te m p e ratu ry n u g atu ? N ak azu je p rzep ro w ad zen ie p ro ced u ry
o p ró żn ian ia system u chłodzenia izolacji b ato n ó w (SC IB ). Stw órzm y zatem in n ą m eto d ę, nadajm y oczywistą nazwę
klasie T (k tó ra ok azała się być o b iek tem kontrolującym tu rb in ę) i klasie i c s (k tó ra zaw iaduje system em chłodzenia
i p o siad a dwie m etody — do n ap ełn ia n ia i o p ró żn ian ia system u):
p u b l i ^ 7 v° i d ) DoCICSVentProcedure() {
T urbi ne t u r b i n e C o n t r o l l e r = new T u r b i n e ( ) ;
Typ zwracany void
turbineController.CloseTripValve(2);
oznacza, że metoda nie
będzie zwracała żadnej Is olationCoolingSystem.Fill();
wartości. IsolationCoolingSystem.Vent();
Maker . CheckAir System();
}
, 5^ T eraz kod jest znacznie bardziej intuicyjny! N aw et jeśli nie wiesz, że p ro c e d u ra o p ró żn ian ia SC IB m usi być
u ru ch am ian a, gdy n u g at je st za gorący, to znacznie łatwiej jest określić przeznaczenie tego kodu :
if (IsNougatTooHot() == t r u e ) {
DoCICSVentProcedure();
i
if QsN ougatTooHotQ == t r u e ) {
DoCICSVentProcedureQ;
C a n d y C o n tr o lle r
DoMaintenanceTests()
DoCICSVentProcedure()
IsNougatTooHot()
p u b l i c v o id DoCICSVentProcedureQ ...
164 Rozdział 3.
Obiekty: zorientuj się!
Zaostrz ołówek
W kodzie przedstawionym
na poprzednich stronach
m ^ m znaleźć je s zc ze
je dną klasę. W pisz tu
jej nazwę i metody.
D is h w a s h e r D is h w a s h e r
Klasa nazywa się
CleanDishes() Dishwasher, więc wszystkie CleanDishes()
AddDetergent() AddDetergent()
jej metody powinny dotyczyć
SetWaterTemperature() SetWaterTemperature()
w mycia naczyń. Jedna z nich -w
ParkTheCar()
— ParkTheCar() — nie ma z tym
jednak nic wspólnego. Powinna
zatem zostać stąd usunięta
i przeniesiona do innej klasy.
Zaostrz ołówek
_____. _______. Kod systemu kontroli wyrobu słodyczy widoczny na ^ .
- v Rozwiązanie poprzednich stronach wywołuje metody trzech innych klas. ±e Maker je s t^ k la s ^ '
Przeanalizuj go jeszcze raz, a następnie uzupełnij diagramy klas. bo pojawia s ię przed
kropką w instrukcji
M aker.CheckAirSystem (),
T u rb in e IsolationCoolingSystem Maker
Fill()
CloseTripValve() CheckNougatTemperature()
Vent()
CheckAirSystem()
166 Rozdział 3.
O biekty: zorientuj się!
Zaostrz ołówek ________________________________________________________________________
Każda z tych klas zawiera poważny błąd projektowy. Napisz, co według Ciebie
jest nieprawidłowe w każdej z nich i w jaki sposób naprawiłbyś taki błąd.
CandyBarWeight()
PrintWrapperO
GenerateReport()
Go()
D e liv e ry G irl
AddAPizza()
PizzaDelivered()
TotalCash()
ReturnTime()
Zaostrz ołówek
Oto jak naprawiliśmy klasy. Jest tylko je d e n sposób p o praw ienia
Rozwiązanie błędów , choć istnieje o g ro m n a liczba innych m ożliwości zap ro jek to w an ia
klas w zależności o d ich p rzeznaczenia.
CandyBarWeight()
Nazwa, klasy .nie. .oddawała.jej przeznaczenia.-. Programista.' . PrintWrapper()
GenerateReport()
który, zobaczy .wiersz. kodu .taki lak. .ę.lass2?.-.Go()., .nie będzie .
MakeTheCandy()
miał. pojęcia.,. co .on. robi-. .Zmieniliśmy .także nazwę .metody .
na. bardziej .opisową— .wy.bra.lis.my M.akeXhe.ę.andy()., ale .m.oż.e .
to .być także .coś innego-.............................................................
168 Rozdział 3.
Obiekty: zorientuj się!
Okno programu
if (
e2.count = e2.count + e l.c o u n t :
x = x + 1;
}
MessageBox. Show(r esul t + " L i c z n i k : " + e2.count);
}
p u b l i c c l a s s _____________ {
p u b l i c i n t ________ 0; Pytanie dodatkowe!
public strin g G dyby o statn i w iersz k o m u n ik atu
return " w it a a a j.. ." ; zaw ierał 24 zam iast 10 , to w jaki
sposób rozw iązałbyś zagadkę?
M ożesz to zrobić, zm ieniając
}
tylko je d n ą instrukcję.
Przypominamy:
każdy fragment
x < 4
z basenu może być
x x < 5 E ch o
użyty więcej niż raz!
y x > 0 T e ster
e2 x > 1 E ch o ( ) e2 = e1;
count C o u n t( ) E ch o e2;
e1 = e1 + 1;
H e llo ( ) E ch o e2 = e1;
e l = co u n t + 1 ; x == 3
E ch o e2 = new E ch o ( ); x == 4
e l.c o u n t = co u n t + 1 ;
e 1 .co u n t = e 1 .count + 1 ;
^ e / c f & S\
\
__________ jo e .R eceiv eC a sh (2 5 );_________
170 Rozdział 3.
Obiekty: zorientuj się!
namespace Your_Project_Name {
p u b l i c p a r t i a l c l a s s Forml Form
W zw iązku z tym
ż e używam y
obiektów Guy
do śledzenia I h ś ć gotówki
zmian w s tanie w banku zw iększa s ię
posiadania Boba i zm n iejsza w zależności
i Joego, m us'm y od te go, ile pieniędzy
zadeklarować p u b l i c Forml() { z o s tało przyznanych lub
zm ienne, u żywając InitializeComponent(): odebranych obiektom
typu Guy. typ u Guy.
}
172 Rozdział 3.
Obiekty: zorientuj się!
Tylko od Ciebie zależy, czy znajdziesz sposób na to, aby Joe i Bob w y s ta rto w a li z popraw nie
u zupełnionym i polam i Cash i Name. Umieść kod zaraz pod I n it ia liz e C o m p o n e n t ( ) .
Rozwiązanie
ćwiczenia
p u b l i c Forml() {
InitializeComponentQ;
To t u t a i u s ta w ia m y p ie rw sz ą ,
in s t a n c ję klasy Guy. P ie r w s z y \ bob = new Guy();
w ie r s z tw o rz y o b ie k t, d w a
p o z o s ta te w y p e łn ia ją je g o p o la . bob.Name = "Bob";
bob.Cash = 100;
P : Dlaczego rozwiązanie nie zaczyna się od „Guy bob = P A co się stanie, jeśli nie pominę tego pierwszego
:
new Guy()” ? Z jakiego powodu opuściłeś pierwsze „Guy” ? „Guy” ? Co się stanie, kiedy w kodzie metody wpiszę Guy
bob = new Guy() zamiast bob = new Guy()?
O : Ponieważ już zadeklarowałeś zmienną bob na górze
formularza. Pamiętasz, że instrukcja i n t i = 5; jest O : Będziesz miał problemy — formularz nie będzie działał,
równoważna dwóm instrukcjom: i n t i ; oraz i = 5;? Tutaj jest ponieważ nie będzie nawet w stanie ustawić zmiennej bob.
podobnie. Mógłbyś zadeklarować zmienną bob w jednej linii w ten Jeśli masz na górze formularza taki kod:
sposób: Guy bob = new G u y ();,a le posiadasz już pierwszą p u b lic p a r t i a l c l a s s Forml : Form {
część instrukcji (Guy b o b ;) w górnej części klasy formularza.
Guy bob;
Potrzebujesz więc tylko drugiej połowy wiersza, czyli części, która
a gdzieś dalej, wewnątrz metody, wpisany jest taki fragment:
ustawia zmienną bob i tworzy nową instancję klasy G u y ().
Guy bob = new G u y();
P : Dobrze, dlaczego w takim razie nie mogę usunąć znaczy to, że masz zadeklarowane dwie zmienne. Jest to trochę
wiersza „Guy bob;” na górze kodu formularza? mylące, ponieważ obie mają tę samą nazwę. Pierwsza z nich jest
jednak widoczna dla całego formularza, a druga — ta nowa, którą
O : Wtedy zmienna bob istniałaby tylko wewnątrz tej specjalnej dodałeś — obowiązuje tylko wewnątrz metody. Następny wiersz
metody p u b l i c F o r m l( ) . Kiedy deklarujesz zmienną wewnątrz (bob.Name = " B o b " ; ) modyfikuje zawartość samej zmiennej
jednej metody, nie możesz uzyskać dostępu do niej z żadnej innej. lokalnej i nie ma żadnego wpływu na zmienną formularza.
Jeśli jednak zadeklarujesz ją na zewnątrz metody, ale wewnątrz Kiedy więc spróbujesz uruchomić program, otrzymasz straszny
formularza lub innej dodanej klasy, to możliwy jest dostęp do niej komunikat o błędzie („NullReferenceException was unhandled"),
z poziomu każdej innej metody w obrębie formularza lub który oznacza, że próbowałeś użyć obiektu, zanim go utworzyłeś
tej klasy. za pomocą słowa kluczowego new.
174 Rozdział 3.
Obiekty: zorientuj się!
jio e = new G u y ( ) { C a s h = 50
BYŁOBY WSPANIALE,
GDYBYM MÓGŁ PORÓWNAĆ KILKA
TRAS I SPRAWDZIĆ, KTÓRA JEST
NAJKRÓTSZA...
\
* Jakich przedmiotów z życia codziennego będzie używał Twój program?
A plikacja pom ag ająca pracow nikow i zoo przy plan o w an iu k arm ien ia m ogłaby zaw ierać
klasy dla różnych typów je d ze n ia i różnych gatunków zwierząt.
^
* Używaj opisowych nazw klas i metod.
K toś patrzący n a nazwy klas i m e to d pow inien być w stan ie odgadnąć,
jakie jest ich przeznaczenie.
B lo c k e d R o a d
Name C lo s e d R o a d
Duration StreetName
ReasonltsClosed
FindDetourO
CalculateDelay()
176 Rozdział 3.
Obiekty: zorientuj się!
p u b l i c Forml() {
InitializeComponent();
bob = new Guy() { Cash = 100, Name = "Bob" };
joe = new Guy() { Cash = 50, Name = "Joe" };
UpdateFormQ;
p u b l i c v o i d Up d a t e F o r mQ {
joesCashLabel.T ext = j oe . Na me + " ma " + j o e . C a s h + zV ' A b y Joe dał
bobsCashLabel.Text = bob.Name + " ma " + b o b . C a s h + zT" pieniądze Bobowi,
bankCashLabel.Text = "Bank ma " + bank + " z l " ; wywołujemy metodę
GiveCash() obiektu
jo e i przekazujem y
je j wynik do metody
p r i v a t e v o i d b u t t o n l _ C l i c k ( o b j e c t s e n d e r , Ev e n t Ar g s e) {
ReceiveCash() Boba.
i f (bank >= 10) {
bank -= j o e . R e c e i v e C a s h ( l O ) ;
UpdateFormQ;
} else {
MessageBox. Show("Bank n i e p o s i a d a t a k i e j i l o ś c i p i e n i ę d z y " ) I
Przyjrzy j s ię
dokładnie sposobowi
wywoływania metod.
Cała zabawa p r i v a t e void b u tto n 2 _ C lic k (o b je c t sender, Ev e n t Ar g s e) { Wyniki zwrócone
polega na bank += b o b . G i v e C a s h ( 5 ) ; przez GiveCash() są
tym , aby p rzekazywane w prost
zorientow ać UpdateFormQ;
do ReceiveCash()
się, kto daje w postaci
pieniądze parametru.
i kto je p r i v a t e void joeGivesToBob_Click(object sender, Ev e n t Ar g s e)
otrzym uje. bob.ReceiveCash(joe.G iveCash(10));
UpdateFormQ;
}
if ( x >0 )
e 2 .c o u n t = e 2 .c o u n t + e l.c o u n t
}
x = x + 1;
}
M essageBox.Show (result + " L i c z n i k : " + e2.count);
}
}
p u b l ic class Echo_____ {
public in t count = 0;
public s trin g Hello ( ) {
r e tu r n "w itaaaj...";
}
}
W wielu przypadkach
używ a sz tych
Więcej typów dla liczb całkowitych typów, bo chcesz
rozw iązyw ać
Kiedyś, daw no, daw no tem u , p am ięć k o m p u tera była b ard zo droga, a p rocesory były p roblemy, w których
napraw dę w olne. M ożesz w ierzyć lub nie, ale użycie niepraw idłow ego typu m ogło w yraźnie pi-zydatny ^j e s t efe k t
„p rzepełnienia".
spow olnić działanie całego p ro g ram u . N a szczęście czasy się zm ieniły i w większości
Pr-zeczy ta s z o tym
przypadków do przechow yw ania liczb całkow itych m ożesz p o p ro stu użyć i n t . C zasam i za kilka m inut.
je d n a k napraw d ę będziesz p o trzeb o w ał czegoś w ię k s z e g o . i raz n a jakiś czas także czegoś
m niejszego. T o dlatego C # daje Ci znacznie w ięcej opcji:
182 Rozdział 4.
Typy i referencje
i n t myl nt = 14. 7;
r Description
Te r az spróbuj zbudować program. Dostani esz taki komunikat: ® 1 C a n n o t im p lic itly c o n v e r t t y p e 'd o u b l e 1 t o 'i n t 1. A n e x p lic it c o n v e r s io n
e x is ts ( a r e y o u m is s in g a c a s t? )
T o ten sam błąd, który pojawi się podczas przypisywania wartości i n t do zmiennej typu
d o u b l e . I D E w tym mo me n c i e próbuj e Ci powiedzieć, że literał 1 4 . 7 m a typ — jest to doubl e .
Jeśli spróbujesz
Możesz zmienić go n a f l o a t , dopisując F na końcu (14. 7F) . 14.7M będzie z kolei traktowane przypisać literat
równoznacznie z t ypem d e c i m a l . typu float do
Lo era to ^ r ó t od angielskiego wartości double lub
Nieco więcej użytecznych typów wbudowanych s 0'e a “money (pieniądze) — serio! literat typ u decimal
do wartości float,
Czasami będziesz chciał zapisać w zmiennej pojedynczy znak, taki jak Q, 7 bą d ź $. W takich to IDE w yśw ietli
przypadkach możesz skorzystać z typu c h a r . Wart ości literału dla typu c h a r są zawsze pomocną wiadomość
przypominającą
zapisywane pomi ędzy znakami apostrofów ( ' x ' , ' 3 ' ) . W apostrofach możesz także umieścić o dodaniu
sekwencje formatuj ące ( ' \ n ' to zn a k nowej linii, ' \ t ' to tabulacja). Są o n e zapisywane odpowiedniej
w kodzie C # jako ciąg dwóch znaków, j e d n a k pr o g r a my zapisują je w pamięci jako j e d en znak. końcówki. Św ietnie!
W rozdziale 9
I w końcu przechodzi my do jeszcze jednego, niezwykle ważnego typu: o b ject . Już widziałeśs, dowiesz się
w jaki sposób mo ż n a tworzyć obiekty, tworząc instancje wybranej klasy. Cóż, każdy z tak znacznie więcej
na tem a t
utworzonych obiekt ów mo ż n a zapisać w zmiennej t ypu o b j e c t . W dalszej części tego rozdziału
związków
dowiesz się wszystkiego o obiektach oraz zmiennych odwołujących się do nich. pom iędzy typami
c har i byte.
_ s h o r t b ę d z ie p rz e c h o w y w a ł
aS liczb y c a łk o w ite
o w a r to ś c ia c h do 32 767.
Liczb long
b ędziesz
używa t w tedy,
gdy będą one b yte przechowuje liczby
naprawdę pom iędzy 0 i 255.
duże.
i n t leaguesUnderTheSea = 20000;
s h o r t s ma l l er L e a g u e s = l eaguesUnderTheSea;
ynnstrz ołówek
Trzy z podanych instrukcji nie skompilują się, ponieważ próbują
do mniejszej zmiennej wrzucić zbyt dużo danych lub przypisać do niej
wartość nieprawidłowego typu. Zakreśl je.
bo ol isDone = y e s ; long r a d i u s = 3;
,2l Spróbuj zbudow ać swój pro g ram . U p s ... — dostaniesz k o m u n ik at taki jak ten:
i n t myIntValue = ( i n t ) myDecimalValue;
To j e s t m iejsce, gdzie rzu tu jesz
. ł - w artość decimal na m t.
Co się sta ło ?
Poświęć m inutę
K om pilator nie pozw oli Ci przypisać w artości do zm iennej, k tó ra m a niepraw idłow y typ — naw et i przejdź d° początku
poprzedniego rozdziału.
wtedy, gdy bez problem ó w m ogłaby o n a p rzechow ać ta k ą w artość. Skutkow ałoby to n iezliczoną ilością S p ra wdź, w ja ki sposót
błędów w p rogram ie, a k o m p ilato r po m ag a, zm uszając C ię do p o stęp o w an ia w praw idłow y sposób. rzutow ałeś podczas
K iedy rzutujesz, składasz w ażną obietn icę — ośw iadczasz kom pilatorow i, że wiesz, iż dwa typy są p rzekazyw ania wartości
NumericUpOown.Va/ue
różne, je d n a k w tym ko nkretnym przy p ad k u przypisanie danych do now ej zm iennej jest praw idłow e. do formularza
Test gadania.
Zaostrz ołówek _______________________________________________________
V Rozwiązanie
Trzy z podanych instrukcji nie skompilują się, ponieważ próbują
do mniejszej zmiennej wrzucić zbyt dużo danych lub przypisać do niej
wartość nieprawidłowego typu. Zakreśl je.
Tup sh o rt przechowuje
w artości od - 3 2 768 b y te days = 365;
do 32 767. Ta liczba
*
j e s t za duża!
Typ byte może przechowywać
liczby m niejsze niż 256
Do ty p u bool można Do tego przypisania
Przy p isać tylko wartości p otrzebujesz typu short.
„■true" i „false".
Więcej informacji na temat typów wartościowych można znaleźć
na stronie http://msdn.m icrosoft.com/pl-pl/library/s1ax56ch.aspx.
186 Rozdział 4.
Typy i eferencje
s p e łn ij to S 9 tn l
Kiedy rzutujesz wartość, która jest zbyt duża,
C# dopasowuje ją automatycznie ™ o w n i e o Z S Z ' ,m 'iak
b y t e myByte = ( b y t e ) my l nt ;
d = d / 123.456;
- s;
przekonwertowanie wyniku
na double.
V Rozwiązanie
Nie zawsze możesz rzutow ać d o w o ln y typ
na każdy inny. Utwórz nowy projekt, przeciągnij
M e s s a g e B o x . S h o w ( " O d p o w i e d ź b r z mi "^+Jd); przycisk na formularz i wpisz poniższe instrukcje
do jego metody. Teraz zbuduj program — spowoduje
Operator + j e s t wystarczająco to powstanie pokaźnej listy błędów. Wykreśl te
sprytny, aby zam ienić decimal instrukcje, które są ich przyczyną. To pozwoli Ci
na string. przekonać się, które typy mogą być rzutowane,
Jest jeszcze je d n a sytuacja, w której C # p rzep ro w ad za a które nie!
autom atyczną konw ersję typów — jest n ią k o n k a te n a c ja , czyli
łączenie łańcuchów znaków (co oznacza, że je d n a w artość typu i n t myInt = 10;
s t r i n g je st przyklejana n a kon iec drugiej — ta k sam o, jak
b y t e myByte = ( by t e) my I nt ;
to robiłeś w oknach M essageBox). K iedy używasz + do łączenia
łańcucha znaków z danym i in nego typu, liczby są autom atycznie doubl e myDouble = (double)myByte;
konw ertow ane na tekst. M am y tu taj tego przykład. D w a
--bool myBool - (bool)myDouble,
początkow e w iersze są p o p raw n e, ale trzeci nie skom piluje się:
s t r i n g mySt r i ng = " f a l s e " ;
lo n g x = 139401930;
—myBool = ( b o o l ) my S t r i ng ;
M e s s a g e B o x . S h o w ( " O d p o w i e d ź b r z mi 11 + x ) ; mySt ri ng = ( s t r i n g ) m y l n t ;
MessageBox.Show(x); mySt ri ng = m y I n t . T o S t r i n g ( ) ;
myBool - (bool)myByte;
K om pilator C # zgłosi b łąd mówiący o niepraw idłow ym
argum encie (w języku C # arg u m e n te m nazyw am y w artość myByte - (byte)niyBool,
przekazyw aną do p a ra m e tru m etody). T o dlatego, że p a ra m e trem s h o r t myShort - ( s h o r t ) m y l n t ;
MessageBox.Show() je st s t r i n g , a p o d an y k o d przekazuje
c h a r myChar - ' x ' ;
l o n g , co je st niepraw idłow ym typem dla tej m etody. K onw ersja
n a typ s t r i n g jest je d n a k b ard zo p ro sta i sprow adza się do mySt ri ng = ( s t r i ng ) my C h a r ,
w ywołania m etody T o S t r i n g ( ) . Jest o n a częścią każdego typu
long myLong - ( l o n g ) my l nt ;
w artościow ego i obiektu. (W szystkie klasy, k tó re stworzyłeś,
także m ają m eto d ę T o S t r i n g ( ) i zw racają nazw ę klasy). decimal myDecimal - (decimal)myLong;
M ożesz przekonw ertow ać x n a coś, co jest używ ane przez mySt ri ng - mySt ri ng + myInt + myByte
Me s s a ge Box. Show( ) , w sposób następujący: + myDouble + myChar;
MessageBox.Show(x.ToString());
188 Rozdział 4.
Typy i referencje
M eto d a będzie działała praw idłow o, jeśli p rzekażesz jej p a ra m e tr typu, k tó reg o się
spodziew a (b o o l) — wywołaj M y M eth o d (tru e) lub M y M e th o d (fa lse ) i wszystko
będzie się kom pilow ało bez problem u.
Do zm iennej lub
C o się je d n a k stanie, gdy p rzekażesz zam iast teg o i n t lub s t r i n g ? ID E wyświetli p arąm etru typu object
moż e s z przypisać
b łąd podobny do tego, k tóry pojaw ił się p o p rzek azan iu 123 do M essageB ox.S how (). w szystko.
S próbuj te ra z jako arg u m en t p o d ać w arto ść logiczną, ale przypisz w arto ść w ynikową
do zm iennej typu s t r i n g lub zastosuj ją w w ywołaniu m eto d y M essageB ox.S how ().
T o także nie będzie działało — m e to d a zwróci typ i n t , a nie lo n g lub s t r i n g , którego
oczekuje M essageB o x .S h o w ().
namespace
for
class
public
else
new
using
if
while
Stwórz kalkulator zwrotu kosztów podróży służbowej. Powinien on umożliwiać użytkownikowi wpisanie
początkowego i końcowego przebiegu pojazdu. Na podstawie tych dwóch liczb program będzie obliczał
ilość przejechanych kilometrów i określał, jaką kwotę należy zwrócić pracownikowi przy założeniu,
Ćwiczenie że firma płaci 39 groszy za każdy przejechany kilometr
m k U suń p r z y c is k i
m in im a liza cji
m a k sy m a liza cji.
Ta etykieta ma
rozmiar 12 p t
i j e s t pogrubiona
★ U pew nij się, że p o le początkow ego sta n u licznika zaw iera m niejszą w arto ść niż p o le stanu
końcow ego. Jeśli je st inaczej, wyświetl k o m u n ik at M essageB ox: „Początkow y stan licznika
m usi być m niejszy niż końcow y”. U staw n a p ask u tytułow ym o k n a te k st „N ie m ogę obliczyć
odległości”.
URUCHOM GO.
U pew nij się, że p o d ajesz praw idłow e liczby. S próbuj zm ienić w artość p oczątkow ą na w iększą
niż końcow a. Spraw dź, czy p ro g ram w yświetla praw idłow y kom unikat.
d o u b le r e i m b u r s e R a te = .3 9 ;
d o u b le am ountO w ed;
p u b lic F o rm 1 () {
In itia liz e C o m p o n e n t( );
if ( s t a r t i n g M i l e a g e <= e n d in g M ile a g e ) {
Ten blok kodu ma
m ile s T r a v e le d = e n d in g M ile a g e -= s t a r t i n g M i l e a g e ; za zadanie obliczyć
liczbę przejechanych
am ountOw ed = m i l e s T r a v e l e d *= r e i m b u r s e R a t e ; kilometrów,
a następnie
l a b e l 4 . T e x t = a m o u n tO w e d .T o S trin g () + 11 z ł " ; pomnożyć ją przez
wspótczynnik
} e ls e { kilometrowy.
M e ssa g e B o x .S h o w (
V
Użyliśm y tutaj
alternatywnego sposobu
} wywotywania metody
M e ssageBox.Show().
} p odaliśmy dwa parametry:
pierws z y to w yśw ietlany
} komunikat, drugi to te k s t
Wydaje się, że przycisk działa prawidłowo, na p a sku tytułow ym okna.
ale jest jeden w ielki problem. Czy potrafisz go znaleźć?
192 Rozdział 4.
Typy i referencje
p r i v a t e v o id b u t t o n 2 _ C l i c k ( o b j e c t s e n d e r , E v e n tA rg s e ) I
URUCHOM PROGRAM.
W pisz kilka w artości i spraw dź, co się będzie działo. N ajpierw w pisz początkow y stan licznika, p o tem
końcowy, a n a stęp n ie kliknij przycisk Oblicz . T eraz kliknij przycisk Pokaż odległość, aby wyświetlić
w artość zm iennej m ile s T r a v e le d .
Połączenie = z operatorem
Przyjrzyj się dokładnie o p erato ro w i, k tó reg o użyliśmy do odjęcia początkow ego
stan u licznika o d końcow ego (-= ). P ro b lem p o leg a n a tym , że oprócz odejm ow ania
o p e ra to r przypisuje wynik do zm iennej p o lewej stronie. T o sam o dzieje się w w ierszu
m nożącym liczbę przejechanych kilom etrów p rzez w spółczynnik kilom etrow y.
Pow inniśm y zastąpić -= i *= o p e ra to ra m i - i *:
p riv a te v o id b u t t o n 1 _ C l i c k ( o b j e c t s e n d e r , E v e n tA rg s e )
To są, o p era to ry
zfożone. Ten
{ tutaj odejmuje
startingM ileage
s t a r t i n g M i l e a g e = ( in t) n u m e r ic U p D o w n l.V a lu e ; od endingM ileage,
ale jednocześnie
e n d in g M ile a g e = (in t)n u m e ric U p D o w n 2 .V a lu e ;
przypisuje też
n ow ą w a rto ść
if ( s t a r t i n g M i l e a g e <= e n d in g M ile a g e ) { do endingM ileage
i milesTraveled.
m i l e s T r a v e l e d = e n d in g M ile a g e -= s t a r t i n g M i l e a g e ;
amountOw ed = m i l e s T r a v e l e d *= r e i m b u r s e R a t e ;
l a b e l 4 . T e x t = a m o u n tO w e d .T o S trw ig () + 11 z ł " ;
} e ls e {
M e s sa g e B o x .S h o w (" P o c z ą tk o w y s t a n l i c z n i k a m u si
być m n ie js z y n iż » ic o w y "
Czy dobrze dobrane nazwy zmiennych mogą tutaj pomóc? N atu raln ie! Przyjrzyj się
d okładnie p rzezn aczen iu każdej zm iennej. Ju ż z sam ej nazwy m i le s T r a v e le d m o żn a uzyskać
część inform acji — wiesz, że je st to zm ienna, k tó rą fo rm u larz wyświetla niepraw idłow o,
i m asz pom ysł, ja k p o w inna być obliczana. M ożesz z tych w niosków skorzystać podczas
przegląd an ia k o d u i szukania źródła błędu. Z n aczn ie tru d n iej jest o d n aleźć b łąd w kodzie,
który wygląda ta k ja k te n poniżej:
mt = eM -= sM
Takie n a zw y w ża den sp o só b
aO = mT *= rR ; n ie pom agają nam o k re ś lić
p rzezn a czen ia zm ienn ych.
194 Rozdział 4.
Typy i referencje
K iedy w ięc tworzysz zm ienną, której typ jest obiektem , tworzysz tym sam ym zm ienną
referencyjną: referen cję do k o n k retn eg o obiektu. P o p atrz n a to:
To je s t sterta przed
uruchomieniem programu.
Nic na niej nie ma.
p u b li c p a r t i a l c l a s s Forml : Form
{
Ta zmienna
nazywa się Guy joe;
joe i będzie A
reprezentowała /
p u bl ic Form1()
Guy. Tworzenie referencji j e s t ja k t ^ r z e m e
{ e ty k ie t, ty/e ż e zamiast: przyhpia.ma
I n i ti a l i z e C o m p o n e n t ( ) ; ich do różnych rzeczy iużyw asz ich_do
oznaczania obiektów, abyś mógł później
uzyskać do n ich do stę p.
joe = new Guy();
}
To j e s t zmienna ■■■a to j e s t obiekt
referencyjna... reprezentowany
prze z zm ienną joe.
To j e s t sterta
po uruchomieniu programu.
Znajduje s i ę tu jeden obiekt,
do którego odwotuje s i ę JEDVNVM sposobem na
zmienna referencyjna joe. odwołanie s ię do tego
obiektu Guy j e s t użycie
zm iennej referencyjnej
o nazw ie joe.
° 6 'e k \ 0 ^
196 Rozdział 4.
Typy i referencje
N igdy nie odnosisz się b ezp o śred n io do obiektu. N ie m ożesz na przykład napisać
k o d u Guy.GiveCash() , gdzie Guy jest nazw ą typu. K o m p ilato r C # nie wiedziałby,
k tóry obiekt m asz na myśli, bo m ożesz m ieć kilka instancji Guy na stercie. Istn ie je wiele różnych
referencji do tego sam ego
P otrzebujesz więc zm iennej referencyjnej, n a przykład jo e , k tó ra odnosi się
obiektu Guy, ponieważ
do k o nkretnej instancji, ta k ja k Guy jo e = new Guy(). wiele różnych m etod używ a
g° do siwoich celów. Każda
T eraz m ożesz wywoływać m etody, n a przykład joe.G iveC ash() . jo e odno si się z referencji ma inną nazwę,
która ma se n s w swoim
do k o nkretnej instancji klasy Guy, a k o m p ilato r C # dokład n ie wie, której instancji kontekście.
m a użyć. P o n ad to , ja k pew nie zauw ażyłeś, m ożesz m ieć wiele etykiet w skazujących
na je d n ą instancję. M ożesz n apisać Guy dad = jo e , a p o te m wywołać dad.GiveCash()
T ak też jest dobrze — to w łaśnie ro b i dziecko Jo eg o każdego dnia.
^ 6 'e k t
k fa s u G u ^ ^ w i e in s t a n c jo
*[asy Guy , dwie zm ie n n i
referencyjne: po j ^ j d l
każdego obiektu Guy.
198 Rozdział 4.
Typy i referencje
Obiekty: _
Zaostrz ołówek
Teraz Twoja kolej. Dany jest jeden długi fragment kodu. Określ, jak dużo
obiektów i referencji znajduje się na każdym etapie. Po prawej stronie
naszkicuj obiekty i etykiety na stercie.
O b ie kty:________
Referencje:
O b ie kty:________
Referencje:________
O b ie kty:________
Referencje:________
[4 rin T in T in = lu c k y ;
Dog la v e r n e = new D og();
la v e r n e .B r e e d = "Mops";
O b ie kty:________
Referencje:________
c h a r l i e = la v e r n e ;
lu c k y = r in T in T in ;
O b ie kty:________
Referencje:
200 Rozdział 4.
Typy i referencje
Stwórz program z klasą reprezentującą słonia. Utwórz dwie instancje klasy E le p h a n t i pozamieniaj ich
referencje. Upewnij się, że instancje te nie zostaną zakwalifikowane do procesu oczyszczania pamięci.
Ćwiczenie
n j ?S t diagram klasu
(V Stwórz nowy projekt Windows Forms Application.
Z b ud u j fo rm u larz p o d o b n y do tego:
2 E £ £ kKrą
6 . E le p h a n t
Kliknięcie P ^ ^ ^ d n d a .W h o A m K ) ,
wywołuje metodę ' ' Name
która w yśw ietla to_okno.
EarSize
i WhoAmI()
Zaostrz ołówek Teraz Twoja kolej. Dany jest jeden długi fragment kodu. Określ, jak dużo
obiektów i referencji znajduje się na każdym etapie. Po prawej stronie naszkicuj
obiekty i etykiety na stercie.
Rozwiązanie
Obiekty: 3
Referencje:
V
żaden nowy obiekt.
Obiekty: 4 U staw ienie lucky
na rinTinTin nic
nie spow oduje, bo
Referencje: _8_ _ te dwie zm ienne
ju ż w cześniej
wskazyw ały na
ten sam obiekt.
202 Rozdział 4.
Typy i referencje
_ _ Stwórz program z klasą reprezentującą słonia. Utwórz dwie instancje klasy E le p h a n t i pozamieniaj ich
R o z w iąz a n ie właściwości. Upewnij się, że instancje te nie zostaną zakwalifikowane do procesu oczyszczania pamięci.
ć w ic z e n ia
To j e s t definicja
klasy Elephant
u sin g System .W indows.Form s;
i je j kod w pliku
Elephant.cs dodanym
c l a s s E lep h an t { Jeśli chcesz, instrukcję using możesz także umieścić do projektu.
w e w n ą trz na w ia só w przestrzeni nazw. Nie zapomnij
p u b lic i n t E a rS iz e ; o w ierszu „using
p u b lic s t r i n g Name; S ystem . Windows,
form s” znajdującym
p u b lic v o id WhoAmI() { s ię na górze klasy.
M essageBox.Show("Moje uszy m ają + E arS ize + " centym etrów s z e r o k o ś c i . 1 Bez niego nie będzie
działała instrukcja
Name + " m ó w i..." ) ; /Vlessage 8 ox.
}
} p u b lic p a r t i a l c l a s s Forml : Form {
E lep h an t lu c in d a ;
E lep h an t llo y d ;
p u b lic Form1()
{
In itia liz e C o m p o n e n t( );
lu c in d a = new E le p h a n t()
{ Name = "L ucinda" , E a rS iz e = 33 };
To j e s t kod klasy formularza llo y d = new E le p h a n t()
z pliku Forml.cs.
{ Name = "L lo y d ", E arS ize = 40 };
}
p r i v a t e v o id b u tto n 1 _ C lic k (o b je c t s e n d e r , EventA rgs e) {
lloyd.W hoA m I();
}
Gdybyś zwyczajnie
przypisał Hoyd do lucinda, p r i v a t e v o id b u tto n 2 _ C lic k (o b je c t s e n d e r , EventA rgs e) {
utraciłbyś w szystkie lucinda.W hoA m I();
referencje w skazując }
na Lloyda i jego obiekt
zostałby usunięty- p r i v a t e v o id b u tto n 3 _ C lic k (o b je c t s e n d e r , EventA rgs e) {
To dlatego potrzebujesz
E le p h an t h o ld e r ; . ... „ ,
referencji holder do Nie ma tuta j insfriAcji new p rzy
obiektu lloyd, zanim \ h o ld e r llo y d , N referencji, ponieważ nie chcemy |
pojawi s ię przy mm llo y d = lu c in d a ; tw orzyć nowej instancji Masy t o n i z m -
Lucinda. lu c in d a = h o ld e r ;
M essageB ox.Show ("O biekty z a m ie n io n e " );
WYSIL
SZARE KOMÓRKI
Jak myślisz, dlaczego nie dodaliśmy metody Swap() do klasy Elephant ?
!
Jeśli w p ro g ram ie istnieje w iele referen cji do o biektu,
to ryzykujesz nie tylko u tra tę ich wszystkich, lecz także
przypadkow ą zm ianę sta n u obiektu. Inaczej m ów iąc, jed n a
referen cja m oże go zm ien ić, podczas gdy d ru g a n ie będzie
m iała pojęcia, że coś zostało zm ienione. P o p a trz n a to:
3 D odaj następujący k o d do przycisku. Czy jesteś w stan ie o d gadnąć, co się stan ie p o jego kliknięciu?
D obrze, przejdźm y dalej. N aciśnij te ra z nowy przycisk. Z aczekaj, to p rzecież okno °t=iek
MessageBox , k tó re wywołuje Lucinda . Czy nie wywoływaliśmy m eto d y WhoAmI()
należącej do Lloyda ?
Zauw aż,
że dane NIE s ą
nadpisyw ane —
Referencje lloyd i ,u cin d ^ ° 9^ c h ma°jednaT
zam iennie. Zmiana w je d n e j z jedyną, rzeczą,
która s i ę zm ienia,
s ą referencje.
1 'Ę ^ y i? i o S a z C s f f l , one
TFN SAM OBIEKT.
204 Rozdział 4.
Typy i referencje
D eklarujesz
tablicę poprzez Ta tablica zawiera
określenie typu bool[] myArray; 15 e le m e n tó w .
i użycie nawiasów
prostokątnych
myArray = new bool
U żyw asz stówa ^ -------------------------- - ¿ r
kluczowego new do myArray[4] = true;
utworzenia tablicy, bo
j e s t to obiekt. Zmienna Ten w iersz u staw ia w artość
przechowująca tablice piątego elem entu myArray na
j e s t więc zm ienną tr ue. J e s t to elem ent p iąty,
referencyjną. bo pierwszym je s t myArray[0],
drugim myArray[1] i' tak dalej.
J
D ostęp
do tych
heights[1] = 70;
elem entów
uzyskujesz heights[2] = 63;
poprzez
indeks, ale
heights[3] = 60;
pracujesz
heights[4] = 58;
z nimi
dokładnie tak
heights[5] = 72; Zauważ, że tablica j e s t obiektem,
samo jak
z normalną chociaż siedem je j elementów j e s t ty pu
zm ienną int.
heights[6] = 74; wartościowego — tak sam o jak te na
p ier wszych dwóch stronach tego rozdziału.
O b \e ^ O b \^
tedenientfM
7 zmiennych Dog
to
u iv ć W
je i L edw ość
u e r f W w ł a S +n
im ninm ninini
w nte\ ^ 7 — ° zn^ l l 0 '¿o 6- 0 1 Z 3 t 5 <>
Dog Dog Dog Dog Dog Dog Dog
206 Rozdział 4.
Typy i referencje
if y J a k to d z ia ła * • •
^ — ------------------------------------
Meats[Randomizer.Next(Meats.Length)]
^ iVleats j e «
\ Z a w ie r a równoznaczne
co „Szynka".
Stwórz formularz.
D odaj do fo rm u larza sześć etykiet, o d l a b e l l do la b e l 6 . N astęp n ie uzupełnij każd ą z nich
za p o m o cą właściwości T e x t i o b iek tu MenuMaker. B ędziesz m usiał zainicjalizow ać obiekt,
używ ając do tego nowej instancji klasy Random. T a k w ygląda kod:
Kiedy uruchomisz
program, sześć
etykiet pokaże
sześć różnych
losowych kanapek
208 Rozdział 4.
Typy i referencje
W ywołaliśm y m e to d ę T e llM e () o b ie k tu lloyd i wstawiliśm y do niej dwa p aram etry : „C ześć” i referen cję
do ob iek tu lucinda. M eto d a używa p a ra m e tru w h o S a id It w celu uzyskania d o stęp u do p o la Name
dow olnego słonia, k tóry został p rzek azan y do niej jak o drugi p aram etr.
Lloyd działa w ięc tak , jakby był wywoływany z ( " W it a j" , lu c i n d a ) , i p o kazuje
taki kom unikat:
° 6 /e k \^
/ V, \
i Nie. istnieją.
głupie pytania
P : W dalszym ciągu nie jestem P : Wciąż nie rozumiem, jak P: Przypomnij mi jeszcze raz
pewien, czy dobrze zrozumiałem to jest z tymi różnymi typami — co robi „ th is ” ?
działanie referencji. przechowującymi wartości o różnej
wielkości. O co w tym chodzi? O : t h i s to specjalna zmienna, której
O : Referencje to sposób na korzystanie możesz użyć tylko wewnątrz obiektu.
z metod i pól obiektu. Gdy utworzysz O : A więc tak. Problem polega na tym, Kiedy jesteś wewnątrz klasy, t h i s pozwala
referencję do obiektu Dog, będziesz mógł że zmienne mają pewien rozmiar bez Ci odwołać się do pola lub metody tej
jej użyć podczas wywoływania jego metod. względu na przechowywaną wartość. konkretnej instancji. Jest to naprawdę
Jeśli posiadasz niestatyczną metodę Jeśli więc masz zmienną i nadasz jej typ pomocne podczas pracy z klasą, której
nazwaną D o g .B a rk () lub D og.B eg(), lo n g , chociaż liczba ta jest naprawdę metody wywołują inne klasy. Jeden obiekt
możesz utworzyć referencję sp o t. Wtedy mała (powiedzmy równa 5), CLR może użyć tego słowa kluczowego w celu
będzie możliwy dostęp do tych metod przydzieli odpowiednią ilość pamięci do przekazania innemu obiektowi referencji
za pomocą odwołań s p o t.B a rk ( ) jej przechowywania, na wypadek gdyby do samego siebie. Gdy s p o t wywołuje
i s p o t.B e g ( ) . Używając referencji, możesz kiedyś stała się duża. Jeśli trochę nad tym metody obiektu ro v e r, przekazując jako
także zmodyfikować informacje w polach pomyślisz, to uznasz, że jest to bardzo parametr t h is , to ro v e r otrzymuje
obiektu, na przykład zmienić pole Breed użyteczne. A poza tym, przecież nazywamy referencję do obiektu spot.
za pomocą odwołania sp o t.B re e d . je zmiennymi dlatego, że mogą się zmieniać
cały czas. Jeżeli posiadasz
P Zaczekaj, czy to nie oznacza,
: CLR przyjmuje, że wiesz, co robisz, klasę, dla której
że kiedy zmieniam wartość poprzez dobierając typ, i ufa, że nie wybierasz
referencję, to zmieniam ją dla
takiego, którego nie potrzebujesz. instancje mogą być
wszystkich referencji wskazujących
na dany obiekt?
Nawet jeśli liczba nie jest teraz duża,
to istnieje szansa, że po kilku operacjach
tworzone, możliwe
O : Tak. Jeśli ro v e r jest referencją do tego matematycznych się zmieni. CLR przydziela jest użycie zmiennej
wystarczającą ilość pamięci, aby poradzić
samego obiektu co sp o t, to zmieniając
sobie z każdą zmienną danego typu.
this w celu uzyskania
ro v e r.B re e d na „Beagle", sprawisz,
że s p o t.B re e d także będzie równe referencji do tej
„Beagle".
właśnie instancji.
CELNE SPOSTRZEŻENIA
^ J e s t jeden szczególny przypadek, w którym typ zm iennej nie j e s t
! de klarowany — dow iesz s ię o nim w rozdziale 14- podczas poznawania
t/ słowa kluczowego var.
Gdy deklarujesz zmienną, podajesz jej typ i nazwę ■ Istnieje kilka typów, które C# potrafi konwertować
Czasem łączysz to z ustawieniem jej wartości. automatycznie (przykładem może być konwersja
s h o rt na in t) . Kiedy kompilator nie pozwoli Ci zapisać
■ Istnieją typy wartościowe dla liczb, które pozwalają na
w zmiennej wartości innego typu, będziesz musiał
pracę z różnymi ich zakresami. Największe liczby powinny
zastosować rzutowanie.
mieć typ lo ng , natomiast najmniejsze (nie większe niż 255)
mogą być deklarowane jako byte. Istnieją pewne słowa, które są zarezerwowane przez język
C# i których nie możesz używać do nazywania zmiennych.
Każdy typ wartościowy posiada rozmiar i nie możesz
Są to między innymi fo r , w h ile , using, new oraz inne,
przypisać wartości typu większego do typu mniejszego.
które mają określone zadanie w języku.
Nie ma tu znaczenia aktualna zawartość zmiennej.
■ Referencje są jak etykiety. Możesz mieć ich tak dużo,
Kiedy korzystasz z literałów, możesz użyć przyrostka F do
jak chcesz, i wszystkie będą odnosiły się do tej samej rzeczy
określenia typu float (15.6F) i M dla typu decimal (36.12M).
Jeżeli obiekt nie posiada referencji, jest niszczony przez
mechanizm oczyszczania pamięci.
p riv a te v o id b u t t o n l C l i c k ( o b j e c t s e n d e r , E v e n tA rg s e )
Iteraq a num er 1: b ig g e s tE a rs .E a rS iz e =
E le p h a n t b i g g e s t E a r s = e le p h a n ts [0 ];
fo r (in t i = 1; i < e le p h a n ts .L e n g th ; i+ + )
{
Iteraq a num er 2: b ig g e s tE a rs .E a rS iz e =
if ( e le p h a n ts [ i] .E a r S iz e > b ig g e s tE a r s .E a r S iz e )
b ig g e s tE a rs = e le p h a n ts [i];
Iteraq a num er 3: b ig g e s tE a rs .E a rS iz e =
} Ten w iersz u staw ia referencję
b iggestE a rs na ten elem ent
t ablicy, na który wskazuje
} elephantsfij.
M e s s a g e B o x .S h o w ( b i g g e s t E a r s . E a r S i z e . T o S t r i n g Q ) ;
Iteraq a num er 4: b ig g e s tE a rs .E a rS iz e =
}
212 Rozdział 4.
Typy i referencje
Magnesiki z kodem
Kod obsługujący kliknięcie przycisku j e s t porozrzucany na
drzw iach lodówki. C zy p o tra fis z zrekonstruow ać go tak , aby
metoda działała i wyświetlała okno zaprezentow ane poniżej? | refNum = in d e x [ y ] ; |
in d e x [0 ] - 1'» l V \~ \
in d e x tH = 3; 1
1 in d e x [2 ] = ° ’> l
1 in d ex [3] = z > -1
| S tr in g [ ] 'is la n d s = new S t r i n g [ 4] ;
- I
| r e s u l t += "\nWyspa = 11;
W ynik: .h e ig h t = (x + 1) * 2;
.le n g th = x + 4;
x = 27; podpowiedź'.
T ria n g le t5 = t a [ 2 ] ; setAreaO NIE
je st metodą
t a [ 2 ] . a r e a = 343; statyczną. Wróć
r e s u l t s += "y = " + y ; do rozdziatu 3 .,
Pytanie dodatkowe! M essag eB o x .S h o w (resu lts + aby odświeżyć
informacje na
" , t5 p o le = " + t 5 . a r e a ) temat stówa
A by uzyskać dodatkow e p unkty, użyj } kluczowego
fragm entów z b asen u do w ypełnienia v o id s e tA re a () static.
pustych m iejsc w oknie z w ynikami. {
(h e ig h t * le n g th ) / 2;
Przypominamy: każdy
fragment z basenu
może być użyty
więcej niż raz.
[p STWÓRZ fo r m u lar z.
T a k pow inien w yglądać nasz form u larz w yświetlony w o knie Form Designer:
216 Rozdział 4.
Typy i referencje
c la s s S ta ts
{
p u b lic in t
p u b lic in t
p u b lic in t 0;
p u b lic in t
p u b lic voi
T o tal+ + ;
if (¡c o rre c tK e y )
{
M issed++;
Każde wywołanie metody
} Update() powoduje
e ls e przeliczenie liczby
{ prawidłowych naciśnięć
C o rrec t+ + ; i zapisanie nowego wyniku
w polu Accuracy.
}
D odaj te dw a p o la n a sam ym p o czątk u k o d u form ularza: Zanim przejdziesz dalej, powinieneś ustawić
wartości trzech właściwości. Właściwości
p u b lic p a r t i a l c l a s s Forml : Form
E n a b le d kontrolki T im er przypisz wartość T rue.
{ Właściwości Maximum kontrolki P ro g r e s s B a r
218 Rozdział 4.
Przejmij kontrolę nad kontrolkami Typy i referencje
r
Jak sądzisz, dlaczego
od szerokości formularza
M yL abel.L eft -= 5;
i f (M yL abel.L eft <= 0) {
GoingForward = t r u e ;
a następnie będziesz wywoływał metodę Move().
Każde wywołanie metody Move() sprawi, że obiekt
LabelBouncer szturchnie etykietę, zmieniając wartość jej
właściwości Left. Jeśli pole GoingForward będzie mieć
trzeba odjąć szerokość 1 wartość true, kontrolka będzie popychana w prawo poprzez
kontrolki? Kiedy przeciągasz k o n tro lę dodanie do właściwości Left liczby 5; w przeciwnym razie
} i um ieszczasz j ą w formularzu, IDE kontrolka będzie popychana w lewo poprzez odjęcie liczby 5.
} u staw ia je j właściwości Top i Left.
Twoje programy mogą korzystać Każda kontrolka ma właściwość Parent, zawierającą referencję
z tych właściwości, aby przesuw ać do formularza, gdyż formularz także jest obiektem!
kontrolki na formularzu.
A oto kod fo rm u larz a. C iekaw e, czy będziesz p o tra fił zrozum ieć, co się w nim dzieje. K orzysta o n z tablicy
obiektów L ab e lB o u n c e r, by przesuw ać etykiety tam i z p o w ro tem w o knie form u larza, a m e to d a obsługująca
zdarzenia T ic k kontro lk i T im er b ezu stan n ie wywołuje m eto d y M ove() tych obiektów L a b elB o u n cer.
} Iabel2
E tykiety odbijają się
label 3 od krawędzi formularza
Kontrolka Timer używa pętli, by wywoływać
Kl,kn'j przy cisk button 1, żeby rozpocząć naw et je ś li go poszerzi
m etodę Move() każdego obiektu button 1
lub zw ęzim y.
LabelBouncer, jednak robi to wyłącznie, przesuwanie etykiety labell Ponowne
je śli w tablicy j e s t zapisana referencj a,
button2 kliknięcie przycisku zatrzyma etykietę.
a nie null. Z apisanie w elem encie t ablicy | button3 |
pozostate przyciski kontrolują
wartości null spowoduje, ż e kontrolka pizesuwan iddwóch pozostałych etykiet.
przestanie być animowana.
220 Rozdział 4.
Typy i referencje
for Um ożliwia tworzenie pętli wykonującej t r z y instrukcje. W pierwszej deklaruje się zmienną,
k tó ra będzie używana. Druga w kolejności sprawdza pewien warunek w ykorzystujący tę zmienną.
Trzecia instrukcja m odyfikuje jej wartość.
public Klasa public m oże być używana przez każdą inną w projekcie. Kiedy zmienna lub m etoda
jest określona jako public, m oże być używana i wywoływana przez klasy i m etody
mieszczące się na zewnątrz klasy, w k tó re j została zadeklarowana.
else Kod rozpoczynający się od else będzie wykonywany wtedy, gdy warunek
instrukcji i f nie będzie spełniony.
while Pętle while są wykonywane, dopóki warunek umieszczony na ich początku przyjm uje wartość true.
ą. Zaostrz ołówek
_ . . Poniżej zaprezentowano tablicę obiektów El e p h a n t oraz pętlę, której zadaniem jest
K U Z W K jZ U M c wyszukanie słonia z największymi uszami. Jaka jest wartość zmiennej bi g g e s t E a r s .
E a rS iz e po każdej Iteracji pętli fo r?
p r i v a t e v o id b u t t o n 1 _ C l i c k ( o b j e c t s e n d e r , E v e n tA rg s e )
Iteraq a num er 1: b ig g e s tE a rs .E a rS iz e =
E le p h a n t b i g g e s t E a r s = e le p h a n ts [0 ];
fo r (in t i = 1; i < e le p h a n ts .L e n g th ; i+ + )
{ Iteraq a num er 2: b ig g e s tE a rs .E a rS iz e = 42
if ( e le p h a n ts [ i] .E a r S iz e > b ig g e s tE a r s .E a r S iz e )
, Referencja bigge s tEars j e s t używana do określenia,
{ który spośród elem entów sprawdzonych do tej pory
b i g g e s t E a r s = e l e p h a n t s [ i ] ; w pętli for ma najw iększe u szy.
} Spraw dź działanie programu, korzystając I t e r a j num er 3: b ig g e s tE a rs .E a rS iz e =
} z debuggera. Umieść w tym m iejscu piunlct
p rzerwania i sprawdzaj, ja kie wartości
przyjm uje wyrażenie biggestE a rs.E arSize-
M e s s a g e B o x .S h o w ( b ig g e s tE a r s .E a r S iz e .T o S tr in g ( ) ) ;
Iteraq a num er 4: b ig g e s tE a rs.E a rS iz e = 44
222 Rozdział 4.
Typy i referencje
To tutaj
inicjalizowana je s t
tablica indexll.
224 Rozdział 4.
Imię i Nazwisko: Data:
Laboratorium C#
Dzień na wyścigach
Laboratorium zawiera specyfikację opisującą program, który
musisz napisać, wykorzystując wiedzę zdobytą w poprzednich
kilku rozdziałach.
Ten projekt jest większy niż wszystkie, które widziałeś tu
do tej pory. Zanim przystąpisz do pisania, przeznacz więc
chwilę na uważne przeczytanie wszystkiego. Nie przejmuj
się, jeżeli utkniesz w jakimś miejscu — nie ma tutaj niczego
nowego. Możesz przejść do dalszej części książki i wrócić
do laboratorium później.
Uzupełniliśmy część projektu za Ciebie i upewniliśmy się,
że masz wszystkie potrzebne elementy... i nic więcej.
Od Ciebie zależy, czy ukończysz pracę. Nie damy Ci
odpowiedzi w postaci kodu, możesz natomiast pobrać pliki
graficzne, których użyliśmy w naszym projekcie, oraz ostateczną,
gotową wersję programu.
Laboratorium C# 225
Dzień na wyścigach
Faceci
Janek, B a rte k i A re k chcą obstaw iać n a wyścigach psów.
Ja n e k rozpoczyna ze stan em k o n ta rów nym 50 zł, B a rte k
m a 75 zł, nato m ia st A re k p o siad a 45 zł. P rzed każdym
wyścigiem decydują oni, czy chcą obstaw iać i ja k dużą kw otę
gotow i są n a to przeznaczyć. Faceci m ogą zm ienić swoje
zakłady aż do m o m e n tu ro zpoczęcia wyścigu. ale gdy już
się zacznie, nie m a możliw ości w ycofania się.
T f W
Dom bukmacherski
D om bukm acherski p rzechow uje inform acje o ilości
y -\ /- -\
gotów ki p osiad an ej p rzez każdego z facetów i w ysokości
W itam y w Domu
aktualnie zaw ieranego zakładu. M inim alna kw ota, v y V y B u km a ch erskim K a ro la
y ■*> Minimalny zakład: 5 zł /- N /- -\
jak ą m ożna przeznaczyć n a zakład, wynosi 5 zł. Jeden zakład na osobę na dany wyścig
Czy masz wystarczającąsumę pieniędzy?
D om bukm acherski
/- N '
um ożliw ia przyjęcie tylko
jed n eg o zakładu o d osoby J V 7
n a dany wyścig.
226
Dzień na wyścigach
Obstawianie
Wszystkie zakłady:
K ażdy zakład o p ie ra się n a zasadzie „podw ójnie albo nic”
— gracz albo pod w aja swój w kład, albo traci wszystko, podwójnie albo nic
co postaw ił. M inim alna kw ota, za ja k ą m o żn a obstaw ić gonitwę,
M inim alny zakład: 5 zł
to 5 zł. K ażdy facet m oże postaw ić m aksym alnie 15 zł na
jed n eg o charta. Jeżeli c h a rt wygra, obstaw iający otrzym uje M aksym alnie 15 zł na psa
(po zakończeniu wyścigu) kw otę dw ukrotnie w iększą o d tej,
jak ą postaw ił. Jeżeli p rzeg ra, po staw io n a kw ota je st o dejm ow ana
Wygrana: pieniądze zyskane
o d stan u jego konta. Przegrana: pieniądze stracone
Powiedzmy, J e g o t h a r tw S c S ż y t ,
A
Z n fk , 10 zt i dodatkowo otrzymuje 10 zt
^ z f y c i i t u S ) . Je śli P ^ g r a t, °d k° nta
odejmowane j e s t 10 zt.
Wyścig
Jeśli chciałbyś zbudować system
W wyścigu b io rą u d ział cztery ch arty biegnące p o prostym odcinku. zm .em ajęcy sz an se wygranyth
Zw ycięzcą jest te n ch art, który jak o pierw szy przekroczy linię m ety. ' P o ^ z e g ó ln y c h psów - f a j obaw
Wyścig je st całkow icie losowy. N ie m a żadnego p rzeszk ad zan ia lub r r r : s Prób° Mać- Napisanie
w L 9? kodu nie tytko pozwoli Ci
ułatw iania i nie m a w iększego praw d o p o d o b ieństw a, że c h a rt wygra zdobyć dośw iadczenie, lecz także
b ęd zie ś w ie tn ą zabaw ą.
kolejny wyścig tylko dlatego, że w cześniej m u się to częściej udaw ało.
227
Dzień na wyścigach
R ozpoczęliśm y T w oją p rac ę o d zap rezen to w an ia opisu klas i pew nych szkielet klasy,
fragm entów kodu, n a których m o żn a się oprzeć. M usisz to wszystko dokończyć. polega na i^^upapni^riiu Trn<!to)(zadanie
Upewnij s ię, że do każdej u b li c c l a s s Greyhound { ^
deklaracji klasy dodałeś
p u b lic i n t S ta rtin g P o s itio n ; / / M ie js c e , g d z ie ro zp o czy n a s i ę P ictu re B o x
słowo kluczowe public. —
p u b lic i n t RacetrackLength ; / / Jak d łu g a j e s t t r a s a
p u b lic P ic tu re B o x MyPictureBox = n u l l ; / / Mój o b ie k t P ictu reB o x
p u b lic i n t Location = 0 ; / / Moje p o ło ż e n ie na to r z e wyścigowym
p u b lic Random MyRandom; / / I n s ta n c ja k la s y Random
Będziesz potrzebował tylko jednej instancji Masy Rand°m
— właściwość „random każdego obiektu Greyhound będzie
p u b lic bool Run() { wskazywać ten sam obiekt Random.
/ / P rzesuń s i ę do p rzodu losowo o 1, 2 , 3 lu b 4 p unkty
/ / Z a k tu a liz u j p o ło ż e n ie P ictu reB o x na fo rm u larz u
/ / Zwróć t r u e , j e ż e l i wygrałem w yścig n
}
wieaztat, co m a sz zrobić.
p u b lic v o id TakeS tartingP osition () {
/ / W yzeruj p o ło ż e n ie i ustaw na l i n i i s ta rto w e j
228
Dzień na wyścigach
p u b lic c l a s s Guy {
p u b lic s t r i n g Name; / / Im ię f a c e t a
p u b lic Bet MyBet; / / I n s ta n c ja k la s y Bet p rzech o w u jąca dane o z a k ła d z ie
p u b lic i n t C ash; / / Ja k dużo p ie n ię d z y p o sia d a To pole działa dokładnie
tak samo jak pole MyLabel
w p rogram ie Labe lBouncer
/ / O s ta tn ie dwa p o la s ą k o n tro lk am i GUI na fo rm u la rz u z rozdz ia łu 4 .
p u b lic R adioB utton M yRadioButton; / / Moje p o le wyboru
p u b lic Label M yLabel; / / Moja e t y k i e t a Kiedy w polu MyLabel zapiszesz ju ż referencję I
do jednej z e tykiet formularza, będziesz mdgt
zmieniad je j te k s t przy użyciu wyrażenia
MyLabe|.Te x t. Dokładnie to samo dotyczy pola
p u b lic v o id U p d a te L a b e ls() { MyRadioButton. p
/ / Ustaw moje p o le te k sto w e na o p is z a k ła d u , a n a p is obok
Kiedy inicjalizujesz / / p o la wyboru t a k , aby pokazyw ał i l o ś ć p ie n ię d z y ("Ja n e k ma 43 z ł" )
obiekt Guy, upewnij
} Tutaj dodaj swój kod.
się, że jego pole MyBet
j e s t ustaw ione na nu i l /
Z araz po inicjalizacji p u b lic v o id C le a rB e t() { } / / Wyczyść mój z a k ła d , aby b y ł równy z e ro
wywołaj metodę Pam iętaj, że zakłady s ą
UpdateLabelsO- reprezentow ane p rzez
p u b lic bool P la c e B e t( i n t Amount, i n t DogToWin)
instan cje klasy B et.
/ / U stal nowy z a k ła d i przechow aj go w p o lu MyBet
/ / Zwróć t r u e , j e ż e l i f a c e t ma w y s ta r c z a ją c ą i l o ś ć p ie n ię d z y , aby o b staw ić
}
To j e s t obiekt,
którego używ a Guy
do reprezentowania p u b lic v o id C o l l e c t ( i n t W inner) {
zaktadów w aplikacji. / / Poproś o w y p ła tę z a k ła d u i z a k tu a liz u j e t y k i e t y
/
} Kluczem do rozwiązania j e s t tu taj _użycie obiektu
Be t... Pozwo'l mu wykonywać swoj ą p rac ę -
}
B et
u b li c c l a s s Bet {
Amount p u b lic i n t Amount; / / I l o ś ć p ostaw ionych p ie n ię d z y
Dog p u b lic i n t Dog; / / Numer p s a , na k tó re g o postaw iono
Bettor p u b lic Guy B e t t o r ; / / F a c e t, k tó ry z a w arł z a k ła d
p u b lic s t r i n g G e tD e s c r ip tio n () {
GetDescription()
PayOut() / / Zwraca s t r i n g , k tó ry o k r e ś la , k to o b s ta w ił w y ścig , ja k dużo p ie n ię d z y
/ / p o s ta w ił i na k tó re g o p sa (" Ja n e k p o s ta w ił 8 z ł na p sa numer 4 ").
/ / J e ż e li i l o ś ć j e s t równa z e r o , z a k ła d n ie z o s t a ł z a w arty
// (" Ja n e k n ie z a w arł z a k ła d u " ) fc- , t t dość c zęste zadanie
}
}
p u b lic i n t PayO ut( i n t W inner) {
Podpowiedz: instancję B et
tworzysz w kodzie klasy / / Param etrem j e s t zw y cięzca w y ścig u . J e ż e l i p ie s w y g ra ł,
Guy. Użyje ona słowa / / zwróć w a rto ść p o sta w io n ą . W przeciwnym r a z i e zwróć w a rto ść
kluczowego th i s i w ten
s posdb przekaże referencję / / p o staw io n ą ze znakiem minus
do sam ej siebie do
inicjalizatora obiektu Bet. } Pam iętaj: form ularz przechow uje psy w tablicy, której indeksy zaczyn ają się
od z e ra . Pies numer 1 je st zapisany w komórce o indeksie O , pies numer dwa
— w komórce o indeksie 1 i ta k dalej. B y podać numer zw ycięzcy, będziesz
m usiał dodać do indeksu liczbę 1.
229
Dzień na wyścigach
OQQ
w klasie głów nego form ularza.
O''
iek t
Formularz m usi
zainicjalizować obie tablice O I z
podczas uruchamiania
T a b lic a r e f e r e n c ji G r e y h o u n d
ioioio
Wśród wielu obiektów
/ T a b lic a r e f i Gu y
Q
wizualnych w ykorzystane
O
zostaną także cztery kontrolki
PictureBox do zilustrow ania
psów . Referencje do nich
um ieśc is z w inicjalizatorach
czterech obiektów Greyhound.
Formularz będzie te ż zaw ierał J 0\ \ Ob''6'*'
o o.
trzy kontrolki RadioButton
i trzy etykiety, które zostaną
u żyte w inicjalizatorach trzech
obiektów typ u Guy.
Ob'®
ob\®
Jeśli Twój program nie będzie się chciał skompilować, a komunikat o błędzie będzie informował
o „inconsistent accessibility" (niespójnej dostępności). upewnij się, że przed deklaracją każdej
z trzech klas dodałeś słowo kluczowe public. (Dowiesz się o nim więcej w dalszej części książki).
230
Dzień na wyścigach
O 0b'\&
r e t u r n tr u e
O Ob'®
O
metod S tartO każdy f a c e t może odebrać wygraną
i Stop(), by rozpow ąć Obiekt Sys*® }
i zakończyć wyścig .
Dom bukmacherski na formularzu informuje
Obiekt Bet decyduje, każdy z obiektów Guy, który ch a rt wygrał
wyścig. W ten sposób realizowane s ą O’«
czy powinien dokonać wypłaty wy p łaty za zw ycięstw a w zakładzie.
Określając zwycię s kiego
G u y s [l].C o lle c t(w in n in g D o g ) M yBet.PayOut(winningDog) psa, nie zapommj zoZać 1
do indeksu tablicy.
Q 0b'\®
Obiekt Guy dodaje wynik metody B e tP ayOu tO
do swojej gotówki. Jeśli ch art zwy ciężył,
"o; Ob'®
if
O
(m ój c h a r t zw yciężył) {
r e t u r n Amount;
ob'®
231
Dzień na wyścigach
Poeksperym entuj trochę z w łaściwością
Tak powinien wyglądać Twój interfejs użytkownika !n terval obiektu Timer, by zm ieniać
szybkość w yścigu.
G raficzny interfejs użytkow nika aplikacji „D zień n a w yścigach” zbudow any jest Do ustaw ienia długości trasy
z fo rm u larza p o d zielon eg o n a dwie części. N a górze znajduje się to r wyścigowy: uży ie szrw łaściwości Width kontrolki
p ictureB ox. Wartość przekażesz
k o n tro lk a P ic tu re B o x dla to ru i d o datkow e cztery dla chartów . D o ln a połow a do p ola RiacetrackLength obiektu
fo rm ularza pokazuje dom bukm acherski, gdzie trzech facetów (Jan ek , B a rte k i A rek) Greyhound. Pozwoli to na określenie
m omentu, w którym p ie s minie linię
m oże obstaw iać re z u lta t wyścigów. Właściwości m e ty . Khlimj p rawym przyciskiem
FormBorderStyle m yszy kontrolkę PictureBox
for-mularza przypisz reprezentują c ą tor i w ybierz opcję
wartość FixedSinngle, „S e nd to B a c k by upewnić się , że
r rt S ‘ ssaw s? P * a właściwości nie będzie ona przesłaniać kontrolek
M aximizeBox oraz prezentujących charty.
M inimizeBox ustaw
na false. ^
Dzień na wyścigach
m in im u m B e t L a b e l
O ,ie Radio Button
O b ib Radio Button
O '/RadioButton
232
Dzień na wyścigach
Będzie s z potrzebował
Zawieranie zakładów p ętli, by zainicjować
każd y ot>ie k t Guy, wywołując
D o obstaw iania użyj k o m p o n en tó w w k o n tro lce GroupBox dom u w tym celu jego metodę
ClearB et() (która sprowadza
bukm acherskiego. P roces te n sk ład a się z trzech etapów : s ię do zrobienia zakładu
i n n O zfofyd,), a na£t<dnie
I m et°d ę UpdateLabelst). to polo teksto w e z a
Żadne zakłady nie zostały jeszcze zawarte,
K iedy p ro g ram je s t u ru ch am ian y p o raz pierw szy lub wyścig d o p iero się M y Label. Odświeża
zakończył, w d o m u bukm acherskim nie są zaw arte jeszcze żad n e zakłady. także ilość gotówki na
przyciskach wyboru,
M ożna zobaczyć całkow itą sum ę pieniędzy posiadanych przez facetów k n rrt : ___ _
M in im a ln y z a k ła d : 5 zł Z a k ła d y
® Janek ma 50 zł pod(Jj Janek nie zawarł zakładu
Upewnij się, że w szystkie obiekty Greyhound będą wspólnie używ ały tego samego obiektu Random!
Gdyby każdy chart u żyw ał własnej instancji tego obiektu, to program mógłby źle działać w przypadku,
gdyby w szystkie charty wygenerowały tę sam ą sekw encję liczb losowych.
233
Dzień na wyścigach
Końcowy produkt
Podczas w y śc ig u c z te ry
Z apew ne wiesz, że T w oja aplikacja „D zień n a wyścigach”
♦ • i a f ■u a v wyścigowym , oz któryś
zostanie u z n an a za skończoną, gdy faceci b ę d ą mogli
zaw ierać zakłady i obserw ow ać wyścigi psów.
obstaM iać' ‘W
w ła ś c iw o ś c i i £ Z ° l l % ° ś X £ .
234
5. Hermetyzacja
Co ma być ukryte*
niech będzie ukryte
Czy kiedykolwiek marzyłeś o odrobinie prywatności? Czasami Twoje obiekty czują się tak
samo. Na pewno nie lubisz sytuacji, w których ktoś, komu nie ufasz, czyta Twój pamiętnik lub przegląda
wykazy Twoich operacji bankowych. Dobre obiekty nie pozwalają innym obiektom na oglądanie swoich
pól. W tym rozdziale nauczysz się wykorzystywać potęgę he rm etyzacji . Sprawisz, że dane o b ie k tó w
będą pryw atne , i dodasz metody, które umożliwią Ci zabezpieczenie dostępu do d anych .
236 Rozdział 5.
Hermetyzacja
• Klienci mogą wybierać rodzaj napojów. Na w ięk szo ^ przyjęć p o d a n y jest a lkoho l,
który kosztuje 20 zł od osoby. Klienci mogą także wybrać p rzyję a e b e z ^ k o M o w .
Krvstyna nazywa to „opcją zdrową". Kosztuje o na tylko 5 zł od osoby i polega ira
podaniu napojów gazowanych i soku zamiast alkoM u. Zorganizowanie W zdrowej t
jest dla niej znacznie łatwiejsze, dlatego Krystyna daje t akim klientom dodat kow y rabat
w wysokości 5% sumarycznych kosztów przyjęcia.
• Przewidziano dwie opcje dotyczące kosztów dekoracji. JeZeli klie nt wybierz<5dKek° ramoZe
normalna, to opłata wynosi 7,50 zł od osoby plus 30 zł opłaty j ^ r o r a r a w h Klientp 1^^aSZe
zwiększyć liczbę ozdób, wybierając opcję f a n t o w ą — kosztuje to I 5 zł od osoby plus
jednorazowa opłata dekoracyjna w wysokości 50 zł.
Dekoracje
fantazyjne
(15 zł od osoby |
+ 50 zł opłaty
Liczba gości. za dekoracje)
Jedzenie Opcja I
V
(25 zł od zdrowa? u
osoby) Zwykła
dekoracja
(7,50 zł od osoby
+ 30 zł opłaty
za dekoracje)
SetHealthyOption()
CalculateCostOfDecorations()
CalculateCost()
238 Rozdział 5.
Hermetyzacja
Oto ja k będzie d ziałać klasa D in n e r P a r ty . A ktu aln y stan obiektu D in n e rP a rty — czyli w artości
zapisane w jego polach — o kreślają sposób w ykonyw ania obliczeń. W ybór opcji zdrow ej oraz
fantazyjnych dekoracji, czy też zm ian a liczby uczestników przyjęcia pow o d u je zm ianę stan u obiektu,
a ta z kolei spraw ia, że w ywołanie m etody C a l c u l a t e C o s t ( ) zwróci in n ą w artość.
Liczba gości. 1
Jedzenie | ^ Opcja i
(25 zł od > zdrowa? [<
osoby) 1
---------- — —.
Za każdym razem, gdy u żytko w n ik zaznaczy pole lub zm ieni liczbę uczestników
przyjęcia, m etody obsługujące zdarzenia a ktu a lizu ją pola i w y w o łu ją m etody ob ie ktu
D in n e rP a r ty , by o d p o w ie d n io zaktu alizow a ć jego stan. Następnie w y w o łu ją metodę
C a lc u la t e C o s t ( ) , by określić c a łk o w ity koszt przyjęcia i w y ś w ie tlić go w etykiecie.
240 Rozdział 5.
Naszym celem j e s t pomóc Ci w s ta niu s ię
doskonałym program istą C #, a najszybszy m
..gdyż wiemy, że sprostasz wyzwaniu! sposobem dotarcia do niego je s't rozw iązyw an ia Hermetyzacja
problemów takich ja k ten.
Z a m ia st używać przycisku do obliczania ceny przyjęcia, fo rm u larz te n ak tualizuje ety k ietę z kosztem
autom atycznie zaraz p o użyciu p o la w yboru lub zm ianie w artości kontro lk i NumericUpDown.
Pierw szą rzeczą, k tó rą pow inieneś zrobić, je st d o d an ie do klasy fo rm u larza m etody, której
Ta metoda będzie zad an iem b ędzie w yświetlanie kosztów.
wywoływana przez
w szystkie inne
utworzone w klasie D odaj n a stęp u jącą m e to d ę do klasy Form l. B ędzie o n a wywoływana podczas kliknięcia k ontrolki
formularza. NumericUpDown:
To w ten sposób Dodaj tę m etodę do klasy formularza —
uaktualniana p r i v a te v o id D isp la y D in n e rP a rty C o s t() obliczy ona k° s z t przyjęcia i um ieści wynik
będzie treść w etyk iecie costLabel.
etyk iety z kosztem {
przyjęcia, jeśli decim al C ost = d in n e rP a rty .C a lc u la te C o s t(h e a lth y B o x .C h e c k e d
cokolwiek ulegnie
zmianie. c o s tL a b e l.T e x t = C o s t.T o S tr in g ( " c " ) ;
Wartość będzie wynosiła t ru e >
} j eśli na formularzu z ^ t r n t e
. c
Zm ień nazw ę e ty kiety, zaznaczone pole wyboru
która w yśw ietla koszt, Przekazanie “c“ do m etody ToString()
s powoduj e wy ś w ietlenie wartości „Opcja zdrowa".
na costLabel.
w formacie walutowym. Jeśli
w opcjach reg ionalnych i językowych
masz: b a w i o n ą Polskę, b ę ^ i e
widoczn y symbol złotego.
p u b l i c v o id S e t H e a l t h y O p t i o n ( b o o l h e a lth y O p tio n ) j
if (h e a lth y O p tio n ) j
C o s tO f B e v e r a g e s P e r P e r s o n = S.OOM;
I e ls e j
Skorzysta/iśm y z „if (fency) z a m iast
C o s tO f B e v e r a g e s P e r P e r s o n = 2O.OOM;
wpisywania „if (fancy == tru e ) ,
I ponieważ in stru kcj a if zaw sze
spraw dza, czy warunek je s t prawdziwy.
I
p u b l i c v o id C a l c u l a t e C o s t O f D e c o r a t i o n s ( b o o l fa n c y ) j
if (fan cy )
j
C o s tO f D e c o r a t io n s = (N u m b e rO fP e o p le * 1S.OOM) + SOM;
I e ls e j
C o s tO f D e c o r a t io n s = (N u m b e rO fP e o p le * 7.SOM) + 3OM;
I
I
p u b l i c d e c im a l C a lc u la te C o s t(b o o l h e a lth y O p tio n ) j
d e c im a l t o t a l C o s t = C o s tO f D e c o r a t io n s +
( ( C o s tO f B e v e r a g e s P e r P e r s o n + C o s tO fF o o d P e r P e rs o n )
* N u m b e rO fP e o p le );
Uży/iśmy nawiasów, aby mieć pewność,
że ob/iczenia m atematyczne ze sła n ą
if (h e a lth y O p tio n ) j wykonane poprawnie.
re tu rn to ta lC o s t • .95M ;
I e ls e j
re tu rn to ta lC o s t;
Ten fragm ent wprowadza 5% rabatu na
I całkowity koszt imprezy, jeżeli wybrana
I z o stała opcja bezalkoholowa.
I
Nie m usisz dodawać instrukcji „usi'ng Sy s tem .Windows . Fo n n s; ^<fo
klasy DinnerParty, gdyż nie wyświetla ona komunikatów przy użyciu
metody M essageBox.Show() ani' ni'e korzysta z żadnych i'nnych klas
dostępnych w tej przestrzeni nazw .N T Framework.
242 Rozdział 5.
Hermetyzacja
Użyliśmy typu decimal dla cen, ponieważ jest on zaprojektowany do obliczeń pieniężnych. Po prostu upewnij się,
że zawsze dołączasz M po literale — jeśli chcesz przechować 35,26 zł, to sprawdź, czy napisałeś 35.26M.
Możesz to łatwo zapamiętać, bo M pochodzi od angielskiego Money, oznaczającego pieniądze!
p r i v a t e v o id fa n c y B o x _ C h e c k e d C h a n g e d (o b je c t s e n d e r , E v e n tA rg s e ) {
d in n e r P a r ty .C a lc u la te C o s tO fD e c o r a tio n s (f a n c y B o x .C h e c k e d );
D is p la y D in n e rP a rty C o s t(); Nazwaliśmy nasze pola wyboru „healthyBox" oraz
} „fancy Box", więc pow inieneś wiedzieć, co s ię
dz ieje wewnąt rz ich procedur obsługi zdarzeń.
p r i v a t e v o id h e a lt h y B o x _ C h e c k e d C h a n g e d ( o b je c t s e n d e r , E v e n tA rg s e ) {
d in n e r P a rty .S e tH e a lth y O p tio n (h e a lth y B o x .C h e c k e d );
D is p la y D in n e rP a rty C o s t() ;
}
p r i v a t e v o id n u m e ric U p D o w n 1 _ V a lu e C h a n g e d (o b je c t s e n d e r , E v e n tA rg s e ) {
d in n e r P a r ty .N u m b e r O f P e o p le = ( in t) n u m e r ic U p D o w n l.V a lu e ;
D is p la y D in n e rP a rty C o s t(); koszty przyjęcia muszą byf powt<5mie
obliczone i wyświetlone za każdym razem,
} gdy zmieni s ię liczba g o ^ łub zaznaczen.e
w polach wyboru.
p r i v a t e v o id D i s p l a y D i n n e r P a r t y C o s t ( ) {
d e c im a l C ost = d in n e r P a rty .C a lc u la te C o s t(h e a lth y B o x .C h e c k e d );
c o s tL a b e l.T e x t = C o s t.T o S tr in g ( " c " ) ;
}
}
Formatowanie łańcuchów znaków
Widziałeś iuŁwfaki sposób ro ż ra («^wertować dowolny obiekt na łańcuch znaków, używaiac
|ego m ^ d y ToSt r ing(>. Jeże|i jako paramet r p rz e je s z "c", to wartość zostanie zamieniona^
b w tę w |oka|nei wa|ucie. Możesz t akże przekazać "f3". Zmienna zostanie wtedy zapisana w postaci
^kk tei cziY z t rzl ma miejscami p° przecinku. "0" (to jest zero) zamieni wartość na liczbę
całlkow itą 0 % na liczbę w pos,taci procent owej, natomiast "n" wyświetli ją, wstawiając separatory
clla_poszczegó|nych rzędów wie|kości. Poświęć minutę i zobacz, jaki będzie efekt zastosowania
każdego z tych parametrów w Twoim programie!
Gdy (uruchamiasz
program, pole
wyboru Dekoracje
Robert (przy telefonie) : Cześć, Krystyna. Ja k id ą przygotow ania ftircłazyjne powinno
do m ojego przyjęcia? być zaznaczone,
gdyż przypisałeś
je go właściwości
Krystyna: Św ietnie. Byliśmy dzisiaj ran o p oszukać dekoracji i jestem
Checked wartość
pew na, że będziesz zadow olony z organizacji przyjęcia. true. Po wybraniu
10 uczestników
Robert: T o super. Słuchaj, w łaśnie zadzw oniła ciotka m ojej żony. kos z t przyjęcia
powinien w ynieść
O n a i jej m ąż zam ierzają do nas przyjechać n a kilka tygodni. Czy 575 zł.
m ożesz m i pow iedzieć, ja k b ę d ą w yglądały szacunkow e koszty p o
zw iększeniu liczby gości z dziesięciu do dw unastu?
244 Rozdział 5.
Hermetyzacja
Planista przyjęć ^ 3
Koszt 660.00 zl
Kiedy z p o w ro te m w fą c z y sz
D e k o r a c je fa n t a z y jn e , k! f o t °
w z r a s t a 1 a ż d o 770 z \T e < c z b y
s ą p o p r o s t u n ie p r a w id ło w e -
Krystyna: R o b ercie , w ydaje mi się, że p o p ełn iłam błąd. W ygląda n a to, że koszty
z dekoracjam i fantazyjnym i podskoczyły do 770 zł. P raw dę m ów iąc, m a to większy
sens. Z aczynam m ieć je d n a k pew n e obaw y i p rzestaję u fać te m u program ow i.
Z am ierzam go o desłać do p o p raw ien ia błędów i przygotow ać kosztorys ręcznie.
Czy m ożem y w rócić do rozm ow y ju tro ?
Robert: N ie chcę płacić 770 zł tylko dlatego, żeby d odać dwie osoby do listy gości.
C ena, k tó rą p o d ałaś m i w cześniej, była znacznie bardziej przystępna. M ogę ci zapłacić
665 zł, tyle, ile pow iedziałaś m i za pierwszym razem , ale ani grosza więcej!
WYSIL __________________
SZARE KOMÓRKI
Dlaczego po każdej zmianie wprowadzonej przez Krystynę w programie pojawiają się błędne wartości?
5 osób
2 0 z ł od osoby za napoje C ałkow ity ko szt napojów = 100 z ł
25 z ł od osoby za jedzenie - C ałkow ity ko szt jedzenia = 125 z ł
15 z ł od osoby za dekoracje C ałkow ity ko szt dekoracji = 125 z ł D° tej poru
plus opłata 50 z ł w szystko gm .
10 osób
i
2 0 0 z ł + 250 z ł + 2 0 0 z ł = 650 z ł
To j e s t kwota/
catkowita, którą
Usuń znacznik z pola wyboru powinniśmy Program dodaje s ta r ą cenę dekoracji' do nowego
Dekoracje fantazyjne, otrzymać. A le nie kosztu jedzenia i napojów.
a następnie ponownie je zaznacz. otrzym aliśm y... Liczy on tak: 2 0 0 zł + 2 5 0 zł + 125 zł = 575 z ł.
Spow oduje to aktualizację w artości p o la / / S ta re dekoracje.
C o s tO fD e c o ra tio n s o b ie k tu D in n e rP a r ty Nowy k o szt jedzenia i napojów.
i w yśw ietlenie praw idłow ej ceny przyjęcia
wynoszącej 650 zł.
246 Rozdział 5.
Hermetyzacja
P r o b le m z b lis k a
Przyjrzyj się dokładnie m eto d zie, k tó ra obsługuje zm iany w artości w k o ntrolce num eri cUpDownl.
W staw ia o n a w artość p o la V alu e do zm iennej N um berO fPeople, a n a stęp n ie wywołuje m etodę
D i s p l a y D i n n e r P a r t y C o s t( ) . W tedy m o żn a już tylko p olegać n a niej, w ierząc, że zajm ie się
pow tórnym przeliczeniem wszystkich kosztów.
Ten w iersz zmienia pole
p r i v a t e v o id nu m ericU p D o w n 1 _ V alu eC h an g ed N u m b e r O f P e o p l e w tej
(o b je c t se n d e r, E v e n tA rg s e ) {
\fy z fo rm u /a rz a .
d in n e r P a r ty .N u m b e r O f P e o p le (in t)n u m e ric U p D o w n l.V a l ui e ;
D is p la y D in n e rP a rty C o s t() ;
Jeśli w ięc dokonasz zm ian w p o lu N um berO fPeople, ta m eto d a nigdy nie zostanie w ykonana:
p u b l i c v o id C a l c u l a t e C o s t O f D e c o r a t i o n s ( b o o lfa n c y ) {
Ta z m ienna ustaw iana j e s t na 125 podczas
if (fa n c y ) { pierw sz ego wywotania i dopóki nie zostanie
uruchomiona j e szc ze raz, nie zm ieni się.
C o s tO f D e c o r a t io n s (N u m b e rO fP e o p le * 15.00M ) + 50M;
}
N ie jest to jed n ak jedyne miejsce pro g ram u , w którym pojaw iają się problem y. O ba p o la w yboru działają niespójnie: jed n o
z nich wywołuje m eto d ę m odyfikującą stan obiektu, nato m iast w drugim stan jest przekazyw any jako argum ent wywołania
m etody. Każdy program ista próbujący zrozum ieć działanie tego p ro g ram u uzna, że jest to totalnie nieintuicyjne!
& k t P '*
C alcul a te C o s t O zw raca 650 zł
N u m b e r O f P e o p l e j^ l O j_________
¿5
°A . mi Jr
'& k t P '*
248 Rozdział 5.
Hermetyzacja
Hermetyzacja oznacza,
Wyciągnij korzyści
że niektóre dane w klasie są prywatne z e swojego lenistwa
— je ś li pom iniesz
.p riva te '1 lub „public",
Istnieje b ardzo p ro sta m eto d a unik n ięcia pro b lem ó w teg o typu: upew nij się, c # uzna, że Twoje
pole j e s t domyślnie
że istnieje tylko je d e n sposób n a użycie Twojej klasy. N a szczęście i w tym prywatne.
przypad ku C # ułatw ia całą spraw ę, um ożliw iając tw orzenie p ó l prywatnych .
D o tej p o ry w idziałeś tylko p o la publiczne. Jeżeli po siad asz o b iek t z takim i polam i,
to każdy inny o b iek t m oże je odczytywać lub zm ieniać. G dy uczynisz te p o la
pryw atnym i, dostęp do nich będzie możliwy tylko z wewnątrz danego obiektu
(lub z innego o b iek tu tej samej klasy).
Oprócz tego, także sta tyczn e metody klasy
mają dostęp do prywatnych pól tej sam ej klasy. Jeśli chcesz, aby Twoje pole było prywatne, m u sisz
uzyć s f°wa kiuczowego p rivate do jego d e k la r a j
}
ten denerwujący btąd.
3 W ydaje się, że ten niegłupi sposób pozw ala chronić tożsam ość agenta,
praw da? D o p ó k i o b iek t wywołujący m eto d y nie p o d a właściwego hasła,
jego nazw isko jest bezpieczne.
A g e n t KG B u ż y w a
O b ie k t c i a A g e n t j ^ t i n s t a n c j ą . w p o z d r o w ie n iu
zewnątrz ) n i e p r a w id ło w e g o h a s t a .
"Dash M a rtin
c/a A 9
\
KGB o trzym uje tylko
Ps e u donim agenta CIA.
Doskonałe, nieprawdaż?
250 Rozdział 5.
Hermetyzacja
Ustawienie zmtennej na
public oznacza, że m°żna
uzyskać do niej -
public string RealName;
a naw et zmienić j ą
z zewnątrz klasy.
c / a A 9 e'
A g en t Jones m oże użyć p ó l z atrybutem priv a te do zachow ania swojej Obiekt kgbAgent nie
może uzyskać dostępu do
tożsam ości w tajem nicy p rzed ag en tam i obcych służb specjalnych. P o tym jak prywatnych pól ciaAgent,
zadeklaruje on swoje p o le jak o pryw atne, jedynym sposobem n a p o b ra n ie jego p°nieważ s ą to instancje
dwóch różnych klas.
danych będzie wywołanie metod, które m ają dostęp do obszarów prywatnych
klasy. Plany ag en ta K G B zostały w ięc pokrzyżow ane! Ustawienie pól i metod jako
prywatnych pozwala mieć
Po pro stu zamień public pewność, że żaden kod nie
na private i spokój. Twoje będzie modyfikował używanych
pola s ą teraz ukryte przed private string realName; przez Ciebie wartości, kiedy
ś w iatem. s i ę tego nie spodziewasz.
m iSA gent j e s t
instancją klasy
B ritishAgent,
także. nia
więc także nie AgentGreetingC'wrona 1 a t a o p ółn ocy")
ma dostępu do
prywatnych pól
ciaAgent.
r
Tylko inny obiekt
ciaA gent m oże je
^/5 A < ł
widzieć.
i Nie .istnieją.
głupie pytania
P:: Dobrze. Mogę więc dostać się do zmiennych skutkiem generować kolejne liczby losowe. Podczas
tworzenia instancji klasy Random nie będziesz widział Jedyny spo
prywatnych poprzez metody publiczne. Co się
tej tablicy. To dlatego, że jej po prostu nie potrzebujesz
jednak stanie, jeżeli klasa z polami prywatnymi
— gdybyś miał do niej dostęp, mógłbyś powstawiać
sób, w jaki
nie umożliwi mi pobrania tych danych, a mój
obiekt potrzebuje ich użyć? tam inne wartości. Skutkowałoby to generowaniem o b ie k t m oże
O : Nie masz wtedy dostępu do tych danych spoza
liczb, które losowe by nie były. Ziarna są zatem uzyskać
całkowicie przed Tobą ukryte.
obiektu. Kiedy piszesz klasę, zawsze powinieneś dostęp do
się upewnić, czy dajesz innym obiektom możliwość
pobrania danych, których potrzebują. Pola prywatne
P : Hej, właśnie zauważyłem, że wszystkie danych prze
procedury obsługi zdarzeń, których używałem,
są bardzo ważnym elementem hermetyzacji, ale są zostały zadeklarowane z użyciem słowa chowywanych
tylko częścią historii. Pisanie klasy z hermetycznymi kluczowego p riv a te . Czy one też są prywatne?
danymi oznacza też praktyczny, łatwy dostęp do
w prywatnych
danych z poziomu innych obiektów, bez udostępniania O : Formularze C# są napisane w taki sposób, polach innego
ważnych informacji, których Twoja klasa potrzebuje. aby tylko ich kontrolki mogły wywoływać zdarzenia.
obiektu,
Kiedy umieścisz słowo kluczowe p r iv a t e na
P Dlaczego miałbym tworzyć pole, do
: początku metody, będzie ona mogła być używana polega
którego inna klasa nie będzie mieć dostępu?
tylko wewnątrz klasy. Gdy IDE dodaje procedurę
na użyciu
O : Czasami klasa musi przechowywać informacje
obsługi zdarzenia do programu, deklaruje ją jako
prywatną. Zabezpiecza w ten sposób metody przed publicznych
niezbędne do prawidłowego działania, ale takie,
których inne obiekty w ogóle nie powinny widzieć. niepowołanym dostępem z poziomu innych obiektów. m etod,
Może jakiś przykład. Kiedy komputery generują liczby Nie ma żadnej reguły mówiącej, że procedury obsługi
losowe, używają do tego specjalnych wartości zwanych zdarzeń muszą być prywatne. W zasadzie możesz to
k tó re
ziarnami. Nie musisz wiedzieć, jak to wszystko działa, sprawdzić samodzielnie — kliknij dwukrotnie przycisk, zwracają dane.
ale każda instancja klasy Random zawiera tablicę a następnie zmień deklarację procedury na p u b lic .
kilkudziesięciu liczb, której obiekt używa, aby z lepszym Kod w dalszym ciągu będzie się kompilował i działał.
252 Rozdział 5.
Hermetyzacja
m- Zaostrz ołówek
r v Daną mamy klasę z kilkoma polami prywatnymi. Zakreśl te instrukcje z poniższej listy,
które nie skompilują się , jeśli będą uruchamiane spoza klasy przy użyciu instancji obiektu
nazwanego mySuperChef.
3. in t loyalCustomerOrderAmount = 54;
4. mySuperChef.secretlngredient = "kardamon";
7. Ja k a będzie w arto ść zm iennej re cip e p o u ru ch o m ien iu wszystkich w ierszy kodu, k tó re się skom pilują?
p u b l i c c l a s s SuperChef
{
p u b l i c s t r i n g co o k ieR ec ip e;
p riv ate strin g secretln g red ien t;
p r i v a t e c o n s t i n t loyalCustom erOrderAmount = 60;
p u b l i c i n t Tem perature;
p riv ate strin g in g re d ie n tS u p p lie r;
p u b l i c s t r i n g G e t R e c i p e ( i n t orderAmount)
{
i f (orderAmount >= loyalCustom erOrderAmount)
{
r e t u r n co ok ieR ecip e + " " + s e c r e t l n g r e d i e n t
}
else
Jedynym sposobem na pobranie sekretnego
{ sktadnika j e s t zam ówienie ^ r o m n e j ^ d z ^ .
r e t u r n co o k ie R ecip e; ciasteczek. Nie m °żesz
s ie do tego pola z z e w n ą te .
}
}
2 . s t r i n g s u p p l i e r = m y S u p e r C h e f . in g r e d ie n tS u p p l ie r ;
Numer 2 i numer 4 nie
3. i n t loyalCustomerOrderAmount = 54;
V
skom pilują s i ę ponieważ
ingredientSu pplier
4. m y S u p e r C h e f . s e c r e t I n g r e d i e n t "kardamon"; i secretIngredient
s ą prywatn e.
W eź 3 jajka, 2 ,5 szklanki mąki, 1 ły żeczk ę soli, 1 ły żec zk ę wanilii i 1,5 szklanki cukru. W szy stk o wymieszaj. i
Piecz przez 1C> m inut w tem p eratu rze 2 0 0 stopni. Smacznego!
254 Rozdział 5.
Hermetyzacja
COŚ TU JEST NIE TAK. JEŚLI ZMIENIĘ JAKIEŚ POLE NA
PRYWATNE, TO JEDYNYM EFEKTEM BĘDZIE BRAK MOŻLIWOŚCI
SKOMPILOWANIA SIĘ INNYCH KLAS PROGRAMU, KTÓRE BĘDĄ
CHCIAŁY SKORZYSTAĆ Z TEGO POLA. JEŚLI JEDNAK PONOWNIE
ZMIENIĘ JE Z PRYWATNEGO N A PUBLICZNE, TO PROGRAM ZNOW U
BĘDZIE SIĘ KOMPILOWAĆ. CZYLI DODANIE MODYFIKATORA PRIVATE
PSUJE PROGRAM. DLACZEGO ZATEM MIAŁABYM CHCIEĆ Z NIEGO
W OGÓLE KORZYSTAĆ?
WYSIL ____
CO SZARE KOMÓRKI
Dlaczego tworzenie niehermetycznych klas może w przyszłości
utrudnić modyfikowanie programu?
ołówek ____________________________________________________
Oto klasa Route z programu nawigacyjnego Maćka. Gdybyś sam miał sobie ułatwić jej
stosowanie, to które z jej właściwości lub metod zadeklarowałbyś jako prywatne?
R o u te
StartPoint ...............................................................................................................................................................
EndPoint
Length
GetRouteLength()
GetStartPoint()
GetEndPoint() ...............................................................................................................................................................
SetStartPoint()
SetEndPoint() ...............................................................................................................................................................
ChangeStartPoint()
ChangeEndPoint()
Ten problem można rozwiązać na wie/e różnych sposobów, a wszystkie z nich mogą być prawidłowe!
Z ap isz tu ten, który według Ciebie j e s t najlepszy.
256 Rozdział 5.
Hermetyzacja
mniej prob
lemów z ich
używaniem.
Obecnie Maciek chce wyobrazić sobie
Punkt swój obiekt Route jako czarną skrzynkę.
początkow y Chciałby przekazać do niego współrzędne
i odczytać długość trasy. Nie chce
zastanaw iać się , w jaki sposób obiekt
ją oblicza. przynajmniej na razie.
Długość trasy
Punkt końcowy
W szystko, czego się nauczyłeś do tej pory, koncen tro w ało się
n a zapew nieniu tego, by p ro g ram coś robił — wykonywał
pew ne działania. W herm etyzacji chodzi je d n a k o coś innego.
N ie zm ienia o n a działania p ro g ram u . B ardziej przypom ina
„szachow ą n a tu rę p ro g ram o w an ia” — p o p rzez ukrycie
pew nych inform acji posiadanych przez klasy ju ż n a e tap ie ich
pro jek to w an ia i tw o rzen ia jesteśm y w stan ie określić strateg ię ich
późniejszego w ykorzystywania. Im ta strateg ia będzie lepsza, tym
bardziej elastyczne i łatw e w pielęgnacji b ęd ą nasze pro g ram y
i tym w iększej liczby błędów będziem y m ogli uniknąć.
258 Rozdział 5.
Hermetyzacja
G dy tworzysz form ularz, aby pozw olić użytkow nikow i n a w pisanie liczby krów
w kontrolce, m usisz m ieć tak że możliw ość zm iany tej w artości w p o lu numberOfCows.
A by tak było, m ożesz utw orzyć m eto d ę, k tó ra zw raca w artość p o la do o b iek tu fo rm u larza
p u b lic co n st in t F e e d M u l t i p l i e r = 3G;
p u b lic i n t G etN um berO fC ow s() Dodamy m etodę, aby
Roln ik potrzebuje inne klasy mogły
30 worków do I uzyska ć dostęp do
wykarmienia liczby krów.
każdej krowy. re tu rn num berO fC ow s;
i
p u b l i c v o id S e tN u m b e rO fC o w s (in t newNumberOfCows)
I
num berO fCow s = new Num berOfCow s;
B a g sO fF ee d = num berO fCow s * F e e d M u l t i p l i e r ; Tu je s t metoda do u s tawiania
liczby krów, która jednocześnie
i zm ienia pole B ^ s ^ F ^ d .
Teraz nie ma możliwości,
numberOFCows j e s t polem prywatnym, aby te dwie zm ienne nie były
zatem do za p isu jego nazwy z synchronizowane.
wy korzystaliśm y notację wielbłądzią.
260 Rozdział 5.
Hermetyzacja
Używasz akcesorów g et i s e t dokład n ie ta k sam o ja k pól. T a k będzie w yglądał k o d dla Kiedy ten w iersz ustaw ia
przycisku, który ustaw ia liczbę krów i p o b ie ra o k reślo n ą liczbę w orków paszy: NumberOfCows na 10,
w tedy akcesor s e t ustaw ia
prywatne pole numberOfCows
p r i v a t e v o id b u t t o n 1 _ C l i c k ( o b j e c t s e n d e r , E v e n tA rg s e ) { i aktualizuje publiczne pole
F a rm e r m y F arm er = new F a r m e r ( ) ; BagsOfFeed.
howManyBags = m y F a rm e r.B a g s O fF e e d ;
T bez pr-oblemu pobrać
tę wartość.
}
Pomimo tego, że kod traktuje NumberOfCows tak s a m ° J ak
pole, uruchaminni/ j e s f akcM or s e t i PfzeokJ ay w anadfFeed
ia r to ś ć 2 0 . Gdy pobierana j e s t P o o B ^ e eOO-
uruchamia s ię akcesor g e t który zwraca 2 0 30, c z y l 600
Z r ó b to !
Stwórz aplikację do przetestowania klasy Farmer
* Y
U tw órz nową aplikację W indows F o rm s Application, której użyjem y w celu
przetestow ania klasy Farm er i o b ejrzen ia właściwości w akcji. Skorzystaj W yniki kierowane na
z m etody C o n s o l e . W r i t e L i n e ( ) do w ypisania wyników w o knie O utput ID E . konsolę są wyświetlane
Uuidędl w oknie Output.
r« D odaj do p ro je k tu klasę F a rm e r: Kiedy w aplikacji typu Windows Forms Application
używasz metody Console.WriteLinej), jej wyniki
p u b l i c c l a s s Farmer {
są wyświetlane w IDE, w oknie Output. Aplikacje
p u b l i c i n t BagsOfFeed;
WinForms zazwyczaj nie prezentują wyników
p u b l i c c o n s t i n t F e e d M u l t i p l i e r = 30; swego działania w ten sposób, niemniejjednak
my będziemy z niego często korzystalijako
p r i v a t e i n t numberOfCows; z narzędzia do nauki.
p u b l i c i n t NumberOfCows {
/ / (dodaj a kceso ry g e t i s e t z p o p rze d n ie j stro n y )
}
p u b l i c p a r t i a l c l a s s Forml : Form {
Farmer farm er;
p u b l i c Form1() {
In itializeC o m p o n en t();
farm er = new Farm er() { NumberOfCows = 15 };
}
p r i v a t e void numericUpDown1_ValueChanged(object s e n d e r , EventArgs e) {
farmer.NumberOfCows = (int)num ericU pD ow nl.V alue;
}
p r i v a t e void c a l c u l a t e _ C l i c k ( o b j e c t s e n d e r , EventArgs e) {
C o n s o l e . W r i t e L i n e ( " P o t r z e b u j ę {0} worków paszy do wykarmienia {1} krów"
farm er.B agsO fF eed, farmer.NumberOfCows);
} / WriteLine() z a s t ę p u j i °0i } . ,
, Y w y „ w o * Console.WdteUnW)
do wyświetlenia wiersza tekstu
w oknie O utput w IDE.
Nie zapominaj, ze kontrolki muszą byc „podłączone” do swoich procedur
obsługi zdarzeń! Dwukrotnie kliknij kontrolki Button oraz NumericUpDown
262 Rozdział 5. w oknie projektanta formularzy, aby IDE wygenerowało odpowiednie metody.
Hermetyzacja
farmer.BagsOfFeed = 5;
T eraz uru ch o m p rog ram jeszcze raz. D ziała dobrze, dopóki nie użyjesz now ego przycisku. W ciśnij go jed n ak , a zaraz
p o nim kliknij ponow n ie przycisk Oblicz . T eraz k o m u n ik at w o knie Output sugeruje, że p o trzeb u jesz p ięciu w orków
paszy — bez względu na liczbę posiadanych krów ! G dy tylko zm ienisz w arto ść w k o n tro lce NumericUpDown, przycisk
Oblicz ponow nie zacznie generow ać praw idłow e wyniki.
T eraz m asz ju ż właściwość zam iast p ola. K iedy C # widzi coś takiego, trak tu je to ta k sam o, jakbyś używał
p o la w ew nętrznego (takiego ja k pryw atne p o le numberOfCows z publiczn ą właściwością NumberOfCows).
T o jeszcze nie napraw iło p ro b lem u . Istn ieje je d n a k b ard zo p ro ste rozw iązanie — u staw ienie w łaściwości
tylko d o o d c z y tu :
p u b l i c i n t BagsO fFeed { g e t ; p rivate s e t ; }
Spróbuj teraz przebudow ać kod — pojawi się kom unikat o błędzie informujący, że ak ce so r set j e s t nied ostęp ny .
T eraz nie m ożesz zmodyfikować w artości pola BagsOfFeed spoza klasy Farmer. A by kod program u ponow nie
się skompilował, musisz pozbyć się wiersza, który próbuje to zrobić, usuń zatem drugi przycisk oraz metodę
obsługującą zdarzenia jego kliknięcia. T eraz Twoja klasa Farmer jest bardziej hermetyczna!
Error List ▼ nx
T » IO 1 Error | Search Error List fi ”
Description File ^ Line ^ Colu... Project ^
O 1 'K alkulatoH crow.Farm er.feedM urtiplier' is inaccessible due to its p rotection level Form1.cs 19 56 K alkulator krow
W inicjalizatorze obiektu mogą znaleźć się tylko publiczne pola i właściwości. W jaki
sposób można prawidłowo zainicjalizować obiekty, jeżeli niektóre pola do ustawienia
mają atryb ut private?
264 Rozdział 5.
Hermetyzacja
} To tuta j instrukcja new wywołuje konstruktor. J e s t ona taka sam a jak każda inna
instrukcja new z jednym wyjątkiem — posiada param etry, które s ą przekazyw ane do m etody
konstruktora. Podczas ich wpisywania zwróć uwagę na w yśw ietlane okno In telliS en se
— wygląda ono dokładnie tak jak w przypadku innych metod.
o n s fru k to ry
w p r z y b liż e n iu Przyjrzyjmy się bliżej konstruktorowi klasy Farm er, abyśmy dokładnie dowiedzieli
się, jakie zjawiska zachodzą w środku.
Konstruktory nic nie Ten konstruktor ma dwa parametry, które dz iałaj ą tak s a ™
zwracają, więc nie mają ja k zwykle. Pierw szy z nich przekazuje liczbę krów, drugi
typ u wartości wynikowej. określa mnożnik paszy.
sb
public Farmer(int numberOfCows, int feedMultiplier) {
Mu sim y najpierw usta w ić mnożnik,
this.feedMultiplier = feedMultiplier;
C
ponieważ druga instrukcja wywołuje
akcesor s e t właściwości NumberOfCows,
NumberOfCows = numberOfCows; ^ który z kolei potrzebuje użyć wartości pola
feedM ultiplier do ustaw ienia BagsOfFeed.
} Potrzebna nam j e s t możliwość rozróżnienia Poniew aż s łowo „th is" j e s t referencją do aktualnego
pola o nazwie feedM ultiplier od parametru obiekt u , t h is.feedM ultiplier odwołuje s ię do pola.
o t ej sam ej nazwie. To właśnie w t akich Gdy by ś pominął „this", nazwa feedM ultiplier zostałaby
s y tuacjach niezwykle przydatne okazu je potraktowana jako parametr. A zatem pierw szy w iersz
s i ę słowo kluczowe „this". konstruktora zap isu je wartość drugiego parametru
wywołania w prywatnym polu o nazwie feedM ultiplier.
■Nie .istnieją.
głupie pytania
O : Oczywiście. Jest to dość powszechna praktyka i klasy często (Pamiętaj, każdy formularz jest tylko kolejnym obiektem,
który do wyświetlania okien, przycisków i innych kontrolek używa
mają takie konstruktory. W zasadzie to już widziałeś jeden
przykład — konstruktor Twojego fo rm ularza. Zajrzyj do środka metod udostępnianych przez platformę .NET i przestrzeń nazw
świeżo utworzonego formularza Windows i znajdź deklarację jego System .W indows.Forms).
konstruktora:
_ Zaostrz ołówek
Przyjrzyj się dokładnie akcesorom get i set przedstawionym w poniższym przykładzie.
Formularz używający klasy C a b le B ill dysponuje jej nową instancją, zapisaną w zmiennej
o nazwie thisM onth, i po kliknięciu przycisku wywołuje jej metodę C alculateA m ount().
Określ wartość wyświetloną w każdym przypadku w okienku informacyjnym.
class C ableBill {
p riva te in t rentalFee;
p ub lic C a b le B ill(in t rentalFee) {
th is .re n ta lF e e = rentalFee;
discount = fa ls e ;
}
p riva te in t payPerViewDiscount;
p riva te bool discount;
p ub lic bool Discount {
set {
discount = value;
i f (discount)
payPerViewDiscount = 2;
else
payPerViewDiscount = 0;
}
}
268 Rozdział 5.
Hermetyzacja
c la s s GumballMachine {
p r iv a t e i n t g u m b a lls;
p r iv a t e i n t p r ic e ;
p u b lic i n t P ric e
{
get
{
r e tu r n p r ic e ;
}
}
p u b lic G u m b a llM a ch in e (in t g u m b a lls , i n t p r ic e )
{
gum balls = th is .g u m b a lls ;
p r ic e = P ric e ;
}
p u b lic s t r in g D ispenseO neG um ball(int p r ic e , i n t c o in s ln s e r te d )
{
if ( t h is . c o in s ln s e r t e d >= p r ic e ) { / / sprawdź p o le
gu m ba lls -= 1;
r e tu r n "Oto tw o ja guma";
} e ls e {
r e tu r n "Wrzuć w ię c e j m onet";
}
}
Użyj całej swojej wiedzy o właściwościach i konstruktorach, aby naprawić program Krystyny do planowania
Ćwiczenie P rzy jf ć Ten nowy program będzie znacznie prostszy i bardziej spójny od jego pierwszej wersji.
^ N umberOfPeopl e = 1°»
270 Rozdział 5.
Hermetyzacja
p r i v a t e v o id fa n c y B o x _ C h e c k e d C h a n g e d (o b je c t s e n d e r , E v en tA rg s e)
{
d i n n e r P a r ty .F a n c y D e c o r a t io n s = fa n c y B o x .C h e c k e d ;
D is p l a y D i n n e r P a r t y C o s t( ) ;
}
p r i v a t e v o id h e a lth y B o x _ C h e c k e d C h a n g e d (o b je c t s e n d e r , E v en tA rg s e)
{
d in n e r P a r ty .H e a l th y O p tio n = h e a lth y B o x .C h e c k e d ;
D is p l a y D i n n e r P a r t y C o s t( ) ;
}
p r i v a t e v o id D is p la y D in n e r P a r ty C o s t( )
{
d e c im a l C o st = d i n n e r P a r t y . C o s t ; Ta metoda aktualizuje k o szt przyjęcia
c o s t L a b e l.T e x t = C o s t . T o S t r i n g ( " c " ) ; w yśw ietlany w formularzu, odwołując
s ię i ^ ^ a w o ś c i C o st za k a ż ó w
} razem, gdy zm ienią s ię opcje wybrane
na formularzu.
Formularz jest teraz prostszy, gdyż nie musi wywoływać metod wykonujących
obliczenia. Obliczenia te są teraz ukryte we właściwości C o s t .
±
Czy zauważyłeś, że nasz nowy form ularz nie musi zbyt w iele robić? Jedyne, czym się
zajmuje, to ustawianie właściwości obiektu na podstawie danych wprowadzonych
Rozwiązania przez użytkownika i zmiana w yniku na podstawie tych właściwości. Przeanalizuj,
w jaki sposób kod obsługujący informacje podawane przez użytkownika oraz
ćwiczeń
w yśw ietlane w yniki jest odseparowany od kodu realizującego obliczenia.
class DinnerParty {
p u b lic const in t CostOfFoodPerPerson = 25;
totalC ost .95M; Teraz, gdy M i a m m ^ są pry w atne i ukryte we w łaściw ości
}
return to ta lC o st; w k£ ? J ns j ^ z g i s ^ ^ ‘"ef apouc™ e ^ a e i^ w
^wry^tyns^ o,!tr^ t^ ^ 3^e^n^ !;^ i '^ i^ ^ ę^auj ^ ^ e e ^ l s^ c>'u cif
272 Rozdział 5.
Hermetyzacja
r- » Zaostrz ołówek
Określ wartość wyświetloną w okienku informacyjnym po wykonaniu tego kodu.
Rozwiązanie
Jaka wartość
1. C ab le B ill j a n u a r y = new C a b l e B i l l ( 4 ) ; zostanie wyświetlona?
M e s sa g e B o x .S h o w (ja n u a ry .C a lc u la te A m o u n t(7 ).T o S tr in g () );
28
42
r- .^Zaostrz ołówek
Zaprezentowany kod ma pewne usterki. Napisz, co według Ciebie jest w nim błędne
Rozwiązanie i w jaki sposób można to naprawić.
^ Drzewo genealogiczne
Twoich obiektów
Czasami CHCIAŁBYŚ być dokładnie taki sam jak Twoi rodzice. Czy kiedykolwiek natknąłeś się
na obiekt, który robiłby praw ie wszystko, czego byś sobie od niego życzył? Czy kiedykolwiek znalazłeś się
w takiej sytuacji, że gdybyś zm ienił dosłow nie kilka rzeczy, obiekt byłby doskonały? Cóż, to tylko jeden
z wielu powodów, które sprawiają, że dziedziczenie zalicza się do najważniejszych koncepcji i technik
w języku C#. Kiedy skończysz czytać ten rozdział, będziesz wiedział, jak rozszerzać obiekty, by móc
wykorzystywać ich zachowania i jednocześnie dysponować elastycznością, która pozwoli Ci te zachowania
modyfikować. Unikniesz p o w ie la nia kodu, przedstaw isz p ra w d z iw y św ia t znacznie dokładniej,
a w efekcie otrzymasz kod ła tw ie js z y do zarządzania.
Ta punkty s ą takia
sam a Jak przy zwykłym
przyjęciu.
25 zł od osoby.
|stnieją dwie opcje kosztów dekoracji. Jeżeli klient sobie a w M rc h
ozdób, koszt wyniesie 7,50 zł od osoby plus dodatlcom o ^ a ta d ^ o r a ^ r a
w wysokości 30 zł. Gdyby chciał rozszerzyć opcję podsta\wową do
fantazyjnych, koszt będzie wynosił 15 zł od ow by plus ^ d n o ra ra ra oprata
w wysokości 50 zł.
j eżeli przyjęcie organizowane jest dla c z te r e j lub mniejszej liczby gości,
użyj tortu 2 0 -centymetrowego (40 zł). W pradcwr^irn razie użyj to rtu
4 0 -centymetrowego (75 zł).
Aplikacja powinna obsługiwać dwa rodzaje przyjęć. UżYj tontrcdld TabCont ro|,
gdzie każda zakładka będzie przeznaczona cda jednego rodzaju.
n m WYSIL ___________
SZARE KOMÓRKI
W przypadku przyjęć urodzinowych nie ma opcji zdrowej. Czy domyślasz się,
dlaczego może to prowadzić do wystąpienia błędów, gdybyś spróbował skopiować
i wykorzystać klasę D innerParty z poprzedniego rozdziału?
276 Rozdział 6.
Dziedziczenie
I1 Nie. istnieją.
istniej .
głupie pytania
P : Dlaczego nie mogę po prostu utworzyć nowej instancji P Skąd mam wiedzieć, co umieścić w nowej klasie?
:
klasy DinnerParty tak jak Michał, gdy chciał porównać
trzy trasy w programie do nawigacji? O : Zanim zaczniesz tworzyć klasę, powinieneś dokładnie
zapoznać się z problemem, który rozwiązujesz. To dlatego
O : Ponieważ po utworzeniu nowej instancji klasy D in n e rP a rty musiałeś porozmawiać z Krystyną — to ona będzie tego programu
miałbyś możliwość zaplanowania dodatkowej imprezy używała. Dobrze, że zrobiłeś obszerne notatki! Na ich podstawie
okolicznościowej. Dwie instancje tej samej klasy mogłyby się możesz utworzyć w klasie metody, pola i właściwości, cały czas
przydać tylko w przypadku, gdybyś chciał zarządzać dwoma mając na uwadze ich zachowanie (co pow inny robić) i ich stan
różnymi kompletami danych tego samego typu. Jeżeli chcesz (co pow inny wiedzieć).
przechowywać dane różnego ty p u , potrzebujesz do tego
różnych klas.
Stwórz program Planista przyjęć wwersji 2.0 Upewnij się, że w szystkie pola
i właściw ości przechowujące
U tw órz nowy p ro jek t — m am y zam iar n apisać dla Krystyny now ą w ersję p ro g ram u , kwoty będą typ u decimal.
k tó ra będzie w stanie o kreślać koszty zarów no przyjęć, ja k i u rodzin. Z aczniem y
o d d o d an ia praw idłow o herm etyzow anej klasy B irth d a y P a rty odpow iedzialnej BirthdayParty
CalculateCostOfDecorations()
CakeSize()
DODAJ NOW Ą KLASĘ BIRTHDAYPARTY DO PROGRAMU
MaxWritingLength()
Już wiesz, ja k p o rad zić sobie z właściwościam i NumberOfPeople
i FancyDecorations — są zwykłymi o d pow iednikam i podobnych
elem entó w w klasie D innerP arty . R ozpoczniem y o d u tw o rzen ia nowej
klasy i d o d an ia ich, a n a stęp n ie dołączym y p o zo stałe zachow ania.
★ D odaj stałą CostOfFoodPerPerson o ra z włąściwości:
NumberOfPeople o raz FancyDecorations . B ędzie Ci także
p o trz e b n a pryw atn a w łaściwość typu i n t o nazw ie actualLength .
(O w szem , także właściwości m ogą być pryw atne!).
p u b lic in t NumberOfPeople { g e t; se t; }
%
278 Rozdział 6.
Dziedziczenie
f c o w e ‘ Poniiei
P ^ w e składnie fcr == ^
p u b l i c bool CakeWritingTooLong
{
get
{ Ta właściw ość definiuje
i f (C akeW riting .L e ng th > M axW ritingLength()) w yłącznie akcesor get, gdyż
w ogóle nie zm ienia stanu
return tru e; obiektu. Używa ona jed ynie pól
else i m etod do wyliczenia wartości
return fa lse ; logicznej.
}
Ta metoda j e s t taka sarna
p r i v a t e decimal C a lc u l a te C o s t O f D e c o r a t io n s ( ) ja k w klasie DinnerParty.
{
decimal c o s t O f D e c o ra t io n s ;
i f (F anc yD ec ora tion s)
c o s t O f D e c o ra t io n s = (NumberOfPeople * 15.00M) + 50M;
else
c o s t O f D e c o ra t io n s = (NumberOfPeople * 7.50M) + 30M;
r e t u r n c o s t O f D e c o r a t io n s ; Klasa BirthdayParty definiuje
} właściw ość Co s t typu
decimal, podobnie jak klasa
DinnerParty. Niemniej jednak
p u b l i c decimal Cost wykonuje ona inne obliczeniai
{ w których j e s t uż ywana
metoda CakeS iz e C) oraz
get właściw ość A c tu a ILength.
{
decimal t o t a l C o s t = C a l c u l a t e C o s t O f D e c o r a t i o n s ( ) ;
t o t a l C o s t += CostO fFoodPerPerson * NumberOfPeople;
decimal c akeC ost;
i f (C akeS ize() == 20)
\
Zerknij na poprzednią stronę
i dokładniej przyjrzyj się , jak w łaściwość
cakeCost = 40M + A ctu alL en gth * .25M; CakeWriting j e s t używana do okreś|enif
else wartości pola ActualLength. Jeśli tekst
cakeCost = 75M + A ctu alL en gth * .25M; j e s t z b y t długi, zwracana j e s t liczba
liter, które faktycznie zm ieszczą s ię na
r e t u r n t o t a l C o s t + cakeC ost; torcie. Kiedy maksymalna liczba liter
zo sta n ie przekroczona, ko szty nie będą
dalej zw iększane.
280 Rozdział 6.
Dziedziczenie
*
2r SKORZYSTAJ Z KONTROLKI TABCONTROL,
BY DODAĆ DO FORMULARZA ZAKŁADKI. Klikaj karty, aby
przełączać s ię między
Przeciągnij k o n tro lk ę TabControl z o k n a Toolbox n a fo rm u larz i zm ień jej ro zm iar tak, nimi. Użyj właściwości
aby zajm ow ała cały jego obszar. Z m ień te k st na każdej karcie, używ ając właściwości TabPages do zmiany
te k s tu każdej kontrolki.
TabPages — przycisk „ . . . ” znajduje się w oknie Properties o b o k właściwości. K iedy go Naciśnij przycisk
klikniesz, ID E wyświetli okno, za p o m o cą k tó reg o w prow adzisz d an e kart. U staw ich i wybierz właściwość
właściwość Text n a „Im p reza okolicznościow a” i „Przyjęcie u ro d zin o w e” . Text każdej karty.
# W klasie fo rm u larza p o trzeb u jesz pól, w których zapiszesz referen cje do obiektów B i r t h d a y P a r t y
i D i n n e r P a r t y . M usisz tak że określić w artości początkow e tych p ó l w k o n stru k to rze form ularza.
b i r t h d a y P a r t y = new B i r t h d a y P a r t y ( ( i n t ) n u m b e r B i r t h d a y . V a l u e ,
f a n c y B ir th d a y .C h e c k e d , c a k e W r i t i n g . T e x t ) ;
D isp lay B irth d ay P arty C o st();
282 Rozdział 6.
Dziedziczenie
%
NO W A WERSJA PROGRAMU JEST GOTOWA — CZAS JĄ WYPRÓBOWAĆ!
U pew nij się, że p ro g ram działa dokład n ie tak, ja k teg o oczekujesz. Spraw dź, czy wyświetla
się odpow iedni ko m u n ik at, gdy napis n a to rcie je st zbyt długi. Z obacz, czy cen a je st zawsze
popraw n a. Jeżeli wszystko działa praw idłow o, zad an ie m o żn a uzn ać za wykonane!
Czy obliczenia s ą
wykonywane prawidłowo?
W tym przypadku 10 osób
oznacza 25 złotych od
osoby (250) plus 75 z ł
za 40-centym etrow y tort,
p lus 7,50 z ł od osoby
za zw yczajne dekoracje,
plus dodatkowe 3 0 z ł
opłaty za ozdoby, plus
0,25 z ł za każdą literę
w napisie (je s t ich 24,
co daje 6 zł).
Kliknij kartę „Przyjęcie
urodzinowe". Upewnij s i ę
ż e cena zm ienia s ię po zm ianie
liczby osób lub zaznaczeniu pola
wyboru „Dekoracje fantazyjne".
A zatem mamy:
2 5 0 + 75 + 75 + 3 0 + 6 = 436 zł.
Zadziałało!
%
z m ieścić na torcie, klasa BirthdayParty
u s tawia wartość właściwości
CakeWritingTooLong na true, a podczas
obliczania kosztu przyjęcia u żywa
maks y malnej idługości napisu. Formularz
nie m usi wykonywać żadnych obliczeń.
%
284 Rozdział 6.
Dziedziczenie
Pośw ięć m inutę i pom yśl, w jaki sposób d odać o p łatę zarów no do klasy D innerP arty ,
ja k i B irth d a yP a rty . Jak i napisałbyś w tym celu k od? G dzie m ógłby się o n znaleźć?
C ałkiem p ro s te ... ale co by się stało, gdybyś m iał trzy p o d o b n e klasy? L ub cztery?
L ub dw anaście? A lbo gdybyś m usiał zarządzać tym k odem i w prow adzać do niego
p o tem więcej takich zm ian? Co by było, gdybyś m iał w prow adzić dokładnie
taką sam ą zm ianę do pięciu lub sześciu blisko związanych ze sobą klas?
Ta strzałka
w diagramie DinnerParty BirthdayParty
klas oznacza, Obie klasy
HealthyOption potomne CakeSize
że DinnerParty
Cost dziedziczą CakeWriting
dziedziczy
z klasy Party. obliczenia Cost
dotyczące
dekoracji
private m ethods: z bazowej, private methods:
CalculateCost więc nie CakeSize()
OfBeveragesPerPerson() m uszą ich MaxWritingLength()
zawierać.
286 Rozdział 6.
Dziedziczenie
Ogólne Ogólne
A W modelu klas s er
może dziedziczyć Każdy ptak j e s t
A
po produkcie zw ierzęciem , ale nie
mleczarskim, każde zw ierzę j e s t
Jed zen ie który z kolei ptakiem. Z w ierzę
moż e dziedziczyć
po jedzeniu.
P r o d u k t m leczarsk i P tak
D ojrzały se r c h e d d a r P rz e d rz e ź n ia c z p ó łn o cn y
Coś, co znajduje ^ ^ y s Z a ^ b u t y
dziedziczy
Szczegółowe z klas powyżej Szczegółowe
to samo dotyczy każdego
s i ę w pary, więc
przedrzeźniacza pofnocnego.
J eżeli mas z p rzepis, w którym
w ym ieniony j e s t se r cheddar,
m ożesz użyć dojrzałego sera D ziedziczyć, czasownik
cheddar. Je <jnak gdy przepis
wymaga użycia sera dojrzałego, — przejmować po rodzicach albo przodkach
to nie mo żesz użyć pierwszego
leps zego — p otrzeb u je sz akurat cechy fizyczne i psychiczne. Chciałaby, aby
teg° k°n kretneg°, dojrzałego sera. dziecko odziedziczyło ¡ej duże, brązowe oczy
a nie drobne, niebieskie oczy męża.
288 Rozdział 6.
Dziedziczenie
r
w ięc ich m e to d a Eat() będzie
m usiała przesłonić A nim al.Eat() .
H ip o p o ta m pływa, w ięc będzie
Jeśli ju ż posiadasz kla sę m iał dod atk o w ą m e to d ę Swim()
potomną dziedziczącą
po klasie bazowej, to musi n ieo b ec n ą w klasie Animal.
ona dziedziczyć w szy stkie
je j zachowania... a.ie może s z
zmodyfikować je w klasie
potomnej tak, aby nie były
identyczne. To do tego słu ży
przestanianie .
WYSIL
SZARE KOMÓRKI
Już wiemy, że niektóre zwierzęta przesłaniają metody MakeNoise() oraz E a t() .
Które z nich będą przesłaniały Sleep () i Roam()? Czy są takie? Jak będzie
wyglądała sprawa z właściwościami — które zwierzęta będą przesłaniały
pewne z nich?
290 Rozdział 6.
Dziedziczenie
Klasy potomne
d ziedziczą po
klasie Animal
w szy stkie
cztery metody,
ale m usim y
przes ł°nić jedynie
M akeN °ise()
oraz Eat().
1
To dlatego
pokazujem y tylko
dw ie metody
w diagramach
klas.
------
[Ą D O K O Ń C Z H IE R A R C H IĘ KLAS.
T eraz, gdy już wiesz, w jaki sposób podzielić Psy i wilki
je d zą i śpią
zw ierzęta, m ożesz d odać klasy F e l i n e o raz C a n in e . w podobny
sposób, jednak
wydają inne
dźwięki.
W zw iązku z tym , że
Feline przestania RoamU,
wszystko, co po niej
dziedziczy, otrzyma nową
metodą RoamO zam iast tej
z klasy Animal.
292 Rozdział 6.
Dziedziczenie
{
R°z s z erza sz klasę
p u b l i c d o u b l e W in g s p a n ; poprzez dodanie
dwukr°pka na końcu
p u b lic v o id F ly ( ) { j ej deklaracji,
a następnie
// k o d p o z w a l a j ą c y p ta k o w i la ta ć
wpisanie klasy,
} po której dziedziczy.
i Nie. istnieją. }
głupie pytania
P : Dlaczego strzałka wskazuje do góry, od klasy ona nawet „świadoma", że jakaś nowa klasa po niej dziedziczy.
pochodnej do bazowej? Czy diagram nie wyglądałby Jej metody, pola i właściwości pozostają dokładnie takie same.
lepiej ze strzałką skierowaną w dół? Klasa pochodna, z drugiej strony, zdecydowanie zmienia swoje
294 Rozdział 6.
Dziedziczenie
«^Zaostrz ołówek
Przyjrzyj się dokładnie modelom klas i deklaracjom, a następnie zakreśl
instrukcje, które nie działają.
i— Zaostrz ołówek
Przyjrzyj się dokładnie modelom klas i deklaracjom,
Rozwiązanie a następnie zakreśl instrukcje, które nie działają.
296 Rozdział 6.
Dziedziczenie
latania. J ^ l Ż k T ^ o ^
w szystko po klasie Bird Vo f ’9“ '’1 d z ie d z ic z y
■pingwinów latających po o k o i c ^ C ^
sytu a cji powinniśmy zrobić? ° * faWaJ
[2 M ożesz p rzek azać tej m eto d zie o b iek t Sandwich, ale rów nie dobrze m ożesz jej p rzek azać instancję klasy BLT.
Poniew aż klasa ta je st rodzajem k anapki, ustaw iam y ją jak o dziedziczącą p o Sandwich.
Z aw sze m ożesz się p o ru szać w dół d iagram u klas — zm ien n a referen cy jn a m oże być
zawsze ustaw io n a n a instancję jed n ej z klas pochodnych. N ie m ożesz je d n a k po ru szać się w górę.
(
L Nie m ożesz jednak przypisać
myS.andMich do zm iennej anotherBLT
P°n“ "to każda kanipka j e t t
typu BLT! To dlatego o s t a w l t r s z
spowoduje wygenerowanie btędu.
Miesz a ne a = 6
b = 5
56
11
Poniżej zaprezentowano krótki program w C#. Jeden
z jego fragmentów jest wycięty! Twoim zadaniem
wiadomości a = 5 65
jest dopasowanie wycinka kodu (po lewej stronie)
i komunikatu wyświetlanego w oknie MessageBox
Ćwiczenie
aplikacji, które pojawia się po jego dodaniu. Nie wszystkie
linijki wyniku mają zostać użyte, a niektóre mogą być
Instrukcje: wykorzystane więcej niż raz. Narysuj linie łączące
1. W ypełnij cztery puste miejsca w kodzie. fragmenty kodu z powiązanymi z nimi wynikami.
2. Dopasuj fragmenty kodu do oczekiwanych rezultatów.
pub lic class A { p u b lic class C : B {
p u b lic in t iv a r = 7; p u b lic ____________ s trin g m3() {
p u b lic ____________ s trin g m1() { return "C m3, " + (iv a r + 6);
return "A ml, }
}
p u b lic s trin g m2() { }
return "A m2, "; p u b lic class Mixed5 {
} p u b lic s ta tic void M a in (s trin g [] args) {
public s trin g m3() { A a = new A ();
return "A m3, "; B b = new B()
Podpowiedz: Naprawdę, dobrze sie
C c = new C() ^ e t a n ó w , co oznacza ten w ? e rlz\o d u .
} A a2 = new C()
s trin g q = " " ; Fragment kodu
p ub lic class B : A { wstawiany jest
p u b lic ____________ s trin g m1() { tutaj
return "B ml, ";
(trzy wiersze)
}
System.Windows.Forms.MessageBox.Show(q);
}
300 Rozdział 6.
Dziedziczenie
Zagadkowy basen
Tw oim zadaniem je st p o b ra n ie fragm entów k o d u z b asen u i w staw ienie
ich w p u ste m iejsca. M ożesz użyć tego sam ego frag m en tu więcej
niż raz i nie m usisz skorzystać ze wszystkich. Celem je st napisanie
zestaw u klas, k tó re się skom pilują i b ę d ą działały razem
w p ro g ram ie . N ie daj się zwieść — to zad an ie je st trudniejsze,
niż Ci się wydaje.
}
public class : Boat {
public move() {
public ...............................................( ) {
return " ............................
return " ......................................." ;
}
}
MjeQZa ne a = 6
b = 5
56
11
wQadomoici a = 5 65
302 Rozdział 6.
Dziedziczenie
■ Nie .istnieją.
głupie pytania
P : Mam pewne wątpliwości P: : Czy mogę dziedziczyć po klasie, P : Co masz na myśli, mówiąc, że mogę
dotyczące punktu wejścia któi
która zawiera punkt wejścia? przesuwać się w diagramie klas w górę,
w „Zagadkowym basenie” — czy to ale nie mogę przechodzić w dół?
oznacza, że mogę mieć program, który OI: : Tak. Punkt wejścia musi być metodą
nie posiada formularza Form1? statyczną, ale nie musi znajdować się O : Gdy posiadasz diagram z klasą, która
w statycznej klasie. (Pamiętaj, słowo kluczowe jest w hierarchii wyżej niż inna, oznacza to,
O : Tak. Kiedy tworzysz nowy projekt s t a t ic oznacza, że nie można tworzyć że jest ona bardziej abstrakcyjna niż ta
Windows Forms Application, IDE tworzy instancji klasy, ale jej metody są dostępne na dole. Bardziej szczegółowe i konkretne
za Ciebie wszystkie jego pliki, włączając zaraz po rozpoczęciu programu. W aplikacji klasy (takie jak S h i r t lub Car) dziedziczą
w to P rogram .cs (który zawiera statyczną z ćwiczenia „Zagadkowy basen" możesz po bardziej abstrakcyjnych (takich jak
klasę z punktem wejścia) oraz Form1.cs wywoływać T e stB o a ts.M a in () z każdej C lo th in g lub V e h ic le ). Jeśli pomyślisz
(zawierający pusty formularz o nazwie innej metody bez deklarowania zmiennej o tym w ten sposób, to z łatwością
Form1 ). referencyjnej lub tworzenia zrozumiesz, że gdy potrzebujesz jakiegoś
Spróbuj tego: zamiast tworzyć nowy projekt nowej instancji obiektu za pomocą pojazdu, to równie dobrze możesz użyć
Windows Forms Application, utwórz projekt instrukcji new). samochodu, jak i motocykla. Jeśli natomiast
pusty, wybierając EmptyProject w miejsce potrzebujesz samochodu, motocykl nie
Windows FormsApplication. Następnie dodaj P W dalszym ciągu nie mogę pojąć,
: będzie dla Ciebie satysfakcjonujący
plik klasy w oknie Solution Explorer i wpisz dlaczego te metody nazywają się Dziedziczenie działa dokładnie tak samo.
wszystko, co znajduje się w rozwiązaniu „virtual” — wydaje mi się, że są Gdy masz metodę z parametrem V e h ic le ,
„Zagadkowego basenu". W związku z tym, rzeczywiste!
a klasa M o to rc y c le dziedziczy z V e h ic le ,
że Twój projekt używa okna MessageBox,
musisz dodać referencję. Kliknij prawym
O : Nazwa „virtual" jest związana
to możesz do niej przekazać instancję klasy
M o to rc y c le . Jeśli jednak funkcja jako
ze sposobem, w jaki .NET radzi sobie
przyciskiem myszy References w oknie parametr przyjmuje obiekt M o to rc y c le ,
z metodami wirtualnymi w tle. Używa
Solution Explorer, wybierz Add Reference, nie możesz przekazać do niej żadnego
w tym celu czegoś, co nazywamy tablicą
przejdź do zakładki .NET i znajdź System. obiektu V e h ic le , ponieważ mógłby on być
metod wirtualnych (lub v ta b le ).
Windows.Forms. (To jest kolejna z wielu rzeczy, instancją klasy Van. Gdybyś mimo wszystko
Jest to tablica zarządzana przez .NET
które IDE robi za Ciebie automatycznie tak zrobi, C# mógłby mieć spore problemy
i przechowująca informacje związane
podczas tworzenia projektu Windows Forms z dostępem do właściwości Handlebars!
z dziedziczeniem. Wie, które metody zostały
Application). Na samym końcu wybierz
odziedziczone, a które przesłonięte. Nie
Properties z menu Project i jako typ aplikacji
wybierz WindowsApplication.
przejmuj się — nie musisz wiedzieć, jak Zawsze m ożesz
to działa, aby używać metod wirtualnych!
Teraz spróbuj to uruchomić... a zobaczysz
rzekazać instancję
wszystkie wyniki! Gratulacje, właśnie C l a s s V iew ▼ nx asy pochodnej
o o |a -
stworzyłeś program C# od podstaw. %
do każdej metody,
< S e a rc h > P ±
t ,
Jeżeli potrzebujesz odśw ieżyć
.
informacje
a [c5] A n a l i z a t o r K a n a p e k
> li P ro je c t R e fe re n c e s
- która spodziewa
ia tem a t metody Mam 1 punktu wejścla i a {> A n a l iz a to r K a n a p e k się parametru
wróć do początku rozdziału 2 -! a % BLT
a li B a s e T y p e s w postaci instancji
Okno Class View można w yśw ietlić przy >
k
F o rm !
M n---- VS. ▼ klasy bazowej.
użyciu menu VIEW, a jest to kolejne C o u n tC a lo rie sQ
Z a m ia st aplikacj ty p u W rn d w s
Forms Application tym r n z ^
stw orzym y aplikację kjm sj|jw ą•
Z ró b t Oznacza to, że nie będzie
304 Rozdział 6.
Dziedziczenie
class Owner {
p riv a te Jewels returnedContents;
p u b lic void ReceiveContents(Jewels safeContents) {
returnedContents = safeContents;
Console.W riteLine("Dziękuję za zwrócenie klejnotów! safeContents.SparkleO ):
}
}
[4 KLASA JEWELTHIEF DZIEDZICZY PO LOCKSMITH.
Locksmith
Z łodzieje klejnotów to ślusarze, którzy zeszli n a złą drogę. P o tra fią o tw ierać sejfy,
lecz zam iast oddaw ać um ieszczone w nich klejnoty właścicielom , k ra d n ą je.
class Locksmith {
p u b lic void OpenSafe(Safe safe, Owner owner) {
O p e n S a fe ()
sa fe .P ickL o ck(th is);
W rite D o w n C o m b in a tio n ()
Jewels safeContents = safe.Open(writtenDownCombination); R e tu rn C o n te n ts()
ReturnContents(safeContents, owner); M etoda O penSafe() obiektu Locksm ith
} ,p obiera kod do otworzenia sejfu , otwiera
g°, po czy m zwraca jego zaw artość
właścicielowi.
p riv a te s trin g writtenDownCombination = n u ll;
p u b lic void WriteDownCombination(string combination) { JewelThief
p rivate sto le n Je w e ls
writtenDownCombination = combination;
}
}
}
Czy sądziłeś, że p ro g ram w ygeneruje in n e wyniki? M oże spodziew ałeś się takiego kom unikatu:
W ygląda n a to, że złodziej zachow ał się jak praw o rząd n y ślusarz! C óż zatem się stało?
Error L ist ^ n x
T » in 1 W a rn in g | © 0 Messages Search Error List p -
306 Rozdział 6.
Dziedziczenie
I w łaśnie ta k się dzieje. Jeśli będziem y dysponow ać o b iek tem klasy Jew elThief i wywołamy jego m eto d ę
R eturnC ontents() , używ ając przy tym zm iennej referencyjnej typu Jew elThief , to wywołamy jej now ą
w ersję. Jeśli je d n a k skorzystam y ze zm iennej referencyjnej typu Locksmith , to zostanie w yw ołana ukryta
w ersja m etody zdefiniow ana w klasie bazowej.
G dy tylko dodasz new do deklaracji m eto d y ReturnContents() w klasie Jew elT hief , ostrzeżenie zniknie,
je d n a k p ro g ram w ciąż nie będzie działać tak, ja k byśmy teg o oczekiwali! N ad a l wywoływana b ędzie w ersja m etody
ReturnC ontents() z klasy Locksmith . D laczego? Poniew aż jest o n a wywoływana z poziom u metody zdefiniowanej
w klasie Locksm ith — a k o n k retn ie z m eto d y Locksm ith.OpenSafe() . Fakt, że cały p ro ces został zainicjow any
przez o b iek t Jew elT hief , nie m a tu żadnego znaczenia. Jeśli klasa Jew elThief jedynie ukrywa m eto d ę
R eturnC ontents() , to jej w łasna w ersja tej m eto d y nigdy nie zostanie wywołana.
Czy domyślasz się, co należy zrobić, aby metoda R eturnC ontents() została przesłonięta,
a nie ukryta? Spróbuj zastanowić się nad tym , zanim przejdziesz na następną stronę!
class Jewe!Thief:Locksmith {
Q1 ,Zlodziej_klejnotow Jew elThief.R eturnC ontents(Zlodziej_klejnotow Jew els/ Z lodziej_klejnotow .O w ner)': JewelThief.es 12 30 Złodziej kle jn o tó w
c a nn ot override inherited m e m b e r 'Zlodziej_klejnotow .Locksm ith.R eturnC ontents
(Zlodziej_klejnotow Jew els, Zlodziej_klejnotow .O w ner)' because it is n o t marked virtu a l, abstract, or
override
class Locksmith {
T eraz ponow nie spróbuj u ru ch om ić p ro g ram . O to jaki re z u lta t pow inieneś uzyskać:
308 Rozdział 6.
Dziedziczenie
Jeśli chcesz
przesłonić metodę
z klasy bazowej,
to zawsze oznacz
użyciu
Dokładnie. W większości przypadków
będziesz chciał przesłaniać metody,
ale możesz je też ukrywać.
O kluczowego
virtual , a w jej
R ozszerzając jakieś klasy, w tw orzonych klasach
pochodnych będziesz zapew ne częściej używał
nowej wersji
przesłaniania niż ukryw ania m eto d . K iedy zatem w klasie pochodnej
zauważysz, że k o m p ilato r o strzega Cię p rzed
ich ukryw aniem , nie ignoruj tego! U pew nij się, użyj słowa
że n apraw dę chcesz ukryć jak ąś m eto d ę, a nie
jedynie zapom niałeś użyć słów kluczowych v i r t u a l
kluczowego
i o v e r r id e . Jeśli zawsze będziesz praw idłow o override . Jeśli
stosow ał słow a v i r t u a l , o v e r r i d e i new, to już
nigdy nie nap o tk asz takich p roblem ów ja k ten,
tego nie zrobisz,
który przedstaw iliśm y w tym przykładzie. możesz niechcący
ukryć metodę
klasy bazowej.
K am eleony zdobyw ają jed zen ie, łapiąc ow ady językiem . K lasa Chameleon
dziedziczy więc p o V e rteb ra te i p rzesłan ia m eto d ę E at() .
p u b lic class Chameleon : Vertebrate {
pub lic override void Eat(Food morsel) {
CatchWithTongue(morsel)
(morsel
Swallow(morsel)
D ig e st(); K Z 'd !e0n mU5i p ofy kać 1 traw ić jed zen ie tak jak
} " J POOW einf ZWJ e rzę - Czy naprawdę m usim y w ięc
p °w ielać ten fragm ent kodu?
}
Z am iast pow ielać kod, m ożem y użyć słow a kluczow ego base do w yw ołania m etody,
k tó ra została p rzesło n ięta. T e ra z m am y do stęp zarów no do starej, ja k i do nowej jej wersji.
p u b lic class Chameleon : Vertebrate {
pub lic override void Eat(Food morsel) {
CatchWithTongue(morsel);
base.Eat(morsel); Ten w iersz wywołuje m etodę Eat() z klasy
} bazowej dziedziczonej przez Cbameleon-
}
T eraz, kiedy ju ż m iałeś okazję p o zn ać n iek tó re pojęcia zw iązane z dziedziczeniem , m am y dla C iebie p roblem
do przem yślenia. Oczywiście w ielo k ro tn e wykorzystywanie k o d u je st dobrym sposobem , by zaoszczędzić sobie
n iep o trzeb n eg o n aciskania klawiszy, je d n a k kolejną w ielką z ale tą dziedziczenia je st u łatw ienie późniejszego
zarządzania kodem i jego pielęgnacji. Czy potrafisz podać a rg u m en ty potw ierdzające tę tezę?
310 Rozdział 6.
Dziedziczenie
klasa dziedzicząca p o niej musi wywoływać jeden z tych konstruktorów . klasu h mUS' wy konać konstruktor
klasy bazowej za każdym razem,
K o n stru k to r klasy p o ch o d n ej m oże m ieć inne p aram e try niż k o n stru k to r bazowej. gdy j e s t tworzona klasa pochodna
Kiedy ostatni raz widziałeś się z Krystyną, miałeś ukończoną klasę do szacowania Jeśli dobrze to rozegramy,
kosztów przyjęć urodzinowych. Teraz chciałaby ona, aby program wprowadzał powinniśmy być w stanie
dodatkow ą o p łatę w wysokości 100 zł za p rzyjęcia powyżej 12 o sób . Wydawać by zmienić dwie klasy bez
się mogło, że musisz napisać ten sam kod dwukrotnie, po jednym razie w każdej dokonywania żadnych zmian
klasie. Teraz, gdy już wiesz, jak działa dziedziczenie, możesz wyprowadzić obie w form ularzu!
klasy z klasy bazowej, która będzie zawierała cały współdzielony kod. Musisz więc
napisać to wszystko tylko raz.
312 Rozdział 6.
Dziedziczenie
Najtrudniejszą częścią tego zadania jest określenie, które części obu właściwości C o st z klas pochodnych
należy przenieść do klasy bazowej P a r t y . Trudność polega na tym, że można to zrobić na wiele sposobów.
Można utworzyć w klasie bazowej P a r t y automatyczną właściwość C o st i pozostawić właściwości C o st
w obu klasach pochodnych w niezmienionej postaci. Jednak w tym ćwiczeniu Twoim zadaniem jest
przeanalizowanie właściwości C o st w klasach D in n e r P a r ty oraz B ir t h d a y P a r t y , wskazanie ich elementów
wspólnych i przeniesienie możliwie jak największego fragmentu kodu do klasy bazowej.
A oto niewielka podpowiedź: W obu klasach, D in n e r P a r ty oraz B ir t h d a y P a r t y , właściwość C o st
powinna się zaczynać w następujący sposób:
o v e r r id e p u b lic decim al C ost {
get {
decim al t o t a lC o s t = b a s e .C o s t;
Nie zapomnij uwzględnić we właściwości C o st w klasie P a r t y , że do ceny imprez mających powyżej
12 uczestników należy d odać 100 zł.
314 Rozdział 6.
Dziedziczenie
p riva te in t CakeSize() {
i f (NumberOfPeople <= 4)
return 20;
else
return 40;
}
p riva te in t MaxWritingLength() { W ła śc iw o śc i
i f (CakeSize() == 20) C a keW ritin g
return 16; i A c tu a lL e n g th or<xz
u ż y w a n e p r z e z n ie
else 4- m e to d y p o zo s t a ły
return 40; w k la s ie B ir th d a y P a rty .
} P odobnie ja k
w ła śc iw o ść
p ub lic bool CakeWritingTooLong { CakeWritingTooL-omg.
get {
i f (CakeWriting.Length > MaxWritingLength())
return tru e ;
else
return fa ls e ;
I
I Pierw sze dwa w iersze kodu
właściwości C ost przenieśliśm y
override p u b lic decimal Cost j d o k l a s y bazowej, 9 ^ . ^
get j identyczne w klasach D mnerParty oraz
decimal to ta lC o st = base.Cost ;
decimal cakeCost;
i f (CakeSize() == 20) k la sy B ir th d a y P a r ty j e s t
b a s e . C o s t w celu wykonania tych
cakeCost = 40M + ActualLength * .25M;
dwóch w ierszy kodu.
else
cakeCost = 75M + ActualLength * .25M;
return to ta lC o st + cakeCost;
I
I
}
PROGRAM JEST PERFEKCYJNY. TERAZ JEST M I Z N AC ZN IE ŁATW IEJ
PROW ADZIĆ INTERES. W IELKIE DZIĘKI!
P ielęgn acja ja j
Nauczanie p szczółek
Ta robotnica potrafi wykonywać dwie
prace: pielęgnować j aja i uczyć,
mate pszczółki; dlatego je j tablica
jobsICanDo zaw iera dwa elem enty .
Utrzymanie ula
Patrol z żądłami
Królowa dysponuje polem
o nazw ie workers,
w którym przechowuje To bardzo wszechstronna
obiekty w szystkich robotnica. Potrafi
pszczół w ulu. wykonywać aż sze ść
różnych prac.
Zbieranie nektaru
Tablica robotnic j e s t prywatna, gdyż Wytwarzanie miodu
żaden inny obiekt nie powinien mieć
możliwości mówienia pszczołom, co P ielęg n a cja ja j
mają robić; z tego względu w szystkie Nauczanie p szczó łek
robotnice m uszą być utworzone
w konstruktorze. Utrzymanie ula
Patrol z żądłami
318 Rozdział 6.
Dziedziczenie
F o rm u la rz tw o rz y ta b lic ę p szczó ł ro b o tn ic . N a s tę p n ie , d la ka żd e j z n ich ,
tw o rz y n o w y o b ie k t W o rk e r i d o d a je go do ta b lic y . Konstruktor każdego M ^ t i J W ^ k a r pobiera
iedern parametr — tablicę ł a ń c u c h zntztow
określającą prace, kt<5re p s z r n fo j e s t w s to n u
Worker[] workers = new Worker[4]; wykonywać.
workers[0] =new Worker(new str in g [] { "Zbieranie nektaru", "Wytwarzanie miodu" });
workers[l] =new Worker(new str in g [] { "Pielęgnacja ja j" , "Nauczanie pszczółek" });
workers[2] =new Worker(new str in g [] { "Utrzymanie ula", "Patrol z żądłami" });
workers[3] =new Worker(new str in g [] { "Zbieranie nektaru", "Wytwarzanie miodu"
"Pielęgnacja ja j" , "Nauczanie pszczółek", "Utrzymanie ula", "Patrol z żądłami" });
queen = new Queen(workers);
t
Formularz z aw i'era pole przechowują c e referencję do obiektu klasy Queen, który Kiedy z o staje Miknięty przycisk
j e s t inicjalizowany poprzez p rzekazanie do konstruktora tablicy obiektów Worker przydz'ielający ps zczołom pracę,
zo sta je wywołana m etoda
A s s ignWork() klasy Queen, która
spraw dza, c zy s ą dostępne
psz c zoty.
K ró lo w a s p ra w d z a ka żd ą
z ro b o tn ic , by p rze ko n a ć AssignWork("Utrzvmanie u la ", 41 • .
się , k tó ra z n ich je s t
w s ta n ie w y k o n a ć
k o n k re tn ą p ra cę .
Formularz wywołuje metodę AssignWork()
i
M etoda AssignW ork() obiektu królowej
oblektu Icrólowiel l<tóra przegląda dostępne
robotnice I dla każdej z nich wywołuje metodę
D°Th isJ°b( ) ; sprawdzanie kończy się, gdy
6/ekt
przegląda tablicę robotnic, wywołując uda się znaleźć robotnicę, która potrafi wykonać
dla każdej z nich m etodę DoThisJob(); zadani'e. Jeśli takiej robotnicy nie uda się znaleźć,
robi to tak długo, aż znajdzie pszczołę, metoda AssignWork() zwraca f a l se.
która wykona zadanie.
Królowa pyta pszczołę, czy
j e s t w sta n ie wykonywać pracę
utr-zyrncm^ ula" p rzez cztery zmiany.
V
K ró lo w a m oże D o T hisJob("Utrzymanie u l a "
p rz y p is y w a ć
zad a n ia ro b o tn ic o m ,
a n a s tę p n ie kaza ć Jeśli robotnica już wykonuje jakieś zadanie, zwraca w artość
im p rz e p ra c o w a ć 6/ekt fa ls e . W przeciwnym razie sprawdza swoją tablicę
j ° bsICanD° . Jeśli znajdzie w niej tę Samą pracę, zwraca true e / r t M'J0
k o le jn ą zm ianę.
w przeciwnym razie zwraca f a l se.
Królowa potrzebuje Twojej pomocy! Wykorzystaj wszystko, czego się nauczyłeś o klasach i obiektach, żeby
napisać system do zarządzania ulem, który pomoże jej zarządzać pszczołami. W tej pierwszej części projektu
zaprojektujesz formularz, dodasz do niego klasy Queen oraz W orker i uruchomisz podstawową wersję systemu
Ćwiczenia
Czasami diagramy klas pokazują pola pryw atne i typy.
P ro g ram p o siad a jed en o b iek t Queen, który zarząd za w ykonyw aną pracą.
Q ueen
★ K lasa Queen używa tablicy obiektów W orker do k o n tro lo w an ia każdej pszczoły
private workers: Worker[]
ro b o tn icy i n a jej podstaw ie m oże stw ierdzić, czy m ają o n e przypisane zadania.
private shiftNumber: int
W artości przechow yw ane są w pryw atnym p o lu W o rk er[] o nazw ie w o rk e rs.
★ F o rm u la rz wywołuje m eto d ę A s s ig n W o rk (), p rzek azu jąc do niej łańcuch
znaków określający zad an ie do w ykonania o raz liczbę typu i n t oznaczającą
AssignWork() ilość zm ian. M eto d a zwróci t r u e , jeże li o d n ajd zie w olną ro b o tn icę do
WorkTheNextShift() w ykonania pracy, lub f a l s e , gdy nie będzie m ogła żadnej znaleźć.
★ Przycisk Przepracuj następną zm ianę wywołuje m eto d ę W o rk T h e N e x tS h ift().
N ak azu je o n a pszczołom wykonywać pow ierzone im z a d a n ia i zw raca gotowy
do w yśw ietlenia ra p o r t zm iany. Przycisk każe każd em u obiektow i typu W orker
pracow ać p rzez je d n ą zm ianę, a n astęp n ie spraw dza jego statu s, aby możliwe
było d o d an ie kolejnego w iersza do rap o rtu .
CurrentJob i S h ifts L e ft s ą ★ Przyjrzyj się uważnie zrzutow i e k ra n u zam ieszczonem u n a n astęp n ej stronie, by
właściwościami tylko do odczyto. określić, jakie są efekty działania m etody W o rk T h e N e x tS h ift(). W pierwszej
kolejności generuje o n a łańcuch znaków („R ap o rt zm iany n u m er 20”). N astępnie
W o rk e r używa pętli f o r , by dla każdego o biektu W orker w tablicy w o rk e rs [ ] wykonać
dwie instrukcje i f . Pierwsza z nich spraw dza, czy robotnica zakończyła aktualne
CurrentJob: string zadanie („R obotnica n u m er 2 zakończyła swoje zadanie”). D ru g a instrukcja i f
ShiftsLeft: int spraw dza, czy robotnica aktualnie pracuje, a jeśli tak, to wyświetla, ile zmian
zajm ie jej jeszcze wykonywanie bieżącej pracy.
private jobsICanDo: string[] K rólow a używa tablicy obiektów W orker do zarząd zan ia g ru p ą ro b o tn ic i pracam i
private shiftsToWork: int p rzez nie wykonywanymi.
private shiftsWorked: int ★ C u r r e n tJ o b je st właściwością tylko do odczytu, k tó ra pozw ala królowej
określić p ra c ę a k tu a ln ie w ykonywaną przez d a n ą ro b o tn icę (Patrol z żądłam i,
DoThisJob()
Utrzymywanie ula i in n e). Jeżeli ro b o tn ica nie w ykonuje żadnego zadania,
WorkOneShift()
zostaje zw rócony pusty łańcuch znaków.
★ O b iek t Queen p ró b u je przypisać zad an ie robotnicy za p o m o cą jej m etody
D o T h is J o b ( ). Jeżeli je st o n a ak tu aln ie w olna i je st to p raca, k tó rą dany typ
pszczoły p o trafi wykonywać, to z a d an ie zo staje zaakceptow ane, a m e to d a
zw raca t r u e . W przeciw nym razie otrzym ujem y f a l s e .
★ Kiedy wywoływana jest m etoda W o rk O n eS h ift(), pszczoła pracuje przez
je d n ą zmianę. Przechowuje ona pole określające, ile jeszcze zmian potrzeba do
zakończenia aktualnej pracy. G dy zadanie zostaje wykonane, czyści pole określające
nazwę bieżącej pracy, dzięki czem u może przyjąć następne zadanie. M etoda zwraca
tr u e , jeśli pszczoła zakończyła zadanie, lub f a l se — w przeciwnym razie.
S tring.IsNullOrEmpty()
W: | » o h ' k £i p z ^ p n c a h o c j; a lz y u n ^ is n iw a n r o ib ia p o ^ w i^ w ^
Cu"-ent J ° b ,est łańcuchem pust ym lub zawiera wartość null, a false - w przeciwnym razie.
320
Dziedziczenie
STWÓRZ FORMULARZ.
Formularz jest dość prosty — cała jego logika znajduje się w klasach Queen i Worker. Posiada on prywatne
pole Queen i dwa przyciski, które wywołują metody AssignWork() oraz W orkTheN extShift() . Będziesz musiał
dodać kontrolkę ComboBox dla zadań realizowanych przez pszczoły (spójrz na odpowiedni zrzut, aby zobaczyć
je wszystkie), kontrolkę NumericUpDown, dwa przyciski oraz wielowierszowe pole tekstowe do wyświetlania
raportu zmiany. Będziesz także potrzebował konstruktora klasy formularza — znajduje się pod zrzutem ekranu.
322 Rozdział 6.
Dziedziczenie
p u b lic c l a s s Queen {
p u b lic Q ueen(W orker[] w o rk ers) {
th i s .w o r k e r s = w o rk e rs; Króhwa dba o prywatność tablicy r° b ° tnic, _p ^ ^ a z j ^
} K ó p w a a tto wym ustaw ieniu żadna klasa nie p w r n m
n í ć ° m ° ż n Z iCm zm iany tych wartości... Nie pow ^ a ich
p r i v a t e W orker[] w o rk e rs; n a w e t widzieć, bo t y k króh w (może wydawać roz tiaz y .
p r i v a t e i n t sh iftN u m b er = 0; Konstruktor ustaw ia w r f o t t pola.
Metoda i f (w o rk e rs [i] .D id Y o u F in is h ( ))
WorkTheN extShift() r e p o r t += "R o b o tn ica numer " + ( i + l ) + " z a k o ń c z y ła sw oje z a d a n i e \ r \ n " ;
obiektu Queen i f ( S tr in g .I s N u llO rE m p ty (w o rk e rs [i] .C u r re n tJ o b ))
nakazu je każdej r e p o r t += "R o b o tn ica numer " + (i + 1) + " n ie p r a c u j e \ r \ n " ;
robotnicy pracować e ls e
prze z jedną zm ianę
i f ( w o r k e r s [ i ] .S h i f t s L e f t > 0)
i dodać w iersz do
raportu w zależności r e p o r t += "R o b o tn ica numer " + (i + 1) + " ro b i '" + w o r k e r s [ i] .C u r r e n tJ o b
od je j s ta tu su . + " ' je s z c z e p rz e z " + w o r k e r s [ i ] . S h i f t s L e f t + " z m ia n y \r \n " ;
e ls e
r e p o r t += "R o b o tn ica numer " + ( i + l ) + " zakończy '"
+ w o r k e r s [ i] .C u r r e n tJ o b + " ' po t e j z m i a n ie \ r \ n " ;
}
r e t u r n r e p o r t;
}
}
Formularz używ a sw oje g o pola
Już pokazaliśmy Ci konstruktor. Tu jest reszta kodu klasy formularza: queen w celu p r z e c h o w a n ia
referencji do obiektiu klasy Q ueen
p r i v a t e Queen queen; który z kolei p o siada tablicę _
referencji do obiektów nob°tn ic .
p r i v a t e v o id a s s ig n J o b _ C lic k ( o b je c t s e n d e r , EventA rgs e) {
i f (queen .A ssig n W o rk (w o rk erB eeJo b .T ex t, ( i n t ) s h i f t s . V a l u e ) == f a l s e )
M essageBox.Show("Nie ma d o stęp n y ch r o b o tn ic do w ykonania z a d a n ia '"
+ w o rk erB eeJo b .T ex t + ...... , "Królowa p s z c z ó ł m ó w i..." ) ;
e ls e
M essageB ox.Show ("Zadanie '" + w o rk erB eeJo b .T ex t + " ' b ę d z ie ukończone za
+ s h i f t s .V a l u e + " zm iany", "Królowa p s z c z ó ł m ó w i..." ) ;
} Przycisk assignJob wywottuje
m etodę AssignW ork() stu żącą
p r i v a t e v o id n e x tS h i f t _ C l ic k ( o b je c t s e n d e r , EventA rgs e) { do przypisyw ania robotnicom
r e p o r t.T e x t = q u ee n .W o rk T h e N e x tS h ift(); zadań i wyśw ietlania
stosownego komunikatu
M essageB ox w zależności
od tego, czy udato s ię zn aleźć
robotnicę zdolną do wykonania
pracy, czy nie.
Użyj dziedziczenia,
aby rozszerzyć system zarządzania pszczołami
Teraz, gdy masz już uruchomiony system podstawowy, użyj dziedziczenia do kontroli konsumpcji
miodu przez każdą pszczołę. Różne typy pszczół spożywają różne jego ilości, ale i tak to
królowa spożywa najwięcej ze wszystkich. Użyjesz zatem technik, które poznałeś podczas nauki
dziedziczenia, do utworzenia klasy bazowej Bee oraz klas pochodnych Queen i Worker.
....- ..... ..
Q ueen W o rk e r
Także klasa królowej private workers: Worker[] CurrentJob: string
będzie m usiała
dziedziczyć po klasie private shiftNumber: int ShiftsLeft: in t
B ee i wywoływać metodę private jobsICanDo: string[]
PoneyConsumptionRate(), private shiftsToWork: in t
by dodać do raportu private shiftsW orked: in t
informacje o miodzie AssignWork()
spożytym podczas WorkTheNextShift()
zmiany. DoThisJob()
DidYouFinish()
override
C HoneyConsumptionRateQ
Z aw SM gdy ^
utworzenie nowe9 ° F™ ' ,(i nie5pOC?ziewanie zajdzie ta k a P ° j L em Y nazwy projektu
pierwszego rozw,?zan^ e P t prawym przyask,e ^ ^ 5tat-ego
powtórne wykorzystanie p 1 -j Acy Existing Item, prze) katalogu projektu
;,o
324 Rozdział 6.
Dziedziczenie
Jeszcze nie skończyliśmy! Królowa odebrała właśnie telefon od swojej kwatermistrzyni, z informacją że
musi wiedzieć, ile miodu wydaje ul na potrzeby żywieniowe swoich robotnic. Jest to wspaniała okazja
Ćwiczenia do wykorzystania Twoich nowych umiejętności dziedziczenia! Dodaj klasę Bee i użyj jej do wyliczania
spożycia miodu podczas każdej zmiany
★ Zmodyfikuj klasę Queen, by dziedziczyła po Bee. Będziesz musiał dodać do jej konstruktora parametr
typu double o nazwie weightMg, który będziesz następnie przekazywał do konstruktora klasy bazowej.
★ Zmodyfikuj także klasę Worker, by dziedziczyła po Bee — w jej konstruktorze będziesz musiał
wprowadzić taką samą zmianę jak w konstruktorze klasy Queen.
P°dp°wiedź: M ożesz w ykorzystać k o i v u n i ^ btę du „nie
zaw iera konstruktora“, który z o b a c z y ć ju ż w p °p rz e dním
rozdziale! Zmodyfikuj klasę Worker, by d z ^ d ^ c z y ^ po
Bee, a n astęp n ie zbuduj projeM. K'iedy IDE w yśw iett'i btąo,
dwukrotnie go kliknij, a IDE a u to m a ty c z n i przej dz ie p r n s i
do konstruktora klasy Worker. To b a i-fa wyg odne!
Możesz użyć IDE, by ułatwić sobie pracę. Przejdź do klasy Worker i wpisz p u b lic o verrid e
— kiedy wpiszesz znak odstępu na końcu, IDE automatycznie wyświetli listę wszystkich metod,
które możesz przesłonić:
public override
0 Equals(object obj)
O GetHashCodeQ
0 |_______ double Bee.HoneyConsumptionRateQ
0 ToStringO
M WYSIL
C5CJ SZARE KOMÓRKI
Wszystkie pszczoły dysponują metodą HoneyConsumptionRate(),
a zarówno obiekty Queen, jak i Worker są pszczołami, gdyż ich klasy
dziedziczą po Bee. Czy zatem nie powinno być jakiegoś jednego spójnego
sposobu wywoływania tej metody dla dowolnego obiektu Bee, niezależnie
od tego, jakiego typu by on nie był?
326 Rozdział 6.
Dziedziczenie
V
konstruktorów obiektów typów Worker i Queen
m usi zo sta ć dodana waga p szczoty.
ret urn report ; Po dodaniu do raportu infcrmacji o sta n ie wszy stk ich
robotnic królowa m usi jed yn ie M t ó w iersz
z informacją o całkowitym sp czy ciu miodu.
328 Rozdział 6.
7. Interfejsy i klasy abstrakcyjne
d j , s ię ' ze i d z i e m y m usieli
przechowywać różne dane robotnic
zależności od wykonywanej
przez nie pracy.
330 Rozdział 7.
Interfejsy i klasy abstrakcyjne
Czy p a m iętasz,
że królowa potrzebuje
To tutaj i^datkow ej ilości
przechowywane są mi°d u ? W tym m iejscu
informacje na tem at p rze s toniliś my je j metodę
wag i i spożycia GetHoneyConsumption().
miodu.
c la s s N e c ta rC o lle c to r : Worker
{
p u b lic in t Nectar { get; se t; }
StingPatrol NectarCollector
p u b lic void F in d F lo w e rs (){...}
StingerLength Nectar
AlertLevel p u b lic void G a th e rN e c ta r(){...}
p u b lic void R eturnT oH ive(){...}
}
SharpenStinger() FindFlowers()
LookForEnemies() GatherNectar()
Sting() ReturnToHive()
<W> WYSIL
1Ą te klasy p rzechow ują'
SZARE KOMÓRKI
informacje specyficzne dl Co by się stało, gdybyś m iał pszczołę,
danego rodzaju pracy.
która musiałaby żądlić i zbierać nektar?
Interfejs w ym aga tego, aby klasa miała określone metody. Chodzi generalnie ;
o to, że kom pilator generuje błąd, jeśli nie znajdzie wszystkich metod wymaganych wię i ej' obie m etod y > DefendTheHive()
oraz ^Aj ternateDe fendTheHive(), różniłyby
przez interfejs w każdej z klas, które go implementują. Metody mogą być napisane f ę tje dynie typem param etru. Gdyby
bezpośrednio w danej klasie lub mogą być dziedziczone po klasie bazowej. Interfejs królowa chciała nauczyć obrony pszczoły
z ajm ujące s ię op ieką nad jajam i lub
nie zajmuje się tym, w jaki sposób one i właściwości się tam znalazły. Ważne jest, utrzym yw aniem ula, to konieczne byłoby
że są dostępne podczas kompilacji kodu. dodanie kolejnych metod. Powstałby
stra szn y bałagan!
332 Rozdział 7.
Interfejsy i klasy abstrakcyjne
334 Rozdział 7.
Interfejsy i klasy abstrakcyjne
f ^ ^
(o STWÓRZ NOW Ą APLIKACJĘ KONSOLOWĄ
I DODAJ NO W Y PLIK KLASY ISTINGPATROL.CS.
IDE doda plik zawierający wiersz c la ss IS tin gP atrol. Zastąp go wierszem z kodem in t e r f a c e
I S t in g P a t r o l i wpisz w nim zawartość interfejsu IStingPatrol przedstawiony dwie strony wcześniej.
Właśnie dodałeś interfejs do swojego projektu! Teraz będziesz już mógł skompilować program.
Description File
© 1 'ImplementacjajnterfejsuJStingPatro Bee1does not implem nt interface lember '1 ti pl ementa cjajnterfejsi. JStingPatrol,IStingPatrol,SharpenStinger(¡nt}' Bee.cs
■
© 2 'ImplementacjajnterfejsuJStingPatro Bee1does not implem nt interface lember '1Tiplementacja_interfejsLJStingPatrol.lStingPatrol.LookForEnemiesQ1 Bee.cs
«
© 3 'ImplementacjaJnterfejsuJStingPatro Bee1does not implem nt interface lember '1 ti pl ementa cjaJnterfejsL J StingPatrol. 1Stin g Patro1,Sting erLength1 Bee.cs
«
© 4 'ImplementacjaJnterfejsuJStingPatro Bee1does not implem nt interface lember '1 ti pl ementa cjaJnterfejsL JStingPatrol.lStrngPatrol,AlertLevel' Bee.cs
r® Tak wygląda klasa TallGuy i kod metody Main umieszczonej w pliku Program.cs, który tworzy jej instancję
za pomocą inicjalizatora obiektu oraz wywołuje jej metodę T alkA bo utY ou rself() . Nic nowego — będziemy
jej używać za chwilę:
p u b lic class TallGuy {
p ub lic s trin g Name;
p ub lic in t Height;
p ub lic void TalkAboutYourself() {
Console.WriteLine("Nazywam się " + Name + " i mam "
+ Height + " centymetrów w z ro s tu .");
}
s ta tic void M a in (s trin g [] args) {
TallGuy ta llG uy = new TallGuy() { Height = 74, Name = "Adam" };
tallG uy.T alkA boutY ourself();
}
}
Już wiesz, że wszystko wewnątrz interfejsu musi być publiczne. Nie przyjmuj tego jednak w ciemno.
Dodaj do projektu nowy interfejs IClown , dokładnie w taki sam sposób, w jaki dodawałeś klasę: kliknij
prawym przyciskiem myszy nazwę projektu widoczną w oknie Solution Explorer , wybierz opcję A d d /N ew
Item , a n a stęp n ie w ybierz * 0 imerface _Upewnij się, że plik będzie mieć nazwę IClown.cs. IDE utworzy plik
interfejsu zawierający następującą deklarację:
in te r fa c e IClown
{
Nie m u sisz wpisywać
Teraz spróbuj zadeklarować wewnątrz niego metodę prywatną: „public" wewnątrz
interfejs u , ponieważ
p r iv a t e v o id H o nk(); każda jego metoda
oraz właściwość
Wybierz w IDE B U IL D /B U IL D Solution. Zobaczysz taki oto błąd: autom atycznie sta je
s ię publiczna.
O 1 The modifier 'private' is not valid for this item
Przejdź dalej i u s u ń m o d y fik a to r d o stę p u p r i v a t e — błąd zostanie zlikwidowany, a program będzie się
kompilował poprawnie.
Zanim przejdziesz do następnej strony, sprawdź, czy potrafisz utworzyć resztę interfejsu IClown
i zmodyfikować klasę TallGuy tak, aby poprawnie go implementowała. Twój nowy interfejs IClown powinien
posiadać metodę void o nazwie Honk, która nie przyjmuje żadnych parametrów, oraz właściwość tylko do
odczytu typu s tr in g o nazwie FunnyThingIHave, która ma akcesor g et , ale nie posiada akcesora s e t .
336 Rozdział 7.
Interfejsy i klasy abstrakcyjne
Błędy zostaną wyeliminowane zaraz po tym, jak dodasz wszystkie metody i właściwości
zdefiniowane w interfejsie. Przejdź zatem dalej i zaimplementuj go. Dodaj właściwość
typu s tr in g tylko do odczytu o nazwie FunnyThingIHave z modyfikatorem dostępu
g et , który będzie zawsze zwracał łańcuch znaków duże buty . Następnie dopisz metodę
Honk() , która będzie wyświetlała w oknie konsoli napis z zawartością „Tut tuut!”.
r s s - y g y r a r « r w ^ m sssssm ^
pub lic s trin g FunnyThinglHave {
get { return "duże b u ty"; } nie będzie tego podziew asz.
} jak s ię tego po nich sp
pub lic void Honk() { In terfejs informuje, że p otrzebujesz publicznej
m etody o nazwie Honk, ale nie mówi, co
Console.W riteLine("Tut t u u t ! 1 powinna ona robić. Może nie robić nic — bez
} względu na to, jeżeli metoda ma prawidłową
sygnaturę, kod będzie s ię kompilował.
Teraz Twój kod się skompiluje! Zmodyfikuj kod metody M ain( ), tak by wywoływała
metodę Honk() obiektu TallGuy i wyświetlała na konsoli napis „Tut tuut!”.
.ale to zadziała:
338 Rozdział 7.
Interfejsy i klasy abstrakcyjne
3 REFERENCJA in t e r f e j s u
BĘDZIE UTRZYM YW AŁA OBIEKT PRZY ŻYCIU
Gdy nie ma żadnych referencji wskazujących na obiekt,
wtedy taki obiekt znika. Nie ma zasady mówiącej, że wszystkie
z tych referencji muszą być tego samego typu!
s t i n ^
Referencja interfejsu jest w takich przypadkach równie dobra.
Jeśli bierzemy pod uwagę aspekt przechowywania obiektów,
to działa ona tak samo jak ich własna referencja.
b i f f = n u ll;
Ten obiekt nie znika,
ponieważ defender
w dalszym ciągu
PRZYPISZ NOW Ą INSTANCJĘ na niego wskazuje.
DO REFERENCJI INTERFEJSU.
W zasadzie nie potrzebujesz referencji do obiektu.
Możesz stworzyć nowy obiekt i przypisać go wprost
do zmiennej referencyjnej interfejsu.
i Nie istnieją, .
Za pomocą „is" możesz sprawdzić, głupie pytania
Ale które z pszczół potrafią zbierać nektar? Inaczej mówiąc, chcemy wiedzieć, O : Ależ skąd. To prawda, że właściwości
która z klas implementuje interfejs I N e c t a r C o lle c t o r . Możemy użyć w interfejsach wyglądają bardzo podobnie
słowa kluczowego i s , a otrzymamy dokładnie to, czego potrzebujemy. do właściwości automatycznych —
widać to na przykładzie właściwości Job
lub S h if t s L e f t interfejsu IWorker
W szystkie robotnice znajdują s ię Mamy tablicę pszczół przedstawionego na następnej stronie.
w tablicy B ees . Użyjemy „is" typ u Worker, które s ą
w celu określenia, jakim typem Jednak absolutnie nie są to właściwości
zdolne w yruszyć na
robotnicy j e s t dana pszczoła. m isję zbierania nektaru. automatyczne. Właściwość Job mógłbyś
Stwórzmy zatem pętlę, zaimplementować następująco.
Worker[] Bees = new Worker[3]; której zadaniem będzie
przeglądnięcie wszystkich
elementów tablicy i użycie public Job { get; p riv a te s e t; }
Bees[0] = new NectarCollector(); „is" do określenia, które
z nich posiadają, metody
“
Bees[1] = new StingPatrol(); i właściwości odpowiednie Prywatny akcesor set jest nam potrzebny,
do wykonania zadania. gdyż właściwości automatyczne muszą
Bees[2] = new NectarStinger(); mieć oba akcesory (nawet jeśli byłyby
one prywatne). Równie dobrze mógłbyś
for (int i = 0; i < Bees.Length; i++) jednak zaimplementować tę właściwość
is pozwala porównywać w poniższy sposób:
{ interfejsy ORfcZ- inne typy-
public Job { get { return
if (Bees[i] is INectarCollector) "Księgowa"; } }
WYSIL ___________________
SZARE KOMÓRKI
Gdybyś miał inną klasę, która nie dziedziczyłaby po klasie W orker, ale im plem entowałaby Interfejs
IN e c t a r C o lle c t o r , ona także mogłaby wykonywać zadaną pracę! Klasa ta nie dziedziczyłaby po W orker,
więc nie mogłaby być przechowywana w tablicy z innymi pszczołami. Czy potrafisz wymyślić jakiś sposób
na obejście problemu i stworzenie tablicy do przechowywania pszczół wraz z obiektami tej nowej klasy?
340 Rozdział 7.
Interfejsy i klasy abstrakcyjne
Rysując na diagramie
Interfejsy mogą dziedziczyć po innych interfejsach klas interfejs,
dziedziczenie
Kiedy jedna klasa dziedziczy z drugiej, to otrzymuje wszystkie metody i właściwości
zaznaczamy
klasy bazowej. Dziedziczenie interfejsów jest jeszcze prostsze. W związku z tym,
linią przerywaną.
że nie ma w nich ciała żadnej metody, nie musisz się martwić o wywoływanie metod
i konstruktorów bazowych. Dziedziczące interfejsy gromadzą po prostu wszystkie
metody i właściwości interfejsów, z których dziedziczą.
S tw orzyliśm y interfejs
public interface IWorker JWorker, z któreg0 będą_
dziedziczyły inne in terfejsy. —
{
string Job { get; }
int ShiftsLeft { get; }
void DoThisJob(string job, int shifts);
void WorkOneShift();
}
(interface) (interface)
IStingPatrol INectarCollector
Każda klasa, która implementuje interfejs StingerLength Nectar
AlertLevel
dziedziczący po IWorker, musi, zaimplementować
jego metody i właściwości
SharpenStinger() FlndFlowersQ
Gdy klasa implementuje interfejs, musi zawierać wszystkie metody, które LookForEnemies() GatherNectarQ
się w nim znajdują. Jeśli interfejs ten dziedziczy po innym, to wszystkie Sting() ReturnToHive()
te właściwości i metody także muszą zostać zaimplementowane.
}
Klasa implementująca
IStingPatrol musi
zaw ierać nic tylko
te m eto d y -
...a h także metody
interfejsu IWorker,
po którym dziedziczy.
To je s t n asza podstawowa
public class Robot klasa Robot. Pszczoły roboty
{ mogą, dziatać na benzynę.
for (int i =
V.
0; i < Bees.Length; i++) {
m etod INectarCollector
w odniesieniu do obiektów
pszczół. S ą one typu
IW orker i nic o nich
if (Bees[i] is INectarCollector) { nie wiedzą.
r Zaostrz ołówek
v Przyjrzyj się tablicy zamieszczonej po lewej stronie. Dla każdej z tych instrukcji napisz,
jaka wartość i spowoduje powstanie wyrażenia o wartości t r u e . Dodatkowo dwa
wiersze nie skompilują się — wykreśl je.
Bees[4] = IStingPatrol; 0, 3 i 6
Bees[5] = null; 3 . (Bees[i] is IWorker)
Bees[6] = Bees[0];
Bees,,, = new ,Ne,,a,,o,,e,,or,,- 0, 1, 2, 3 i 6
344 Rozdział 7.
Interfejsy i klasy abstrakcyjne
Rzutowanie w górę
działa w odniesieniu do obiektów i interfejsów
Kiedy zastępujesz klasę bazową klasą potomną — tak jak wtedy, gdy zamieniałeś urządzenie na ekspres
do kawy lub zamiast Sandwich wstawiałeś BLT — postępowanie takie nazywamy rzutowaniem w górę .
To naprawdę potężne narzędzie pomocne podczas budowania hierarchii klas. Jedyną niedogodnością
przy rzutowaniu w górę jest możliwość stosowania tylko właściwości i metod klasy bazowej. Inaczej
mówiąc, kiedy traktujesz ekspres do kawy jako urządzenie, nie możesz zażyczyć sobie przyrządzenia
kawy lub wypełnić go wodą. M ożesz natomiast sprawdzić, czy jest podłączony do gniazdka — o ile
oczywiście jest to coś, co można zrobić z urządzeniem (w tym przypadku właściwość PluggedIn jest
częścią klasy Appliance ).
. .Twój kod nie będzie się chciał skompilować, a IDE wyświetli błąd:
Stanie się tak, ponieważ po wykonaniu rzutowania w górę, na klasę bazową, możesz mieć
dostęp tylko do właściwości i metod, które są zgodne z referencją używaną podczas
korzystania z obiektu.
i ^ !
346 Rozdział 7.
Interfejsy i klasy abstrakcyjne
w pisaniu kropki
w yśw ietli s ię okno
Consum ePower
Equals
m isterToasty j e s t referencją
Oven, więc m ożesz za je j pomocą
F
♦
IntelliSense, któm GetHashCode uzys kać dostęp do w szystkich
Oven
♦♦ A Microwave
będzie zawierato m etod i właściwości... ale
GetType Capacity Capacity
w szystkie sktadowe ponieważ j e s t to najmniej ogólny
możliwe do u życia . H eatUp ty p , może w skazyw ać tylko
Pluggedln na obiekty typ u Oven.
Preheat Preheat() HeatUp()
HeatUp() Reheat()
Reheat() MakePopcornQ
I C o o ks F oo d c o o k e r ;
if (m isterToasty is ICooksFood) {
coo ker = m i s t e r T o a s t y as ICooksFood;
cooker.
348 Rozdział 7.
Interfejsy i klasy abstrakcyjne
9 Oto nowy kod metody Main(), który nie działa. Czy jesteś w stanie powiedzieć dlaczego?
sta tic void Main(string[] args) {
ScaryScary fingersTheClown = new ScaryScary("duże buty", 35);
FunnyFunny someFunnyClown = fingersTheClown;
IScaryClown someOtherScaryClown = someFunnyClown;
someOtherScaryClown.Honk();
Console.ReadKey();
}
Zwróć uwagą na to, ze
klown j e s t przerażający. LEPIEJ, ZEBYS ZROBIŁ TO
ĆWICZENIE POPRAWNIE!
W P R Z E C IW N Y M R A Z IE ..
Rozwiązania
ćwiczeń in te rfa ce IClown {
s trin g FunnyThinglHave { get; }
void Honk();
}
}
Console.W riteLine(this.FunnyThingIHave):
i
W z w iązku z tym, że S c aryS ca ry j e s t klasą pochodną FunnyFunny,
ta natomias t imp lemen tu je interfejs IClown, to ScaryScary także
go im plem entuje. J
class ScaryScary : FunnyFunny, IScaryClown {
p u b lic ScaryScary(string funnyThingIHave, in t numberOfScaryThings)
: base(funnyThingIHave) {
this.numberOfScaryThings = numberOfScaryThings;
}
p riv a te in t numberOfScaryThings;
p u b lic s trin g ScaryThingIHave {
get { return "Mam " + numberOfScaryThings pająków"; }
} M ozesz u sta w ić referencję FunnyFunny
p u b lic void S c a re L ittle C h ild re n () {
Z Z ar!0ść ° bie ktu S c a ry S c a r y : p o n ew a z
Console.WriteLine("Buu! Mam c ię ! 1 i T J r j y Ldziedziczy po FunnyFunny.
} Nus moze s z jednak p rzy p isać referencji
1ScaryC/own do j akiegokolwiek klowna,
}
T o n e w e nle wie s z , czy j e s t on przerażający.
To d/atego m u sisz uzyć słowa kluczowego a l
s ta tic void M a in (s trin g [] args) {
ScaryScary fingersTheClown = new ScaryScary("duże b u ty", 35)
FunnyFunny someFunnyClown = fingersTheClown;
1
IScaryClown someOtherScaryClown = someFunnyClown as ScaryScary
someOtherScaryClown.Honk();
Console.ReadKey();
M ożesz także wywotać m etodę S c a reLittleChildren(),
} używ ając zmiennej referencyjnej someOtherScary Clown
— nie m ożesz jednak tego zrobić przy użyciu zmiennej
someFunnyClown.
350 Rozdział 7.
Interfejsy i klasy abstrakcyjne
Wprowadź te dw ie zm iany do
Modyfikatory dostępu zmieniają widoczność wtasnego r°zw iązania ćwiczenia.
Na stęp nie zm ień modyfikator
dostęp u protected z powrotem
Przyjrzyjmy się bliżej modyfikatorom dostępu oraz sposobowi, w jaki zmieniają na private i sprawdź, ja kie
one zasięg różnych składowych klasy. Wprowadziliśmy dwie zmiany: wewnętrzne p ow stały btędy.
pole funnyThingIHave ma teraz modyfikator protected. Zmieniliśmy także
metodę S careL ittleC hild ren () tak, aby używała pola funnyThingIHave.
O Oto dwa interfejsy. IClown definiuje klowna, który gra na swojej trąbce
i ma jakąś śmieszną rzecz. IScaryClown dziedziczy po IClown. Straszny
klown ma wszystko to, co zwyczajny, a dodatkowo ma jakieś przeraźliwe
rzeczy i straszy dzieci. (To się nie zmieniło od poprzedniego przykładu).
Stowo kluczowe th is _t akźe a n io m a to , eto
interface IClown { czego odnosi s ię zm ienna. M o w C# coś
ta kiego: „Spójrz na bieżącą instancję klasy
string FunnyThingIHave { get; } i znajdź cokolwiek, co j e s f z m ą z w ^ a n e
— naw et w tedy, gdy je s t to 29°™?
void Honk(); z parametrem lub zm ienną fo tefin .
}
I
To dość powszechny sp °s°b
interface IScaryClown : IClown { użycia this, ponieważ
parametr i pole w ew nętrzne
string ScaryThingIHave { get; } mają tę sa m ą nazw ę.
funnyThingIHave odn° si się
void ScareLittleChildren(); do parametru, podczas gdy
this.funnyThingIH ave dotyczy
} pola w ewnętrznego.
352 Rozdział 7.
Interfejsy i klasy abstrakcyjne
}
private int numberOfScaryThings;
public string ScaryThinglHave {
get { return "Mam 11 + numberOfScaryThings pająków"; }
} S t°w°^ k/ucz°w e protected nakazuje
uczynić to po/e prywatnym d/a
public void ScareLittleChildren() { kazż iego z wyj ą tk iem instancji k/as
potomnych.
Console.WriteLine("Nie możesz mieć mojego
+ base.funnyThinglHave); i
} Stowo kluczowe base
} nakazuje C# użyć wartości p ozostaw i/i po/e funnyThingIHave
z klas y tiazowej, lecz w tym U a t°,rem p r ^ ^ kompi/ator wygenerowałby
} przy padku moglibyśm y użyć w m o m e n t gdy zm ieni/iśm y je na
takż e this . Czy potrafisz ? ° ° ^ ^ • F ^ y F ^ ! ,; ‘"0 d o ,,ę p '" ‘ d a ka k l a s ,
pow iedzieć dlaczego?
l4 Poniżej znajduje się kod metody Main(), który tworzy instancje klas FunnyFunny i ScaryScary. Zwróć uwagę
na sposób zastosowania słowa kluczowego as do rzutowania zmiennej someFunnyClown w dół na referencję typu
IScaryClown.
private void Main(string[]args) {
ScaryScary fingersTheClown = new ScaryScary("duże buty", 35);
FunnyFunny someFunnyClown = fingersTheClown;
IScaryClown someOtherScaryClown = someFunnyClown as ScaryScary;
someOtherScaryClown.Honk();
e
Console.ReadKey(); Pí°klWlJ;Śćmy ■clomkodu dodat kową instrukj by
■Nie .istnieją.
głupie pytania
354 Rozdział 7.
Interfejsy i klasy abstrakcyjne
Shopper
Obiekty niektórych klas TotalSpent
CreditLimit
nigdy nie powinny być tworzone
ShopTill YouDropO
Pamiętasz hierarchię klas w symulatorze zoo? Na pewno skończyłeś swoją zabawę, BuyFavouriteStuff()
tworząc grupy hipopotamów, psów i lwów. Co jednak z klasami Canine i Fenine ?
Co z klasą Animal ? Okazuje się, że istnieją klasy, których instancje w ogóle nie
powinny być tworzone... i w zasadzie nie miałoby to sensu, gdyby mimo wszystko
powstały. Mamy przykład.
class Shopper {
z\ Engineering
Student
Co się stanie, gdy utworzysz instancję klasy Shopper ? Czy jest w ogóle sens to robić?
Przeciwieństwem
metody abstrakcyjnej
j e s t metoda konkretna.
To taka, która posiada
ciało. W szystkie klasy,
Q NIE M O ŻN A JEDNAK TWORZYĆ z którymi pracowałe ś do
INSTANCJI KLASY ABSTRAKCYJNEJ t ej pory, były konkretne.
356 Rozdział 7.
Interfejsy i klasy abstrakcyjne
o
ZACZEKAJ! CO TY MÓWISZ? KLASA, KTÓREJ
INSTANCJI NIE M O ŻNA UTWORZYĆ? A NIBY PO CO
MIAŁABYM PISAĆ COŚ TAKIEGO?
Czasami dzieją się złe rzeczy, gdy chcesz utworzyć obiekty, które nigdy nie powinny być
tworzone. Klasa na szczycie diagramu klas posiada zwykłe pola, które w założeniu mają
być ustawiane przez klasy potomne. Klasa Animal mogłaby zawierać obliczenia zależne
To je s t klasa Klubu od wartości logicznej HasTail lub V e rte b ra te , ale nie ma możliwości samodzielnego
A strofizyki z Obiektowa ustawienia tych pól. A stro fizycy mają dwie m isje —
używana do wysSyiW JUM
ytania - jedną m Marsa, drugą na Wenus.
rakie t na różne planety. M a m y tutaj przykład..
class P la n e tM is s io n { Nie ma se n su ustaw ianie tych \
c la s s Venus : Plan etM issio n {
p u b lic long RocketFuelPerMile pól w klasie bazowej, ponieważ
nie w iemy, jakiej rakiety p u b lic Venus() {
p u b lic long RocketSpeedMPH; będziemy używ ać i na j aką M ilesTo Planet = 40000000;
p u b lic in t MilesToPlanet; j p lanetę j ą wyślemy.
R o cketFuelPerM ile = 100000;
RocketSpeedMPH = 25000;
p u b lic long UnitsOfFuelNeeded() {
}
return MilesToPlanet * RocketFuelPerMile;
}
} c la s s Mars : Plan etM issio n {
p u b lic in t TimeNeeded() {
p u b lic M ars() {
return MilesToPlanet / ( in t) RocketSpeedMPH;
M ilesTo Planet = 75000000;
}
R o cketFuelPerM ile = 100000;
p u b lic s trin g FuelNeeded() {
RocketSpeedMPH = 25000;
return "Będziesz potrzebował "
}
+ UnitsOfFuelNeeded()
} Konstrukt° ry klas pochodnych Mars oraz
+ " jednostek paliwa, aby się tam dostać. Zajmie Ci to Venus u s t awiają trzy pola odziedziczone
+ TimeNeeded() + " g od zin ."; z klasy PlanetM ission. Pola te nie są
u s t awiane , jeżeli bezpośrednio tw orzysz
m ^ n c j ę klasy bazowej. Co s ię w takim
} razie dzieje, gdy metoda FuelNeeded()
próbuje ich u żyć?
p riva te void button1_C lick(object sender, EventArgs e) {
Mars mars = new Mars();
MessageBox.Show(mars.FuelNeeded());
}
© 1 M isjaM iedzyplanetam a.V enus' d oes not im p lem ent inherited abstract m em b er
M isjaM iedzyplanetarna.PlanetM ission.SetM issionlnfo(int, int, lo n g)1
m. Zaostrz ołówek
Masz szansę zaprezentować swoje zdolności artystyczne. Po lewej stronie znajdziesz deklaracje
v Interfejsów i klas. Twoim zadaniem jest narysowanie z prawej strony odpowiadających im
diagramów klas. Zrobiliśmy za Ciebie pierwszy z nich. Nie zapomnij o użyciu linii przerywanej
do oznaczenia implementacji interfejsu oraz linii ciągłej do oznaczenia dziedziczenia.
1) interface Foo { }
1)
class Bar : Foo { }
2)
2) in terface Vi n n { }
abstract class Vo u t : Vi n n { }
3)
3) abstract class Muffie : Whuffie { }
class Fluffie : Muffie { }
i n t e r f a c e Whuffie { }
4)
4) class Zoop {}
class Boop : Zoop { }
class Goop : Boop { }
5)
5) class Gamma : D e l t a , Epsilon { }
interface Epsilon { }
interface Beta { }
class Alpha : Gamma, B e t a { }
class Delta { }
360 Rozdział 7.
Interfejsy i klasy abstrakcyjne
Po lewej stronie znajdziesz zestawy diagramów klas. Twoim zadaniem jest przełożenie ich
na prawidłowe deklaracje C#. Numer 1 zrobiliśmy za Ciebie.
Top
/ 2
2)
Clack
Tip
3)
4 4)
Bar
5)
Zeta
5 y
Baz
Beta 1
Alpha
KLUCZ
T rozszerza
implementuje
S
Delta 1 C la c k 1 klasa
1 Clack 1
C la c k
-1
Rozwiązanie
2) (interfejs) 5) I (interfejs)
K in te rffi 4 Z oop
Vinn W huffie 1 D elta f
L 1
\f \
~7*s
Boop I (interfejs)
V out Muffie G am m a Beta
A s
/ 1S
362 Rozdział 7.
Interfejsy i klasy abstrakcyjne
— Jak chcesz!
m diamentem Śmierci!
Unikaj niejednoznaczności!
Języki, które pozwalają na utworzenie piekielnego diamentu śmierci, mogą doprowadzić
do pewnych nieprzyjemnych konsekwencji, ponieważ będziesz potrzebował dodatkowych
reguł przy rozstrzyganiu niejednoznacznych sytuacji... co wymaga większego nakładu pracy
podczas tworzenia Twojego programu! C # zabezpiecza przed pojawieniem się tego typu
sytuacji poprzez zastosowanie interfejsów. Gdyby T elevision oraz MovieTheater były
interfejsami, a nie klasami, ta sama metoda ShowAMovie() mogłaby usatysfakcjonować
oba z nich. Interfejs wymaga tylko tego, aby metoda była dostępna.
364 Rozdział 7.
Interfejsy i klasy abstrakcyjne
Zagadkowy basen
Twoim zadaniem jest pobranie fragmentów kodu z basenu i wstawienie ich
w puste miejsca. Możesz użyć tego samego fragmentu więcej niż raz
'i -— —^ i nie musisz wykorzystać ich wszystkich. Celem jest napisanie zestawu
klas, które się skompilują, będą działały i wyświetlą komunikat
zaprezentowany poniżej.
class : {
p u b lic ................... Nose { p u b lic Acts() : base("Acts") { }
p u b lic override ......................... {
s trin g Face { get; } return 5;
} }
Tu j e s t p u n kt wejścia
}
— to j e s t kompletnu
abstract class .............. :............. program C#.
p u b lic v irtu a l in t Ear() class : {
{ p u b lic override s trin g Face {
return 7; get { return "Of76"; }
} }
p u b lic Picasso(string face) p u b lic s ta tic void M a in (s trin g [] args) {
{ s trin g re s u lt = " " ;
................... = face; Nose[] i = new Nose[3];
} i[0 ] = new A c ts ();
p u b lic v irtu a l s trin g Face i[1 ] = new Clowns();
{ i[2 ] = new Of76();
{ ................. fo r ( in t x = 0; x < 3; x++)
} {
s trin g face; re s u lt += ( ^ II II
} + . + "\n ";
}
class ................. : { C onsole.W riteLine(result);
p u b lic Clowns() : base("Clowns") { } Console.ReadKey();
}
LJ filey//CyUsers/ - W ynik
5 Acts
Przypom inam y: 7 Clowns
7 Of 76
k a żd y fragment
z basenu może
zostać u żyty
w ięcej niż ra z
^ Dziedziczenie <-
J
Hermetyzacja
Stowo „polimorfizm" ^
oznacza „wiele postaci
% Abstrakcja
Czy m ożesz wyobrazić
sobie ta ki przypadek,
że obiekt przyjmuje
U żyw asz je j podcza s tw orzenia w \ g Ig p o s ta c i
modelu klas. Rozpoczynasz u; Twoim kodzie?
od klasy najbardziej Polimorfizm
ogólnej — abstrakcyjn ej —
a następnie tw orzysz klasy
bardziej szczegółowe
po niej dziedziczące.
366 Rozdział 7.
Interfejsy i klasy abstrakcyjne
I I Rzutowanie w dół za pomocą słowa kluczowego as. Me toda Mainta inTheHive() jako
parametr p r z y j m j _M o rker. Używ a
void MaintainTheHive(IWorker worker) { ona as do u sta w ienia referencji
worker na HiveM aintainer.
i f (worker is HiveMaintainer) {
HiveMaintainer m aintainer = worker as HiveMaintainer;
Długie ćwiczenie
Zbudujm y dom! Stwórz model domu, używając do tego celu klas
reprezentujących pokoje i lokalizacje oraz interfejsu dla każdego miejsca,
które posiada drzwi.
368 Rozdział 7.
Interfejsy i klasy abstrakcyjne
^ Utwórz klasy.
Na początku utwórz klasy Room oraz Outside na podstawie modelu klas. Następnie dodaj dwie kolejne:
OutsideWithDoor, która dziedziczy po Outside i implementuje IHasExteriorDoor, oraz RoomWithDoor,
która jest klasą pochodną Room i także implementuje IHasExteriorDoor.
Qs>—
frontYard
F^r
>v— 1 , —
efa Q ü ^ /e ^ R 0 o ^
DoorLocation
Rozpocząteś od utworzenia interfejsu O
Exits[] IH asExteriorDoor i dodania tych dwóch k|as, które Exits[] J
go implementują. Jedna z nich dz ied ziczy po Room E xits j e s t tab/icą
druga j e s t klasą potomną Outs id e . N adszedt czas referencji Location.
na ich dokończenie. /ivingRoom ma jedno
w yjście, więc je go
Dokończ budowanie klas i utwórz ich instancje. tab/ica Exits będzie
miała długość 1.
M asz już wszystkie klasy. N ad szed ł czas n a ich d okończenie i utw o rzen ie obiektów .
★ B ędziesz m usiał zadbać o k o n stru k to r dla klasy O utside , k tóry będzie ustaw iał w łaściwość tylko
do odczytu hot i przesłan iał w łaściwość D e s c rip tio n , aby d odać te k st „T utaj jest b ard zo g o rąco .”,
gdy hot będzie m iała w artość tru e . G o rąco je st n a p o d w ó rk u za dom em . N ie je st n a to m ia st gorąco
na p o d w ó rk u p rz ed nim i w ogrodzie.
★ K o n stru k to r klasy Room m usi ustaw ić p o le Décoration i pow inien p rzesłonić w łaściwość D e s c rip tio n ,
aby d odać „W idzisz tu taj (dekoracje)’’. W salonie znajduje się antyczny dywan, w jad aln i kryształowy
żyrandol, a w kuchni m o żn a zobaczyć nierdzew ne stalow e sztućce i rozsuw ane drzwi prow adzące
w prost n a p o dw órko za dom em .
★ F o rm u larz m usi utw orzyć wszystkie te o biekty i przechow yw ać referen cję do każdego z nich.
D odaj w ięc do niego m e to d ę C reateO bjects() i wywołaj ją w jego k o n stru k to rze.
E xits j e s t Każda
tab/icą ★ Stw órz in stancje obiektów dla każdej z sześciu lokalizacji dom u. T a k będzie w yglądał lokalizacja
referencji je d e n z tych wierszy: ma sw oje
do obiektów własne pole
Location. Ten RoomWithDoor livingRoom = new RoomWithDoor("Salon", w klasie
w iersz tw orzy formularza.
"antyczny dywan", "dębowe drzwi z mosiężną klamką");
taką tab/icę
i um ieszcza ★ T w oja m e to d a C reateO bjects() pow in n a w ypełnić p o le E x its [] dla każdego obiektu:
w niej dwa
To s ą naw iasy klamrowe. W szystko
e/em enty. fron tY ard.E xits = new Location[] { backYard, garden inne spowoduje pow stanie błędu.
370 Rozdział 7.
Interfejsy i klasy abstrakcyjne
Zbadaj dom
ComboBox zaw iera listę
To j e s t wielowierszową kontrolka w szystkich wyjść, więc
TextBox, która wyśw ietla wartość Przyjm u je nazwę e x its.
m etody D escription() dla bieżącej Upewnij s ię, że je j
lokalizacji. Nazwaliśmy ją właściwość DropDownStyle
Kliknij przycisk goHere, description. j e s t ustawiona na
aby przenieść s ię DropDownList.
do innej lokalizacji. /
t
Podpowiedz: Gdy w ybierzesz elem ent referencją UMMfim. A za te m, pomimo tego, ż e w skazuje ono na
na liście, jego indeks będzie s<ę . o biekt imp lem ent ujący I HasExte riorDoor, nie m ożesz po prostu wpisa ć
pokrywał z indeksem °ćlpow<edntej "curren c.ation.P oorLocation'', ponieważ DoorLocation nie j e s t składową
lokalizacji w tablicy Exitsll. klaf y Location. B ęd zie sz m u sia ł użyć rzutowania w dół, aby wydobyć
z obiektu m iejsce, do którego można p rzejść przez drzw i.
Długie ćwiczenie
— rozwiązanie
Tak będzie wyglądał kod tworzący model domu. Użyliśmy klas do reprezentowania pokoi
i lokalizacji oraz interfejsu dla każdego miejsca, które posiada drzwi.
in te r fa c e IH a s E x te rio rD o o r j
To je s t interfejs IHasExteriorDoor.
s t r in g D o o rD e s c rip tio n j g e t; J
L o c a tio n D o orL ocatio n j g e t; s e t ; J
J
c la s s Room i L o c a tio n j
p r iv a t e s t r in g d e c o ra tio n ; Klas a Room dz iedziczy z Location
i dodaje wewnętrzne pole dla
p u b lic R o o m (strin g name, s t r in g d e c o ra tio n ) w łaściw oś c i tylko do odczytu
Decora tion. J e j i n s t r u k t o r j e u sta w ia.
i base(name) j
t h is . d e c o r a tio n = d e c o ra tio n ;
J
372 Rozdział 7.
Interfejsy i klasy abstrakcyjne
c la s s O u tsid e : L o c a tio n {
p r iv a t e bool h o t;
Klasa O utside j e s t podobna_do
p u b lic O u ts id e ( s tr in g name, bool ho t) Room — dziedziczy po klasie
Lo cation i dodaje wewnętrzne
: base(name) pole dla w taściw ości Hot, która
{ j e s t w ykorzystywana w metodzie
D escription (), rozszerzonej
t h is . h o t = h o t;
w s t o s unku do klasy bazow ej.
}
Długie ćwiczenie
— rozwiązanie (ciąg dalszy)
Tak wygląda kod formularza. Wszystko znajduje się w pliku Form1.cs, wewnątrz deklaracji Form l.
fro n tY a rd = new O utsideW ithD oor("P odw órko przed domem" f a ls e , "dębowe d rzw i z m osiężną klam ką")
backYard = new O utsideW ithD oor("P odw órko za domem", tr u e "rozsuwane d r z w i" ) ;
garden = new 0 u ts id e ( " 0 g r ó d " , f a ls e ) ;
To tu taj przekazuj emy
o p isy drzwi do
d in in g R o o m .E x its = new L o c a tio n [] { liv in g R o o m , k itc h e n konstruktorów
OutsideW ithDoo r.
liv in g R o o m .E x its = new L o c a tio n [] { diningRoom } ;
k it c h e n . E x it s = new L o c a tio n [] { diningRoom };
fr o n t Y a r d . E x it s = new L o c a tio n [] { backYard, garden } Tutaj wypełniana j e s t tablica
E xits[] dla każdej instancj i .
b a c k Y a rd .E x its = new L o c a tio n [] { fro n tY a rd , garden }
M usim y poczekać do m om en t,
g a rd e n .E x its = new L o c a tio n [] { backYard, fro n tY a rd } gdy w sz y stk ie instancje zostaną
utworzone — w przeciwmyrn
razie nie mielibyśm y czego
liv in g R o o m .D o o rL o c a tio n = fro n tY a rd ; u m ieścić w każdej z tworzonych
fro n tY a rd .D o o rL o c a tio n = livin g R o o m ; tablic!
Dla obiektów
k itc h e n .D o o rL o c a tio n = backYard; IH asExteriorD oor
musim y u sta w ić
b a ckY a rd .D o o rL o ca tio n = k itc h e n ; potoż enie drzw i.
374 Rozdział 7.
Interfejsy i klasy abstrakcyjne
t
g o T h ro u g h T h e D o o r.V isib le = f a ls e ;
} To spraw ia, że p rzycisk „Przejdź przez drzwi" sta je s i ę _niewidoczny,
jeżeli
je ż e li hieżaca
bieżąca lokalizacja nie imp i m plem
le m e n t m terfe
entuje t e r f ^jsuu W a sEx^ to rO ooir.
%
Jeszcze nie skończyliśmy!
Fajnie było stworzyć m od el dom u, ale czy nie byłoby jeszcze le pie j, gdybyśmy zam ien ili
go w grę? Z ró b m y to! Zagrasz w chowanego przeciw ko ko m p u te ro w i. Będziesz m usiał
dodać klasę Opponent i zapewnić je j m ożliw ość ukrycia się w jednym z pomieszczeń.
M u sim y stworzyć znacznie większy dom. Będziem y także po trze bo w ali m iejsc do ukrycia!
D o da m y nowy in te rfe js, dzięki czemu n ie któ re p o koje będą m ia ły kryjó w ki. N a końcu
zm od yfikujem y fo rm u la rz, aby u m o ż liw ia ł ich sprawdzanie i przechowywanie danych
na tem at liczby ruchów w ykonanych podczas p ró b odszukania przeciw nika. B rzm i
zachęcająco? Oczywiście!
-►Zaczynajm y!
★ Będzie on potrze bo w ał pryw atnego po la typu L o c a tio n (m y L o c a tio n ), aby w iedział, gdzie jest.
Będzie także wyposażony w po le typu Random używane podczas przem ieszczania się w losowe miejsce.
★ K o n s tru k to r po b ie ra lokalizację początkow ą i przypisuje je j w artość do po la m y L o c a tio n , a także zapisuje
w p o lu random nową instancję klasy Random. R ozpoczyna od po d w ó rka przed dom em (ustaw im y to w klasie
fo rm u la rza ), a następnie przechodzi losowo pom iędzy m iejscam i z kryjów ką. N a początku gry przemieszcza
się 10 razy. K ie d y napotyka drzw i wejściowe, rzuca m onetą. N a podstaw ie w yn iku określa, czy przez nie
przejść, czy nie.
★ D o da j m etodę M o ve (), k tó ra przemieszcza prze ciw n ika z bieżącej lo ka liza cji do in ne j. Po pierwsze, jeżeli
znajduje się on w p o k o ju z drzw iam i, rzuca m onetą. G dy ra n d o m .N e x t(2 ) jest rów ne 1, przechodzi
przez drzw i; je śli nie, zostaje w miejscu. W ybie ra w ted y losowe wyjście z aktualnej lo ka liza cji i przez
nie przechodzi. Jeżeli pom ieszczenie nie m a żadnej kryjó w ki, cała pro ced ura jest pow tarzana — T w ój
przeciw nik znów w ybiera losowe wyjście z aktualnej lo kalizacji i przechodzi przez nie. W ykonu je tę
czynność, aż znajdzie od po w ie dn ią kryjów kę.
★ D o da j m etodę C h e c k (), k tó ra przyjm uje ja k o p a ra m e tr lokalizację i zwraca t r u e , je że li p rze ciw n ik się
w niej ukryw a, lu b f a ls e — w przeciw nym razie.
376 Rozdział 7.
Pamiętaj, że każdy problem programistyczny można rozwiązać na wiele sposobów. Interfejsy i klasy abstrakcyjne
Jeśli Twoje rozwiązanie działa, lecz jest inne niż nasze — to świetnie!
\
Tego przycisku będzie sz uży wat
do sprawdzania kryjów ki w danym
pom ieszczeniu. B ę dzie on widocz ny
tylko w m iejscu , które j ą p° s ia da..
Gdy ąra uruchamiana j e s t po Gdy będzie pokazywrny, w taściwof ć
raz pierw szy, w yśw ietlany j e s t Text zmieniona z ° sta n ie z „
tylko przycisk X r y j sie!" Kiedu na stowo „Spraw dź" z dodaną nazwą
90 klikniesz, formularz w p o t ? m iejsca do ukrycia — dla pokoju
t a t o w y m odliczy do d ziesięciu z kryjówką pod t<5źkiem te k st
przycisku przyjm ie postać
M m e n fr l wy wota metodę
„Spraw dź pod tóżkiem“ .
Rozbuduj oryginalny program, dodając większą liczbę pokoi, kryjówki oraz przeciwnika,
który będzie się przed Tobą chował.
Rozwiązania
ćwiczeń ^ ---- - I Hidir,gp la ce- Posiada
ktik0 J P stn n 9 z akcesorem get,
in te r fa c e IH id in g P la c e { ^ ZWraca nazwę m ieJsca do “ krycia.
s t r in g HidingPlaceName { g e t; }
}
p u b lic s t r in g HidingPlaceName { g e t; p r iv a t e s e t ; }
p u b lic o v e r r id e s t r in g D e s c rip tio n {
get {
r e tu r n b a s e .D e s c rip tio n + " Ktoś może ukrywać s ię " + HidingPlaceName +
}
}
}
378 Rozdział 7.
Interfejsy i klasy abstrakcyjne
c la s s O u ts id e W ith H id in g P la c e : O u ts id e , IH id in g P la c e j
p u b lic O u ts id e W ith H id in g P la c e (s trin g name, bool h o t, s t r in g hidingPlaceNam e)
: base(name, h o t)
HidingPlaceName = hidingPlaceN am e; ^ . _
I K lt s t OuTsldeW lThHldlrgPltce
I dziedziczy po O utside i implementuje
IH idingPlace, Ttk stm o jt k czyni To
p u b lic s t r in g HidingPlaceName j g e t; p r iv a t e s e t ; I RoomWiThHid ir gp lt c e.
p u b lic Form1() {
In itia liz e C o m p o n e n t( ) ; O utsideW ithD oor fro n tY a rd ;
}
Opponent opponent;
p r iv a t e v o id M oveToANewLocation(Location new Location) {
Moves++;
c u rre n tL o c a tio n = ne w L ocatio n;
RedrawForm();
}
Metodo M oveToAN ewLoctTion() u s T tlt nowe
p r iv a t e v o id RedrawForm() j położenie i przerysow uje formularz.
e x it s . I t e m s . C le a r ( ) ;
fo r ( in t i = 0; i < c u r r e n tL o c a tio n .E x its .L e n g th ; i+ + )
e x its .Ite m s .A d d ( c u r r e n tL o c a tio n .E x its [i].N a m e ) ;
e x its .S e le c te d In d e x = 0;
d e s c r ip tio n .T e x t = c u r r e n tL o c a tio n .D e s c r ip tio n + " \ r \ n ( r u c h numer + Moves + " ) " ;
if (c u rre n tL o c a tio n is IH id in g P la c e ) j
IH id in g P la c e h id in g P la c e = c u rre n tL o c a tio n as IH id in g P la c e ; P ^ rz ^ u je m y nazwy
kryjówki, bo na razie
ch e c k .T e x t = "Sprawdź " + h id in g P la ce .H id in g P la ce N a m e ; mamy jed y n ie obiekt
c h e c k .V is ib le = t r u e ; currentLocation, który
nie ma w łaściw ości
I
Hidingp/aceName. Możemy
e ls e w tym mie jsc u użyć as.
c h e c k .V is ib le = f a ls e ; Dzię ki rzutowaniu w dół
otrz; m am ; referencję do
if (c u rre n tL o c a tio n is IH a s E x te rio rD o o r) zm iennej IH idingPlace.
go T h ro u g h T h e D o o r.V isib le = t r u e ;
e ls e
go T h ro u g h T h e D o o r.V isib le = f a ls e ; RedrawFormO wypetnia rozwijaną listę , ustaw ia
teks t (dodają c liczbę posu nięć), a następnie
w yśw ietla lub ukrywa przyciski w zależności
od tego, czy pom ieszczenie posiada drzwi oraz
j akie ś m iejsce do ukrycia.
380 Rozdział T.
Interfejsy i klasy abstrakcyjne
fro n tY a rd = new O utsideW ithD oor("P odw órko przed domem", f a ls e , " c ię ż k ie dębowe d r z w i" ) ;
backYard = new O utsideW ithD oor("P odw órko za domem", t r u e , "rozsuwane d r z w i" ) ;
garden = new O u ts id e W ith H id in g P la c e ("O g ró d ", f a ls e , "w s z o p ie " ) ;
d riv e w a y = new O u ts id e W ith H id in g P la c e ("D ro g a do ja zdow a", t r u e , "w g a ra ż u ");
d e s c r ip tio n .T e x t = "Gotowy czy n ie n a dch od zę!": P rzycisk hide j e s t tym, który uruchamia
A p p lic a tio n .D o E v e n ts ( ); grę. P ierw szą rzeczą, j a ką r ° b i, j est
S y s te m .T h re a d in g .T h re a d .S le e p (5 0 0 ); ukrycie sie b ie samego. N astępnie
odlicza on do d z ie się ciu i nakazuje
przeciwnikowi wykonać ruch. Na k°ńcu
g o H e re .V is ib le = t r u e ; pokazuje pierw szy p rzycisk oraz pole
wyboru i um ieszcza gracza w salonie.
e x i t s . V is i b l e = tru e ;
M etoda M °veT °A N ew Loca ti°n () w yw °łuje
M oveToAN ew Location(livingR oom ); RedrawF°rm ().
382 Rozdział 7.
Interfejsy i klasy abstrakcyjne
} r e tu r n 5;
}
p u b lic a b s tra c t c la s s Picasso : Nose }
p u b lic v i r t u a l i n t E a r() {
r e tu r n 7; publ ic c la s s Of76 : Clowns {
} fo r ( in t x = 0 ; x < 3 ; x++)
{
p u b lic c la s s Clowns : Picasso { r e s u lt += ( i[x].Ear() + " "
p u b lic C low ns() : b a se("C lo w ns") { } +i[x].Face) + "\n " ;
} }
M essageB ox.S how (result)
Przechowywanie a
^ dużej ilości danych
Z deszczu pod rynnę. W rzeczywistym świecie nie musisz się zwykle zajmować danymi
w małych ilościach i niewielkich fragmentach. Nie, Twoje dane przychodzą do Ciebie w grupach,
stosach, pękach, kopach. Potrzebujesz jakiegoś potężnego narzędzia do ich zorganizowania.
Nadszedł czas, aby przedstawić kolekcje. Pozwalają one przechow yw ać i sortow ać dane,
a także zarządzać tymi z nich, które Twój program musi przeanalizować. Dzięki temu możesz
myśleć o pisaniu programów do pracy z danymi, a samo ich przechowywanie zostawić kolekcjom
s
P « ..» » przylm
?
„ 30 rrjdzaju
M
<*» o - y
J
386 Rozdział 8.
Typy wyliczeniowe i kolekcje
}
nazwa typu
T eraz możesz odw ołać się do tych w artości w następujący sposób:
Na końcu znajduje
{
Wo r k e r b u z z = new W o r k e r ( J o b . A t t o r n e y G e n e r a l ) ;
Tu je s t htąd, który
zostanie wychwycony
przez kompilator.
O ^ ^ ^ V p ^ ^ | jc z * m o w e J o b ^ jo e 5 n o t c o n t a | n ^ d e f m r t i o n f o r ^ A t t o m e y G e n e r a r ^ ^
Co się je dn ak stanie, gdy będziesz chciał użyć naprawdę dużych liczb dla jednego
z typów wyliczeniowych? D om yślnym typem w artości w yliczeniow ych jest i n t . W takim
przypadku musisz w ięc określić go jaw nie, używając do tego o p era to ra :, ta k ja k tutaj:
■1 ° ™ k a z u je kompilatorowi
public enum TrickScore : long { traktować w artości typu
Sit = 7, wyliczeniowego TrickScore
jako typ fong, nie j ako inf
Beg = 2500000000025
}
Gdybyś spróbował skompilować ten kod bez określenia typu jako long, otrzymałbyś taki oto komunikat:
Cannot i m p l i c i t l y c o n v e rt ty p e 'lo n g ' to 'in t '.
388 Rozdział 8.
Typy wyliczeniowe i kolekcje
Wykorzystaj całą swoją wiedzę o typach wyliczeniowych w celu utworzenia klasy przechowującej kartę do gry.
Ćwiczenia
Talia kart to doskonały przykład na to, że ograniczenie wartości jest bardzo ważne. Nie ma osoby,
która chciałaby zmienić swoje karty i zmierzyć się z Joker o f Clubs lub 13 of Hearts. Oto sposób,
Rozwiązania w jaki napisaliśmy klasę Card:
ćwiczeń
enum Suits {
Spades,
J e ś li nie o k reślisz w artości, to pierw szy
Clubs, elem ent nu nś d e j e s t równy 0, drugi 1
Diamonds, trzeci 2 i tak dalej. '
Hearts
}
enum Values {
Naszym typom wyliczeniowym nadaliśmy
Ace = 1, To tu taj ustaw iam y wart° ś ć nazwy w liczbie mnogiej — S u its oraz
Two = 2, V a lu es.A ce m 1 V alues — natomiast właściwościom
Three = 3, klasy Card, w których wartości tych
Four = 4, typ ó w są przechowywane, nadaliśmy
Five = 5, nazwy w liczbie pojedynczej: S u it
i Value. Jak sądzisz, dlaczego tak
Six = 6,
postąpiliśmy? Przyjrzyj się nazwom
Seven = 7, typ ó w wyliczeniowych stosowanych
Eight = 8, w innych przykładach w tej książce.
Nine = 9, Czy zastosowanie typó w S u it i Value
Ten = 10, byłoby lepszym rozwiązaniem?
Jack = 11,
Queen = 12,
King = 13
}
class Card {
public Suits Suit { get; set; }
public Values Value { get; set; }
390 Rozdział 8.
Typy wyliczeniowe i kolekcje
m WYSIL ______________
ra SZARE KOMÓRKI
Jak dodać do klasy Deck metodę S h u f f l e ( ) , która przestawia karty całkowicie losowo?
A co z metodą, która pobierałaby z talii pierwszą kartę? W jaki sposób dodałbyś do niej
jedną z kart?
fw Każda tab lica posiada swoją długość i aby z nią pracować, musisz tę długość znać.
Chcąc um ieścić w niej k ilk a elem entów pustych, możesz użyć re fe re n cji n u l l .
In d eksy 3 , 4, 5 i 6
s ą równe nuli, wiąc
nie przechowują
żadnych kart.
° 6 'e k t
Tablica ma długość 7, ale
przechow uje tylko 3 karty
M usisz przechowywać wartość, k tó ra określa, ile k a rt jest w danej c h w ili w użyciu. Potrzebujesz do tego
p o la typ u i n t — nazwiem y je to p C a rd . Będzie ono określało indeks ostatniej ka rty w tablicy.
Nasza tab lica trzech k a rt m ogłaby m ieć długość 7, ale ustaw im y to p C a rd na 2.
T eraz je d n a k sprawy się ko m p liku ją . Dość łatw o jest dodać m etodę P e e k (), k tó ra zwraca
referencję do ka rty znajdującej się na górze ta lii — w ten sposób możesz ją podejrzeć. Co się
je d n a k stanie, gdy zechcesz dodać kartę? G dy to p C a rd jest m niejsze niż długość tablicy, możesz
ją wstaw ić po d wspom nianym indeksem i dodać do to p C a rd 1. Jeżeli je d n a k tab lica jest pełna,
w ted y musisz utw orzyć nową, większą i przekopiow ać do niej istniejące elem enty. U sunięcie karty
jest dość proste — musisz potem ty lk o zadbać o to, aby zmniejszyć to p C a rd o 1 i pod je j indeksem
wstaw ić n u l l . A co się stanie, gdy będziesz chciał pozbyć się elem entu ze ś ro d k a lis ty ? Jeśli
usuniesz kartę n u m er 4, będziesz m usiał przesunąć n u m er 5 na je j miejsce, a następnie to samo
zrob ić z ka rtą n u m er 6, po tem 7 ... o rety, co za bałagan!
392 Rozdział 8.
Typy wyliczeniowe i kolekcje
394 Rozdział 8.
Typy wyliczeniowe i kolekcje
myList.Add(a);
myList.Add(b);
string o = myList[1];
^.Zaostrz ołówek
Twoim zadaniem było wypełnienie pozostałej części poniższej tabeli na podstawie
Rozwiązanie kodu obsługującego listę po lewej stronie i napisanie, jak wyglądałby kod, gdybyś
zamiast tego używał zwykłej tablicy.
s t r in g a = "A c h !"
m y L is t.A d d (a ); m y L is t[0 ] = a;
s t r in g b = "Bum"; s t r in g b = "Bum";
m y L is t.A d d (b ); m yList[1] = b;
ł t
L is ty są ob ie kta m i korzystającym i z m etod W p rzypadku tab licy jesteś nieco ograniczony.
ta k samo ja k wszystkie inne klasy, których Musisz ustalić je j ro zm ia r podczas je j tw orzenia
używałeś do tej pory. M ożesz w yśw ietlić w ID E i wszystkie przeprowadzane na niej operacje
spis dostępnych m etod, w pisując . o b o k nazwy muszą zostać napisane ręcznie.
t
listy. Param etry do nich rów nież przekazujesz
identycznie ja k do zwykłych m etod w klasach,
któ re sam utworzyłeś. Platforma .N ET posiada klasę
A rray ufatw iającą niektóre z tych
cZynn° ś c i . my jednak skoncentrujem y
s ię na obiektach L is t , ponieważ s ą
one znacznie ta tw iejsze w użyciu .
396 Rozdział 8.
Typy wyliczeniowe i kolekcje
Z a p a r o w a liś m y listę
List<S hoe> shoeC loset = new L is t< S h o e > (); obiektów Sh oe o nazwie
M ożesz użyć in s tru kcj i s hoeCloset.
new wewnątrz metody
shoeC loset.A dd(new Shoe() L is t.A d d ().
{ S ty le = S ty le .S n e a k e rs , C o lo r = "C zarny" foreach jest specjalnym rodzajem
shoeC loset.A dd(new Shoe() pętli dla listy. Instrukcje są
{ S ty le = S ty le .C lo g s , C o lo r = "Brązowy" }); wykonywane dla każdego
shoeC loset.A dd(new Shoe() umieszczonego w niej obiektu.
{ S ty le = S ty le .W in g tip s , C o lo r = "C zarny" } ) ; Przedstawiona tu pętla tw orzy
shoeC loset.A dd(new Shoe() id e n tyfika to r o nazwie shoe —
{ S ty le = S ty le .L o a fe r s , C o lo r = " B ia ły " });
w miarę ja k przechodzi ona przez
kolejne elementy listy, id e n tyfika to r
shoeC loset.A dd(new Shoe()
ustawiany jest na pierwszy z nich,
{ S ty le = S ty le .L o a fe r s , C o lo r = "Czerwony" })
potem drugi, trzeci itd ., aż do
shoeC loset.A dd(new Shoe()
wyczerpania się obiektów.
{ S ty le = S ty le .S n e a k e rs , C o lo r = " Z ie lo n y " })
Ta instrukcja
i n t numberOfShoes = sh o eC lose t.C o un t ; zw raca całkowitą Pętle foreach potra fią także ° p e r° wać
liczbę obiektów na tablicach! W zasadzie p °tra fią ° ne
fo re a ch (Shoe shoe in sho eC lose t) Shoe umieszczonych pracować z każdą kolekcją.
s h o e .S ty le = S t y le . F lip f lo p s ; na liście .
Tu j e s t klasa Shoe oraz typ
s h o e .C o lo r = "Pomarańczowy"; wyliczeniowy S ty le , których
Pętla foreach używam y...
} wykonuje s ię raz dla
M etoda Rem ove() usuwa
obiekt na podstaw ie każdego obiektu S h °e
w liśc ie sh °e C l° se t . p u b lic c la s s Shoe {
j e go referen cji. Metoda
Rem oveA t() robi to na p u b lic S ty le S ty le ;
podstaw ie indeksu.
p u b lic s t r in g C o lo r;
M etoda C lear() }
shoeC loset.R em oveAt(4) .
u su wa z listy
wsz y stk ie obiekty.
p u b lic enum S ty le {
Shoe th ird S h o e = s h o e C lo s e t[2 ];
Z a p isa liśm y refem ncje Sneakers,
Shoe secondShoe = sh o e C lo s e t[1 ] do dwóch obiektów
Shoe tu ż przed L o a fe rs ,
s h o e C lo s e t.C le a r();
wyczyszczeniem lis ty S andals,
Dodaliśmy jeden z n nich
r
F lip f lo p s ,
s ^ e H o s e U M ^ M r^ o e ); ^ ^ j i f f i j u t t a rn
W in g tip s ,
if (sh o e C lo se t.C o n ta in s(se co n d S h o e )) nie ma.
C logs,
C o n s o le .W rite L in e ("A to c i n ie s p o d z ia n k a ." );
Ten w e rs z nigdy nie z ° s tanie wykonany, ponieważ Contains() }
Ten wj
zwrócJ fa ls e . Do wy czyszczonej lis ty dodaliśmy tylko obiekt
th irdS hoe, n i e m a tam natom iast secondShoe.
jesteś tutaj ► 397
Członkowie m ają swoje przyw ileje
CELNE SPOSTRZEŻENIA —
Typy generyczne mogą przechowywać każdy typ
■ L i s t jest klasą platformy .NET
W idziałeś już, w ja k i sposób L i s t może przechowywać łańcuchy znaków ■ Lista dynam icznie zm ienia swój rozm iar
lub o b ie kty klasy Shoe . Możesz także utw orzyć listę liczb całkow itych do osiągnięcia odpowiedniej wielkości.
lub dowolnych wym yślonych przez C iebie obiektów . T a właściwość czyni Posiada określoną pojemność, jeśli jednak
listę kol e kc j ą g e n e r y c z n ą . K ie d y tworzysz now y o b ie k t L i s t , wyraźnie dodasz do niej pewną liczbę elementów,
określasz jego typ: możesz m ieć listę liczb całkow itych, łańcuchów rozszerzy się ona, aby je pomieścić.
znaków lu b o b ie któ w Sh o e . Przez to praca z lista m i staje się o wiele ■ Aby dodać coś do listy, użyj A d d ( ) .
łatw iejsza — po ich u tw o rze n iu do kła dnie znasz typ przechowywanych Aby coś z niej usunąć, skorzystaj
w nich elem entów. z R em ove() .
Nie oznacza to, że dodałeś literę „T ". J e s t to sposób za p isu , ■ Możesz usuwać elementy, używając ich
który w idzisz za każdym razem, gdy klasa lub in terfejs
może opero w a ć'na dowolnym typie danych. C zęść <T> indeksu , z wykorzystaniem R e m o ve A t() .
oznacza, że może sz w to m iejsce w staw ić typ, na przykład
■ Typ listy deklarujesz za pomocą
L is t<Shoe>. Ograniczy s z w ten sposób zakres typów,
z którymi lista pracu je. pa ram e tru ty p u , który jest ujęty
w nawiasy trójkątne. Na przykład
L i s t < F r o g > oznacza, że lista będzie
List<T> name = new List<T>(); mogła zawierać tylko obiekty typu Frog.
^ , •
■ Aby odnaleźć coś na liście (jeśli dany
L is ty mogą być
z ^ J r o T ć to samo co tablice
element jest na niej umieszczony),
możesz użyć I n d e x O f( ) .
i jeszcze kilka dodatkowych rzeczy.
■ Aby pobrać liczbę elementów listy,
użyj właściwości C o u n t .
P la tfo rm a .N E T o fe ru je zestaw in te rfe jsó w generycznych, któ re
pozwalają tw orzonym przez C iebie ko le kcjo m pracować z każdym ■ Możesz skorzystać z metody
C o n t a in s ( ) , aby określić, czy dany
typem danych. Klasa L i s t im p lem e ntuje wszystkie te interfejsy.
obiekt wchodzi w skład listy
D z ię k i tem u możesz utw orzyć listę liczb całkow itych i pracować
z nią praktycznie ta k samo, ja k pracujesz z listą o b ie któ w Shoe . ■ fo re a c h jest specjalnym rodzajem pętli,
która pozwala na przejrzenie wszystkich
Sprawdź to sam . W pisz w I D E słowo L i s t , k lik n ij je prawym elementów listy i wykonanie kodu z ich
przyciskiem myszy, a następnie w ybierz opcję Go To Definition. udziałem. Składnia foreach wygląda
Zostaniesz przeniesiony do dekla racji klasy L i s t . Im ple m en tu je następująco: fo r e a c h ( s t r i n g s
ona k ilk a interfejsów : i n S t r i n g L i s t ) . Nie musisz jawnie
nakazywać pętli zwiększania licznika
o jeden — iteracja zostanie wykonana
To stą d pochodzą metody Rem oveAt(),
IndexO f() oraz In se rt().
automatycznie dla każdego elementu.
398 Rozdział 8.
Typy wyliczeniowe i kolekcje
Magnesiki z kodem
Czy potrafisz tak poukładać magnesy z kodem,
aby utworzyć działający projekt Windows Form,
który po naciśnięciu przycisku wyświetli
komunikat zaprezentowany poniżej?
a.A dd("cztery");
}
a. R e mo v e A t ( 2 ) ;
if (a.IndexO f("cztery") != 4) {
RemoveAtO usuwa a.A dd(fourth);
elem ent znajdujący }
s ię pod indeksem 2
— je s t to trzecia if ( a . C o n t a i n s C ' d w a 11) ) ( M etoda printL() uży wa
pozycja listy. p ę tli foreach, aby prz e jść
a.A dd(tw opointtwo); przez ca łą lis tę tańctuM iu
znaków i dodać je j e lementy
do jednego dużego obiektu
typu string . Po wszy stkim
wyśw ietlany j e s t komunikat
MessageBox.
result + = -Xn" + e l e m e n t
}
MessageBox.Show(result);
400 Rozdział 8.
Typy wyliczeniowe i kolekcje
N iektóre z nich
to kaczki krzyżówki.
p u b l i c enum KindOfDuck {
M allard,
Tak będzie wyglądał inicjalizator Mus c ovy,
*
jesteś tutaj ► 403
U staw ianie kaczek w rzędzie
/ kaczka
T \
11-cm
k N- \u
ekt
Po posortowaniu listy
kaczek w obiekcie
znajdują s ię ciągle
\ 7
kaczka
\ ekt te sam e elementy ^ k t
— s ą po p ro stu
14-cm inaczej poukładane.
k N. 'i
'ekt
404 Rozdział 8.
Typy wyliczeniowe i kolekcje
D ucks.Sort();
Poniżej zaprezentowano przykład, w któ rym w idoczny jest sposób dekla racji klasy
porów nującej ro zm ia ry o b ie któ w Duck. D o da j go do swojego p ro je k tu ja ko nową klasę:
406 Rozdział 8. *
Typy wyliczeniowe i kolekcje
Ćwiczenia
Jeżeli kliknie sz Implement interface 'ICom parer<Card> ', I D E autom atycznie wstaw i do klasy wszystkie m etody
i właściwości, k tó re musisz zaim plem entować. W tym przypadku u tw o rzy pustą m etodę C o m p a re (), k tó ra
posłuży do porów nyw ania dwóch kart, x i y. U z u p e łn ij ją tak, aby zw róciła 1, je że li ka rta x jest większa niż y,
-1 , gdy jest mniejsza, a 0, je że li są to te same karty. U p e w n ij się, że każdy k ró l będzie um ieszczony za w aletem .
Każdy w a le t z k o le i p o w in ie n znaleźć się za czwórką, a całość za dow olnym asem.
\
if ( x . S u it < y . S u it ) { leżeli x i W m ają tę sam ą
re tu rn -1 ;
}
if ( x . S u it > y . S u it ) {
T a r!? * , oo “ S * “
re tu rn 1; _ wrześnie jszych instrukcji
re tu n f riie została wykonana.
}
re tu rn 0;
Je ż e li ż adna z instru kcji retu rn
nie została wy konana, oznacza to,
że karty m uszą być takie sam e —
zw racane j e s t zero.
To j e s t lista generyczna
s t a t ic v o id M a in ( s tr in g [] args) obiektów Card, która
przechowuje karty.
{ Kiedy ju ż s ię na niej
Random random = new Random(); znajdą, sortow anie ich
C o n s o le .W rite L in e ("P i§ c losowych k a r t : " ) za pomocą IComparer
je s t proste.
L ist< C a rd> cards = new L is t< C a rd > ();
f o r ( i n t i = 0 ; i < 5; i++)
{
cards.Add(new C a rd ((S u its )ra n d o m .N e x t(4 ),
(V a lu e s )ra n d o m .N e x t(l, 1 4 ) ) ) ;
C o n s o le .W rite L in e (c a rd s [i].N a m e );
}
Metody Console,ReadKey() używamy, by aplikacja
konsolowa nie została zamknięta po wykonaniu
C o n s o le .W rite L in e (); tego, co miała zrobić. Rozwiązanie to jest
C o n s o le .W rite L in e ("T e same k a r ty po so rto w a n e :1 doskonałe podczas nauki, lecz zupełnie się nie
sprawdza w przypadku tworzenia prawdziwych
c a rd s .S o rt(n e w C ardC om parer_byV alue());
programów uruchamianych z poziomu wiersza
fo re a c h (Card card in cards) poleceń. Jeśli chcesz, to naciśnij w IDE kombinację
{ klawiszy Ctrl+F5 — program zostanie uruchomiony
C o n so le .W rite L in e (ca rd .N a m e ); bez debugowania, a po jego zakończeniu IDE
wyświetli komunikat „Press any key to continue..."
} ( Naciśnij dowolny klawisz, by kontynuować...)
C onsole.R eadKey(); m ----------------------------------------- i będzie czekać na naciśnięcie dowolnego
klawisza. Pamiętaj, że w tym przypadku nie jest
uruchamiany debugger, a zatem punkty przerwania
i obserwowanie zmiennych nie będą działać.
410 Rozdział 8.
Typy w yliczeniow e i kolekcje
override
Equals(object obj)
GetHashCodeO
iToStringO 1 string object.ToStringO
Returns a string that represents the current object.
U ru ch o m program i po no w nie przyjrzyj się liście. T eraz I D E w yśw ietla zawartość o b ie któ w Duck!
Często postępujem y w ten sposób z obiektam i. Jednak teraz, kie dy nasza klasa Duck dysponuje ju ż m etodą
T o S t r in g ( ) , pow in niśm y ją w ykorzystać w m etodzie P r in tD u c k s ( ) :
D o da j tę m etodę do swojego pro g ra m u i po no w nie go uruchom . W yśw ietlone w y n ik i będą takie same. Teraz,
gdybyś kiedyś chciał, na przykład, dodać do o b ie ktu Duck właściwość Gender, wystarczy zaktualizow ać m etodę
T o S t r in g ( ) , a zm iany zostaną uw zględnione we wszystkich miejscach, w ja kich jest ona używana (w tym także
w m etodzie P r in tD u c k s ( ) ) .
T w o ja klasa Card posiada ju ż właściwość Name zwracającą nazwę karty: f 0 korie t t n e^ b i Zop e ra torS *+“
wy wołuj e ją autom atycznie.
p u b lic s t r in g Name
{
g e t { r e tu r n V a lu e .T o S trin g () + " o f " + S u it . T o S t r in g ( ) ; }
}
412 Rozdział 8.
Typy wyliczeniowe i kolekcje
używasz IEnumerab!e<T> p o d lu p ą
Przejdź do ID E , znajdź w n im zm ienną L is t< D u c k > i skorzystaj z IntelliSense, In icja liza to ry kolekcji działają
na dowolnych obiektach
by przyjrzeć się je j m etodzie G e tE n u m e ra to r(). Z acznij wpisywać „.G e tE n u m e ra to r” IEnumerable<T> — o ile tylko
i zobacz, co się pojaw i: dysponują one metodą A dd().
d u c ks .G e tE n
GetEnumerator List< Duck> .Enum erator List<Duck> .GetEnumeratorO
Returns an enum erator th a t iterates th ro u gh th e System.Collections.Generic.List<T>.
T o właśnie ten o b ie k t pozw ala na przeglądanie listy w ja kiejś kolejności. Poniżej pokazaliśm y
p rzykład p ę tli fo re a c h przeglądającej listę L is t< D u c k > i przechowującej aktualnie
analizow any elem ent w zm iennej duck. Kiedy kolekcja
fo re a c h (Duck duck in ducks) { implementuje
Console.WriteLine(duck);
interfejs
}
A o to faktyczny, u k ry ty przed program istą w ygląd tej samej pętli:
IEnumerable<T>,
daje Ci możliwość
IEnumerator<Duck> enum erator = d u cks.G e tE n u m e ra to r();
w h ile (en u m e ra to r.M o ve N e xt()) {
przejrzenia
Duck duck = e n u m e ra to r.C u rre n t; swojej zawartości
Console.WriteLine(duck); w określonym
} porządku.
ID is p o s a b le d is p o s a b le = enum erator as ID is p o s a b le ;
if (d is p o s a b le != n u ll) d is p o s a b le .D is p o s e ();
Formalnie rzecz b io rą c to j e sz c z e
(N a razie nie zaprzątaj sobie głowy dw om a osta tn im i w ierszam i tego kodu. nie w szystko, ale pewnie złapałeś ,
In te rfe js ID is p o s a b le poznasz do kła dniej w rozdziale 9.). o co chodzi...
D w ie przedstawione powyżej pętle w yśw ietlają te same kaczki. Możesz sam się o tym przekonać,
kie dy je uruchom isz — obie w yśw ietlą identyczne w yn iki.
A o to co się w nich dzieje. K ie d y przeglądasz zawartość listy lu b tab licy (bądź ja k ie jk o lw ie k innej ko le k c ji),
m etoda M ove N e xt() zwraca t r u e , je śli istnieje ko le jn y elem ent, w przeciw nym razie zwracana jest wartość
f a ls e . W łaściwość C u rre n t zawsze zwraca referencję do bieżącego elem entu.
D o d a j to wszystko razem, a otrzym asz pętlę fo re a c h !
Sprób uj poeksperym entować, modyfikując metodę ToStringO klasy Duck w taki sposób, by inkrementowała
w łaściw ość S iz e . Uruchom debugger i wskaż zmienną Duck m yszką. N astępnie wskaż ją ponownie.
Pam iętaj, że za każdym razem, gdy to robisz, ID E wywołuje metodę ToStringO obiektu.
Jak myślisz, co by się stało, gdyby w trakcie wykonywania p ę tli fo r e a c h
metoda ToStringO zm ieniała jedno z pól obiektu?
fo re a ch ( B ird b ir d in b ir d s ) {
C o n s o le .W rite L in e ( b ird );
Kiedy kaczki zo sta ły _
} ju ż zrzutowane w górą
do I£numerable<8ird>,
możemy je dodać
do lis ty obiektów Bird.
414 Rozdział 8.
Typy wyliczeniowe i kolekcje
Możesz tworzyć własne przeciążone metody tno ^ sz folć e użyć ^ instrukcji using.
Jeśli chcesz Z w ied zieć się czegoś
D o tej p o ry używałeś p r z e c i ą ż on y c h m e t o d , a nawet przeciążonych ko n stru kto ró w , poświęć k i lica mi m t^ y iz a jr z e ć W o
któ re były częściami wbudowanych klas i o b ie któ w .N E T F ram ew ork. M ożesz ju ż punktu trzecieg° dodtntku „Pozostałości"
umieszczonego na końcu książki.
ocenić, ja k bardzo są one użyteczne. Czy nie byłoby świetnie, gdybyś m óg ł tworzyć
m etody przeciążone we własnych klasach? W zasadzie możesz — i jest to naprawdę
łatw e! M usisz tylko napisać dwie lu b więcej m etod, k tó re będą posiadały tę samą
nazwę, ale inne param etry. Z ró b to !
Z araz po w pisaniu DoesC ardM atch( I D E p o in fo rm u je Cię, że rzeczywiście utw orzyłeś przeciążoną m etodę:
C a rd . DoesCardMatch (|
A 1 of 2 ▼ b o o l C a rd .D o e sC a rd M a tch (C a rd cardToCheck, Suits su it) I
Poświęć chw ilę na eksperym entowanie z tym i dwoma m etodam i, aby przyzwyczaić się do przeciążania.
Ćwiczenia
UTW ÓRZ FORMULARZ, KTÓRY POZWOLI
N A PRZENOSZENIE KART POMIĘDZY DW OMA TALIAM I.
Klasę k a rty utw orzyłeś ju ż wcześniej. Nadszedł czas na stworzenie klasy do przechowywania dow olnej ich liczby.
N azw iem y ją Deck. W rzeczywistości ta lia ma 52 karty, ale klasa Deck może przechowywać każdą ich liczbę.
M oże także nie m ieć żadnej karty.
Następnie stworzysz fo rm u la rz, k tó ry będzie pokazyw ał zawartość dwóch o b ie któ w Deck. Podczas pierwszego
uru ch o m ie n ia p ro g ra m u zestaw 1. będzie posiadał do 10 losowych kart, a zestaw 2. będzie zaw ierał pełną ich
talię, czyli 52 karty. O ba zestawy będą posortow ane w e dłu g k o lo ru oraz w artości — będziesz m óg ł przyw rócić
początkow y stan każdego z nich, klika ją c jeden z dwóch przycisków Przywróć. F o rm u la rz będzie także posiadał
przyciski (nazwane o d po w ie dn io „ < < ” oraz „ > > ” ) służące do przenoszenia k a rt pom iędzy zestawami.
P rzyciski te m ają na stęp ują ce nazwy: moveToDeck2 (górny) i _moveToDecki
(dolny). P rzem ieszczają one karty z jedn ego zestaw u do drugiego.
A b y pokazać zaw artość zestaw ów ,
M ożesz u żyć w łaściw ości Name użyj kontrolek Li'stB ox. Po kliknięciu
przycisków w celu określenia przy cisk u moveToDeckl wybrana
k<xrta. z zes t awu 2 . j e s t przenoszona
ich n azw i ułatw ienia an alizy
do zesta w u 1.
kodu. Po dwukrotnym
kliknięciu p rzycisku funkcja
obsługi zdarzenia przyjm ie
Te przyciski nazwano shufflel
odpowiednią nazw ę. i sh u ffle2 . Wywotują one metodę
D eck.ShuffleO odpowiedniego
zestawu i przerysowują ten zesta .
Każdy z przycisków re se tl
i re se t2 w p ierw sze j
k o h jn o śd iwywotuje metodę
ResetD eck(), a następnie
RedrawDeck().
O prócz procedur obługi zdarzeń dla sześciu przycisków będziesz jeszcze potrzebował dwóch m etod dla formularza.
N ajpierw dodaj m etodę R e se tD e ck(), która będzie przywracać początkowy stan zestawu. Jako param etr przyjmie
i n t : jeżeli zostanie przekazana liczba 1, przywracany będzie początkowy stan zestawu 1. Sprowadzać się to będzie
do wyczyszczenia listy i wylosowania do 10 kart. G dy przekazana zostanie liczba 2, przywracany będzie stan zestawu
2. — drugi ob ie kt Deck będzie znów zawierał pełną talię 52 kart. Teraz dodaj taką metodę:
p u b lic v o id RedrawDeck( i n t DeckNumber) {
i f (DeckNumber == 1) {
Z wróć uw agę lis tB o x 1 . I te m s .C le a r ( ) ; Metoda RedrawDeck()
aktualizuje dwie
wykorzyót an ia foreach. ( s t r in g cardName in deck1.G etCardNam es()) kontrolki L is tB o x bez
lis tB o x l.Ite m s .A d d (c a rd N a m e ); względu na zaw artość
pętli foreach - ^
w ce lu dodania la b e ll. T e x t = "Zestaw 1. (" + d e c k l.C o u n t + " k a r t ) " ; obiektów Deck.
każdej karty } e ls e {
z zestaw u do lis tB o x 2 . I te m s .C le a r ( ) ;
listy . fo re a ch ( s t r in g cardName in deck2.G etCardNam es())
M stB o x2 .Ite m s.A d d (ca rd N a m e );
la b e l2 .T e x t = "Zestaw 2. (" + deck2.C ount + " k a r t ) " ;
416 Rozdział 8.
Typy wyliczeniowe i kolekcje
Utwórz klasę do przechowywania zestawów kart wraz z formularzem, który będzie jej używał.
Rozwiązania
ćwiczeń Tu j e s t konstruktor, który tw orzy pełną
ta lię kart. Używa do tego zagnieżdżonej
c la s s Deck { w nim pętli for. Pętla zewnętrzna
p r iv a t e L ist< C a rd > c a rd s ; przechodzi przez kolory. Oznacza to,
p r iv a t e Random random = new Random(); że wewnętrzna przechodzi przez 13
w artości cztery razy, raz dla każdego
koloru.
p u b lic Deck() {
cards = new L is t< C a rd > ();
f o r ( i n t s u i t = 0; s u i t <=3; s u it+ + )
f o r ( i n t v a lu e = 1; v a lu e <=13; value++)
cards.A dd(new C a r d ( ( S u it s ) s u it , (V a lu e s ) v a lu e ));
}
Tu je s t kolejny konstruktor
p u b lic Deck(IEnum erable<Card> in it ia lC a r d s ) { ta klasa posiada dwa przeciązon
cards = new L is t< C a r d > ( in itia lC a r d s ) ; konstruktory, każdy z innymi
} param etram i.
p u b lic i n t Count { g e t { r e tu r n c a rd s .C o u n t; } }
418 Rozdział 8.
Typy wyliczeniowe i kolekcje
if (x .V a lu e < y .V a lu e )
r e tu r n -1 ;
Z a m ia st if/ e ls e if uży liśm y
re tu r n 0; zestaw u in strukcji if.
To działa, ponieważ każda
}
z nich j e s t wyk°nywana
tylko wtedy, gdy poprzednie
się nie wykonały —
w przeciwnym wypadku
wcz e śn ie jsza in stru kcja
Deck d e c k l; return kończy metodę■
Deck deck2;
Random random = new Random();
p u b lic Form1() {
In itia liz e C o m p o n e n t( ); Konstruktor formularza musi
R e s e tD e c k (l); ponownie u sta w ić dwa
R esetD eck(2); zesta w y kart i wy św ie tlić j e .
R edraw D eck(l);
RedrawDeck(2);
}
p r iv a t e v o id R e s e tD e c k (in t deckNumber) {
if (deckNumber == 1) {
i n t numberOfCards = random .N ext(1, ll) ;
deck1 = new Deck(new C a rd [] { });
fo r ( in t i = 0; i < numberOfCards; i+ + )
deck1.Add(new C a rd ((S u its )ra n d o m .N e x t(4 ),
(V a lu e s)ra n d o m .N e xt(1 , l4 ) ) ) ;
d e c k 1 .S o r t( ) ;
} e ls e
deck2 = new D e ck(); A b y przyw rócić zesta w 1 metnrln •
} wywołuje random N ext() uu ro i, u P.,ertvszej kolejności
« następnie tworzy ^ ^ ^ kart’
wylosowaną liczbę kart dodać do niego
M etoda RedrawDeck() się posortowaniem zestaw u PrS^ fo r- Działanie kończy
napisana została je s t łatwe - po p ro ttu Z o S y raCanie ze^ w u 2 . *
po p rostu tworzona j e s t instancja Deck.
w instrukcjach
do ćw iczenia.
Tu j e s t dalszy ciąg
Nadanie kontrolkom od po w ie dn ik nazM kodu formularza.
Rozwiązania zdecydowanie ułatwia czytanie Twojego kodu.
ćwiczeń — cd Gdubu nazywały s ię b u ttm T ^ H c b butt ° n2 _ C|ick
i tak dalej, nie w iedziałbyś, na tod którego
przycisku patrzysz!
420 Rozdział 8.
Typy wyliczeniowe i kolekcje
“ S ó r S o S ;™ j4 « o .9osie
pod tym indeksem .
★ Dodanie elementu.
Możesz dodać do słow nika dany elem ent, przekazując klucz i w artość do jego m etody A d d ().
★ Usunięcie elementu.
T a k ja k w przypadku listy, możesz usunąć elem ent ze słow nika za pom ocą m etody R em ove().
A b y z niej skorzystać, musisz ty lk o przekazać nazwę klucza. U su nię ty zostanie zarówno on,
ja k i jego wartość. Klucze w słowniku są unikatowe. Każdy z nich pojawia s ię
dokładnie raz. W artości mogą pojawiać s ię dowolną liczbę
m y D ic tio n a ry .R e m o v e ("ja k iś k lu c z " ) ; razy, a więc dwa klucze mogą m ieć tę sam ą w artość.
W ten sposób podczas wyszukiwania lub usuwania klucza
"K" Pobranie listy kluczy. słownik dokładnie wie, do czego ma s ię odwołać.
Możesz pobrać listę wszystkich kluczy słow nika przy użyciu właściwości Keys oraz przejrzeć je,
korzystając z fo re a c h . Z w ykle będziesz używ ał ko le k c ji kluczy w ta k i o to sposób:
in t howMany = m y D ic tio n a ry .C o u n t;
422 Rozdział 8.
Typy wyliczeniowe i kolekcje
Długie ć w ic z e n i e ____________________________________________________________________________
H L Utwórz grę Idź na ryby!, w którą będziesz mógł grać przeciwko komputerowi
R O Z P O C Z N IJ M Y O D SPECYFIKACJI.
Każdy pro fe sjo nalny p ro je k t in fo rm atyczny rozpoczyna się od specyfikacji i nie m a tu ta j żadnych w yjątków . Będziesz
tw orzył klasyczną grę karcianą I d ź n a ryby! R ó żn i lu dzie grają w nią na nieco odm iennych zasadach. Poniżej
zaprezentowano reguły, do których będziesz się stosował:
424 Rozdział 8.
Typy wyliczeniowe i kolekcje
A ktualny zestaw
kart trzymanych
przez gracza
Te kontrolki w yśw ietlany je s t
TextBox w te j kontrolce
noszą nazwy L is tB o x o nazwie
textP rogress listH and. M ożesz
i textBo oks. u sta w ić je j nazwę
przy użyciu
w łaściw ości
Name.
Długie
D łu ćwiczenie
ciąg dalszy
TAK WYGLĄDA KOD DLA FORM ULARZA.
426 Rozdział 8.
Typy wyliczeniowe i kolekcje
Długie
D łu ćwiczenie
ciąg dalszy
TERAZ TRUDNA CZĘŚĆ: UTW ÓRZ KLASĘ PLAYER.
c la s s P la y e r
{
Popatrz uważnie na każdy z komentarzy —
p r iv a t e s t r in g name;
p o w ied zą Ci one dokładnie, co każda metoda
p u b lic s t r in g Name { g e t { r e tu r n name; } } ¡Powinna rot>ić . Two im zadaniem j e s t ich
p r iv a t e Random random; napisan ie.
p r iv a t e Deck cards ;
p r iv a t e TextBox textBoxOnForm;
428 Rozdział 8.
Typy wyliczeniowe i kolekcje
/
do zwracanych grup i usuwa karty tej wartości z ręki gracza.
public IEnumerable<Values> PullOutBooks () {
List<Values> books = new List<Values>(); M etoda Peek(), którą dodaliśm y do klasy Deck,
for (int i = 1; i <= 13; i++) {
j e s t bardzo pomocna. Pozwala ona programowi
Values value = (Values)i; obejrzeć dowolną kartę w z esta w ie , ale,
int howMany = 0; w p rzeciw ieństw ie do Deal(), nie usuw a je j.
for (int card = 0; card < cards.Count; card++)
if (cards.Peek(card).Value == value)
howMany++;
if (howMany == 4) {
1
books.Add(value); Będziesz m u s ia ł utw orzyć DWIE przeciążone w ersje
for (int card = cards.Count - 1; card >= 0; card--) m etody AskForACard(). Pierw sza z nich będzie używana
cards.Deal(card); p rzez gracza podczas żądan ia — obejrzy on karty
} trzym ane w ręce i znajdzie ^ o któ rą p ro si p rze c iwnik.
Druga będzie używ ana p rzez żądające go danej w artośd .
return books; Obie m etody będą chcia ły od KAŻD e g o (z arówno
od grac zy kom puterow ych, ja k i od człow ieka) kart,
}
które s ą zgodne z przekazaną w artością.
Dłu
Długie ćwiczenie
ciąg dalszy
W POZOSTAŁA CZĘŚĆ ZADANIA: STWÓRZ KLASĘ GAME.
F o rm u la rz przechow uje je dn ą instancję klasy Game. Zarządza ona całą
J f e '
rozgrywką. P opatrz do kła dnie, w ja k i sposób jest wykorzystyw ana w form ularzu .
430 Rozdział 8.
Typy wyliczeniowe i kolekcje
ES? • u rn a s p
f ! a :z j s ,
p w ^ ł j^ y s i ju t r ¿ ¡ 2 s z . n r i- w ą s u
tańcich d akó w gruD jaką wyznaczy łeś w drugie 'j pę™ , i na tej podstawie wygeneruje odpowiedni
p u b lic s t r in g GetWinnerName() {
// Ta metoda wywoływana j e s t na końcu g ry . Używa ona swojego własnego sło w n ika
// (D ic tio n a ry < s trin g , int> w in ners) w c e lu o k r e ś le n ia lic z b y grup
// będących w posia da n iu każdego z g ra czy na końcu ro zg ry w ki. Na początku k o rz y sta z p ę t l i
// fo re a c h w o d n ie sie n iu do b oo ks.K eys - foreach (Values value in books.Keys) - aby w ypełn ić
// sło w n ik winners lic z b ą grup każdego z u czestn ikó w . N astępnie p rzeg lą d a ten
// nowy sło w n ik , aby z n a le ź ć gracza z n a jw ię kszą ic h lic z b ą . Na końcu
// po raz o s ta tn i p rzeg lą d a zmienną winners i tw orzy l i s t ę zw ycięzców w fo rm ie łańcucha
// znaków podobnego do te g o : „ Janek i Ed ek". J e ż e l i i s t n i e j e ty lk o je d e n zw ycię zca ,
// zwracany j e s t n a stęp u ją cy c ią g znaków: „E d e k : 3 g ru p y". N przeciwnym r a z ie
// p r z y b ie ra on p o s ta ć : „Remis pomiędzy Janek i Ed ek: 2 g ru p y".
}
. . . .
Użyj stałej Ewii-Mment.NewLine, by dodać znak nowego wiersza
^
p0 ry Używałeś \ n/ by dodawac znak nowe9° wiersza w wyświetlanych tekstach. Jednak .NET
wyg°t^nć ^ kt ó ra daie anal°g iczne, możliwości. Jest nią Environment.NewLine. Zawsze zawiera
W j satT7ą,vvartł 05,c — \ r \ n . .Gdy byś sPrawdził znaki, k+óre są używane w plikach tekstowych! w systemie
W indows okaaałoby si?' że .na końcu każdego a wierszy znajdują się zawsze te same dwa: ' \ r ' i '\n'. Inne
M le a i2 S X !S l takie l;lk U nix' o znaczalą konj ec wiersza t ekstu za pomocą jednego znaku: '\n'. Metoda
MessageBox.Show() jest natyle sprytna, że potrafi automatycznie zamienić znak '\n' na prawidłowe znaki
nowego wierna, jednał* stał el Environment.NewLine może zwiększyć przejrzystość kodu.
Console^HteUneO 5 ^ dodawana na kodcu każdego wiersza wyświetlanego przy użyciu metody
431
Rozwiązanie ćwiczenia
Długie ćwiczenie
Rozwiązanie
Oto wypełnione metody klasy Game:
432 Rozdział 8.
Typy wyliczeniowe i kolekcje
D łu g ie ć w i c z e n i e
»
R o zw iązan ie (c d .)
Stwórz
nową kolejkę Queue<string> myQueue = new Queue<string>();
łańcuchów
znaków. \ To tutai dodajemy do kolejki
myQueue.Enqueue("pierwszy w kolejce"); / cztery elementy. Kiedy próbujemy
myQueue.Enqueue("drugi w kolejce");
myQueue.Enqueue("trzeci w kolejce"); \ porządku, w jakim zostały
\ ustawione.
myQueue.Enqueue("ostatni w kolejce");
Peek() pozwata
„rzucić okiem" string takeALook = myQueue.Peek(); 0
na pierwszy pierwsze Dequeue() wyciąga
etement string getFirst = myQueue.Dequeue(); @
w kotejce bez
usuwania go. string getNext = myQueue.Dequeue(); 0 z, kotejki pierwszy etement. Drugi
etement przesuwa się na pierwsze
miejsce — następne wywołanie
Dequeue() wyciągnie go.
int howMany = myQueue.Count; 0
myQueue.Clear();
MessageBox.Show("Peek() zwróciło: " + takeALook + "\n"
Metoda Clear() + "Pierwsze Dequeue() zwróciło: " + getFirst + "\n"
usuwa wszystkie
elementy kolejki. + "Drugie Dequeue() zwróciło: 1 + getNext + "\n"
+ "Count przed Clear() było równe 1 + howMany + "\n"
+ "Count po Clear() jest równe " + myQueue.Count); 0
“ H I
OK
436 Rozdział 8.
Typy wyliczeniowe i kolekcje
OK
W szystkie cztery
elem enty zostafy
eiememy —J
przekopiowane
■ ___ i-i do
rln ...i zawsze możesz użyć pętli
nowych kolekcji. foreach , aby uzyskać dostęp
do wszystkich elementów
zarówno stosów, ja k i kolejek!
438 Rozdział 8.
Typy wyliczeniowe i kolekcje
Napisz program, który pomaga stołówce pełnej drwali wydawać naleśniki. Rozpocznij od klasy
Lumberjack, uzupełniając ją brakującym kodem. Następnie zaprojektuj formularz i dodaj procedury
Ćwiczenia obsługi zdarzeń do przycisków.
3 Dana jest klasa Lumberjack . Uzupełnij akcesor get FlapjackCount
oraz metody TakeFlapjacks i E atF lapjacks . public enum Flapjack {
Chrupkiego,
pub lic class Lumberjack { Wilgotnego,
p riv a te s trin g name; Rumianego,
p u b lic s trin g Name { get { return name; } } Bananowego
p riv a te Stack<Flapjack> meal; }
p u b lic Lumberjack(string name) {
this.name = name;
meal = new Stack<Flapjack>();
}
p u b lic in t FlapjackCount { get { / / Zwróć Ilo ś ć . } }
p u b lic void TakeFlapjacks(Flapjack food, in t HowMany) {
/ / Dodaj o k re ślo n ą lic z b ę n a leśn ików do sto s u meal.
}
p u b lic void EatFlapjacks() {
/ / U ypisz w yniki w o knie k o n s o li.
}
Stwórz taki oto formularz. Pozwala on na wpisywanie imion drwali w polu tekstowym, przez co dostają się oni
do kolejki śniadaniowej. Drwalowi znajdującemu się na początku możesz dać talerz naleśników, a następnie
powiedzieć mu, żeby poszedł je zjeść, klikając przycisk N astępny drwal. Napisaliśmy procedurę obsługi kliknięcia
dla przycisku D odaj naleśniki. Użyj kolejki o nazwie breakfastLine w celu przechowywania informacji o drwala
i f (crispy.Checked== true)
food = Flapjack.Chrupkiego Zwróć uwagę na
specjalną składnię
else i f (soggy.Checked == true)
Ten przycisk powinien pobrać food = Flapjack.Wilgotnego „else if'.
następnego drwala z kolejki, else i f (browned.Checked == true)
wywołać jego metodę EatFlapjacks() food = Flapjack.Rumianego; Peek() zwraca referencję
i zaktualizować kontrolkę List8°x. else do pierwszego drwala
food = Flapjack.Bananowego; w kolejce.
Będziesz musiał dodać metodę
RedrawList(), aby wypełnić kontrolkę Lumberjack currentLumberjack = breakfastLine.Peek();
List8ox aktualną zawartością kolejki. currentLumberjack.TakeFlapjacks(food,
Wszystkie trzy przyciski będą ją
(int)howMany.Value);
wywoływały. Mamy podpowiedź:
używa ona pętli foreach. j RedrawList(); Kontrolka NumericUpDown nazywa się howMany.
Pole tekstowe nosi nazwę nextInLine.
Ten program w yśw ietla w yniki w oknie konsoli, a zatem
aby ją zobaczyć, będziesz m usiał w yśw ietlić w IDE okno Output. jesteś tutaj ► 439
Rozwiązanie ćwiczenia
440 Rozdział 8.
9. Odczyt i zapis plików
Czasem opłaca się być Irwałym. Do tej pory wszystkie programy były krótkotrwałe.
Uruchamiały się, działały przez chwilę i były zamykane. Czasami nie jest to wystarczające,
zwłaszcza jeżeli zajmujesz się ważnymi danymi. Musisz mieć możliwość zapisania swojej pracy.
W tym rozdziale pokażemy sposób zapisywania danych do pliku, a następnie wczytywania
tych informacji z powrotem do programu. Dowiesz się co nieco o klasach strumieni .NET
i zetkniesz się z tajemnicami systemów szesnastkowego i dwójkowego .
input = stream.Read(.
^ /a rz
input zawiera dane
odczytane ze strumienia ..•a strumień pracuje
Używasz bezpośrednio z plikiem.
obiektu Stream...
stream .W rite(output);
output zawiera dane
zapisywane do pliku
^°'% y f
Używasz innego
obiektu strumienia,
ale proces wygląda
tak samo.
442 Rozdział 9.
Odczyt i zapis plików
[n Z A P IS A Ć D A N E D O S T R U M IE N IA .
Strumienie
Do strumienia możesz wpisać dane tekstowe i binarne, używając pozwalają na
w tym celu jego metody W r ite ( ) . zapis i odczyt
O D C Z Y T A Ć D A N E Z E S T R U M IE N IA .
danych. Używaj
Aby pobrać dane z pliku, pamięci, sieci lub czegokolwiek innego, co korzysta ze strumieni, strumienia
możesz użyć metody Read(). Można odczytywać dane z naprawdę dużych plików, nawet właściwego
tak dużych, że się nie mieszczą w pamięci.
dla danych,
Z M IE N IA Ć P O Ł O Ż E N IE W E W N Ą T R Z S T R U M IE N IA . z którymi
Większość strumieni udostępnia metodę Seek() , która umożliwia określenie położenia pracujesz.
w strumieniu i wykonywanie w tym miejscu operacji odczytu lub zapisu danych.
% r-/!
FiteStream może być
p°dłącz°ny w danej
chwiti tytko do jednego
ptiku.
Nazywamy to kodowaniem
Eureka! » la im n io m io in i
(powiemy sobie więcej na ten O l 1 3 4 - 5 4
temat nieco ptóm ej-J. V
’W
Zamknij plik, aby inne programy miały do niego dostęp.
Wstawienie @ na początku
nazwy pliku nakazuje C#
traktować łańcuch znatów
bezpośrednio i nie zwracać
uwagi na sekw enj f°rmatujące
takie jak \ t dla tabulacji Iub \ n
dla nowej linii.
[ S
Stream W riter sw = new S tre a m W rite r(@ " c :\ ta jn y p l a n . t x t " ) ;
446 Rozdział 9.
Odczyt i zapis plików
public
bool Blobbo (StreamWriter sw)
sw.WriteLine(Zap);
Zap = "zielono-purpurowa";
return fa ls e ;
W ynik:
W ramach przypomnienia:
publ ic class Flobbo
| pub w na.szych zagadkach używamy
dziwacznych nazw zmiennych
p riva te s trin g Zap; i metod cetowo — gdybyśmy
używati naprawdę dobrych
p u b lic Flobbo(string Zap) nazw, to zagadki byłyby
th is.Z ap = Zap; zbyt proste! Pamiętaj,
by nie używać takich nazw
} w swoim kodzie, dobra?
U
S tre a m W rite r("a ra .tx t"); r umieszczona instrukcja
using System.IO;.
sw.WriteLine(Zap);
W ynik:
Zap = "czerwono-pomarańczowa"
return tru e ;
448 Rozdział 9.
Odczyt i zapis plików
t
Podczas tworzenia obiektów
StreamReader oraz StreamWriter
otworzyły one własne strumienie.
Wywołanie ich metod Close()
nakazuje im je zamknąć. jesteś tutaj ► 449
Nie w ychodź ze strum ieni
CryptoStream
dziedziczy
po abstrakcyjnej klasie
Stream tak samo jak
inne klasy strumieni.
% \i <#
Zagadkowy basen
Twoim za d a n iem jest pobranie fragmentów kodu
z basenu i wstawienie ich w puste miejsca.
Możesz użyć tego samego fragmentu
więcej niż raz i nie musisz wykorzystać
ich wszystkich. Celem jest napisanie
programu, który wyświetli komunikat
zaprezentowany po prawej stronie. p u b lic class Pizza
p riv a te ______
p u b lic class Pineapple { p u b lic Pizza(_
const __________ d = "dostaw a.txt"; .w r ite r = w rite r;
p u b lic ______________________ }
{ North, South, East, West, Flamingo } p u b lic void .Fargo f)
p u b lic s ta tic void M a in (s trin g [] args ) { w rite r. _ (f);
____ o = new ___________________ ("zam 0w ienie.txt") w rite r. ();
Pizza pz = new Pizza(new _______________(d, tru e )):
}
pz. ___________(Fargo.Flamingo);
fo r ( w = 3; w >= 0; w--) {
Pizza i = new Pizza
p u b lic class Party
(new ______________(d, fa ls e ) );
p riv a te _______ reader;
i.Idaho((Fargo)w );
p u b lic Party(_ reader)
Party p = new Party(new __________ (d ));
.reader = reader;
p. _______________(o );
I
I
p u b lic void HowMuch(______ . q)
o. ______________("To wszystko, kochani!");
o. () q. __________ (reader. ());
reader. ();
} }
Przypominamy:
każdy fragment
z basenu może
zostać użyty
więcej niż raz!
I
Klasa Pizza obiekt
p ub lic class Pizza {
p riv a te StreamWriter w rite r; IdahoO za p°mocę
p u b lic Pizza(StreamWriter w rite r) j
th ls .w r ite r = w rite r; metoda WriteLineO wywofuje
automatycznie.
I
p u b lic void Idaho(P1neapple.Fargo f) j
w rite r.W rite L in e (f);
w rite r.C lo s e ();
I
452 Rozdział 9.
Odczyt i zapis plików
To je s t okno
dialogowe
fo ld e r B r o w s e D ia lo g .
+ " P lik i z w a r to ś c ia m i o d d z ie lo n y m i p rz e c in k ie m ( * .c s v ) |* .c s v |
^ W s z y s tk ie p lik i
D ia lo g R e s u lt r e s u l t = o p e n F i l e D i a l o g 1 .S h o w D i a l o g ( ) ;
if ( r e s u l t = D ia lo g R e s u lt.O K ) {
O p e n S o m e F il e ( o p e n F i le D ia l o g l.F il e N a m e ) ;
}
Z
Okna dialogowe wyświetla się za pomocą ShowDialog().
5 ; = ^ % zn o s|
n ao w zo śast
zostało Anuluj, będzie ona równa DialogResult.Cancel.
454 Rozdział 9.
Odczyt i zapis plików
if ( r e s u l t == D ia lo g R e s u lt.O K ) {
' Metoda ShowDialogO oraz
S a v e T h e F ile (s a v e F ile D ia lo g l.F ile N a m e );
V M 9 - dokładnie taksamojak w przypadku
obiektu OpenFUeDialog.
}
Zapisywaniejako
î J¿ « M ó jF o ld e r ► D o m y ś ln e P rz e sz u k a j: D o m y ś ln e
Właściwość Title
pozwala zmienić O rg a n iz u j T N o w y f o ld e r
P B b A rc h iw u m
J J O M ó jF o ld e r
D o m y ś ln e
określony b J ll x a m p p
S P R A W D Z I Ć , C Z Y IS T N IE J E .
Możesz sprawdzić, czy plik istnieje, korzystając Fiłefnfo działa prawie
z metody E x is ts ( ) . Zwróci ona tru e , jeżeli jest taki fak samo ja k File
plik, lub fa ls e , jeżeli go nie ma.
Jeżeli zamierzasz intensywnie pracować z plikiem
O D C Z Y T A Ć C O Ś L U B Z A P IS A Ć D O P L IK U rozsądnym rozwąz^em jest utworzenie instancji
Możesz użyć metody OpenRead(), aby pobrać dane aSy ,Fi,le In fo zamiast korzystania ze statycznych
metod klasy F ile . y ^"ych
z pliku, oraz C reate() lub O penW rite() , aby je
zapisać. K|asa Ffelnfo posłuży do wykonywania tych samych
czynności co F ile , a|e najpierw musisz utworzyć
D O D A Ć D O P L IK U T E K S T . jej instanqę. Mcvesz to zrobić, a następnie uzyskać
Metoda AppendAllText() pozwala na dodanie dostęp do jej metody E x is ts () lub OpenRead()
tekstu do już istniejącego pliku, natomiast gdy nie ma w dokładnie taki sam sposób jak w przypadku F ile .
go jeszcze w chwili jej wywołania, zostaje utworzony. Jedyną różnicą jest to, ze klasa F ile jest szybsza dla
niewielk'ej liczby operaq l ii Fi le In fo le piej sprawuje
P O B R A Ć I N F O R M A C J E O P L IK U się przy dużych zadaniach uje
Metody GetLastAccessTime() oraz
GetLastW riteTime() zwracają datę i czas ostatniego
dostępu do pliku lub ostatniej modyfikacji.
& P O B R A Ć L IS T Ę P L I K Ó W Z N A J D U J Ą C Y C H SIĘ W K A T A L O G U
Możesz utworzyć tablicę plików umieszczonych w katalogu, używając do tego
metody G e tF ile s () — po prostu przekaż w jej wywołaniu nazwę katalogu,
o którym chcesz pobrać informacje, a reszta zostanie wykonana automatycznie.
^ U SU N Ą Ć KATALOG.
Usuwanie katalogu jest także wyjątkowo proste. Użyj metody D e le te () .
456 Rozdział 9.
Odczyt i zapis plików
■ Nie .istnieją.
głupie pytania
Znacznie lepiej czyta się taki kod, zwłasza jeśli zmienne zapisane są
w jednym wierszu.
P : Używam StreamWriter do zapisywania plików. Po co
mam wiedzieć, że tworzy on za mnie obiekt FileStream ?
P : Dlaczego umieściłeś @ na początku łańcucha znaków, O : Jeśli tylko zapisujesz lub odczytujesz z pliku kolejne wiersze
który zawiera nazwę pliku? w określonym porządku, musisz jedynie wiedzieć o klasach
m. Zaostrz ołówek
" .NET udostępnia dwie klasy z zestawem metod statycznych do pracy z plikami
_ i katalogami. Klasa F ile umożliwia pracę z tymi pierwszymi, natomiast klasa
' D irectory z drugimi. Napisz, co według Ciebie robi każda z tych linijek kodu.
Directory.SetCreationTime(@"c:\SYP\Bonk",
new DateTime(1976, 09, 2 5));
DateTime myTime =
Directory.GetCreationTime(@"c:\SYP\Bonk");
458 Rozdział 9.
Odczyt i zapis plików
Zaostrz
¿u u su ołówek
V R O Z W ią Z Q nie NET udostępnia dwie klasy z zestawem metod statycznych do pracy z plikami
i katalogami. Klasa F ile umożliwia pracę z tymi pierwszymi, natomiast klasa
D irectory z drugimi. Twoim zadaniem było napisanie, co robi każda linijka kodu.
}
i f (Directory.Exists(@ "c:\SYP\Bonk")) { Sprawdź, czy katalog c:\SYP\Bonk istnieje.
Directory.Delete(@ "c:\SYP\Bonk"); Jeśli tak, usuń go.
}
Directory.CreateDirectory(@ "c:\SYP\Bonk"); Utwórz katalog C:\SYP\Bonk.
s tr in g [] f ile s = D irectory.G etFiles(@ "c:\w indow s\", Pobierz listę plików w c:\windows, które są
" * .lo g " , S earch O ptio n .A llD irectories); zgodne z wzorcem *.log, włączając w to pliki
we wszystkich podkatalogach.
F ile.W riteA llText(@ "c:\S Y P \B onk\dziw ak.txt", Utwórz plik o nazwie dziwak.txt (o ile jeszcze nie
To je s t pierwsza lin i j k a , istnieje) w katalogu c:\SYP\Bonk i zapisz do niego
to je s t druga lin i j k a , trzy wiersze tekstu.
a to je s t o statnia l i n i j k a . " ) ;
File.E ncrypt(@ "c:\S YP \Bonk\dziw ak.txt"); Skorzystaj z wbudowanego mechanizmu
To je st alternatywa szyfrowania Windows w celu zakodowania pliku
dla CryptoStream. dziwak.txt, używając do tego danych z konta
zalogowanego użytkownika.
File.Copy(@ "c:\SYP\Bonk\dziwak.txt", Skopiuj plik c:\SYP\Bonk\dziwak.txt
@ "c:\S Y P \kopia.txt"); do c:\SYP\kopia.txt.
File.SetLastW riteTim e(@ "c:\SYP\kopia.txt", myTime); Zmodyfikuj czas ostatniego zapisu pliku kopia.txt
w katalogu c:\SYP tak, aby był równy czasowi
przypisanemu do zmiennej myTime.
460 Rozdział 9.
Odczyt i zapis plików
{
// Summary:
// Performs a p p l i c a t i o n - d e f i n e d tasks
// a s s o c i a t e d with f r e e i n g , releasing, or
// r e s e t t i n g unmanaged r e s o u r c e s .
void DisposeQ;
Każda klasa implementująca IDisposable
zwalnia zarezerwowane wcześniej zasoby
zaraz po wywotaniu Dispose(). Jest
to prawie zawsze ostatnia operacja Alokować, czasownik
wykonywana na obiekcie.
przydzielac przyda środki na różne cele.
Go To Definition Dział programistyczny zirytowało
postępowanie szefa, ponieważ
zaalokował on wszystkie pokoje
f s o ,2 an ^e : j b s po ^ o uyom j b T
pr z , l
konferencyjne na potrzeby zbędnego
seminarium zarządu.
s ę wdo‘m o p r;.^ ^ sk ;rgo dysznyu;I,^ ^ ^ ^ m ^ ;;<wy b^ '^ d y^ ° !;:inDEwsabmanp ^ 5 :
t a kże nacisnąć klawisz F12. y iera° ° P < j Z m e n u m ° ż e s z
Kłopoty w pracy
Poznaj Damiana. Lubi on swoją pracę programisty C #, ale uwielbia także od czasu do
czasu skorzystać z urlopu na żądanie. Jego szef nie toleruje sytuacji, w których jego
podwładni urządzają sobie wolny dzień. Damian musi więc znaleźć dobrą wymówkę.
Czasami Damian
jest zbyt teniwy,
Program do zarządzania wymówkami aby wymyśtać nowe
Damian chce .
przechowywać wszystkie usprawiedtiwienie.
Dodajmy zatem
swoje wymówki
w jednym miejscu. przycisk, który
wczyta tosową
Pomóż mu w takim
razie wybrać do tego wymówkę z jego
odpowiedni folder. katatogu.
Utwórz program zarządzający wymówkami, aby Damian mógł mieć nad nimi kontrolę. Excuse
Description: string
Ćwiczenia Results: string
(n UTW ÓRZ FORMULARZ. LastUsed: DateTime
ExcusePath: string
Ten formularz ma kilka specjalnych możliwości:
★ Podczas pierwszego uruchomienia programu powinien być aktywny tylko przycisk OpenFile(string)
Save(string)
Folder — pozostałe trzy mają być nieaktywne aż do momentu wybrania folderu.
★ Kiedy formularz będzie otwierał lub zapisywał wymówkę, czas zapisu pliku powinien być
wyświetlany przez kontrolkę Label z właściwością AutoSize ustawioną na False
i właściwością BorderS tyle ustawioną na Fixed3D.
★ Po zapisaniu wymówki formularz wyświetli okno MessageBox „Wymówka zapisana”.
★ Przycisk Folder będzie wyświetlał okno dialogowe wyboru folderu. Gdy użytkownik wybierze folder,
aktywne staną się przyciski Zapisz, O twórz oraz L osow a wymówka.
★ Formularz będze potrafił określić, czy zmiany zostały zapisane. Jeżeli nie będzie niezapisanych zmian,
to na pasku tytułowym będzie wyświetlany napis „Program do zarządzania wymówkami”. Kiedy
użytkownik wprowadzi zmiany do jednego z trzech pól, formularz doda do paska tytułowego
symbol gwiazdki (*). Zostanie on usunięty, w przypadku gdy dane zostaną zapisane
lub zostanie otworzona nowa wymówka. Kiedy przeciągniesz
★ Formularz będzie musiał przechowywać informacje o bieżącym folderze oraz o tym, na formularz pole
tekstowe i klikniesz
czy aktualnie wyświetlana wymówka została zapisana. Możesz określić, czy tak się nie stało, j e dwukrotnie,
korzystając z procedur obsługi zdarzenia Changed dla trzech kontrolek. utworzysz dla niego
procedurę obstugi
UTW ÓRZ KLASĘ EXCUSE I PRZECHOWUJ JEJ INSTANCJĘ W FORMULARZU zdarzenia Changed.
Dodaj do formularza pole currentExcuse w celu przechowywania aktualnej wymówki. Będziesz potrzebował
trzech przeciążonych konstruktorów : jednego wywoływanego podczas ładowania formularza, jednego
do otwierania pliku i jednego dla losowej wymówki. Dodaj metodę OpenFile() do otwierania wymówek
(używaną przez konstruktor) oraz Save() do ich zapisu. Dopisz jeszcze metodę UpdateForm(), która będzie
aktualizowała zawartość kontrolek (uzyskasz dzięki niej wskazówki dotyczące budowy klasy):
p riv a te void UpdateFormfbool changed) Ten parametr określa, czy zawartość
i f (¡changed) {
Pamiętaj, th is .d e s c rip tio n .T e x t = currentExcuse.D escription rormularza nie ulegta zmianie. Będziesz
! oznacza NIE t h is .r e s u lt s .T e x t = cu rre n tExcu se .R esu lts; musiat dodać w formularzu pole stużące
— to wyrażenie th iL^ astU sed .V alu e = currentExcuse.LastUsed; do przechowywania tego statusu
pozwala i f_fi!S iring .IsN u llO rEm p ty(cu rren tExcu se.Excu sePath ))
sprawdzić, leD ate.Text = File.G etLastW riteT im e (cu rren tE xcu se .Excu seP a th ).T o String ();
czy ścieżka "Program do zarządzania wymówkami"; Dwukrotnie kliknij trzy kontrolki do wprowadzania
do katalogu
wymów }
danych. IDE utworzy wtedy za Ciebie i c h procedury
NIE je else obstugi zdarzeń Changed. Będą one w pierwszej
pusta łub t h is .T e x t = "Program do zarządzania wymówkami*"; kolejności zmieniaty instancję Excuse, a _następnie
równa nuli. this.formChanged = changed; wywotywaty UpdateForm(true) — potem ju ż tyfco
od Ciebie zależy zmiana pól na fomtuhrztu.
Nie zapomnij także aktualizować w konstruktorze formularza pola LastUsed obiektu wymówki:
public Forml() {
In itializeC o m p o nent();
currentExcuse.LastUsed = lastU sed.Value;
}
SPRAW, ABY PRZYCISK FOLDER OTW IERAŁ OKNO WYBORU FOLDERU
Kiedy użytkownik naciśnie przycisk Folder, formularz powinien wyświetlić okno dialogowe wyboru folderu.
Będzie musiał przechowywać folder w jednym z pól, aby inne okna dialogowe mogły go używać. Gdy formularz
wczytywany jest po raz pierwszy , przyciski Z a p isz , O twórz i L osow a wym ów ka powinny być nieaktywne . Zostaną
one jednak uaktywnione, kiedy użytkownik wybierze odpowiedni folder.
464 Rozdział 9.
Odczyt i zapis plików
★ Każda wymówka zapisywana jest w oddzielnym pliku. Pierwszym jego wierszem jest wymówka,
drugim jej skutek, a trzecim czas ostatniego użycia (skorzystaj z metody T o S trin g () kontrolki
DateTimePicker ). Klasa Excuse powinna posiadać metodę Save() i za jej pośrednictwem
zapisywać wymówkę do określonego pliku.
★ Po otwarciu okna dialogowego Zapisz jako powinien wyświetlić się ten katalog, który został
wybrany przy użyciu przycisku Folder. Nazwa pliku powinna być ustawiona na wymówkę połączoną
z rozszerzeniem „.txt”.
★ Okno dialogowe powinno posiadać dwa filtry: Pliki tekstowe (*.txt) oraz Wszystkie pliki (*.*) . Jeśli
użytkownik spróbuje zapisać bieżącą wymówkę, ale pozostawił puste pole jej samej lub jej rezultatu,
to formularz powinien wyświetlić okno dialogowe z ostrzeżeniem:
★ Kiedy okno dialogowe jest otwierane, to katalog początkowy powinien być ustawiony na ten, który został
wybrany za pomocą przycisku Folder.
★ Dodaj do klasy Excuse metodę Open(). Jej zadaniem będzie wczytywanie wymówki z pliku.
★ Do odczytania zapisanej daty użyj Convert.ToDateTime() . Wynik wpisz do kontrolki DateTimePicker .
★ Gdy użytkownik próbuje otworzyć jakąś wymówkę, a bieżąca nie została zapisana, wyświetlane jest
okno dialogowe:
★ Formularz musi zapisać obiekt Random w pewnym polu i przekazać go do jednego z przeciążonych
konstruktorów obiektu Excuse.
★ Jeżeli bieżąca wymówka nie została zapisana, powinno zostać wyświetlone to samo okno dialogowe,
które zostało pokazane dla przycisku Otwórz.
Stwórz program zarządzający wymówkami, aby Damian mógł mieć nad nimi kontrolę.
Rozwiązania
ćwiczeń Ten formularz używa pól do przechowywania
obiektu Excuse i wybramg0 folderu, a także
p riv a te Excuse currentExcuse = new Excuse()
pamięta, czy bieżąca wymówka Się zmienita-
p riv a te s trin g selectedFolder = PrzeChowuje również obiekt Random dla
p riv a te bool formChanged = fa ls e ; przycisku Losowa wymówka■
Random random = new Random();
466 Rozdział 9.
Odczyt i zapis plików
p riv a te v o id W r i t e P a r t I n f o ( B o d y P a r t p a r t , S tr e a m W r ite r w r i t e r ) {
if ( p a r t == B o d y P a rt.H e a d )
w rite r.W rite L in e ( " g T o w a j e s t o w ło s io n a " );
e ls e if ( p a r t == B o d y P a r t .S h o u l d e r s )
w rite r .W r ite L in e ( " r a m io n a s ą s z e r o k ie " ) ;
Używamy serii instrukcji
e ls e if ( p a r t == B o d y P a r t.K n e e s ) ^ if/else. Powstaje dtuga
sekwencja „if (part —
w r ite r .W r ite L in e ( " k o la n a s ą g u z o w a te " ) ; [opcja])".
e ls e if ( p a r t == B o d y P a r t.T o e s )
w rite r.W rite L in e ( " p a lc e są m a lu tk ie " ); Mamy tutaj końcowe 6 ^ na wypadek
gdyby żadna z poprzednich wartości' nie byta
e ls e <£.--------------------------------------------------- prawidłowa.
w r ite r .W r ite L in e ( " n ie o k r e ś lo n a część j e s t n ie o k r e ś lo n a " );
WYSIL ___________
CC' SZARE KOMÓRKI
Co może się stać podczas pisania kodu z wieloma instrukcjami i f / e l s e ? Pomyśl
o prostych literówkach, błędach spowodowanych przez nawiasy klamrowe,
pojedyncze znaki równości itp.
468 Rozdział 9.
Odczyt i zapis plików
470 Rozdział 9.
Odczyt i zapis plików
Istnieje pewna metoda dostępna w każdym łańcuchu znaków, która okaże się bardzo
pomocna. Jest to S p l i t ( ) . Pozwala ona przetworzyć łańcuch na tablicę jego fragmentów,
które powstają po podzieleniu tekstu początkowego. Jako parametr przyjmuje tablicę
char[] zawierającą znaki używane do podziału łańcucha.
472 Rozdział 9.
Odczyt i zapis plików
O biekt po serializacji.
Wartości sktadowych
Width oi-az Height
instancj i zapisywane
dv«a
o b ie k t m a ________ są do pliku file.dat
Ten razem z dodatkowymi
01000110
informacjami, których
f e n * / .,» • potrzebuje CLR do
jej późniejszego
przywrócenia (takimi
jak typ obiektu
i każdego z jego pól).
file.dat
Width Height
Obiekt na stercie jeszcze raz
Później...
Pomyśl nad tym przez minutę. Która część obiektu może być unikatowa? Wyobraź sobie,
co musi zostać przywrócone, aby obiekt był dokładnie taki sam jak ten wcześniej zapisany.
W jaki sposób wszystko to, co jest obecne na stercie, musi zostać zapisane do pliku?
W YTĘŻ
U M Y SŁ
Co musi się stać z tym obiektem Car, aby mógł on zostać zapisany,
a następnie przywrócony do swojego oryginalnego stanu?
Powiedzmy, że samochód ma trzech pasażerów, trzylitrowy silnik
oraz opony radialne całosezonowe... Czy te rzeczy nie są częścią
stanu? Co powinniśmy z nimi zrobić?
"K * «
,r. Wszysl co Si«
Każdy z obiektów
Passenger yoaiada
referencje do mnych
obiektów. Czy je także
powinniśmy zap^rt?
474 Rozdział 9.
Odczyt i zapis plików
using System.Runtime.Serialization.Formatters.Binary;
Jeśli już dokonałeś serializacji i obiekt znalazł się w pliku, możesz użyć metody
D e s e ria liz e () obiektu B inaryForm atter do jego powtórnego wczytania. Metoda
zwraca referencję, więc musisz zrzutować zwracaną wartość na typ zgodny z typem
zmiennej, w której chcesz ją zapisać.
476 Rozdział 9.
Odczyt i zapis plików
Następnie dodaj do formularza przyciski Z a p isz Joego i Wczytaj Joego. Oto kod ich procedur obsługi,
który serializuje obiekt jo e w pliku Plik_faceta.dat oraz wczytuje go z niego:
using System.IO; ^ ----- Będziesz potrzeb^^
tych dwóch linijek using.
using System .R untim e.Serialization.Form atters.Binary; Pierwsza jest potrzebna dla
metod związanych z plikami
i strumieniami, druga dotyczy
p riva te void saveJoe_Click(object sender, EventArgs e) serializacji.
{
using (Stream output = F ile .C re a te ("P lik _ fa c e ta .d a t")) {
BinaryFormatter form atter = new BinaryForm atter();
fo rm a tte r.S e ria liz e (o u tp u t, jo e );
}
}
p riva te void loadJoe_C lick(object sender, EventArgs e)
{
using (Stream input = File.O penR ead("P lik_faceta.dat"))
BinaryFormatter form atter = new BinaryForm atter();
joe = (G uy)fo rm a tte r.D e serialize(inp ut);
}
UpdateForm();
}
Uruchom program i pobaw się nim przez chwilę.
Gdyby Joe miał 200 zł z transakcji z Bobem, zaoszczędzonych podczas działania programu, to szkoda byłoby
mu utracić te pieniądze tylko dlatego, że program musiał zostać zakończony. Teraz aplikacja może zapisać
Joego do pliku i przywrócić jego stan w dowolnej chwili.
Co się stanie, jeśli usuniesz p lik P likjaceta.dat z folderu bin/Debug, a następnie klikniesz przycisk Wczytaj Joego?
U T W Ó R Z N O W Y P R O JE K T I D O D A J K LA SY D ECK O R A Z C A R D .
Kliknij prawym przyciskiem myszy w oknie Solution Explorer i wybierz Add/Existing Item . Dodaj klasy
Card oraz Deck (a także typy wyliczeniowe S uits oraz Values i interfejsy CardComparer_bySuit
i CardComparer_byValue), których używałeś w rozdziale 8. Będziesz także potrzebował dwóch klas
do porównywania kart, ponieważ używa ich Deck. IDE skopiuje pliki do nowego projektu — upewnij się tylko,
że zmieniłeś wiersz namespace na górze każdego pliku klasowego tak, aby był zgodny z jego przestrzenią nazw.
O Z N A C Z W S Z Y S T K IE K L A S Y A T R Y B U T E M [S E R IA L IZ A B L E ].
Jeżeli tego nie
Dopisz atrybut [S e r ia liz a b le ] do każdej klasy dodanej do projektu. zrobisz, C# nie
pozwoli Ci na
serializację klas
do pliku.
D O D A J D O F O R M U L A R Z A K IL K A U Ż Y T E C Z N Y C H M E T O D .
Metoda RandomDeck() tworzy losowe zestawy kart. DealCards()
rozdaje wszystkie karty i wypisuje je w oknie konsoli.
Ten fragment tworzy
Random random = new Random(); pusty zestaw i dodaje
p riv a te Deck RandomDeck(int Number) { kilka losowych kart,
używając do tego klasy
Deck myDeck = new Deck(new Card[] { ) ) ; Card z poprzedniego
fo r ( in t i = 0; i < Number; i++) { rozdziatu.
myDeck.Add(new Card(
(Suits)random.Next(4),
(Values)random.Next(l, 14)))
}
return myDeck;
D O B R Z E , P R Z Y G O T O W A N IA Z A K O Ń C Z O N E ...
P R Z Y S T Ą P M Y D O S E R IA L IZ A C J I T E G O Z E S T A W U
Rozpocznij od dodania przycisków służących do serializacji losowego zestawu do pliku i wczytania
go z powrotem. Sprawdź informacje wyświetlone na konsoli, aby przekonać się, czy odczytałeś
to samo, co zapisałeś.
p riv a te void b u tto n l_ C lic k (o b je c t sender, EventArgs e) {
Deck deckToWrite = RandomDeck(5); O b ie k t B in a r y F o r m a t t e r
przyjmuje d o w o ln y obiekt
Na poprzedniej using (Stream output = File.C reate("Z estaw 1.dat")) o zn a c zo n y a tr y b u te m
kartce znajdziesz BinaryFormatter b f = new BinaryForm atter(); S e r ia liz a b le — w ty m
instrukcję b f.S e ria liz e (o u tp u t, deckToWrite); p r z y p a d k u D eck . Z ap isu je
using, którą rqo zdo
r strum
. < r /i n n m o
ienia za pomocą,
należy dodać do }
DealCards(deckToWrite, "To, co zapisałem do p lik u ") metody SerializeO-
formularza■
}
D O K O N A J S E R IA L IZ A C J I W I E L U Z E S T A W Ó W D O T E G O S A M E G O P L IK U
Po otwarciu strumienia możesz zapisywać tyle danych, ile tylko chcesz. Możesz umieszczać w tym
samym pliku dowolną liczbę obiektów. Dodaj zatem jeszcze dwa przyciski, które będą zapisywały do
pliku losową liczbę zestawów. Sprawdź okno konsoli, aby upewnić się, że wszystko wygląda dobrze.
p riv a te void button3_C lick(object sender, EventArgs e) {
using (Stream output = File.C reate("Z estaw 2.dat")) { Zwróć uwagę na sposób,
BinaryFormatter b f = new BinaryForm atter(); w jaki ten wiersz odczytuje
fo r ( in t i = 1; i <= 5; i++) { pojedynczy zestaw i używa
(Deck) do zrzutowania
M ożesz z s e r ia liz o w a ć Deck deckToWrite = RandomDeck(random.Next(l, 10)); wartości zwracanej przez
za pomocą tego b f.S e ria liz e (o u tp u t, deckToWrite); Deserialize() na typ Deck.
sam ego strum ienia DealCards(deckToWrite, "Zestaw numer " + i + " zapisany" Dzieje się tak dlatego,
} że Deserialize() zwraca
} obiekt, nie zna jednak
doktadnie jego typu.
}
p riv a te void button4_C lick(object sender, EventArgs e)
using (Stream input = File.OpenRead("Zestaw2.dat")) { Dopóki rzutujesz obiekty odczytane
BinaryFormatter b f = new BinaryForm atter(); z pliku na wtaściwy typ, dopóty nie
fo r ( in t i = 1; i <= 5; i++) { ma żadnych ograniczeń liczby obiektów,
które można poddać serializacji.
Deck deckToRead = (D e c k )b f.D e se ria lize (in p u t);
DealCards(deckToRead, "Zestaw numer " + i + " odczytany"
}
}
}
R Z U Ć O K I E M N A Z A P I S A N E P L IK I.
Otwórz w Notatniku plik Zestaw1.dat. (Metoda F ile .C re a te () utworzyła go w katalogu
bin\Debug wewnątrz katalogu projektu). Nie jest to może podobne do czegoś, co czytasz
na plaży, ale zawiera wszystkie informacje niezbędne do odtworzenia całego zestawu kart.
jesteś tutaj ► 479
Tworzenie znaków
ZACZEKAJ CHWILĘ. JAKOŚ NIE PRZEKONUJE
MNIE TO ZAPISYWANIE OBIEKTÓW DO DZIWNYCH PLIKÓW,
KTÓRE PO OTWARCIU WYGLĄDAJĄ JAK JEDNO WIELKIE ŚMIETNISKO. GDY
ZAPISYWAŁAM ZESTAW KART W POSTACI ŁAŃCUCHÓW ZNAKÓW, MOGŁAM
ZOBACZYĆ WYNIKI W NOTATNIKU I OGARNĄĆ TO, CO BYŁO W ŚRODKU.
CZY ZADANIEM C # NIE JEST UPROSZCZENIE WSZYSTKIEGO TAK, ABY
BYŁO DLA MNIE ZROZUMIAŁE?
W dalszej części książki poznasz mniej zwarty i bardziej czytelny (i nadający się do edycji!)
format serializacji danych.
.NET używa do kodowania znaków lub ich łańcuchów systemu Unicode. Na szczęście Windows
posiada małe, użyteczne narzędzie, które pomaga zrozumieć jego działanie. Otwórz Tablicę znaków
(aby ją uruchomić, wystarczy wyświetlić ekran startowy systemu Windows, następnie pasek funkcji, Za kulisami
wybrać na nim opcję Szukaj, bądź nacisnąć Windows-R i wpisać charm ap.exe).
Kiedy patrzysz na te wszystkie symbole i litery używane w wielu różnych językach na całym świecie, to zdajesz sobie
sprawę, jak wiele rzeczy musi być zapisanych w pliku, aby przechować tekst. To dlatego .NET koduje wszystkie
łańcuchy znaków i znaki w formacie zwanym Unicode. Kodowanie oznacza, że logiczne dane (na przykład litera „H”)
są pobierane i zamieniane na bajty (numer 72). Takie postępowanie jest konieczne, ponieważ litery, liczby, typy
wyliczeniowe i inne dane muszą kiedyś skończyć, przyjmując postać bajtów na dysku lub w pamięci. I to między innymi
dlatego Tablica znaków jest przydatna — pokazuje sposób kodowania liter przez odpowiednie liczby.
480 Rozdział 9.
Odczyt i zapis plików
R Z A P I S Z Z W Y K Ł Y Ł A Ń C U C H Z N A K Ó W D O P L IK U I W C Z Y T A J G O Z P O W R O T E M .
Użyj tej sam ej m etody W r ite A llT e x t( ) , której używałeś w edytorze tek stu , aby pierw szy przycisk zapisywał łańcuch
znaków Eureka! do plik u o nazw ie eureka.txt. N astęp n ie stw órz now ą tablicę bajtów o nazw ie eurekaBytes ,
wczytaj do niej d an e z plik u i wypisz wszystkie w czytane bajty.
F ile .W rite A llT e x t("e u re k a .tx t", "E ureka!");
b yte [] eurekaBytes = F ile .R e a d A llB yte s("e u re k a .tx t");
foreach (byte b in eurekaBytes) ^ Metoda ReadAlBytesO zwraca referencję do
Consol e .Wr i t e ( "{0} ", b) ; n ^ e j tablicy bajtów, którazawiera wszystkie
C onsole.W riteLine(); bajtyodczytane z pliku.
W o knie Output zobaczysz n astęp u jące w artości: 69 117 114 101 107 97 33. Otwórz teraz p lik w Prostym
edytorze tekstu , który n apisałeś w cześniej w tym rozdziale. W yświetli o n „E u re k a!”.
S S P R A W , A B Y D R U G I P R Z Y C IS K W Y Ś W I E T L A Ł B A J T Y W P O S T A C I S Z E S N A S T K O W E J .
N ie tylko Tablica znaków p o tra fi w yświetlać liczby w po staci szesnastkow ej. Praw ie wszystkie p ro g ram y m ające coś
w spólnego z kodow aniem , których używasz do czytania danych, b ę d ą je wyświetlały w tym system ie. W arto zatem
p oznać zasady pracy z nim . U tw órz w p ro g ram ie p ro ce d u rę obsługi zd arzen ia analogiczną do pierwszej. Z m ień
tylko w iersz C on sole .W rite () n a następujący:
od 0 do 6.
byte[] greeting;
greeting = File.ReadAllBytes(filename);
ininininininini
7 zmiennych ty pu byte
/ O
„87
l
105
z
116 97
i *
106
ę
33
t
33
Jf liczby to wa,
To je st statyczn a metoda Unicode dla zna
tablic, która odwraca
w napisie „Wita
porządek bajtów. Używamy
L i tylko po to, aby pokazać,
ż e z m ia n y w p r o w a d z o n e
do tablicy bajtów zo sta ją Array.Reverse(greeting);
dokładnie zap isan e w pliku.
File.WriteAllBytes(filename, greetir
Tcwze -------------
Wywołaj teraz jego metodę W r ite () . Za każdym razem nowe bajty dodawane są na końcu pliku.
Zawierają one zakodowaną wersję dowolnych wartości przekazanych w postaci parametrów.
w rite r.W rite (in tV a lu e ); Każda instrukcja Write() koduje
odpowiednią wartość do p°staci ba.jtów
w rite r.W rite (s trin g V a lu e ); i przesyła je do obiektu FileStream. Obiekt klasy FileStream
w rite r.W rite (b y te A rra y ); Możesz do nich przekazać ka±dy zapisuje bajty na kmcu
typ wartościowy, a z°stanie m pliku.
w rite r.W rite (flo a tV a lu e ); automatycznie zakodowany.
w rite r.W rite (ch a rV a lu e );
r Zaostrz ołówek
Użyj teraz tego samego kodu, z którego korzystałeś wcześniej, w celu odczytania
zapisanego pliku.
Oto podpowiedz: Łańcuchy
b yte [] dataW ritten = File.R eadAllB ytes("danebinarne.dat"); znaków mogą mieć różną
długość, więc muszą
foreach (byte b in dataW ritten) zaczynać się od liczby,
która ją określa. Możesz
C onsole.W rite("{0:x2} ", b); odnaleźć kody znaków
Console.W riteLine(" - {0} bajtów ", dataW ritten.Length); Unicode, używając
Tablicy znaków.
Console.ReadKey();
Zapisz wynik z okna O utput w pustych polach poniżej. Czy potrafisz określić, które bajty
dotyczą poszczególnych instrukcji W rite () ? Oznacz każdą ich grupę nazwą zmiennej.
bajtów
8 ó 2 9 e 8 0 2 0 ó 5 7 ó £ 7 4 6 1 ó d 2 1 2 £ 8 1 0 0 7 4 f ó d 8 f 5 4 1 4 5 - 2 0 bajtów
L-
intValue stringValue byteArray floatValue charValue
char przechowuje
Pierwszym bajtem w łańcuchu znaków
jest 6 — jest to jego długość. Możesz
użyć Tablicy znaków do wyszukanJa
£ Jeśli użyjesz Kalkulator Windows
do zamiany tych bajtów z postaci
szesnastkowej na dziesiętną
znaki Unicode.
zajmuje tylko jeden
bajt — zakodowany
każdego znaku w napisie „Witaj!“ — to przekonasz się, że są to
wartości z tablicy byteArra.y. jest wartością
rozpoczyna się on od U+0057, a kończy U+0045.
na U+0021.
Powiedz obiektowi BinaryReader, jakich typów danych oczekujesz, wywołując jego różne metody.
int intRead = reader. Readlnt32 (); R?py artośc!owy ma w obiekcie
DinaryReader swoją własną metodę, która
string stringRead = reader. ReadStr1ng(); zwraca jego dane. Większość z nich nie
byte[] byteArrayRead = reader. ReadBytes(4 ); ( potrzebuje żadnych parametrów, ale ReadBytes()
flo a t floatRead = reader. ReadS1ngle(); l ieMo w r s in f^ ^ T ćbę bajtów
char charRead = reader. ReadChar(); do odczytania.
Wyświetl teraz wczytane wartości, aby przekonać się, że wszystko działa prawidłowo.
Jeśli dodajesz
ten kod na Console.Write("int: {0} string: {1} bajty: ", intRead, stringRead);
końcu programu foreach (byte b in byteArrayRead)
z poprzedniej Console.Write("{0} ", b);
strony, nie
zapomnij dodać Console.Write(" flo a t: {0} char: {1} floatRead, charRead);
do niego kolejnego }
SK ^O , C°nsole. ReadKey ( ) ;
któremu okno
konsoli zostanie Tak będzie wyglądał rezultat wyświetlony w oknie konsoli:
zamknięte dopiero
po naciśnięciu int: 48769414 string: Witaj! bajty: 47 129 0 116 flo a t: 461,695 char:
j akiegoś klawisza.
484 Rozdział 9.
Odczyt i zapis plików
. karta 1 — N otatn ik
W pliku można
P lik E d y c ja F o rm a t W id o k Pom oc
wyróżnić kilka
I ■■‘‘li [[ MRecznaSerializacjaKart., Version=1.0,0.0, C u l t u r e s eut rai., PublicKeyToken=null£[
stów, jednak jeg° [RecznaSerializacjaKart.CardC Q<5uit>k BackingField[<Value>k BackingField[[RecznaSerializac jaKart. Suits]
przeważająca. część RecznaSerializac jaKart .ValuesD 0 Dy " " 'RecznaSerializacjaKart .SuitsC [value DO 0
jest nieczytelna- [ ii ‘ ‘ ‘ReeznaSerializacjaKart.ValuesO flvalue DO [ D
Nie zapomnij
■— y ' o dwóch
instrukcjach
using.
Napisz kod do ręcznego tworzenia nowego pliku, który zawiera króla pik.
Weźmiemy jedną z odczytanych tablic, zmodyfikujemy ją tak, aby zawierała nową kartę,
i zapiszemy ją z powrotem.
486 Rozdział 9.
Odczyt i zapis plików
karta3 — Notatnik -
I P lik E d y c ja F o rm at W id o k Pom oc
A le jest inne rozw iązanie — istnieje pew ien sposób prezen tacji o kreślany jak o w idok szesnastkow y (ang. hex du m p ),
k tó reg o użycie stanow i dość pow szechną tech n ik ę p rzeg ląd an ia danych binarnych. Je st o n z pew nością bardziej
przydatny niż przegląd an ie plików w N o tatn ik u . F o rm a t szesnastkow y — lub hex — to wygodny sposób w yświetlania
bajtów . K ażdy z nich w ym aga do zapisu dw óch znaków , w ięc n a stosunkow o niew ielkiej przestrzen i m ożesz
zobaczyć w iele danych. Je st to tak że fo rm at, w którym m o żn a łatw o d ostrzec pow tarzające się w zorce. Przydaje się
do w yśw ietlania danych binarnych, k tó re m ają długość 8, 16 lub 32 bajtów . W iększość tych danych m a te n d en c ję do
łączenia się w grupy 4, 8, 16 lub 32 b ajtó w ... ta k ja k w szystkie typy w C # . N a przykład in t zajm uje 4 bajty i podczas
zapisu do pliku w łaśnie tyle w ymaga. T a k o to w ygląda te n sam plik w yświetlony w p ostaci szesnastkow ej za po m o cą
jed n eg o z w ielu darm ow ych pro g ram ó w tego typu dostępnych dla system u W indows:
C:\temp>H
V
OJ
O
kO
LO
0000: 57 63 6f 64 7a(b9^€3 64 6 f 20 74 6a 20 Wchodząc do te j
I
I
Każdy wiersz w formacie szesnastkowym reprezentuje szesnaście znaków z wejścia, które zostały użyte
do jego wygenerowania. W naszym przypadku pierwsze cztery znaki są odległością od początku pliku —
pierwszy wiersz rozpoczyna się znakiem 0, następny znakiem 16 (lub 10 szesnastkowo), potem następuje
znak 32 (szesnastkowo 20) i tak dalej (inne programy tego typu mogą wyświetlać dane nieco inaczej,
ale taka postać będzie dla nas satysfakcjonująca).
in t j = 0x20;
MessageBox.Show("Wartość je s t równa " + j ) ;
Kiedy używasz operatora + do łączenia liczby z łańcuchem znaków, liczba ta jest Strimg.Format() używa takich
samych parametrów jak
zamieniana na wartość dziesiętną. Możesz użyć statycznej metody S trin g .F o rm a t() Console.WriteLine(), więc
do zamiany danej liczby na łańcuch znaków w postaci szesnastkowej: nie będziesz musiał się
wiele uczyć, aby tę metodę
s trin g h = S trin g .F o rm a t("{0 :x2 }", j ) ; stosować.
488 Rozdział 9.
Odczyt i zapis plików
s . .
knidu tańcuch znaków posiada metodę która
Kwżr<
acataec!ochrazament. W tym p rz y p a d k o w a ° n*Pee™sze
charactersRead znaków, rozpoczynając od .
cPopatrz na początek_pętli^i znajdź m<ej sce, .gdz<e j e t _
(P°pat"„.';a c h & „ r : R £ i; ^ o c »
I p:o,cz';S’k
ustawiana zmienna
zwraca liczbę znaków czytanych do tablicy).
jesteś tutaj ► 489
G enerow anie w idoku szesnastkowego
Utwórz nowy projekt typu Console Application i n azw ij go hexdum per . Jego kod zamieściliśmy
na następnej stronie. Poniżej przedstawiliśmy wyniki generowane przez nasz program.
Jeśli uruchomisz program bez podcnw^a
żadnych argumentów, to wyświetli on Kod błędu zostanie także zwrócony
komunikat o błędzie i zakończy ^ a łrn i^ w przypadku przekazania nazwy
zwracając stosowny kod błędu. pliku, który nie istnieje.
t
: 0 0 0c 0 2 0 0 00 0 0 4 d 52 ....... M R e c z n a S e r
0020: 69 61 6c 69 7a 61 6 3 6a ializacjaKart, V
0030: 65 72 7 3 69 6f 6 e 3d 31 e r s i o n = l . 0.0. 0 ,
0040: 4 3 75 6c 74 75 72 65 3d Culture=neutrai,
0050: 20 50 75 62 6c 69 63 4b PublicKeyToken=
Zazwyczaj w celu wyświetlania
0060: 6e 75 6c 6c 05 01 00 00 nuli Ręczna
0070: 53 65 72 69 61 6c 6 9 7a SerializacjaKart
tekstów w oknie wiersza
0080: 2e 4 3 61 72 64 02 00 00 .Card <Suit> polecenia używamy metody
0090: 6b 5f 5 f 42 61 6 3 6 b 69 k BackingF i e l d . Console.WriteLine(), jednak
00a0: 3c 56 61 6c 75 65 3 e 6b <Value>k Backin
0 0 b0: 67 4 6 6 9 65 6c 64 0 4 04 g F i e l d . . . R e cznaS
tym razem do wyświetlania
00C0: 65 72 6 9 61 6c 6 9 7a 61 erializacjaKart. komunikatów o błędach
00d0: 53 75 6 9 74 73 02 0O 00 Suits Ręczna skorzystamy z metody
0 0 e0: 53 65 72 69 61 6c 6 9 7a SerializacjaKart
Console.Error.WriteLine(),
00f0: 2e 56 61 6c 75 65 7 3 02 . V a l u e s ..........
0100 : fd ff f f ff lc 52 65 63 RecznaSeria
by nie były one przekierowywane,
0110: 6c 69 7a 61 63 6a 61 4 b lizaćjaKart.Suit jeśli użyjemy znaków > lub >>
0120: 73 01 0 0 0 0 00 0 7 7 6 61 s value ... do przekierowania wyników
0130: 00 00 00 00 00 0 0 0 0 05 . . . . . . . . . . . . . Rec generowanych przez
0140: 7a 6e 61 53 65 72 6 9 61 znaSerializaćjaK
0150: 61 72 74 2e 56 61 6c 75 art .Values v
nasz program.
0160: 61 6c 75 65 5f 5f 0 0 0 8 alue .......... .
0170: 0b
490 Rozdział?.
Jeśli właściwość args.Length ma
Argumenty z wiersza polecenia wartość różną od 1, to w wierszu Odczyt i zapis plików
zostaną przekazane do programu polecenia nie przekazano żadnych
przy użyciu parametru args. argumentów lub ich liczba była
większa od jednego.
{
if (args.Length != 1) Zwróć uwagę, że używamy tu
ST"
metody Console.Error.WriteLine().
Upewnijmy się,
f (!F ile .E x is ts (a rg s [0 ])) że została przekazana
prawidłowa nazwa
C o n sole .E rror.W rite Line ("P lik nie is tn ie je : { 0} ", a rg s [0 ]); pliku. Jeśli taki plik nie
istnieje, wyświetlimy inny
System.Environment.Exit(2); komunikat, a program
Nie potrzebujemy zwróci inny kod błędu■
obiektu StreamReader,
using (Stream input = File.0penRead(args[0])) gdyż odczytujemy
bajty bezpośrednio
in t p o sitio n = 0; ze strumienia.
Aby odczytywać bajty ze
b yte [] b u ffe r = new b yte[16 ]; strumienia i zapisywać je
w buforze, używamy metody
while (p o sitio n < input.Length) Stream.Read(). Zwróć uwagę,
{ że tym razem buforem jest
in t charactersRead = input.R ead(buffer, 0, b uffe r.L e n g th ); tablica typu byte. To ma sens
— odczytujemy bajty, a nie
i f (charactersRead > 0) znaki z pliku tekstowego.
{
C onsole.W rite("{0}: ", S tring.F orm at("{0:x4} p o s itio n ));
p o sitio n += charactersRead;
492 Rozdział 9.
Odczyt i zapis plików
Zmień Program do zarządzania wymówkami Damiana tak, aby używał poddanych serializacji
Ćwiczenia plików binarnych z obiektami Excuse zamiast plików tekstowych.
Zmień Program do zarządzania wymówkami Damiana tak, Musisz zmienić tylko cztery
aby używał poddanych serializacji plików binarnych wiersze w kodzie formularza: dwa
Rozwiązania z obiektami E xcuse zamiast plików tekstowych. w procedurze obsługi zdarzenia C lic k
ćwiczeń przycisku Zapisz oraz dw a w przycisku
Otwórz. Zmienią one okna dialogowe
w ten sposób, aby używ ały nowego
p r iv a t e v o id s a v e _ C lic k ( o b je c t sen de r, EventArgs e) { rozszerzenia .excuse.
/ / is t n i e ją c y kod
s a v e F 1 le D 1 a lo g l.F 1 lte r = "P l1k1 wymówek (* .e x c u s e )|* .e x c u s e |W s z y s tk 1 e p l i k i ( * . * ) |
saveF1leD 1alogl.F1leN am e d e s c r lp tlo n . T e x t + ".e x c u s e "
/ / is t n i e ją c y kod
Standardowe okna
} dialogowe Otwórz
i Z apisz jako robią
p r iv a t e v o id o p e n _ C lic k (o b je c t sen de r, EventArgs e) { tu ta j sztuczką.
/ / is t n ie j ą c y kod
o p e n F 1 le D 1 a lo g l.F 1 lte r =
"P l1k1 wymówek ( * .e x c u s e )|* .e x c u s e |W szystk1e pl1k1 (*
openF1leD 1alogl.F1leN am e = d e s c r lp tlo n . T e x t + ".e x c u s e ";
/ / is t n ie j ą c y kod
}
494 Rozdział 9.
Imię i Nazwisko: Data:
Wyprawa
Laboratorium zawiera specyfikację opisującą program, który
musisz napisać, wykorzystując wiedzę zdobytą w poprzednich
kilku rozdziałach.
Ten projekt jest większy niż te, które widziałeś do tej pory.
Przeczytaj więc uważnie wszystko, zanim przystąpisz do pisania,
i przeznacz na to chwilę czasu. Nie przejmuj się, jeżeli utkniesz
w miejscu — nie ma tutaj niczego nowego. Możesz przejść
do dalszej części książki i wrócić do laboratorium później.
Uzupełniliśmy część detali projektu za Ciebie i upewniliśmy się,
że masz wszystkie potrzebne elementy... i nic więcej.
Do Ciebie należy ukończenie pracy. Ten program można
napisać na bardzo wiele sposobów, a żaden z nich nie jest tym
„jedynie słusznym”. Gdybyś jednak potrzebował podpowiedzi,
to wiedz, że znaleźli się czytelnicy, którzy potwierdzili swoje
próżne przechwałki, publikując rozwiązanie tego laboratorium
na CodePlex, GitHub lub innych witrynach do publikacji kodu
i współpracy nad nim.
A
Laboratorium C# 495
Wyprawa
Specyfikacja: utwórz grę przygodową Można napisać tradycyjną, okienkową aplikację dla
systemu Windows, która będzie się automatycznie
T w o im zadaniem jest utw orzenie gry przygodow ej, w któ re j dostosowywać do dowolnej wielkości ekranu; jednak
potężny w o jo w n ik wyrusza na misję i dzielnie walczy, poziom wykracza to poza ramy materiału, który chcemy przekazać
za poziom em , ze śm ie rte lnie niebezpiecznym i wrogam i. w tej książce. (Dowiesz się, jak to robić przy użyciu XAML,
Utw orzysz sy stem tu ro w y . Oznacza to, że n a jp ierw ruch w następnym rozdziale, niemniej jednak nie pomoże Ci to
w ykonuje gracz, a następnie przeciw nik. G racz może w tw orzeniu aplikacji WinForms). Oznacza to, że kontrolki
przesunąć się lub zaatakować; po tem m ożliw ość ruchu
P ictureB ox, GroupBox oraz T ableL ayoutP anel używane
do prezentacji inw entarza gracza mogą dobrze wyglądać
i ataku dostaje każdy z w rogów. G ra toczy się do czasu,
w oknie Designer, lecz po uruchomieniu programu pojawić
aż gracz p o kon a wszystkich p rze ciw n ikó w na wszystkich
się w dziwnych miejscach. Wystarczy je poprzeciągać tak,
siedm iu poziom ach lu b zginie.
by wyglądały odpowiednio na Twoim ekranie.
496
Wyprawa
Atak
0
1 S0 M™ >
N ietoperz lato
wokół czegoś
w sp o só b losowy.
Kied y znajduje
s i ę w pobliżu
gracza, powoduje Duch powoli porusza s ię
u tra tę niew ielkiej w stron ę gracza. Gdy jtuż
liczby jego znajdzie s ię w jeg o pob^żtu,
pu nktów ż y c ia . atakuje i odbiera średn ią liczbę
punktów życia.
497
Wyprawa
W
Gracz, jego wrogowie, e liksiry oraz
i
bronie s ą wyśw ietlane w osobnych Czy zw róciłeś
kontrolkach P ictu reB ox. uwagę na to, że
kontrolki P ictu reBox,
TableLayoutPanel
Punkty życia gracza. i j ego wrogow z punktami życia
s ą kontrolkami Label u m ę c z o n y m i oraz kontrolki
wewnątrz kontrolki TableLayoutPanel GroupBox
z przyciskam i
s ą um ieszczone
•w lH Jg rT - ' łjdtH ltPPftltij
□uch .jghpstH rtP ojrtsJ w dziwnych m iejscach?
To wtaśnie w te
Upiór
miejs c a m usieliśm y
j e przeciągnąć, by
L p4 aplikacja wyświetlona
• ! : -
na naszym ekranie
wyglądataa p r
prawidłowo.
f
-
□ 1 - H
!_____i
Każda z tych ikon to P ictu reB ox. y O czyw iście można
nap isa ć ten program
w taki sposób,
Każda z tych grup przycisków że będzie wyglądał
Znaki strzałek możesz znaleźć
zostata um ieszczona we
wtasnej kontrolce GroupBox.
i w programie Tablica z nak-ów
(od U+2190 do U+2193),
skopiować i w kleić do
prawidłowo na
każdym ekranie,
jednak wykracza
to poza informacje,
w ła ściw ości T ext przycisk ó w . które chcieliśm y
Ci przekazać w tej
Pobierz obraz tła oraz rysunki broni, Itsiążce. Nie martw s ię
j ednak — pokażemy Ci,
j a k to robić, podczas
przeciwników i gracza z serwera Helionu: tw orzenia aplikacji dla
S klep u Windows.
ftp ://ftp .h elio n .p l/p rzy k la d y /csh ru 3 .zip .
498
Wyprawa
499
Wyprawa
Obiekt Game
ObieN^ za rzą d za graczem ,
O biekt Game zajmuje się tura m i bronią, i lis tą
Is tn ie je tylko jedna broń na p r z e c iw n ik ó w .
każdym poziomie, więc gra
K ie d y jeden z przycisków ruch u fo rm ularza
potrzebuje jed yn ie j e j referencji,
zostaje k lik n ię ty , ten w yw ołuje m etodę M ove() a nie catej h sty . Gracz jednak
o b ie ktu Game. Pozwala ona graczowi w ykonać p °sia d a List<Weapon> do
przechowywania ekwipunku.
ruch, a następnie um o żliw ia przemieszczenie się
każdem u z przeciw ników . D o o b ie ktu Game należy
W diagramie pominęliśm y param etry.
zatem zarządzanie turow ym systemem gry. Każda metoda Move() j ako ^argumem
przyjmuj e kierunek. Niektóre z nich
O to p rzykła d sposobu działania przycisków ruchu: pobierają także obie k t R-wdom.
e(
2. p la y e r.M o v e ()
Ć lik n ifty
przycisk
T
Kiedy gracz klika jeden
ruchu z czterech przycisków M etoda M ove() obiektu
ruchu, formularz Game w p ierw sze j
wywołuje metodę M ove() kolejności wywołuje O b ie V ^
Ob\eV^
obiektu Game. analogiczną metodę gracza,
i ł« » i m i il/n n /if r-t ir ^ n
4. if (N e a rP la y e r())
q;
enem y.M ovef)
g a m e .H itP la y e r();
Po wykonaniu ruchu
przez gracza obiekt Game
ObieVN
nakazuje za pomocą
M ° y e ( ') uczynić to samo
każdemu z przeciwników.
O b te ^
Je ż e li którykolwiek z wrogów
znajdzie s ię w pobliżu gracza
po wykonaniu sw ojego ruchu,
Q * O b te ^
to p rzystą p i do ataku.
500
Wyprawa
Q
Direction dla czterech obiekty przesuw ane są
przycisków kierunku. w inne m iejsce.
2. U p d a te C h a ra c te rs ();
inwentarza
in v e n to ry B o w .B o rd e rS ty le =
Q bieV-t B o rd e rS ty le .F ix e d S irg le ;
O b 'te ^
Obramowania irv e rto r y S w o r d .B o rd e rS ty le =
w szystkich innych W łaściw ość B o rd erS tyle
---------------------------- B o rd e rS ty le .N o re ; zaznacza aktywny przedmiot
broni należy ukryć.
w ekwipunku gracza.
501
Wyprawa
503
Wyprawa
Zarówno Player,
ja k i Enemy dziedziczą
po M over.
r
Klasa Player przeciąża Przeciw nicy
metodę M ove(), wewnątrz M ożesz teraz wywotać nie potrzebują
której wywoływana j e s t Nearby() i /VloveQ na metody A tta ck (),
metoda M ove() klasy bazoiue). obiekcie Enemy oraz Player. ponieważ ich
atak zo sta ł
wbudowany
Dodaj typ wyliczeniowy D irection w metodę
M ove().
Klasa M over, podo bn ie ja k k ilk a innych klas, po trze bu je typu
w yliczeniow ego D ir e c t io n . U tw ó rz go zatem i wstaw do niego
cztery w artości: Up, Down, L e f t oraz R ig h t.
504
Wyprawa
1 M s z j ,u „ woM . ,
505
Wyprawa
p u b lic i n t H itP o in ts { g e t; p r iv a t e s e t; }
506
Wyprawa
Jeżeli b ro ń jest jedynym prze dm io tem , k tó ry posiada gracz, spraw, aby była a ¿ e »
autom atycznie w ybierana. D z ię k i tem u będzie on m ógł użyć je j natychm iast za d a n io w a H a ll P h y ln
w następnej turze.
507
Wyprawa
508
Wyprawa
class B a t : Enemy {
public B a t (Game game, Point location)
: base(game, location, 6) N ietoperz rozpoczyna g ^
posiadając s z e ś ć punktów
{ } P r ^ ^ p ^ ^ r x ie nie b ęd ziesz potrzebował życia, w ięc do konstruktora
^ y t utaj ^żadnego konstruktora; klasa bazowa klasy bazowej przekazuje 6.
zajm ie s ię w szystkim .
public override void M ove (Random random) {
/ / Tutaj umieść swój kod.
} N ietoperz porusza. s ię
} częściow o w spo3Ób
losowy. Używa w ięc
klasy Random, aby r
Każda z klas poch^nych przez połowę czasu
klasy bazowej E nemy przem ieszczać s ię
pośrednio dziedziczy taMżo w przypadkowym L S - » to * * » “ f“ “
po M over. kierunku. ^
509
Wyprawa
Każda
w łaściw ość p u b lic a b s tra c t v o id A t ta c k ( D ir e c tio n d ir e c t io n , Random random);
Name broni
zwraca Każda broń p o sa d a
je j nazwę p ro te c te d bool DamageEnemy(Direction d i r e c t io n , i n t ra d iu s , określony zas i ęg
(„M iecz“ , i n t damage, Random random) { i sposób ataku, więc
„Buława“ , różnie implementu je
P o in t ta r g e t = g a m e .P la y e rL o c a tio n ;
„Łuk“). metodę A tta ck ().
f o r ( i n t d is ta n c e = 0; d is ta n c e < r a d iu s ; d is ta n c e + + ) {
fo re a ch (Enemy enemy in game.Enemies) {
if (N e a rb y(e n e m y.L o ca tio n , t a r g e t , d is ta n c e ) ) {
enem y.H it(dam age, random);
r e tu r n t r u e ;
} M etoda DamageEnemy() wywoływana
} j e s t przez A tta ck (). Najpierw
próbu je odszukać przeciwnika we
t a r g e t = M o v e (d ire c tio n , t a r g e t , gam e.Boundaries) wskazanym kierunku i w określonej
} odległo ści. Je ż e li znajdzie, wywołuje
r e tu r n f a ls e ; je go metodę H it() i zwraca true.
J e ś li nie, zwraca fa lse.
}
Metoda N earby() klasy M over() pobiera jedynie dwa parametry — obiekt P o in t oraz liczbę i n t
— i porównuje obiekt z bieżącym położeniem, zwracając wartość true, jeśli punkt znajduje się blisko
niego. Ze względu na obliczenia wykonywane przez metodę DamageEnemy() będziesz musiał dodać
przeciążoną wersję metody N e a rb y (), która pozwoli porównać dwa punkty i zwróci wartość tru e ,
jeśli będą one położone w określonej odległości od siebie. Będziesz także musiał przeciążyć metodę
Move(), tak by pozwalała przesunąć punkt w określonym kierunku i zwracała nowy obiekt P oint.
Ciekawe, czy uda Ci się wymyślić, jak należy zmodyfikować przedstawione wcześniej metody
N earby() i M ove(), by przeciążone metody nie powielały żadnego kodu.
510
Wyprawa
511
Wyprawa
N am e N am e
p u b lic in te r fa c e IP o tio n {
bool Used { g e t; } A tt a c k () A tt a c k ()
}
M agiczne m ikstury d z i c z ą po klapoeóh e a p
ponieważ s ą używane w U h sam sposob j ak
I ^ t b n pozwala użyć
magicznej m ikstury pronie - gracz klika ich ik°n y w ekw ipunku>
ty lko raz. Z a pomocą aby j e wybrać, a następnie* korzysta z jedn ego
„ if (weapon is IP o tio n T z przycisków ataku, aby ich tażyć.
można także określić, Pow inieneś być w stanie
czy obie k t Weapon je s t napisać te Masy, używając
miks t u rą. Zawdzięczam y d iagramu kla s i poniższych
to interfejso w i. informacji.
tru e .
¡„stancje BluePotion.
A tt a c k ()
512
Wyprawa
W szystko zaczyna się od obsługi zdarzenia Load, k tó re przekazuje X, Y, szer° k°5ć oraz wysokość lub dwa
o b ie kto w i Game p ro sto ką t określający obszar lochów. T a k w ygląda kod, punkty (w przeciwlegjych rogach). Po jej
k tó ry p o zw o li C i rozpocząć pracę: utworzeniu możesz uzyskać dostęp do
jej składowy ch Le ft, Right, Top i B o tto m ,
p rivate Game game;
a także wartości X , Y, W id th i Height.
p rivate Random random = new Random();
p rivate void Form1_Load(object sender,
EventArgs e) {
game = new Game(new Rectangle(78, 57, 420, 155));
game.NewLevel(random);
To s ą wymiary rysunku lochów umieszczonego w tle, który
UpdateCharacters(); pobrn^ ś z ftp i w sta w iłeś do formularza. B y ć może będziesz
m usiat trochę poeksperymentować, aby znaleźć w artości
} odpowiadające położeniu lochu w Twojej ap likacji.
__ i
iloi
Pam iętaj o podwójnym kliknięciu
F o rm u la rz posiada oddzielną funkcję obsługi zdarzeń dla każdego klik n ię c ia k o n tro lk i Pi ctureBox.
G dy u żytko w n ik k lik a iko nę miecza, gra w pierwszej kolejności sprawdza, czy rzeczywiście znajduje się on
w ekw ipu nku , używając m etody CheckPlayerInventory() o b ie ktu Game. Jeżeli gracz przechow uje tę broń,
fo rm u la rz w yw ołuje game.Equip(), aby ją wybrać. Następnie ustawia właściwość BorderStyle każdej
k o n tro lk i, aby narysować obw ódkę przy m ieczu i zadbać o to, by przy innych ikonach ra m ki były niewidoczne.
Istn ie ją także fun kcje obsługi zdarzeń dla każdego z czterech przycisków ruchu.
Są one naprawdę proste. Każda z nich w pierwszej kolejności w yw ołuje m etodę
game.Move() z właściwą w artością Direction , a następnie m etodę form ularza
UpdateCharacters().
Upewnij s ię , że zm ien iłeś z powrotem
widoczność i nazwy przycisków
po wybraniu przez gracza miecza,
łuku lub buławy.
C ztery fun kcje obsługi zdarzeń dla przycisków ataku także są proste.
K ażdy w yw ołuje game.Attack() , a następnie m etodę UpdateCharacters()
form ularza . Jeżeli gracz w yb ra ł m agiczną m iksturę , to m etoda jest wywoływana
w ten sam sposób — poprzez game.Attack() — choć ta b ro ń nie m a kie run ku.
Spraw zatem, aby przyciski L e w o , Praw o i D ó ł w m om encie trzym ania magicznej
m ikstu ry były niewidoczne, a napis na przycisku Góra zam ieniał się na Wypij.
513
Wyprawa
O to czego potrzebujesz:
Po w yko na niu się p ę tli przeglądającej wszystkich prze ciw n ikó w na danym p o zio m ie sprawdź
zm ienną showBat. Jeśli n ietop erz został zabity, showBat w dalszym ciągu będzie rów ne f a ls e .
W ta kim przyp ad ku powiązana z n im k o n tro lk a P ic tu re B o x po w in na być niewidoczna, a pole
tekstow e z liczbą p u n któ w życia wyczyszczone. T o samo zrób z showGhost oraz showG houl.
514
Wyprawa
s w o rd .V is ib le = f a ls e ; Upewnij s ię , że nazwy
pokrywają s ię z tymi nanuam'.
b o w .V is ib le = f a ls e ; Bardzo łatwo d o p r^ a d z ’^ do
r e d P o tio n .V is ib le = f a ls e ; trudnych do u su n ięcia btędow,
b lu e P o tio n .V is ib le = f a ls e ; j e ś li nie s ą one zgodne.
m a c e .V is ib le = f a ls e ;
C o n tro l weaponControl = n u l l ;
s w itc h (game.WeaponInRoom.Name) { B ę d z ie sz m iał więcej
case "M ie c z ": < -------------------- przypadków dla każdego
weaponControl = sword; b re a k; typu broni.
515
Wyprawa
Aplikacje przeznaczone dla Sklepu W ind ow s są bardziej skom plikow ane niż program y WinForms. Dlatego też
program y W inForms są napraw dę e fe ktyw n ym narzędziem do nauki, jednak nie radzą sobie ró w nie dobrze,
gdy chodzi o napisanie niezw ykle o d lo to w e j aplikacji. Czasami w a rto cofnąć się o krok i pomyśleć o tym ,
jak się uczymy, gdyż to pomoże nam robić to bardziej efe ktyw nie . A zatem spróbujm y to zrobić teraz.
Stw orzyłeś już sobie doskonałe podstaw y, zdobyw ając po dstaw ow ą w iedzę o języku C#, obiektach, kolekcjach
i innych narzędziach .NET. Teraz jednak w ró cim y do tw o rz e n ia aplikacji dla Sklepu W ind ow s przy w yko rzysta niu
języka XAML. Na kilku następnych stronach spróbujem y skorzystać z m ożliw ości IDE do badania o b ie któ w , które
tw o rz ą i któ rym i zarządzają program y WinForms. Kiedy później użyjesz IDE do badania o b ie k tó w tw orzo nych
w aplikacjach dla Sklepu W ind ow s pisanych przy użyciu języka XAM L, staraj się w y c h w ytyw a ć różnice
— oraz, co jest ró w n ie w ażne, podobieństw a pom iędzy nimi.
W y k o n a j p o n iż s z e c z y n n o ś c i, z a n im z a c z n ie s z d a ls z ą le k tu r ę te g o ro z d z ia łu
Cały rozd zia ł 1. i znaczną część rozd zia łu 2. przeznaczyliśm y na przedstaw ienie tw orzenia a p lika cji przeznaczonych
dla Sklepu W indow s przy w ykorzystaniu języka X A M L oraz p la tfo rm y .N E T F ra m e w o rk fo r W indow s Store.
T eraz wykorzystam y wiedzę zdobytą w tam tych dwóch rozdziałach. Jeśli chcesz w m ożliw ie ja k największym stopniu
skorzystać z le k tu ry tego rozdziału, sugerujemy, żebyś w yko na ł k ilk a przedstaw ionych poniżej czynności.
★ D la nie któ rych przesiadka z W in F o rm s na stosowanie języka X A M L jest całkow icie bezproblem ow a,
innych natom iast może drażnić. Te czyn n o ści przygotow aw cze po m og ą C i szybciej p rzysw o ić sobie
n a jw a żn ie jsze idee.
★ W ró ć do rozd zia łu 1. i ponow nie, od samego początku stwórz aplikację R a tu j ludzi. T ym razem up ew nij się,
że wpisałeś ręcznie cały kod.
★ I nie zapom nij przea n a lizo w a ć k o d u a p lika cji ! W ciąż znajdują się w n im fragm enty, k tó ry m i jeszcze nie
zajm ow aliśm y się szczegółowo, choć pow inieneś ju ż rozpoznawać je na tyle dobrze, byś m ó g ł rozpocząć
tw orzenie m entalnych podstaw.
★ Spróbuj dobrze zrozum ieć ta jn ik i działania gry. N ie staraj się je d n a k ro b ić tego za wszelką cenę. Jak ju ż
w spom inaliśm y — wciąż pozostało sporo zagadnień, k tó ry m i w tej książce jeszcze nie zajm ow aliśm y się.
★ Jeszcze ra z w y k o n a j p ro je k t X A M L z rozd zia łu 2. N ie zapom nij zrob ić także ćwiczenia. T eraz pow inieneś
być gotowy!
W tej książce nie p rze dstaw iliśm y w szystkich m ożliw ości aplikacji WinForms. W rzeczyw istości korzystają one
z silnika graficznego określanego jako GDI+, potrafiącego generować zaskakująco do bry in te rfe js graficzny
i m a te ria ły do druku oraz doskonale obsługiw ać interakcję z u żytko w n ikie m (choć w ym aga ona znacznie
w iększego nakładu pracy niż w przypadku korzystania z języka XAML). Jednym z najważniejszych sposobów nauki
po dstaw ow ych zasad program ow ania jest przeanalizow anie te j samej rzeczy w ykonanej na dw a różne sposoby.
Od czasu pierwszego projektu, Ratuj ludzi, przedstawionego w rozdziale 1., poznałeś już całkiem
sporo ważnych pojęć związanych z językiem C# i nabyłeś dużo praktyki w posługiwaniu się nimi.
Teraz nadszedł czas, by założyć kapelusz, wziąć lupę i przetestować swoje detektywistyczne
umiejętności. Sprawdź, czy będziesz w stanie odszukać wszystkie elementy C# w kodzie
aplikacji Ratuj ludzi. Aby ułatwić Ci początki poszukiwań, podaliśmy jedną z odpowiedzi.
Czy jesteś w stanie znaleźć pozostałe?
□ Z a s to s o w a n ie in ic ja liz a t o r a .
□ D o d a n ie d o te j s a m e j k o le k c ji o b ie k tó w d w ó c h ró ż n y c h ty p ó w .
□ W y w o ła n ie m e to d y s ta ty c z n e j.
□ Z a s to s o w a n ie m e to d y p ro c e d u ry o b s łu g i z d a rz e ń .
□ U ż y c ie s ło w a k lu c z o w e g o as w c e lu r z u to w a n ia o b ie k tu w d ó ł.
□ P rz e k a z a n ie d o m e to d y r e fe r e n c ji d o o b ie k tu .
^ U tw o rz e n ie in s ta n c ji o b ie k tu i w y w o ła n ie je d n e j z je g o m e to d .
□ U ż y c ie ty p u w y lic z e n io w e g o w c e lu p r z y p is a n ia w a rto ś c i.
Od czasu pierwszego projektu, Ratuj ludzi, przedstawionego w rozdziale 1., poznałeś już całkiem
sporo ważnych pojęć związanych z językiem C# i nabyłeś dużo praktyki w posługiwaniu się nimi.
Teraz nadszedł czas, by założyć kapelusz, wziąć lupę i przetestować swoje detektywistyczne
umiejętności. Sprawdź, czy będziesz w stanie odszukać wszystkie elementy C# w kodzie
aplikacji Ratuj ludzi. Aby ułatwić Ci początki poszukiwań, podaliśmy jedną z odpowiedzi.
Czy jesteś w stanie znaleźć pozostałe?
(Na niektóre z pytań jest więcej niż jedna prawidłowa odpowiedź).
D
k U . ł. poniże\ zam ieściliśm y te , które
e te k ty w is ty c z n e ^ m udało s ię znaleźć; Ty
mogłeś znaleźć zu pełnie inne!
p o sz u k iw a n ia ! ¡j
E Z a s to s o w a n ie ła ń c u c h a z n a k ó w ja k o k lu c z a p o d cza s p o b ie ra n ia
R o z w ią z a n ie o b ie k tu z k o le k c ji D i c t i o n a r y .
Z a s to s o w a n ie in ic ja liz a t o r a .
A D o d a n ie d o te j s a m e j k o le k c ji o b ie k tó w d w ó c h ró ż n y c h ty p ó w .
...W .!m ?t°dzi?. .Sta.rtGa.met).. do...kolekcji. .pla yA rea C h ild ren . ....
doda wa ny j e s t ob e k t S ta c kPa ne. (czto w ie k) oraz ob ie kt
^ W y w o ła n ie m e to d y s ta ty c z n e j.
Z a s to s o w a n ie m e to d y p ro c e d u ry o b s łu g i z d a rz e ń .
^ U ż y c ie s ło w a k lu c z o w e g o as w c e lu r z u to w a n ia o b ie k tu w d ó ł.
P rz e k a z a n ie d o m e to d y r e fe r e n c ji d o o b ie k tu .
^ U tw o rz e n ie in s ta n c ji o b ie k tu i w y w o ła n ie je d n e j z je g o m e to d .
U ż y c ie ty p u w y lic z e n io w e g o w c e lu p r z y p is a n ia w a rto ś c i.
W m e to d zie EndTheG am e() .j€isit' używ any. ty p w y/iczen io w y
r® O tw ó rz p ro je k t Prosty edytor tekstów, k tó ry napisałeś w rozdziale 9., i otw ó rz p lik Form 1.D esign er.cs. Przewiń
jego zawartość w d ó ł i odszukaj deklaracje pól. Powinieneś ujrzeć po je dn ej dekla racji po la dla każdej k o n tro lk i
umieszczonej na form ularzu .
K o n tro lk i takie ja k T ableL ayou tP ane l oraz F low La youtP a nel, mogące zawierać inne kon tro lki, dysponują właściwością
o nazwie C o n tro ls . Jest to obiekt typu C o n tr o lC o lle c tio n , któ ry pod bardzo wielom a względami przypom ina obiekt
typu L is t< C o n tro l> . Każda kon tro lka umieszczana na form ularzu jest obiektem klasy pochodnej klasy C o n tr o l,
a dodanie jej do kolekcji C o n tro ls panelu spowoduje, że zostanie ona wyświetlona wewnątrz niego. Przewiń zawartość
p lik u w dół, do miejsca, w którym do panelu Flow LayoutPanel dodawane są przyciski Zapisz i Otwórz:
this.flowLayoutPanell.Controls.Add(this.save);
this.flowLayoutPanell.Controls.Add(this.open);
this.Controls.Add(this.tableLayoutPanell);
r Zaostrz ołówek +
Przyjrzyj się instrukcjom new oraz wywołaniom metody C o n t r o l s . A dd()
wygenerowanym przez IDE w formularzu Prostego edytora tekstu
i narysuj graf obiektów tworzonych podczas wykonywania tego kodu.
- «75*
p o s ia d a ją c y m U ^ ^
ek t
O b ie k t
i przejrzeć g ra f obiektów .
Locals Watch 1
I Watch 1 ^ n X
Name Type
Kolekcja Controls IQ ta b le L a y o u tP a n « H1.Controls jSystem .W indows.Forms.Ta b le la yo u tC o n tro lC o lle ctio n H
kontrolki * base S ystem .W indow s.Form s.C ontrol.C ontrolC ollection {Sysi
TdbteLayouPanel * Container System .W indows.Form s.TableLayoutPanel
z awiera dwie * N o n-P u b lic m em bers
kontrolki: TextBox * Results V iew
oraz FfowLayoutPanel * .[0] o b je ct {System .W indows.Forms.TextBoxj
za w ierają c ą przyciski * •[!] o b je ct {System .W indows.Form s.FlowLayoutPanell
Z a p isz i Otwórz. Locals Watch 1
Okazuje się, że klasa System .W indows.Form nie R o zw iń pozycję „base” , by w yśw ietlić właściwości, k tó re ob ie kt
posiada właściwości C o n tro ls . Właściwość ta jest odziedziczył po swojej klasie bazowej:
dziedziczona po jej klasie bazowej, C o n ta in e rC o n tro l,
I W a tch 1 ▼ □X
która z kolei dziedziczy ją po swojej klasie bazowej, Name Type
S c r o lla b le C o n t r o l, a ta od swojej klasy - th is SimpleTextEditor.Forml
bazowej C o n tro l. R ozwijaj pozycję IE * b a s e --------------- —^ © * base System.Windows.Forms.Form {SimpleTextEditi
- base System.Windows.Forms.ContainerControl {Sin
w oknie Watch, aby przejść hierarchię dziedziczenia,
s * base System.Windows.Forms.ScrollableControl (Sirr
docierając aż do klasy S yste m .W in d o w s.F o rm s.C o n tro l. S y s te m .W in d o w s .F o rm s .C o n tro l (S im p le T e x tE c W
(T o właśnie po niej klasa Form dziedziczy kolekcję Locals Watch 1
C o n tro ls ). Następnie rozwiń pozycję Results View
kolekcji C o n tro ls . Znajdziesz w niej po jednym obiekcie
dla każdej fc m to lM umieszczonej na f orm ularzu! To . . jes t Twoje ostatn ie sp otkaw e z ap likacjamk .WinFor" 'S ^ d u iS T one
Z r ó b to !
do tworzenia obiektów interfejsu użytkownika
Używając kodu X A M L do tworzenia interfejsu użytkow nika aplikacji przeznaczonych dla Sklepu W indows, w rzeczywistości
tw orzym y g ra f obiektów . Podobnie ja k w aplikacjach W inF orm s, także i w tym przypadku można skorzystać z I D E oraz jego
okna Watch do przeglądnięcia tych obiektów . O tw órz program , którego w ro zd zia le 2. u żyw a liśm y do „za b a w z in s tru k c ją
if-else” . Następnie otw órz p lik M ainPage.xam l.cs, umieść p u n k t przerw ania w konstruktorze, w wierszu zawierającym
w ywołanie m etody In it ia liz e C o m p o n e n t( ) , a następnie sko rzysta j z m o żliw o ści ID E , b y zbadać u tw o rzon e w a p lik a c ji
o b ie k ty in te rfe js u u żytko w n ika .
Uruchom debugowanie, a następnie naciśnij klawisz F1 0 , aby wejść do metody In itia liz e C o m p o n e n t() . Visual Studio 2012
fo r W indows 8 posiada nieco inny układ okien od Visual Studio przeznaczonego do tworzenia tradycyjnych aplikacji, gdyż dysponuje
większymi możliwościami; na przykład pozwala na otwieranie w ielu okien Watch (co przydaje się, kiedy chcemy śledzić większą
liczbę danych). W yświetl to okno, wybierając z menu opcję DEBUG/Windows/W atch/Watch 1, a następnie wpisz w nim t h is :
W a tc h 1 ▼ n X
N am e ^Fypë -
Watch 1 T □ X
Spróbuj je d n a k dodać do okna Watch właściwość la b e lT o C h a n g e .G rid lu b labelT oC hange.C olum nS pan. K o n tro lk a
la b e lT o C h a n g e jest typ u W in d o w s .U I.C o n tro ls .T e x tB lo c k , a klasa ta nie deklaruje żadnej z tych właściwości.
Czy jesteś w stanie odgadnąć, co się dzieje w tym kodzie X A M L ?
Z atrzym aj pro gra m , o tw ó rz p lik M ainPage.xam l.cs i odszukaj w n im deklarację klasy M ainPage. Przyjrzyj się jej
dekla racji — ja k widać, dziedziczy ona po klasie Page. U m ieść w skaźnik myszy nad słowem Page, ta k by I D E w yśw ietliło
pełną nazwę klasy:
przesuń ws kaź n ik m yszy nad słowo
Page> by wy św ie tlić nazwę k/asu.
public seal e d partial class M a i n P a g e : Page ,. J
^
public M a i n P a g e O
class Windows.UI.Xaml.Controls.Page ^
Encapsulates a page o f content that can be navigated to.
t h i s . I n i t ia liz e C o m p o n e n t ( ) ;
T eraz ponow nie u ru cho m pro gra m i naciśnij klawisz F 1 0 , by wejść do m etody I n it ia liz e C o m p o n e n t ( ) . Przejdź
do okna Watch i rozw iń elem enty t h i s , następnie base i jeszcze raz base, przechodząc tym samym nieco w górę
h ie ra rc h ii dziedziczenia.
IW atch 1 ▼ nX
N am e Type -
■this Chapter2Program2.MainPage
base W indows.UI.Xaml.Controls.Page {Chapter2Program2.MaínPage}
base W indows.Ul.Xaml.Controls.UserControl {Chapter2Program2.MainPa
Rozwiń te
ffl * base W indows. Ul.Xaml.Controls.Control {Chapter2Program2. Main Page)
elem enty, by
w yśw ietlić ~ & Content W indows.Ul.Xaml.Ul Element {Windows.UI.Xaml.Controls.Grid}
klasy bazowe. S:a:'c '--e -o e rs Rozwiń elem ent Content i spraw dź jego w ęzeł [W indows.UI.Xam l.Cont rols .G ridJ.
Poświęć chw ilę, by do kła dnie zbadać o b ie kty wygenerowane na podstaw ie kod u X A M L . Przyjrzym y się im nieco do kła dniej
w dalszej części książki. N a razie po p ro stu je prze jrzyj, byś u św iad om ił sobie, ja k w iele o b ie któ w tw orzy T w o ją aplikację.
To zostaje
przekształcone
w element
<ListBox/>
To zostaje przekształcone
w element
<ScrollViewer/>
To zostaje przekształcone
To je s t kolejna kontrolka dostępna w przyborniku.
w element To zostaje przekształcone
Prezentuje ona sekw encję łańcuchów znaków,
dodając do nich pionowe i poziome paski w element
<ScrollViewer/>
przew ijania, je ś li te k st będzie w iększy od <Button/>
rozmiarów kontrolki.
530 Rozdział 10.
Projektowanie aplikacji dla Sklepu Windows z użyciem XAML
Idź na ryby!
Im ię Ręka
K o n tro lk i te zostaną umieszczone w siatce, k tó re j wiersze i ko lu m n y będą się zm niejszać i powiększać w zależności
od w ielko ści ekranu. D z ię k i tem u gra będzie m ogła się zm niejszać i powiększać, dostosowując się do ekranu.
D o określania różnych k o n fig u ra c ji ekranu możesz skorzystać z okna D evice dostępnego w ID E .
Device - n
Display
r— 1 .3 6 6 , 768
1______ 1140dpi # 100% wjtr
Różne opcje dostępne na liście Display pozwalają wyśw ietlać stronę
Theme
przy w ykorzystaniu różnych rozdzielczości oraz proporcji ekranu.
Default
Show chrome
Usuń zaznaczenie pola „Show chrome", by ukryć
Override scaling □
obrazek reprezentujący oprawę ekranu urządzenia.
Clip to display □
CD
Postępy gry
Szablon B a sic Page zaw iera siatkę składającą się z dwóch wierszy. Jej górny wiersz zaw iera nagłów ek z nazwą aplikacji.
Z k o le i w drugim w ierszu um ieszczony jest obszar treści, zde finiow a ny przez poniższą siatkę. Cała ta siatka jest
um ieszczona w w ierszu 1. u kła d u (oraz w jego jedynej ko lu m n ie o num erze 0).
NjW
'
Każda etykie ta um ieszczona na stronie ( „ Im ię ” , „Postępy gry” itd .) jest k o n tro lk ą T e x tB lo c k posiadającą
n ie w ie lk i margines u góry oraz u dołu, ja k rów nież określoną w artość właściwości S u b H e a d e rT e x tS ty le :
K o n tro lk a S c r o llV ie w e r wyśw ietla postępy gry i dysponuje paskam i przew ijania, któ re zostaną w yśw ietlone,
kie dy tekst stanie się zbyt dług i i przestanie się m ieścić w obszarze k o n tro lk i:
O to kolejn e k o n tro lk i, T e x tB lo c k oraz S c r o llV ie w e r , służące do w yśw ietlania grup kart. D om yślną
w artością w yrów nania k o n tro le k S c r o llV ie w e r w p io n ie oraz w po zio m ie jest S t r e t c h i okaże się, że jest
ona naprawdę przydatna. S kon figu ruje m y wiersze i ko lu m n y sia tki w ta k i sposób, by k o n tro lk i S c r o llV ie w e r
rozszerzały się, dostosowując się do ekranów o różnych wielkościach.
< T e xtB lo ck T ext= "G ru py" S ty le = "{S ta tic R e s o u rc e S u b h e a d e rT e xtS tyle }"
M a rg in = "0 ,2 0 ,0 ,2 0 " G rid .R o w = "4 "/>
O
w < S c ro llV ie w e r F o n tS ize = "2 4 " Background="W hite" F oreg rou nd= "B lack"
G rid.R ow = "5" G rid.RowSpan="2" />
P ośrodku sia tki dodaliśm y kolum n ę o szerokości 40 pikseli, by zapewnić odstęp pom iędzy k o n tro lk a m i
w obu kolum nach. Oznacza to także, że pozostałe k o n tro lk i — L is tB o x oraz B u tto n — muszą zostać
umieszczone w trzeciej ko lu m n ie . K o n tro lk a L is tB o x zajm uje wiersze do 2. do 6., a zatem zastosowaliśmy
w niej właściwości G rid .R o w = "1 " oraz G rid .R o w S p a n = "5 ". D z ię k i tem u rozw iązaniu k o n tro lk a ta będzie się
powiększała i zmniejszała, dostosowując do w ielko ści strony.
Pamięta j, że numeracja
< T e xtB lo ck Text="R ęka" S ty le = "{S ta tic R e s o u rc e S u b h e a d e rT e x tS ty le }" w ierszy i kolumn rozpoczyna
G rid.R ow = "0" G rid.C o lu m n = "2" M a rg in = "0 ,0 ,0 ,2 0 " /> s ię od zera , a zatem
w przypadku kontrolek
umieszczonych w trzeciej
< L is tB o x x:N am e="cards" Background="W hite" F on tS ize = "2 4 " H e ig h t= "A u to " kolumnie należy użyć
M a rg in = "0 ,0 ,0 ,2 0 " G rid .R o w = "l" Grid.RowSpan="5" G rid .C o lu m n = "2 "/> w ła ściw ości Grid.Column=“2 ‘ .
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="5*"/> <ColumnDefinition Width="2*"/>
<RowDefinition
Height="Auto"/> <TextBlock/> <TextBlock
Grid.Column= "2"/>
Row="1" oznacza drugi w iersz, gdyż
ich numeracja zaczyna s ię od 0.
<RowDefinition
Height="Auto"/> <StackPanel Grid.Row="1"> <ListBox
<TextBlock/> Grid.Column="2"
<Button/> Grid.RowSpan="5"/>
</StackPanel>
Ta kontrolka L is tB o x
<RowDefinition zajm uje p ięć kolejnych
Height="Auto"/> <TextBlock Grid.Row="2"/> w ierszy, w tym także
wiers z czw arty, który
będzie s ię powiększał,
zajm ując cały wolny
obsza r stron y. Dzięki
<RowDefinition/> <ScrollViewer temu lista będzie s ię
Ten w iersz ma domyślną wysokość 1*, pow iększać, zajm ując
Grid.Row="3"/> a umieszczona w te j komórce kontrolka całą prawą kolumnę
S cro llV ie w e r ma domyślne w artości wyrównania strony.
w poziomie i w pionie, czyli S tre tc h . Oznacza
to, że będzie s ię pow iększać i zm niejszać,
dostosow ując s ię do w ielkości strony.
<RowDefinition
Height="Auto"/> <TextBlock Grid.Row="4"/>
<RowDefinition
Height="Auto" <ScrollViewer Grid.Row="5" Grid.RowSpan="2">
MinHeight="150"/>
t
W te j kontrolce S cro llV ie w e r użyliśm y w ła ściw ości
G rid.R ow Span o w artości “2 “ , by zajmowała ona dwa
.................... są sia d u ją ce w iersze. U m ieściliśm y ją w szóstym
<RowDefinition
Height="Auto"/> w ierszu (czyli w ła ściw ość G rid.R ow ma w artość "6", <Button
gdyż numeracja w ierszy w język u X A M L zaczyna Grid.Row="6"
s ię od 0), którego minimalna w ysokość wynosi
150; dzięki czemu mamy pewność, że kontrolka Grid.Column="2" />
S cro llV ie w e r nie będzie niższa od te j w artości.
ł-
Numeracja w ierszy i kolumn w języku X A M L rozpoczyna s i ę <°d t0, dlatego też w tej kmttmhe B u tton wier-sz zo sta ł okre ś lony
jako 6, a kolumna jako 2 (aby pominąć kolumnę środkową). W łaściwości określające wyrównanie kontrolki w pionie i poziomie
mają wartość S tretch , dzięki czemu przycisk zajm ie cały obszar komórki. Wysokość w iersza zostą ła o k ^ h n a . jako
dzięki czemu zostanie ona dostosowana do wymiarów zaw artości (czyli przy cisk u oraz jego marginesów).
=/Grid>
S '
® Idź na ry
W iersz n a g łó w ka jest
l u to m a ty c z n ie d o d a w a n y do
sza b lo n u Basic Poge, który
zastosujesz w d a ls z e j części
ro z d z ia łu , k ie d y g ra I d z na ry b y.
Imię
b ę d z ie j u ż d z ia ła ć .
W aplikacji Idź na ryby! w k o n tro lce < G rid > zaw ierającej wszystkie pozostałe
kontrolki aplikacji skorzystam y z właściwości M a rg in , by stworzyć o d stęp od
kraw ędzi strony. W arto ścią tej właściwości m oże być: je d n a liczba (określająca
w ielkość wszystkich czterech m arginesów : lew ego, g órnego, praw ego i dolnego),
dwie liczby (pierw sza z nich ok reśla w ielkość m arg in esu lew ego i praw ego, a druga:
górnego i dolnego) bądź też cztery liczby o k reślające, odpow iednio, w ielkość
Strona używa marginesów, by dodać odstęp
m arginesu lew ego, górnego, praw ego i dolnego.
o wielkości jednej jednostki pomiędzy
T w oja aplikacja Idź n a ryby! m a lewy m argines o w ielkości 120 pikseli (czyli etykietam i i pozostałymi elementami.
O prócz tego pom iędzy etykietam i o raz innym i e lem en tam i strony tak że zostały
d o dane m arginesy o w ielkości jed n ej jednostki:
■ Nie. istnieją.
głupie pytania
OZastosowanie
: * w określeniu wysokości wiersza P : Czy jest jakiś prosty sposób, by mieć
lub szerokości kolumny sprawia, że wiersze lub pewność, że moja aplikacja będzie dobrze
kolumny będą powiększane proporcjonalnie, wyglądać na różnych monitorach?
wypełniając cały obszar siatki, Jeśli siatka zawiera
trzy kolumny o szerokościach 3 * , 3* i 4 * , to każda O : Tak, IDE udostępnia przydatne narzędzie, które
z dwóch pierwszych kolumn będzie zajmowała 30% zapewnia takie możliwości. Okno Designer pozwala
szerokości całej siatki pomniejszonej o szerokości sprawdzać, jak projektowane strony XAML będą
kolumn o ustalonej lub automatycznie wyznaczanej wyglądały na różnych urządzeniach, i to na kilka
(Auto ) szerokości; trzecia kolumna (4 * ) będzie sposobów. Można skorzystać z okna Device, by
zajmowała 40% szerokości siatki, wyświetlić stronę w różnych rozdzielczościach oraz
trybach podziału ekranu. W dalszej części książki
Jest pewien powód, dla którego domyślna szerokość
pokażemy, jak można uruchamiać aplikację w
i wysokość określona jako 1* , ma sens, Jeśli wszystkie
symulatorze, zapewniającym możliwość prowadzenia
wiersze i kolumny będą miały te domyślne wartości, to
z nią interakcji w symulowanych urządzeniach
niezależnie od wielkości siatki wielkości poszczególnych
wyposażonych w ekrany o różnej wielkości.
wierszy i kolumn zawsze będą równe,
Użyj języka XAML, by przeprojektować każdą z klasycznych aplikacji Windows na aplikację przeznaczoną
dla Sklepu Windows. Dla każdej z nich utwórz nowy projekt Blank App i zmień stronę główną na stronę
utworzoną przy użyciu szablonu Basic Page (dokładnie tak samo jak w aplikacji Ratujludzi). Następnie
Ćwiczenia zmodyfikuj strony, wprowadzając odpowiednie zmiany w siatce i dodając do niej kontrolki. Te nowe
aplikacje nie muszą działać. Wystarczy, że utworzysz odpowiedni kod XAML, tak by pasowały one
wyglądem do poniższych formularzy.
Odszukaj odpowiednie miejsce w kodzie XAML strony Basic Page i dodaj tam
nową kontrolkę G rid lub S ta c k P a n e l, w której umieścisz pozostałe kontrolki
strony. Tę nową siatkę powinieneś umieścić w drugim wierszu (G rid .R o w = 'T ")
nowo utworzonej strony Basic Page.
AAML dość elastycznie
<Grid Style="{StaticResource LayoutRootStyle}">
podchodzi do kolejności
<Grid.RowDefinitions> znaczników.
<RowDefinition Height="140"/> Popros iliśm y C ię, b yś dodawał
<RowDefinition Height=" kod'X A M L nowego układu strony
Oto kontrolka <Grid> umieszczona poniżej defin ic ji w ierszy, gdyż
</Grid.RowDefinitions» . na tej stronie. Zwinęliśmy ją dzięki temu łatw iej go będzie
______________________________ w edytorze XAML. odnaleźć w pliku. Niektórzy
<Grid| Grid.Row="l" Margin=”120,0".,T]> program iści lubią zapisyw ać kod
A/ńm*' w takiej sam ej kolejności,
<!-- Back button and page title --> w ja k ie j posziczególne kontrolki
i-,:; Odszukaj ten komentarz, s ą umies zcz ane na stro n ie. Mogą
je um ieszczać za zamykającym
<Grid.ColumnDefinitions> Z o d a f n O w ą ^ t n Ę <0%°'. z nacznikiem </ G r id>, kończącym
<ColumnDefinition Width="Auto"/> sia tk ę z a wi'e ra jącą p rzycisk ze
%trz a!ką w. lewo oraz ty tu ł aplikacji.
Kadzimy, żeby ś poeksperym entował,
z ap isu ją c kod w różnych m iejscach,
gdyż warto, by ś samemu określił,
które miejs c e będzie Ci s ię
wydawało najbardziej intuicyjne.
538 Rozdział 10.
Projektowanie aplikacji dla Sklepu Windows z użyciem XAML
Zastosuj kontrolki S ta c k P a n e l, by zaprojektować układ tego formularza. Składa się on z dwóch grup kontrolek. W nagłówkach tych
grup został zastosowany styl G ro u p H e a d e rT e x tS ty le , sam e grupy oddziela od siebie margines o wysokości 40 pikseli, a poniżej
nagłówków został dodany margines o wysokości 20 pikseli. W etykietach umieszczonych nad kontrolkami został zastosowany styl
B o d y T e x tS ty le , a nad kontrolkami dodano margines o wysokości 10 pikseli. Pomiędzy kontrolkami dodano poziomy margines
o szerokości 20 pikseli.
Może się zdarzyć, że kiedy spróbujesz w ybrać opcję N e w Item ..., by dodać do projektu now ą stronę
Basic Page, IDE w y ś w ie tli w oknie D esig n e r kom unikat o błędzie — Visual Studio będzie bow iem oczekiwać,
że projekt zostanie zbudowany. W ta kim przypadku w yb ie rz opcję Rebuild S olution, a błąd zniknie. jesteś tutaj ► 539
Rozwiązanie ćwiczenia
Użyj języka XAML by przeprojektować każdą z klasycznych aplikacji Windows na aplikację przeznaczoną
dla Sklepu Windows. Dla każdej z nich utwórz nowy projekt Blank App i zmień stronę główną na stronę
utworzoną przy użyciu szablonu Basic Page (dokładnie tak samo jak w aplikacji Ratujludzi). Następnie
Rozwiązania
zmodyfikuj strony wprowadzając odpowiednie zmiany w siatce i dodając do niej kontrolki. Te nowe
ćwiczeń aplikacje nie muszą działać. Wystarczy, że utworzysz odpowiedni kod XAML, tak pasowały one wyglądem
do poniższych formularzy.
_____________To j e s t m argines, który m iałeś zastosow ać.
O puszczenie w artości prawego i dolnego
<StackPanel G rid .R o w = "l" M a rg in = "1 2 0 ,0 "> m arginesu s praw i, ±e będą one miały takie same
< T e xtB lo ck T e x t= " P rz y d z ia ł pra c ro b o tn ic o m " w ielkości ja k marg inesy lewy i górny 0 2 0 i O).
S ty le = "{S ta tic R e s o u rc e G ro u p H e a d e rT e x tS ty le }"/>
<StackPanel O r ie n ta tio n = " H o r iz o n ta l" M a rg in = "0 ,2 0 ,0 ,0 ">
<StackPanel M a rg in = "0 ,0 ,2 0 ,0 ">
< T e xtB lo ck T e xt= "Z a d a n ie r o b o tn ic y " M a rg in = "0 ,0 ,0 ,1 0
S ty le = "{S ta tic R e s o u rc e B o d y T e x tS ty le }"/>
<ComboBox S e le c te d In d e x = "0 ">
<ComboBoxItem C ontent= "N auczanie p s z c z ó łe k "/>
<ComboBoxItem C o n te n t= "P ie lę g n a c ja j a j " / >
<ComboBoxItem C ontent= "U trzym yw anie u la " / >
<ComboBoxItem C o ntent= "W ytw arzanie m io d u "/> Czy Twój kod XAM L w yg lą d a inaczej
<ComboBoxItem C o n te n t= "Z b ie ra n ie n e k ta ru " /> niż ten? XAM L pozw ala na tw o rze n ie
stron o bardzo podobnym (lub naw et
<ComboBoxItem C o n te n t= "P a tro l z ż ą d ła m i"/>
identycznym ) w yg lą d zie na w iele
</ComboBox>
różnych sposobów.
</S tackP a ne l>
<StackPanel>
< T e xtB lo ck T e x t= " S h ifts " M a rg in = "0 ,0 ,0 ,1 0 "
S ty le = "{S ta tic R e s o u rc e B o d y T e x tS ty le }"/>
<TextBox/>
</S tackP a ne l>
< B u tto n C o n te n t= "P rz y p is z to zadanie r o b o tn ic y " M a rg in = "2 0 ,2 0 ,0 ,0 "
S ty le = "{S ta tic R e s o u rc e T e x tB u tto n S ty le }" />
To j e s t nagłówek drugiej
< /S ta ckP a n e l> grupy, powyżej niego j e s t
< B utto n C o n te n t= "P rz e p ra c u j następną zm ianę" M a rg in = "0 ,2 0 ,0 ,0 " /> margines o w ysokości 40
p ik se li, a poniżej margines
o w ysokości 2 0 pikseh.
< T e xtB lo ck T e xt= "R a p o rt ze zm iany" M a rg in = "0 ,4 0 ,0 ,2 0 "
S ty le = "{S ta tic R e s o u rc e G ro u p H e a d e rT e x tS ty le }"/>
< S c ro llV ie w e r B o rd e rT h ickn e ss= "2 " B ord erB ru sh= "W h ite" H e igh t= "2 50 "
C ontent= "
R aport zmiany numer 20
R obotnica numer 1 ro b i 'Z b ie r a n ie n e k ta r u 1 je s z c z e prze z 2 zmiany
R obotnica numer 2 za ko ń czyła swoje zadanie
R obotnica numer 2 n ie p ra cuje& #1 3; — . Oto przykładowy te k st, który
R obotnica numer 3 ro b i 'P a tr o l z ż ą d ła m i' je s z c z e prze z 3 zmiany u m ieściliśm y w polu rapoHu.
W łaściw ość Content ignoruje
R obotnica numer 4 r o b i 'N auczanie p s z c z ó łe k ' je s z c z e prze z 4 zmiany znaki nowych w ierszy dodaw<me
" /> w kodzie X A M L — dodaliśmy je
tu ta j, by poprawić czytelnoś ć
< /S ta c k P a n e l
rozwiązania.
K O N TE K ST DANYCH
0/e/ct
/
Kontekst danych je s t
zw yczajną referencją,
zapisyw an ą we w faściw ośc
DataContext kontrolki. % >ść & W iązanie danych operuje
w yłącznie na właściw ościach.
Jeśli spróbujem y w ykorzystać
w tym celu publiczne pole,
O prócz tego po trze bn y jest ob ie kt, z któ ry m k o n tro lk a zostanie powiązana — w tym przypadku jest to o b ie k t Guy zapisany
w zm iennej jo e , którego właściwość Cash m a wartość 3 2 5 .5 0 . K o n te kst jest określany przez zapisanie re fe re n cji do
o b ie ktu Guy we właściwości D a ta C o n te x t k o n tro lk i.
Kontekstem danych dla tej kontrolki
T eraz pow iązanie zostało ju ż określone! Podałeś kon tekst danych będący instancją klasy Guy i określiłeś ścieżkę
pow iązania odw ołującą się do właściwości Cash. Skoro zastosowane pow iązanie m a postać Cash, zatem k o n tro lk a
T e x tB lo c k prze jrzy o b ie k t danych, p o s z u k u ją c w n im w ła ściw o ści o na zw ie Cash.
M ożn a także po m in ąć określanie ścieżki pow iązania i zapisać we właściwości k o n tro lk i je dyn ie wyrażenie { B in d in g } .
W ta k im przypadku w yśw ietlony zostanie w y n ik zw rócony przez w yw ołanie m etody T o S t r in g ( ) klasy Guy.
i
problem ów . Jeśli ścieżka po w ią zan ia będzie się
o d w o ływ a ć do w łaściw ości, która nie jest dostępna
w kontekście danych, to kon tro lka nie będzie
w yśw ie tla ć ani a ktu alizow a ć danych, a jednocześnie
C/^ o ś ć C/WO' nie w ystą p ią żadne problem y w d zia ła n iu programu.
Utworzenie
powiązania między
POWIĄZANIE w ła ściw ością
« m ííím ím ííím ím ííím ííííííííííííííííííííííííííííí; Ite m sS o u rc e kontrolki
ItemsSource="{ B in d i n g } " Li'stBox oraz kolekcją
ObservableCollection
$ sp ra w i, że kontrolka
wy św ie tli w szystk ie
elem enty kolekcji.
K iedy używasz k o n tro lek G rid lub S ta c k P a n e l , inne, um ieszczane w ew nątrz nich k o n tro lk i są zapisyw ane pom iędzy
ich znacznikiem otw ierającym i zam ykającym . W taki sam sposób m o żn a także p ostępow ać, używ ając innych kontrolek.
N a przykład w artość właściwości T e x t k o n tro le k T e xtB o x lub T e x tB lo c k m o żn a określić, zapisując łańcuch znaków
pom iędzy ich znacznikiem otw ierającym i zamykającym:
Ten z a p is j e s t odpowiednikiem
określenia w ła ściw ości Te x t.
<TextBlock>To jest prezentowany tekst</TextBlock>
W takim przypadku now e w iersze m ożna tworzyć, używ ając znaczników < L in e B re a k /> , a nie sym boli & # 1 3 ; . W obu
przypadkach sprow adza się to do zastosow ania znaku U n ico d e o w artości U + 0 0 1 3 , k tóry je st in terp re to w an y jak o znak
now ego w iersza. M ożna go tak że zapisać w form ie szesnastkow ej — & #xD ; ; z kolei sym bol £ pozw ala um ieścić
w łańcuchu znak £ (pam iętasz p ro g ram T ablica znaków ?).
<TextBlock>
<Run Text="Pierwszy wiersz"/>
<LineBreak/>
<Run Text="Drugi wiersz"/>
</TextBlock>
K ażde z tych trzech rozw iązań będzie w yglądało n a ek ran ie ta k sam o, lecz jed n o cześn ie spow oduje w ygenerow anie innego
grafu obiektów . K ażdy znacznik <Run> je st przekształcany w o d ręb n y o b iek t łań cu ch a znaków , a każdy z tych obiektów
m oże m ieć sw oją w łasną nazwę:
<Border Background="Blue"
BorderBrush="Green" BorderThickness="3">
</Border>
Kontrolka S cro llV ie w e r dziedziczy po (lo n te n fco n M ,'
czyli tej sam ej kontrolce, której uży te ś, by s tworzyć
obcych w grze Ratuj ludzi. Utworzona, przez C ieb\e
kontrolka ContentControl zaw ierała konfrofaę Grid,
a w niej trzy kontrolki E llip se .
■Nie .istnieją.
głupie pytania
P : Moja strona zawiera siatkę, w której jest umieszczona inna P : Dlaczego niektóre kontrolki, takie jak TextBlock,
siatka zawierająca StackPanel. Czy istnieje ograniczenie liczby mają właściwość Text , a niektóre właściwość Content?
kontrolek, które można umieszczać w innych kontrolkach?
OPonieważ mogą wyświetlać wyłącznie tekst; właśnie dlatego
:
O Nie. Można umieszczać kontrolki wewnątrz innych kontrolek,
: dysponują właściwością Text typu S trin g , a nie właściwością Content
a z kolei te w jeszcze innych. W dalszej części rozdziału nauczysz się typu o b je c t. Jest to tak zwana domyślna właściwość kontrolki.
tworzyć własne kontrolki, zaczynając od utworzenia kontenera W przypadku kontrolek takich jak Grid oraz StackPanel tą domyślną
i dodając do niego dalsze kontrolki. Na przykład siatkę można umieścić właściwością jest kolekcja C hildren.
w dowolnej innej kontrolce z treścią — zrobiłeś już to w grze Ratuj
ludzi, tworząc wroga z kontrolki Grid i trzech kontrolek E llip se . P : Czy powinienem wpisywać kod XAML ręcznie, czy raczej
To jedna z mocnych stron stosowania języka XAML do projektowania używać okna Designer IDE i przeciągać kontrolki z przybornika?
aplikacji — pozwala ona na tworzenie złożonych stron przy użyciu
prostych kontrolek. OWarto spróbować obu tych sposobów i wybrać ten, który Ci
:
bardziej odpowiada. Wielu programistów korzysta niemal wyłącznie
P : Gdybym mógł określić układ strony, używając kontrolki Grid z okna Designer, choć jest też sporo takich, którzy go niemal wcale nie
używają, gdyż przekonali się, że wpisywanie kodu XAML jest szybsze.
lub StackPanel, to której z nich powinienem użyć?
Dzięki technologii IntelliSense wpisywanie kodu XAML jest faktycznie
O : To w dużym stopniu zależy od sytuacji. Na to pytanie nie ma wyjątkowo łatwe.
jednej dobrej odpowiedzi: czasami lepszym rozwiązaniem będzie użycie
kontrolki S tack P an el, czasami Grid, a czasami połączenia ich obu. P : Przypomnijcie mi jeszcze raz, dlaczego miałem poznać
A nie są to bynajmniej nasze jedyne możliwości. Można skorzystać technologię WinForms? Czemu nie zacząłem od razu uczyć się
z kontrolki Canvas (której użyłeś w grze Ratuj ludzi), pozwalającej na języka XAML i tworzenia aplikacji dla Sklepu Windows?
wyświetlanie innych kontrolek w miejscach określonych przy użyciu
właściwości C anvas.L eft oraz C anvas.Right. Wszystkie te trzy OPonieważ istnieje wiele pojęć, których znajomość znacznie
:
kontrolki są klasami pochodnymi klasy Panel , a jedną z odziedziczonych ułatwia zrozumienie języka XAML. Na przykład przyjrzyjmy się kolekcji
po niej możliwości jest dodawanie i prezentowanie dowolnej liczby C hildren. Gdybyś nie rozumiał, czym są kolekcje, to czy odpowiedź
innych kontrolek. na trzecie pytanie zamieszczone na tej stronie miałaby dla Ciebie
jakikolwiek sens? Może. Jednak znacznie łatwiej ją zrozumieć, wiedząc
P : Czy to oznacza, że są kontrolki, w których można umieszczać czym są kolekcje. Z drugiej strony przeciąganie kontrolek z przybornika
i umieszczanie ich na formularzu jest naprawdę łatwe. Korzystanie
tylko jedną inną kontrolkę?
z technologii WinForms wymaga naprawdę znacznie mniej wiedzy niż
OOwszem. Spróbuj umieścić na stronie kontrolkę ScrollV iew er.
: projektowanie stron z użyciem języka XAML (co jest zrozumiałe, gdyż
A następnie spróbuj umieścić wewnątrz niej dwie inne kontrolki. XAML jest znacznie nowszą i znacznie bardziej elastyczną technologią).
Oto co zobaczysz: Dzięki temu, że poświęciliśmy kilka rozdziałów, prezentując WinForms,
nabrałeś nieco praktyki w projektowaniu aplikacji z graficznym
interfejsem użytkownika i tworzeniu interesujących projektów. A to
^ T h e p ro p e rty 'C o n te n t' is set m e r e th a n o nce. z kolei pozwoliło Ci przyswoić sobie wiele ważnych pojęć. Oprócz tego,
poznanie dwóch sposobów utworzenia tych samych projektów jest
Dzieje się tak dlatego, że w tym przypadku XAML określa wartość bardzo wartościowe. To właśnie z tego powodu wróciliśmy do kilku
właściwości Content kontrolki ScrollV iew er, a ta jest typu o b ject. projektów z poprzednich rozdziałów: poznając dwa sposoby napisania
Spróbuj jednak zastąpić kontrolkę S crollV iew er kontrolką Grid: tej samej aplikacji, będziesz mógł lepiej zrozumieć zarówno technologię
WinForms, jak i aplikacje dla Sklepu Windows.
£
. £>
ek t W ?
Kontrolka T e x tB o x korzysta
z dwukierunkowego powiązania,
by określać wielkość menu.
Oznacza to, że w ko n tro lce T e xtB o x nie trzeba
podawać właściwości x:Name. Ponieważ k o n tro lk a ta Dwukierunkowe
powiązanie
jest powiązana z właściwością Num berO fItem s o b ie ktu utworzone
MenuMaker, nie m usim y pisać żadnego kod u C # , w kontrolce TextBox
oznacza, że
k tó ry by się do niego odwoływał. początkowo zostanie
w niej wyświetlona
warto ść w łaściw ości
Num berOfItem s
oraz ż e później,
kiedy użytkownik
wprowadzi nową
warto ść w kontrolce,
wa rtość w łaściw ości
zostan ie odpowiednio
zmodyfikowana.
Także obiekty
L is tV ie w oraz
TextBlock zostały
powiązane
z właściw ościam i
obiektu
M enuMaker.
Oto w yzw an ie dla programistów. B azu jąc na podanych do tej pory inform acjach, powiedz, ja k w iele z nowej,
poprawionej aplikacji dla Niechlujnego Janka jesteś w stanie napisać bez zaglądania na następną stronę?
r ó b to !
3 U tw órz nowy projekt i zastąp stronę MainPage.xaml stroną
utworzoną według szablonu Basic Page.
U tw ó rz nowy p ro je k t a p lik a c ji ty p u W indows Store. N astępnie usuń z niego p lik M ainPage.xam l , u tw ó rz now ą
s tro n ę n a p o d sta w ie sz a b lo n u B a sic Page i n a d a j je j n azw ę M ainPage.xam l . Po w prow adzeniu tych zmian
będziesz m usiał po no w nie zbudować p ro je k t. T o są do kła dnie te same czynności, k tó re wykonałeś, tworząc
aplikację R a tu j ludzi (zajrzyj do rozd zia łu 1., je śli musisz sobie odświeżyć pam ięć).
z s ws ; , ¡aiksA ddSd r z ^
548 Rozdział 10.
Projektowanie aplikacji dla Sklepu Windows z użyciem XAML
Czy potrafisz samodzielnie stworzyć taką stronę, bazując wyłącznie na tym zrzucie ekranu?
M usisz okre ślić kon tekst danych dla k o n tro lk i S ta c k P a n e l. D z ię k i tem u zostanie on przekazany do
wszystkich k o n tro le k umieszczonych w ew nątrz niej.
W końcu, d w u kro tn ie k lik n ij przycisk, aby wygenerować szkielet pro ced ury obsługi zdarzeń C lic k . O to cały
ko d obsługi tych zdarzeń — ogranicza się on do zaktualizow ania menu:
p r iv a t e v o id n e w M e n u _C lick(o b je ct sen de r, RoutedEventArgs e) {
menuMaker.UpdateMenu();
} Jest pewien sposób pozw alający na łatw ą zm ianę n azw y procedury obsługi
zd arze ń , ta k by jednocześnie zaktu alizo w ać zarów no kod C # ja k i X AM L.
Z a jrz y j do punktu 8 . dodatku „Po zostało ści” , by dowiedzieć się więcej
550 R ° z d z ia ł 10. o narzędziach refakto ryzacji dostępnych w V isual Studio IDE.
Projektowanie aplikacji dla Sklepu Windows z użyciem XAML
A teraz uru cho m pro gra m ! Spróbuj zm ienić liczbę w p o lu T extB ox. W pisz w n im wartość 3 i k lik n ij przycisk
— pro gra m wygeneruje nowe m enu zawierające trzy kanapki.
T eraz możesz ju ż wypróbow ać pow iązania danych, by przekonać się, ja k bardzo są one
elastyczne. Spróbuj wpisać w p o lu tekstow ym „xyz” lu b zostawić je puste. N ic się nie stanie! Zw róć uwagę na
W pisując co ko lw ie k w ko n tro lce T e xtB o x, umieszczamy w niej łańcuch znaków. K o n tro lk a wygenerowaną datę. Nie
z mienia s i ę ona, choć
całkiem in te lig e n tn ie określa, co m a z tym łańcuchem zrobić. W ie, że ścieżka pow iązania menu j e s t aktualizowane.
ma postać N um berO fItem s, zatem przegląda kon tekst danych, sprawdzając, czy jest w nim Cóż, chyba wciąż
pozosta je nam je sz c z e
dostępna ja k a k o lw ie k właściwość o tej nazwie, a następnie stara się w m ożliw ie najlepszy
co ś do zrobienia.
sposób skonwertować łańcuch znaków na typ tej właściwości.
H M M ... WŁAŚCIWOŚĆ
NUMBEROFITEMS MOJEGO KONTEKSTU
DANYCH JEST TYPU IN T, ALE NIE W IEM,
JAK SKONWERTOWAĆ ŁAŃCUCH „X Y Z "
N A LICZBĘ. W TAKIM RAZIE CHYBA LEPIEJ
NIC NIE BĘDĘ ROBIĆ.
ó 'e k t T e -n
D o k ła d n ie w ten sam sposób m ożna tw orzyć instancje niem al wszystkich klas i zapisywać je w polach o b ie ktu strony.
Pozwalają na to ta k zwane zasoby sta tyczn e dodawane do kod u X A M L . Co w ięcej, m echanizm w iązania danych bardzo
dobrze w spółpracuje z ta k im i zasobami statycznym i, zwłaszcza je śli w ykorzystam y także okn o D esigner ID E . W ró ć zatem
do p ro gra m u dla N iechlujneg o Janka i przekształć o b ie k t MenuMaker w zasób statyczny.
xmlns:local="using:NiechlujnyJanekRozdzial10"
< !--TODO: Delete this line if the key AppName is declared in A p p . x a m l -->
<x:String x :Key="AppName">Witamy u Niechlujnego Danka</x:String>
< /P a g e . R esources>
W o k ie n k u zostaną w yśw ietlone wszystkie klasy dostępne w przestrzeni nazw, których możesz używać.
W ybie rz MenuMaker i nadaj je j nazwę menuMaker:
<local:MenuMaker x:Name="irienuMaker"/>
Teraz strona zaw iera statyczny zasób typ u MenuMaker o nazwie menuMaker.
T w ó j pro gra m będzie działał do kła dnie ta k samo ja k wcześniej. A le czy zauważyłeś, co się stało w ID E , kie dy do
ko d u X A M L dodałeś kon tekst danych? G dy ty lk o to zrobiłeś, I D E u tw o rzyło instancję klasy MenuMaker i użyło
je j właściwości do określenia zaw artości powiązanych ko n tro le k. W okn ie Designer natychm iast p o ja w iło się
wygenerowane m enu — jeszcze zanim zdążyłeś u ru ch o m ić aplikację. N iezłe!
Wielkość menu
Karkówka, sos francuski, chleb ryżowy Indyk, sos francuski, chleb biały
Zmodyfikuj znacznik < L is t V ie w > , dodając do niego prosty szablon danych. Używa on
prostego wyrażenia { B i n d i n g } , by wywoływać metodę T o S t r in g ( ) elementów listy.
< L is tV ie w Ite m s S o u rc e = "{B in d in g Menu}" M a rg in = "0 ,0 ,2 0 ,0 "> Nie zm ieniaj nic wewnątrz
To naprawdę znacznika L is tV ie w , tylko
< L is tV ie w .Ite m T e m p la te >
prosty szablon za stą p /> znakiem > i dodaj
danych, co w ięcej, <DataTemplate> poniżej zam ykający znacznik
wygląda on bardzo < T e xtB lo ck T e x t= " { B in d in g } " /> </ListView>. N astępnie pomiędzy
podobnie do znacznikami L is tV ie w dodaj
</D ataTem plate> znacznik ListV iew .Item T em p la te
standardowego
szablonu < /L is tV ie w .Ite m T e m p la te > zaw ierający szablon danych.
używanego do < /L is tV ie w >
w yświetlania
elementów Dodanie {Binding} bez żadnej ś c ie ż ki p ° woduje
L istV ie w Ite m . wywołanie metody ToStringO powiązanego obiektu.
■Nie .istnieją.
głupie pytania
P : A zatem do określania układu mogę używać kontrolek O : Zastosowanie właściwości x:Key powoduje, że zasób statyczny
StackPanel lub Grid. Mogę tworzyć zasoby statyczne w kodzie jest dodawany do kolekcji Resources i kojarzony z podanym kluczem,
XAML lub używać pól definiowanych w kodzie ukrytym. Mogę natomiast nie jest tworzone pole (a zatem nie możesz użyć identyfikatora
ustawiać właściwości kontrolek lub używać wiązania danych. AppName w kodzie C#, musisz odwoływać się do takiego zasobu, używając
Po co jest tyle sposobów na realizację tego samego celu? kolekcji Resources). W razie użycia właściwości x:Name zasób jest
dodawany do kolekcji Resources, a oprócz tego jest dodawany jako
O: Ponieważ C# oraz XAML są niezwykle elastycznymi narzędziami pole do obiektu Page. To dzięki temu byłeś w stanie wywołać metodę
do tworzenia aplikacji. Ta elastyczność pozwala projektować bardzo UpdateMenu() na rzecz statycznego zasobu MenuMaker.
szczegółowe strony działające na wielu różnych urządzeniach z różnymi
ekranami. Dzięki nim dysponujesz obszernym zestawem narzędzi, P : Czy ścieżka powiązania musi wskazywać na właściwość
których możesz używać do tworzenia odpowiednich stron. Nie postrzegaj zawierającą łańcuch znaków?
zatem tej sytuacji jako źródła zamieszania, wyobraź sobie, że dysponujesz
wieloma opcjami, spośród których możesz wybrać te najlepsze. O: Nie. Można wiązać właściwości dowolnych typów, O ile tylko można
skonwertować typ właściwości źródłowej i docelowej, to powiązanie
P : Wciąż nie do końca rozumiem, jak działają zasoby statyczne. będzie działać. W przeciwnym razie dane zostaną zignorowane. Pamiętaj
także, że nie wszystkie właściwości kontrolek są łańcuchami znaków.
Co się dzieje, kiedy umieszczę znacznik wewnątrz sekcji
<Page.Resources>? Załóżmy, że w kontekście danych jest dostępna właściwość logiczna
0 nazwie EnableMyObject. Możesz ją powiązać z dowolną właściwością
O: Dodając tam znacznik, aktualizujesz obiekt Page. Odszukaj zasób typu logicznego, taką jak IsEnabled. W takim przypadku kontrolka
AppName, w którym podałeś nową wartość, by zmienić nagłówek strony: będzie aktywowana i dezaktywowana zależnie od wartości właściwości
EnableMyObject:
<x:String x:Key="AppName">Witamy u Niechlujnego Janka</x:String>
IsEnabled="{Binding EnableMyObject}"
A teraz przejrzyj kod, który IDE dodało podczas tworzenia szablonu Basic
Page, i sprawdź, gdzie ten zasób jest używany: Oczywiście, jeśli powiążesz ją z właściwością tekstową, to będzie
wyświetlany jedynie tekst True lub F alse (co, jeśli się nad tym nieco
<TextBlock x:Name="pageTitle" Grid.Column="1" zastanowić, będzie całkowicie zrozumiałe).
Text="{StaticResource AppName}"
Style="{StaticResource PageHeaderTextStyle}"/>
P : Dlaczego IDE wyświetla dane na formularzu, kiedy utworzę
zasób statyczny i powiązanie w kodzie XAML, lecz nie kiedy zrobię
Strona używa go, by określić tekst. A zatem, co się tak naprawdę to w kodzie C#?
dzieje? Możesz się o tym przekonać, używając IDE. Umieść punkt
przerwania w procedurze obsługi kliknięć przycisku, uruchom O: Ponieważ IDE rozumie kod XAML, zawierający wszystkie informacje
aplikację i kliknij przycisk. Do okna Watch dodaj wyrażenie niezbędne do utworzenia obiektów koniecznych do wyświetlenia strony
this.Resources["A ppNam e"] — zauważysz, że zawiera ono Kiedy tylko utworzyłeś w kodzie XAML zasób MenuMaker, IDE utworzyło
referencję do łańcucha znaków. W taki sposób działają wszystkie zasoby instancję tej klasy Nie było w stanie zrobić tego na podstawie instrukcji
statyczne — dodanie takiego zasobu do kodu powoduje utworzenie new w konstruktorze, gdyż mógł on zawierać wiele innych instrukcji
obiektu i zapisanie referencji do niego w kolekcji o nazwie Resources. 1także one musiałyby zostać wykonane. IDE wykonuje kod ukryty C#
wyłącznie w wyniku uruchomienia aplikacji. Jeśli jednak dodasz do
P : Czy mogę używać zapisu {StaticResource} we własnym strony zasób statyczny, to IDE go utworzy, tak samo jak tworzy instancje
kontrolek TextBlock, StackPanel i wszystkich innych. Ustawia także
kodzie, czy tylko w szablonach, takich jak Basic Page?
właściwości tych kontrolek, by je wyświetlić w oknie Designer, a zatem,
O : Oczywiście — możesz tworzyć i używać zasobów w taki sposób. kiedy określisz kontekst danych oraz ścieżki powiązań, także one zostaną
W szablonie Blank Page nie ma niczego szczególnego, podobnie jak użyte i w efekcie menu pojawi się w oknie IDE.
w innych szablonach używanych w tej książce. Zawierają one zwyczajny
kod C# i XAML i nie robią niczego, czego nie mógłbyś zrobić samemu.
Zasoby statyczne dodawane do strony
P : Użyłem właściwości x:Name, by określić nazwę zasobu są tworzone w momencie wczytywania
MenuMaker, natomiast zasób AppName używa właściwości x:Key.
Czym one się różnią?
strony, a później mogą być używane
przez dowolne obiekty aplikacji.
Nazwa „zasób statyczny" jest _niec°
myląca. Są one tworzone dla każdej
instancji i w żadnym przypadku nie jesteś tutaj ► 555
przypominają pó statycznych-
Z... z... z... zmiany
Możesz zadbać, by obiekty danych informowały o swoich zmianach właściwości docelowe i powiązane kontrolki. Należy
w tym celu zaimplementować interfejs INotifyPropertyChanged, zawierający tylko jedno zdarzenie: PropertyChanged.
Wystarczy zgłosić to zdarzenie po każdej zmianie wartości właściwości i obserwować, jak powiązana kontrolka zostanie
automatycznie zaktualizowana.
K O N TEK ST DANYCH
^ ° Ó /^ ^
Q Q . .
*
! < • • • •
Będzie to wyglądało nieco inaczej od przykładów, które widziałeś w rozdziałach 7. i 8. Nie będziesz musiał dodawać
żadnych metod ani właściwości. Zamiast tego dodasz zdarzenie:
public event PropertyChangedEventHandler PropertyChanged;
A następnie dodasz poniższą metodę OnPropertyChanged(), której będziesz używał do zgłaszania zdarzenia PropertyChanged:
private void OnPropertyChanged(string propertyName) j
PropertyChangedEventHandler propertyChangedEvent = PropertyChanged; To jest standardowy
wzorzec zgłaszania
i f (propertyChangedEvent != null) { zdarzeń stosowany
propertyChangedEvent(this, new PropertyChangedEventArgs(propertyName)) ; w .NET Framework.
I
I
Teraz jedyną rzeczą, którą musisz zrobić, by poinformować powiązaną kontrolkę o zmianie właściwości, jest wywołanie
metody OnPropertyChanged() i przekazanie do niej nazwy właściwości, która uległa zmianie. Chcemy, by po każdej
zmianie menu była aktualizowana kontrolka TextBox powiązana z właściwością GeneratedDate; w tym celu wystarczy,
że dodamy do metody UpdateMenu() jeden wiersz kodu:
public void UpdateMenu() {
Menu.Clear();
for (in t i = 0; i < NumberOfItems; i++) {
Menu.Add(CreateMenuItem()); Nie zapomnij zaimplementować
I interfejsu IN otifyPropertyChanged .
GeneratedDate = DateTime.Now;
Mechanizm wiązania danych działa tylko wtedy,
gdy kontrolki im plem entują ten interfejs. Jeśli do
OnPropertyChanged(MGeneratedDateM) ;
deklaracji klasy nie dodasz : IN o tify P r o p e r ty C h a n g e d ,
}
to pow iązane kontrolki nie będą aktualizowane
Teraz po wygenerowaniu nowego menu powinna się
— i to naw et jeśli obiekt danych będzie zgłaszał zdarzenia
także zmienić data.
P ro p e rty C h a n g e d .
Zakończ przerabianie gry Idź na ryby! do postaci aplikacji dla Sklepu Windows. Będziesz musiał zmodyfikować
kod XAML przygotowany wcześniej w tym rozdziale, dodając do niego powiązania danych, skopiować wszystkie
klasy i typy wyliczeniowe z aplikacji napisanej w rozdziale 8. (lub pobrać je z serwera FTP wydawnictwa Helion)
Ćwiczenia i zaktualizować klasy Player oraz Game.
^ Dodaj istniejące pliki klas i dostosuj deklarowaną w nich przestrzeń nazw do bieżącej aplikacji.
Do swojego projektu dodaj następujące pliki, pochodzące z gry Id ź na ryby!, którą napisałeś w rozdziale 8.:
Values.cs, Suits.cs, Card.cs, D eck.cs, CardComparer_bySuit.cs, CardComparer_byValue.cs, G am e.cs oraz Player.cs.
Możesz skorzystać z opcji A d d Existing Item dostępnej w oknie Solution Explorer, jednak w każdym z tych plików
będziesz musiał zmienić przestrzeń nazw, dostosowując ją do przestrzeni używanej w projekcie (jak już robiłeś
w przypadku kilku innych projektów w tej książce).
Spróbuj zbudować projekt. W plikach G am e.cs oraz Player.cs powinny pojawić się błędy, przypominające te
przedstawione poniżej:
© 2 T h e typ e or nam espace nam e 'TextBex could not be fo u nd (are you m issing a using directive or an assem bly reference?)
© 3 T h e typ e or nam espace nam e 'TextBox' could not be fo u nd (are you m issing a using directive or an assem bly reference?)
^ Dodaj instancję klasy Game jako zasób statyczny i określ kontekst danych.
Zmodyfikuj kod XAML strony, dodając do niego instancję klasy Game jako zasób statyczny, i użyj jej jako kontekstu
danych dla siatki zawierającej stronę Idź na ryby!, którą przygotowałeś we wcześniejszej części rozdziału. Oto kod XAML
definiujący zasób statyczny: <local:Game x:Name="game"/>. Będziesz także potrzebował nowego konstruktora w klasie
Game, gdyż zasoby mogą być tworzone tylko wtedy, gdy klasa definiuje konstruktor bezargumentowy:
p u b lic Game() {
PlayerName = "Edek";
Hand = new O b s e rv a b le C o lle c tio n < s trin g > ();
ResetGam e();
}
[ 4) Do klasy Game dodaj publiczne właściwości, których użyjesz do zdefiniowania powiązań danych
z kontrolkami.
Oto właściwości, które powiążesz z kontrolkami na stronie:
p u b lic bool GamelnProgress { g e t; p riv a te se t; }
p u b lic bool GameNotStarted { get { re tu rn ¡Gam elnProgress; }}
p u b lic s t r in g PlayerName { g e t; s e t ; }
p u b lic O b serva b le C o llectio n < strin g > Hand { g e t; p riv a te s e t ; }
p u b lic s t r in g Books { get { re tu rn D e sc rib e B o o k s(); } }
p u b lic s t r in g GameProgress { g e t; p riv a te s e t ; }
Poniżej przedstawiliśmy wszystkie zmiany, które należało wprowadzić w kodzie klasy Game,
włącznie z kodem, który podaliśmy już w instrukcjach do ćwiczenia.
Rozwiązania
ćwiczeń using System.ComponentModel;
using System.Collections.ObjectModel; Te instrukcje są niezbędne,
by móc korzystać z interfejsu
I NotifyPropertyChanged i klasy
class Game : INotifyPropertyChanged { Ot>servableCollection.
private List<Player> players;
private Dictionary<Values, Player> books;
private Deck stock;
p ub lic bool GameInProgress { get; p riv a te s e t; }
Te właściwości p ub lic bool GameNotStarted { get { retu rn !GameInProgress; } }
są używane p ub lic s trin g PlayerName { g e t; s e t; }
przez mechanizm p ub lic ObservableCollection<string> Hand { g e t; p riv a te se t; }
wiązania danych. p ub lic s trin g Books { get { retu rn DescribeBooks(); } }
p ub lic s trin g GameProgress { get; p riv a te s e t; }
To jest nowy konstruktor klasy
p ub lic Game() { Game. Tworzymy tylko jedną
PlayerName = "Edek"; kolekcję i czyścimy ją w momencie
Hand = new O bservableC ollection<string>(); przywracania początkowego stanu
ResetGame(); gry. Gdybyśmy tworzyli nowy
obiekt, to formularz straciłby
} odwołanie do niego, a aktualizacje
Te metody
zapewniają zostałyby przerwane.
działanie p ub lic void AddProgress(string progress) {
mechanizmu GameProgress = progress + Environment.NewLine + GameProgress;
wiązania OnPropertyChanged("GameProgress");
danych w grze.
Nowe wiersze }
są dodawane
na górze, p ub lic void ClearProgress() { Wszystkie programy, które napisałeś
dzięki czemu GameProgress = String.Empty; podczas lektury tej książki, można
wcześniejsze OnPropertyChanged("GameProgress"); przerobić na aplikacje dla Sklepu
zdarzenia są Windows napisane przy użyciu języka
przesuwane ku }
dołowi kontrolki
XAML. Można je jednak napisać
ScrollViewer. p ub lic void StartGame() { na bardzo wiele sposobów, co jest
ClearProgress(); szczególnie prawdziwe w przypadku
GamelnProgress = true ;
korzystania z języka XAML! To właśnie
dlatego pokazaliśmy Ci tak dużo kodu
OnPropertyChanged(MGameInProgressM);
w ramach opisu ćwiczenia.
z 71 OnPropertyChanged(MGameNotStartedM);
Oto metoda Random random = new Random();
StartGame(), którą players = new List< P la yer> ();
pokazaliśmy już players.Add(new Player(PlayerName, random, th is ) ) ;
wcześniej. Czyści players.Add(new PlayerC'Bartek", random, t h is ) ) ;
ona postępy gry,
tworzy graczy, players.Add(new Player("Janek", random, th is ) ) ;
rozdaje karty, D eal();
a następnie players[0].S ortH and();
aktualizuje Hand.Clear();
postępy gry
foreach (S trin g cardName in GetPlayerCardNames())
i 9rupy.
Hand.Add(cardName);
i f (¡GamelnProgress)
AddProgress(DescribePlayerHands());
OnPropertyChanged("Books");
}
Ta metoda wcześniej zwracała wartość typu bool, dzięki której formularz mógł
aktualizować przebieg gry. Teraz musi ona jedynie wywoływać metodę AddProgress(),
a o resztę zatroszczy się mechanizm wiązania danych.
public void PlayOneRound(int selectedPlayerCard) {
Values cardToAskFor = players[0].Peek(selectedPlayerCard).Value;
for (int i = 0; i < players.Count; i++) {
i f (i == 0)
players[0].AskForACard(players, 0, stock, cardToAskFor);
else
players[i].AskForACard(players, stock);
i f (PullOutBooks(players[i])) {
AddProgress(players[1].Name + ma nową grupę");
int card = 1;
while (card <= 5 && stock.Count > 0) {
players[i].TakeC ard(stock.Deal());
card++;
I
I Grupy uległy zmianie, a formularz musi
OnPropertyChanged("Books"); o tym wjedzieć, by mógł zaktualizować
kontrolki ScrollViewer.
players[0].SortHand();
i f (stock.Count == 0) {
AddProgress("Na kupce nie ma już żadnych k a rt. Gra skończona!");
AddProgress("Zwyc1ęzcą J e s t... " + GetW1nnerName());
ResetGame();
re tu rn ;
Oto zmiany wprowadzone w metodzie
I PlayOneRound(), które aktualizują
I postęp gry, gdy została_on(j
Hand.Clear(); zakończona, bądź aktuahzują rękę
foreach (S trin g cardName in GetPlayerCardNames()) i grupy, jeśli gra jeszcze trwa■
Hand.Add(cardName);
i f (!GameInProgress)
AddProgress(DescribePlayerHands());
I
Przepraszam, że przerywam
to je st n o w y ro z d z ia ł ► 565
Gdzie one się podziały?
O
CHWILECZKĘ, CO JEST?
GDZIE SĄ KLASY DO
OBSŁUGI PLIKÓW?
SPRAWDZAŁEM WSZĘDZIE
W PRZESTRZENI NAZW SYSTEM.IO,
ALE NIE MOGĘ ZNALEŹĆ KLAS DO
OBSŁUGI PLIKÓW! NIBY JAK MAM TERAZ
ZAPISYWAĆ DANE W PLIKACH
I ODCZYTYWAĆ JE?
System.10.
BinaryReader
BinaiyWriter
{} Com pression
^ EndOf5treamException
FileNotFoundException
lnvalidDataExceptior
^5 IOExceptior ni. „„ ż«*.jJ*»#
« , M em oiyStream
Path
NIE MOGĘ
TAKŻE ZNALEŹĆ KLASY
BINARYFORMATTER. W JAKI SPOSÓB
MAM TERAZ SERIALIZOWAĆ SWOJE
O
OBIEKTY?
System.Runtime.Serialization.
C ol Iecti on Data C ontractAttri butę
ContractNam espaceAttribute
To wygląda całkiem obiecująco. DataC ontractAttri butę
DataC ontractResalver
DataC ontractSerializer
DataC ontractSerializerSettings
DataMemberAttribute
DateTim eForm at
# EmrtTypelnformation
A p lik a c je d la S kle p u W in d o w s d y s p o n u ją
d o s k o n a ły m i n a rzę d zia m i do o b s łu g i w e jś c ia -w y jś c ia .
Tworzone przez nas aplikacje dla Sklepu Windows muszą być intuicyjne, K’>edy zobaczysz
spójne i muszą błyskawicznie reagować na działania użytkowników. To wskaźnik
w kształcie
z tych powodów .NET Framework for Windows Store Apps zawiera klasy klepsydry, będzie
i metody pozwalające na wyświetlanie okienek dialogowych związanych ^ _ to oznaczało, że
używasz programu,
z wybieraniem plików oraz obsługę operacji wejścia-wyjścia w sposób który się zablokowa
asynchroniczny — co oznacza, że nie wstrzymują one działania aplikacji i przestał reagować
w czasie, gdy okienko jest widoczne lub podczas zapisywania pliku. Co a użytkownicy tego
nienawidzą! (Ty
więcej, dzięki przeprowadzaniu serializacji przy wykorzystaniu kontraktu również, prawda?).
danych, aplikacje mogą zapisywać pliki, na których można wygodniej
pracować i które są znacznie łatwiejsze do zrozumienia.
Aplikacje dla Sklepu Windows zapewniają sobie wrażliwość i szybkość reakcji, używając operatora await
oraz modyfikatora async. Możesz się przekonać, jak one działają, obserwując sposób wyświetlania okienka
dialogowego MessageDialog, które w żaden sposób nie blokuje działania aplikacji:
Operator aw ait sprawia, że metoda, w której został umieszczony ten kod, zatrzyma się i będzie oczekiwać na
zakończenie wywołania metody ShowAsync() — a ona zostanie zablokowana aż do momentu wybrania przez
użytkownika jednego z poleceń. Jednak w tym samym czasie reszta aplikacji będzie reagow ać n a in n e zdarzenia.
Gdy tylko metoda ShowAsync() zostanie zakończona, zostanie wznowione wykonywanie metody, która ją
wywołała (choć może to nastąpić dopiero po zakończeniu obsługi innych zdarzeń, których wykonywanie zostało
rozpoczęte wcześniej).
Jeśli metoda używa operatora await, koniecznie musi zostać zadeklarowana przy użyciu modyfikatora async:
Kiedy w deklaracji metody zostanie użyty modyfikator async, zyskasz możliwość określania, w jaki sposób metoda
ta będzie wywoływana. Możesz ją wykonać w standardowy sposób. W takim przypadku, gdy zostanie napotkany
operator await, realizacja wróci do kodu wywołującego, dzięki czemu aplikacja nie zostanie zablokowana.
M ożesz się sam em u p rzeko n ać, ja k to działa; wystarczy, że utw orzysz nowy p ro je k t B la n k App
i dodasz do niego następ u jący k o d X A M L: -Ą r ^
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Center"> V - Z r ó b to ! ^
<Button Click="Button_Click_l" FontSize="36">Czy je ste ś szczęśliwy?</Button>
<TextBlock x:Name="response" FontSize="36"/>
<TextBlock x:Name="ticker" FontSize="36"/>
</StackPanel>
A o to kod ukryty aplikacji. B ędziesz tak że m usiał d odać instrukcję using Windows.UI.Popups;,
gdyż do tej p rzestrzen i nazw n ależą klasy MessageDialog o raz UlCommand.
Do zapisu oraz odczytu danych z plików aplikacje dla Sklepu Windows używają klas Windows.Storage. Ta przestrzeń
nazw zawiera między innymi klasę FileIO, która, jak pokazuje okienko IntelliSense, dysponuje metodami, które mogą
nam coś przypominać.
FileIO.
© AppendLinesA sync Te metody przypominają metody dostępne
w klasie File. Na przykład klasa FileIO
© AppendTextAsync
definiuje metody AppendLinesAsync() oraz
© Equals ReadTextAsync(), a w klasie F ile są dostępne
ResclBufferAsync metody AppendLines() oraz ReadText().
Różnica polega na tym, że wszystkie metody klasy
ReadLinesAsync
FileIO zostały zdefiniowane z wykorzystaniem
ReadTextAsync modyfikatora async, a do wykonywania
Referent eEquals faktycznych operacji na plikach używają
© W riteEufferAsync
operatora await. Dzięki temu możemy pisać kod,
który operując na plikach, nie doprowadza do
© WriteEpytesAsync blokowania aplikacji.
}
Referencji typu IStorageFile można bezpośrednio
W przypadku wybrania jednego pliku zwracany użyć w wywołaniu FileIO.ReadTextAsync(), by
jest obiekt typu IStorageFile. Na kilku kolejnych odczytać zawartość pliku.
stronach dowiesz się o nim znaczmie więcej.
570 Rozdział 11.
Async, aw ait i serializacja kontraktu danych
};
p ic k e r.F ile T y p e C h o ic e s .A d d ("P lik tekstow y", new L i s t < s t r i n g > ( )
p ic k e r.F ile T y p e C h o ic e s .A d d ("P lik d z ie n n ik a ",
new L i s t < s t r i n g > ( ) { ".lo g ", ".d a t
I S t o r a g e F i l e s a v e F ile = a w a it p i c k e r . P ic k S a v e F ile A s y n c ( ) ;
if ( s a v e F i le == n u l i ) re tu rn ;
a w a it F i l e I O . W r i t e T e x t A s y n c ( s a v e F i le , t e x t T o W r i t e ) ;
SkyDrive -
Przejdź w gorę (§ )
Spróbuj przero b ić prosty ed y to r tekstów z rozdziału 9. w aplikację dla S klepu W indow s. D o odczytu
i zapisu plików wykorzystasz w niej klasy FilelO , FileOpenPicker o raz FileSavePicker . Jed n ak
w pierw szej kolejności stworzysz stro n ę głów ną aplikacji. A poniew aż b ędzie to aplikacja dla Sklepu
W indow s dysponująca m ożliw ością odczytu i zapisu plików , p o w inna także dysponow ać paskiem
aplikacji z przyciskami O twórz i Z a p isz; d odasz go, korzystając z m ożliwości ID E .
K ontrolka AppBar przypom ina Scrol lViewer lub Border, gdyż m ożna w niej umieszczać inne kontrolki.
O prócz tego kontrolka ta wie, ja k pokazywać się i chować. D ziała zatem ja k wszystkie inne paski aplikacji.
A by ją dodać, wystarczy umieścić w kodzie strony sekcję <BottomAppBar> lub <TopAppBar>.
r® U tw órz now ą aplikację dla S klepu W indow s, używ ając p ro je k tu B la n k A p p , a n astęp n ie zastąp plik
M ainPage.xaml stro n ą u tw o rzo n ą n a podstaw ie szablonu B asic Page. O to zaw artość tej nowej strony:
Skorzystaj z o kn a D ocum ent Outline, by wybrać elem en t Page (lub zaznacz dow olną kontrolkę i kilkakrotnie naciśnij
klawisz E sc). Przejdź do o kna Properties, rozwiń sekcję C om m on i odszukaj w niej właściwość BottomAppBar:
J Common
K liknij przycisk I. aby dodać p a se k aplikacji. K iedy to zrobisz, ID E d o d a do strony następ u jący frag m en t kodu:
<common:LayoutAwarePage.BottomAppBar>
<AppBar/>
</common:LayoutAwarePage.BottomAppBar>
3 Usuń znacznik <AppBar/> w edytorze XAML. Uzupełnij kod, dodając do niego element
<StackPanel> zawierający dwa przyciski: Otwórz i Zapisz:
<common:LayoutAwarePage.BottomAppBar>
<AppBar x:Name="bottomAppBar" Padding="10,0,10,0">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="openButton" Click="openButton_Click"
Style="{StaticResource OpenFileAppBarButtonStyle}"/>
<Button x:Name="saveButton" IsEnabled="false" ^ .
Pod stylami początkowo będzie
Click="saveButton_Click" wyświetlana niebieska falista
Style="{StaticResource SaveAppBarButtonStyle}"/> linia - zniknie, kiedy “ suniesz
</StackPanel> komentarze, w których style ą
umieszczone.
</AppBar>
</common:LayoutAwarePage.BottomAppBar>
Wybierz z menu głównego opcję E D IT /F ind and Replace/Q uick F ind i odszukaj łańcuch znaków
OpenFileAppBarButtonStyle:
Naciskaj przycisk aż w końcu znajdziesz poniższy element <Styl e> w pliku StandardStyles.xaml:
<Style x:Key="OpenFileAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
<Setter Property="AutomationProperties.AutomationId" Value="OpenFileAppBarButton"/>
Dodaj sekwencje --> oraz < !--, by komentarz nie obejmował tego elementu (oznaczają one bowiem, odpowiednio:
początek i koniec komentarzy XML) i zmień nazwę przycisku na polską:
<Style x:Key="OpenFileAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
<Setter Property="AutonationProperties.AutomationId" Value="OpenFileAppBarButton"/>
To samo zrób ze stylem SaveAppBarButtonStyle. Wyszukaj go i zmień kod tak, by nie był umieszczony
w komentarzu.
Na koniec wybierz znacznik <AppBar> w oknie kodu XAML. Spowoduje to wyświetlenie paska aplikacji
w oknie do projektowania interfejsu użytkownika.
O tw ó rz plik
®
Zapiw
.
procedury obsługi zdarzeń Click.
[5^ Oto kod ukryty całego programu. Korzysta on z właściwości TextBox.Text , by modyfikować
Przepisanie programu,
tekst umieszczony w polu. Modyfikujemy w tym celu właściwość obiektu, rezygnując który napisałeś już
z korzystania z techniki wiązania danych. Robimy to celowo, by kod tej aplikacji był możliwie wcześniej, używając
jak najbardziej podobny do edytora, który napisałeś w rozdziale 9. Dzięki temu będziesz miał przy tym nowej
punkt odniesienia umożliwiający porównanie obu programów, na wypadek gdybyś chciał technologii, jest
doskonałym s po
szczegółowo porównywać różnice pomiędzy programami WinForms oraz aplikacjami dla
sobem, by Twój m ó z g
Sklepu Windows. Będziesz także potrzebował poniższych instrukcji using , które powinieneś przyswoił sobie
umieścić na samym początku pliku: n o w y materiał.
using Windows.System;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.UI.Popups;
ptan(jncMm
O
Pasek aplikacji możesz wyświetlić,
dotykając ekranu lub klikając aplikację
myszą, a następnie wykonując gest
przeciągnięcia od dołu strony w górę.
Możesz ta.kże użyć kombinacji klawiszy
Windows+Z.
® ®
Jeśli chcesz serializować instancje klasy, definiujesz jej kontrakt danych, umieszczając przed definicją klasy
atrybut [DataContract], a następnie poprzedzając składowe klasy, które chcesz serizalizować, atrybutami
[DataMember]. Poniżej przedstawiliśmy prostą klasę Guy z dodanym kontraktem danych:
using System.Runtime.Serialization;
Atrybuty [DataContract] oraz [DataMember] należą
do przestTzeni nazw System.Runtime.Serialization.
[DataContract]
class Guy { Atrybut [DataContract] tworzy
kontrakt danych tej klasy.
[DataMember]
public string Name get; private set; }
<Guy xmlns="http://schemas.datacontract.org/2004/07/XamlGuySerializer"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Age>37</Age>
<Cash>164.38</Cash> Każda składowa z danymi
reprezentowana przez odrębny znacznik.
<Name>Joe</Name> Takie rozwiązanie jest nieporównanie
</Guy> bardziej czytelne niż serializacja binarna!
Każdy obiekt IStorageFolder reprezentuje folder dostępny w systemie plików i dysponuje metodami służącymi
do wykonywania operacji na plikach, takimi jak:
★ CreateFileAsync() — ta asynchroniczna metoda tworzy plik w folderze.
★ CreateFolderAsync() — ta asynchroniczna metoda tworzy jeden folder wewnątrz innego.
★ GetFileAsync() — metoda pobiera plik z folderu i zwraca obiekt IStorageFile.
★ GetFolderAsync() — metoda pobiera folder i zwraca obiekt IStorageFolder.
★ GetItemAsync() — metoda pobiera plik lub folder i zwraca obiekt IStorageltem.
★ Metody G etFilesA sync(), GetFoldersAsync() oraz GetItemsAsync() zwracają kolekcje elementów
— są to kolekcje bardzo prostego typu IReadOnlyList, który pozwala pobierać elementy na podstawie
indeksów, lecz nie udostępnia metod pozwalających na ich dodawanie, sortowanie ani porównywanie.
Następnie wystarczy użyć obiektu IStorageFile, by otworzyć plik do zapisu lub odczytu,
wywołując w tym celu metodę OpenAsync() (która zwraca obiekt IRandomAccessStream).
Kiedy już dysponujemy kontraktem danych i strumieniem, jedyną rzeczą niezbędną do zapisu
i odczytu obiektów w plikach XML będzie obiekt klasy DataContractSeri al i zer: IStorageFolder IStorageFile
ContentType
CreateFileAsync() FileType
using Windows.Storage;
Potrzebujesz tych CreateFolderAsync()
using Windows.Storage.Streams; instrukcji using. G etFileAsync()
CopyAndReplaceAsync()
CopyAsync()
using System.Runtime.Serialization; GetFolderAsync()
MoveAndReplaceAsync()
GetItemAsync()
MoveAsync()
GetFilesAsync()
OpenAsync()
Guy joe = new Guy("Joe" 37, 164.38M); GetFoldersAsync()
OpenTransactedWrite-
GetItem sAsync()
* Async()
Oto obiekt Guy z kontraktem danych, który przedstawiliśmy na poprzedniej stronie.
D ataC ontractSerializer s e r ia liz e r =
new DataContractSerializer(typeof(Guy));
IStorageFolder localFolder Obiekt przeprowadzający serializację musi
znać typ serializowanych obiektów. Oto jak
ApplicationData.Current.LocalFolder; każemy mu serializować obiekty Guy wraz
z całym grafem obiektów zależnych.
Jest je d n a k pew ien haczyk. A plikacje dla S klepu W indow s m ogą b ez ograniczeń zapisywać i odczytywać
pliki z folderu lokalnego; jeśli je d n a k aplikacja ta k a zechce skorzystać z innego fo ld eru , b ędzie jej trzeba
nad ać odpow iednie u praw n ien ia, dodając niezbędne możliwości do manifestu pakietu . Jeśli jaw nie
pozw olim y aplikacji odczytywać i zapisywać pliki w folderze lokalnym , to każdy, kto ją zainstaluje
ze Sklepu W indow s, będzie w stan ie zauważyć, że dysponuje o n a tym i możliw ościam i.
A by dodać do aplikacji możliw ość korzystania z biblioteki dokum entów , dw u k ro tn ie kliknij p lik Package.manifest
w oknie Solution Explorer, kliknij k a rtę Capabilities i zaznacz p o le w yboru D o cu m en t Library.
zapewnić
U ae this page to specify system feature] ^ the docum ents libiaty capability is declared, then one or m ore file type associations must be added in the Declarations tab.
aplikacji prawo
do odczytu
Capabilities: D e s c r ip tio n :
i zapisu plików
T h is capability is subject to Store policy. See "M ore Inforn
w folderze ocum ents Library
ch an g e or delete files in th e Docum ents Library fo r th e lo
biblioteki Q Enterprise Authentication Docum ents Library that are defined using the File Typ e A ;
dokumentów. 0 Internet (Client) Docum ent Libraries on HomeGroup PCs.
Kliknij k artę D eclarations, z rozw ijanej listy w ybierz opcję File Type A ssociation i kliknij przycisk A d d . Spow oduje to
w yświetlenie form ularza, k tó reg o dw a p o la b ę d ą o znaczone znakam i „X ” n a czerw onym tle. W p o lu N a m e wpisz
x m l_ file type , a w p o lu File — .xml.
Nam e:
Edit f l a g s -----------y g r —
I | O pen is safe
I | A lw a y s un safe
“C .
Z ap isz m anifest p ak ietu i zam knij go. T e ra z T w oja aplikacja m oże odczytywać i zapisywać p lik .xm l w k atalo g u biblioteki
dokum entów użytkow nika.
Serializacja o b ie k tó w Guy
Mam na imię Joe, mam 37 la l i 176,22 złotych w kieszeni, Mam na imię Bob, m am 45 lat i 4,6 8 złotych w kieszeni, a Mam na im ię Ed, m am 43 lat i 37,51 złotych w kieszeni, a
a m oją atutow ą kartą je s t Six o f C lubs m oją atutową kartą je s t Four o f Sp ades m oją atutową kartą je st Five o f Diamonds
Dodaj do strony statyczny zasób GuyManager (i przy okazji określ nazwę aplikacji). Możesz już teraz dodać
Klasę GuyManager dodasz na następnej stronie. do projektu pustą klasę
GuyManager, aby pozbyć
<Page.Resources> się błędu wyświetlanego
przez IDE w tym
<local:GuyManager x:Name="guyManager"/> znaczniku — wypełnisz
ją na następnej stronie.
<x:String x:Key="AppName">Serializacja obiektów Guy</x:String> Nie zapomnij ponownie
</Page.Resources> zbudować rozwiązania po
dodaniu nowej klasy, by
usunąć błąd z okna do
n»Amkł^iiii/snin «tron
< G r id . R o w D e f in it io n s >
< R o w D e f in it io n / >
< R o w D e f in it io n / >
< /G r id . R o w D e f in it io n s >
<StackPanel>
< T e xtB lo ck T e x t = " { B in d in g J o e }" S t y le = " { S t a t i c R e s o u r c e I te m T e x t S t y le } " Każda kolumna
M a rg in = "0 ,0 ,0 ,2 0 "/> w górnym wierszu
zawiera StackPanel,
< B utto n x:Name="WriteJoe" C o n te n t= "Z a p isz Joego" C l i c k = " W r i t e J o e _ C l i c k " / > w którym są
</S tackP a ne l> umieszczone
kontrolki TextBlock
<StackPanel G r id .C o lu m n = "l"> oraz Button.
< T e xtB lo ck T e x t = " { B in d in g Bob}" S t y le = " { S t a t i c R e s o u r c e I te m T e x t S t y le } "
M a rg in = "0 ,0 ,0 ,2 0 "/>
< B utto n x:Name="WriteBob" C o n te n t= "Z a p isz Boba" C l ic k = " W r i t e B o b _ C li c k " / >
</S tackP a ne l>
Ta kontrolka TextBlock jest powiązana
z właściwością Ed obiektu GuyManager.
<StackPanel G rid .C o lu m n = "2"> ^
< T e xtB lo ck T e x t = " { B in d in g Ed}" S t y le = " { S t a t i c R e s o u r c e I te m T e x t S t y le } "
M a rg in = "0 ,0 ,0 ,2 0 "/>
< B utto n x:Name="WriteEd" C o nten t= "Z ap is z Eda" C l i c k = " W r i t e E d _ C l i c k " / >
</S tackP a ne l>
Pierwsza komórka drugiego
wiersza zajmuje dwie kolumny.
<StackPanel G r id .R o w = "l" Grid.ColumnSpan="2" M a r g in = " 0 ,0 , 2 0 , 0 " > ^ ----- Umieściliśmy w niej kilka kontrolek
<TextBlock>Nazwa o s t a t n ie g o zapisanego p lik u < / T e x t B lo c k > powiązanych z właściwościami. Jak
<TextBox T e x t = " { B in d in g Path, Mode=TwoWay}" M a r g in = " 0 , 0 , 0 , 2 0 " / > sądzisz, _dJaczego ścieżkę ^ ępu
<TextB lock>Data u tw o rz e n ia < /T e x tB lo c k > rnfSw^hhSrn,, w konchę TextBox?
< T e xtB lo ck T e x t = " { B in d in g L a te s t G u y F ile . D a t e C r e a t e d .Lo ca lD a te T im e }" M a r g in = " 0 ,0 , 0 , 2 0 "
S t y le = " { S t a t i c R e s o u r c e S u b h e a d e rT e x tS ty le }" /> X.
<TextBlock>Typ z a w a rt o ś c i< / T e x t B lo c k > Kontrolkę można powiązać z właściwością
< T e xtB lo ck T e x t = " { B in d in g L a t e s t G u y F ile . C o n t e n tT y p e } " obiektu. Właściwość LatestGuyFile
S t y le = " { S t a t i c R e s o u r c e S u b h e a d e r T e x tS ty le }" /> jest obiektem typu IStorageFile,
</S tackP a ne l> a te kontrolki TextBlock zostały
powiązane z jego właściwościami.
<StackPanel G r id .R o w = "l" G rid .Colu m n="2">
< B utto n x:Name="ReadNewGuy" Content= "W czyta j o b i e k t Guy" Click="ReadNewGuy_Click"
M a rg in = "0 ,1 0 ,0 ,0 "/>
< T e xtB lo ck S t y le = " { S t a t i c R e s o u r c e I t e m T e x t S t y le } " M a r g in = " 0 ,0 , 0 , 2 0 " >
<Run>Nowy f a c e t : </Run>
<Run T e x t = " { B in d in g NewGuy}"/>
< /T e x tB lo c k >
</S tackP a ne l>
</ Grid> Jeszcze nie skończyliśmy — przewróć kartkę! ------
p riv a te Guy joe = new Guy("Joe", 37, 176.22M); Wartość pola wewnętrznego tej właściwości
p u b lic Guy Joe je s t określana przez metodę ReadGuyAsync(),
a kontrolki TextBlock zostały powiązar\e
{ z właściwościami DateCretóeii oraz
get { return jo e ; } ContentType.
}
Ten kod
r la te s tG u y F ile =
a w a it g u y s F o ld er.C rea te F ile A sync(gu yT oW rite .N am e + " . x m l " ,
C re a tio n C o llis io n O p tio n .R e p la c e E x is tin g );
tworzy plik
XML, otwiera
strumień u sing (IRandomAccessStream stream =
i zapisuje
a w a it la te stG u yF ile .O p e n A s yn c (F ile A c ce ss M o d e .R e a d W rite ))
w nim graf
obiektu Guy. using (Stream ou tputStrea m = s tre a m .A s S tre a m F o rW rite ())
{
D a t a C o n t r a c t S e r i a l i z e r s e r i a l i z e r = new D a t a C o n t r a c t S e r i a l i z e r ( t y p e o f ( G u y ) ) ;
s e r i a l i z e r . W r i t e O b j e c t ( o u t p u t S t r e a m , g u y T o W rite );
p r i v a t e v o id W r it e J o e _ C l i c k ( o b j e c t sender, RoutedEventArgs e) {
guyManager.WriteGuyAsync(guyManager.Joe);
}
p r i v a t e v o id W r i t e B o b _ C li c k ( o b je c t sender, RoutedEventArgs e) {
guyManager.WriteGuyAsync(guyManager.Bob);
}
p r i v a t e v o id W r i t e E d _ C l i c k ( o b j e c t sender, RoutedEventArgs e) {
guyManager.WriteGuyAsync(guyManager.Ed);
}
p r i v a t e v o id ReadNewGuy_Click(object sender, RoutedEventArgs e) {
guyManager.ReadGuyAsync();
}
jesteś tutaj ► 585
Czym je t zadanie w rzeczywistości?
W efekcie pojawi się błąd, oznaczony czerwoną, falistą linią; jednocześnie w oknie Error L ist zostanie wyświetlony
przydatny komunikat:
Aby jedna metoda asynchroniczna mogła wywołać drugą, wywoływana metoda musi zwracać obiekt klasy Task
(bądź klasy pochodnej dziedziczącej po Task<T>, jeśli metoda musi zwracać jakąś wartość). Ponieważ metoda ReadGuyQ
zwracała void, zatem aby rozwiązać problem, wystarczy zastąpić w jej deklaracji void typem Task.
public async Task ReadGuyAsync() Zgodnie z zalecaną konwencją nazewniczą nazwy metod
{ ^ asynchronicznych, które mają być wywoływane przy użyciu
operatora await, powinny się kończyć słowem Async. Dlatego też
// Kod z poprzedniej strony zmieniliśmy nazwę ReadGuy() na ReadGuyAsync().
}
Teraz metodę można już wywoływać przy użyciu operatora await i będzie ona działać jak wszystkie inne metody
asynchroniczne i przekazywać sterowanie w momencie rozpoczęcia operacji asynchronicznej. Gdyby metoda miała zwracać
jakąś wartość, powinna ona być typu Task<T>. Na przykład, gdyby metoda ReadGuyAsync() miała zwracać wczytany
z pliku obiekt Guy, to jej wartość zwracaną należałoby zadeklarować, używając typu Task<Guy>.
Menedżer w ym ów ek
Wymówka
Muszę zabrać mojego kota do zwierzęcego psychologa.
Ostatnio użyte
Ustaw na aktualny dzień i godzinę
Data pliku
2013-12-05 23:0ft37 +0KM
Aby uruchomić symulator, kliknij strz a łk ę widoczną z prawej stro n y przycisku * Local Machlne * j wybierz opcję * Slm,,lati’[ *.
Gdy t o zrobisz, aplikacja zostanie uruchom iona w sym ulatorze, pokazującym jak działa w trybie pełnoekranowym i reaguje na
zdarzenia d o ty k u oraz inne zdarzenia sprzętow e.
Więcej informacji o sym ulatorze m o żesz znaleźć na stronie http://m sdn.m icrosofi.com /pl-pl/library/windows/apps/
hh441475.aspx.
588 Rozdziału.
Async, aw ait i serializacja kontraktu danych
GuyManager. Ten sam wzorzec zastosujesz, pisząc nową aplikację dla Damiana. ChooseNewFoIderAsync()
O penExcuseAsync()
OpenRandomExcuseAsync()
To kolejny przykład zasady projektowej nazywanej separacją zagadnień, o której wspominaliśmy
SaveCurrentExcuseAsync()
w rozdziałach 5. i 6. Klasa Guy musi jedynie udostępnić kontrakt danych, natomiast podjęcie decyzji, UpdateFiIeDateAsync()
co z tym kontraktem zrobić, należy już do innej klasy, takiej jak GuyManager. A żadna z tych klas nie SaveCurrentExcuseAsAsync()
ma nawet wiersza kodu związanego z aktualizacją interfejsu użytkownika, gdyż to, czym się zajmują, W riteExcuseAsync()
R eadExcuseAsync()
nie ma nic wspólnego z wyświetlaniem wymówek — to leży w gestii obiektu M ainPage .
Procedura obsługi i
zdarzeń Click wywołuje
metodę ChooseNewFolder()
e /fte ^ 0
K m M M umieszczone na tej stronie wyświetlają dane,
^r-zystiając z wiązania danych. W polach tekstowych
zostały zast°s° wane powiązania dwukierunkowe
^rntująp^ się bezpośrednio do bieżącego obiektu Excuse,
u ^ ^ p m 'm ^ przez obiekt ExcuseManager.
W IĄ Z A M I-
D W U K IE R U N K O W I \
Właściwość
Results
ekt
WYSIL _____
SZARE KOMÓRKI
Klasy E xcuse oraz E xcuseM anager nie mają żadnego kodu związanego z aktualizacją interfejsu użytkownika. Powinieneś
także wiedzieć, że serializacji kontraktu danych oraz technik programowania asynchronicznego można także używać, pisząc
tradycyjne programy WinForms. Czy potrafiłbyś je wykorzystać do zmodyfikowania wcześniejszej wersji aplikacji do zarządzania
wymówkami (napisanej jako program WinForms) w taki sposób, żeby zapisywała i odczytywała te sam e pliki, których używa
menedżer wymówek Damiana dla Sklepu Windows?
<Page.Resources>
<local:ExcuseManager x:Name="excuseManager"/>
<x:String x:Key="AppName">Menedżer wymówek</x:String>
</Page.Resources>
Poniżej przedstawiliśmy kod XAML strony — ma ona prosty układ, bazujący na kontrolce
StackPanel. Ustaw w niej kontekst danych — ma nim być zasób statyczny ExcuseManager.
Wymówka
| Muszę zabrać mojego kota d o zwierzęcego psychologa.
Wyniki
1 Szef naprawdę uważa, że trzeba dbać o zdrowie psychiczne zwierzaków.
-------------------------------------------------------------------------------------- 1
Ostatnio użyte Przyjrzyj się zawartości okna Toolbox. Nie znajdziesz
Ustaw na a ktualny dzień i godzinę
w nim żadnej kontrolki do w ybierania dat! S k o rzy stamy
zatem ze zwyczajnego pola TextB ox i przy c isk u, który
będzie w nim w yśw ietlał bieżący c zas. Wykorzy s ta my
Data pliku także wbudowane metody .NET s łużące do konwersji
2013-12-05 2300:37 +01:00 tekstu na dane typu D ateTime.
A w jaki sposób XAML zmienia ikony przycisków? Przyjrzyj się dokładniej stylom, które przeniosłeś poza komentarz
w pliku StandardStyles.xam l — zobaczysz w nim wartości szesnastkowe  w stylu przycisku Folder oraz 
w stylu przycisku Zapisz. Zawartość przycisku jest zwyczajnym tekstem prezentowanym czcionką Segoe UI Symbol,
natomiast jego ikona — znakiem Unicode wyświetlonym tą samą czcionką.
Aby zapisać wartość szesnastkową
w'pUku XAML (bądź w jakimkolwiek innym
pliku XML), mależy ją poprzedzić sekwencją
^ znaków &#x i zakończyć średnikiem (;).
Content="⛱"
AutomationProperties.Name="Losowa wymówka"
W taki sposób należy określać
nazwę przycisku.
Poniżej przedstawiliśmy większą część kodu klasy ExcuseManager — dokończenie jej, jak i napisanie N ewExcuseAsync()
klasy Excuse, wykonasz w ramach ćwiczenia. Definiuje ona dwie właściwości publiczne, które będą SetToCurrentTime()
ChooseNewFolderAsync()
używane do celów wiązania danych: CurrentExcuse oraz FileDate. Pierwsza z nich będzie zawierać O penExcuseAsync()
aktualnie wczytany obiekt Excuse, natomiast druga jest łańcuchem znaków prezentującym datę OpenRandomExcuseAsync()
utworzenia pliku bądź napisem „Nie wczytano wymówki” (jest on wyświetlany, gdy żadna wymówka SaveCurrentExcuseAsync()
await WriteExcuseAsync();
Metoda SaveCurrentExcuseAsync() najpierw sprawdza, czy
bieżąca wymówka jest równa null bądź czy jej opis jest
pu b lic async Task ReadExcuseAsync() pustym łańcuchem znaków, a jeśli któryś z tych warunków
jest spełniony, to wyświetla komunikat. Jeśli wymówka jest
/ / Napiszesz tę metodę prawidłowa, wywoływana jest metoda WriteExcuseAsync(),
która ją zapisuje. Jeśli jeszcze nie ma żadnej wymówki, jest
ona tworzona poprzez wywołanie metody CreateFileAsync().
pu b lic async Task WriteExcuseAsync()
I I Napiszesz tę metodę
I I Napiszesz tę metodę
Nowa wymówka
®
Folder
© ®
Losowa Otwórz
®
Zapisz Zapisz jako—
wymówka
\ \ ____________________________
Przyciski Losowa wymówka oraz Zapisz działają w yłącznie wtedy, gdy użytkownik
w ybrał folder, dlatego też procedura obsługi przycisku Folder używa wartości
zwracanej przez w yw ołanie metody ChooseNewFolderAsync(). Jeśli w artością tą
jest tru e , przyciski Losowa wymówka oraz Zapisz zostają uaktywnione.
Nowa wymówka
(!)
FoMer
®
Losowa
®
Otwórz
®
Zapisz
®
Zapisz jako—
wymówka
PropertyChanged event
Ćwiczenie
DateTime d;
bool datelsValid = DateTime.TryParse(value, out d);
lastUsed = d; INotifyPropertyChanged
PropertyChanged event
Metoda TryParse() zwraca wartość true, jeśli data jest prawidłowa, bądź wartość f a ls e -
w przeciwnym razie. Jeśli użytkownik wpisał nieprawidłową datę, metoda ustawia właściwość
DateWarning (przeznaczoną tylko do odczytu), zapisując w niej: "Nieprawidłowa data: 11
i wpisaną wartość daty. Komunikat ten zostanie wyświetlony na czerwono w kontrolce
TextBlock, informując użytkownika, że wpisana data nie jest prawidłowa. Nie zapomnij
także wywołać zdarzenia PropertyChanged, by poinformować stronę o zmianie wartości
właściwości DateWarning.
ExcuseManager
^2 ) Zaimplementuj metodę ExcuseManager.WriteExcuseAsync(). CurrentExcuse
FileDate
Ta metoda otwiera strumień i serializuje bieżącą wymówkę, zapisując ją w pliku
reprezentowanym przez obiekt IStorageF ile, przechowywanym w polu excuseFile. NewExcuseAsync(]
SetToCurrentTime()
Następnie wyświetla komunikat informujący o prawidłowym zapisaniu wymówki i wywołuje
ChooseNewFdderAsync(]
metodę UpdateFileDateAsync(), by zaktualizować właściwość FileDate. OpenExcuseAsync()
OpenRandomExcuseAsync()
Zaimplementuj metodę ExcuseManager.ReadExcuseAsync(). SaveCurrentExcuseAsync(]
Ta metoda otwiera strumień i tworzy nowy obiekt Excuse, wczytując serializowaną wymówkę UpdateFieDateAsync(]
z pliku określonego przez pole excuseF ile. Metoda wywołuje zdarzenie PropertyChanged, SaveCurrentExcuseAsAsync(]
WriteExcuseAsync()
by poinformować stronę o zmianie wartości właściwości CurrentExcuse, a następnie ReadExcuseAsync(]
wywołuje metodę UpdateFileDateAsync(). Będziesz także musiał zaimplementować
interfejs INotifyPropertyChanged i dodać metodę OnPropertyChanged.
[ 4) Zaimplementuj metodę ExcuseManager.SaveCurrentExcuseAsync().
Ta metoda wyświetla stronę FileSavePicker, pozwalając użytkownikowi wybrać plik XML,
w którym zostanie zapisana wymówka. Jeśli użytkownik wybierze plik, wywoływana jest
metoda WriteExcuseAsync(), która zapisuje wymówkę.
Poniżej zostały przedstawione metody, które musisz dodać do klasy E xcuseM anager. Koniecznie
Rozwiązanie upewnij się, że klasa implementuje interfejs IN o tify P ro p e rty C h a n g e d .
ćwiczenia
p u b lic async Task ReadExcuseAsync()
{
using (IRandomAccessStream stream =
await excuseFile.OpenAsync(FileAccessMode.Read))
using (Stream inputStream = stream.AsStreamForReadQ)
{
D a ta C o n tra c tS e ria liz e r s e r i a l i z e r = new D a ta C o n tra c tS e r ia liz e r( ty p e o f(E x c u s e ) );
CumentExcuse = seria lize r.R e a d O b je ct(in p u tS tre a m ) as Excuse;
}
await new MessageDialog("Wymówka wczytana z p l i k u " + excuseFile.Name).ShowAsync();
OnPropertyChanged("CurrentExcuse");
^ await UpdateFileDateAsync(); Metody do odczytu i zapisu obiektów Excuse są
} bardzo podobne do analogicznych metod stosowanych
w aplikacji Serializacja obiektów Guy.
p u b lic async Task WriteExcuseAsync()
{
using (IRandomAccessStream stream =
await excuseFile.OpenAsync(FileAccessMode.ReadWrite))
using (Stream outputStream = stream.AsStreamForWriteQ)
{
D a ta C o n tra c tS e ria liz e r s e r i a l i z e r = new D a ta C o n tra c tS e r ia liz e r( ty p e o f(E x c u s e ) );
s e ria liz e r. W r ite O b je c t( o u t p u tS t r e a m , CumentExcuse);
}
await new MessageDialog("Wymówka zapisana w p l i k u " + excuseFile.Name).ShowAsync();
await UpdateFileDateAsync();
}
p u b lic async void SaveCumentExcuseAsAsync()
{
FileSavePicker p ic k e r = new FileSavePicker
{
SuggestedStartLocation = Picke rLocatio nld .D ocum entsLibra ry,
SuggestedFileName = CumentE xcuse.Descriptio n,
CommitButtonText = "Zapisz p l i k wymówki"
};
p ic k e r.F ile T y p e C h o ic e s .A d d ("P lik XML", new L i s t < s t r i n g > ( ) { ".x m l" } ) ;
IS to ra g e F ile newExcuseFile = await picke r.P ick S a ve F ile A s yn c ();
i f (newExcuseFile != n u l l )
{ Metoda SaveCurrentExcuseAsAsync() wyświetla stronę do wyboru
excuseFile =newExcuseFile; pliku, a następnie zapisuje wymówkę do pliku wskazanego przez
await WriteExcuseAsync(); użytkownika. Aktualizuje także pole excuseFile, by zachować
} informację o pliku wymówki (dzięki temu po kliknięciu przycisku
} Zapisz wymówka zostanie zapisana do tego samego pliku).
p u b lic event PropertyChangedEventHandler PropertyChanged;
To standardowy kod do
p r i v a t e void OnPropertyChanged(string propertyName) ^ ------------- wywotywania zdarzenia
{ PropertyChanged.
PropertyChangedEventHandler propertyChangedEvent = PropertyChanged;
i f (propertyChangedEvent != n u l l )
{
propertyChangedEvent(this, new PropertyChangedEventArgs(propertyName));
}
}
Oto nowa klasa E xcuse. Zawiera ona kontrakt danych obejmujący właściwości D e s c r i p tio n i
R e s u lt s oraz pole w ewnętrzne la s tU p d a te d używane przez właściwość L a stU p d ate d .
using System.ComponentModel;
using S y s te m .R u n tim e .S e ria liz a tio n ;
[DataMember]
p u b lic s tr in g D e sc rip tio n { g e t; s e t ; }
Dodanie atrybutu [DataMember) _do pola
[DataMember]
wewnętrznego lastUsed p°w°duJe> ze
p u b lic s tr in g R e s u lts { g e t; s e t ; } będzie ono zapisywane i odtwarzrme
podczas serializacj i deserializacJ<-
[DataMember]
p riv a te DateTime lastU sed = DateTim e.M inValue;
p u b lic s tr in g LastUsed
{
get
{
i f (lastU se d != DateTime.M inValue)
re tu rn la s tU s e d .T o S trin g Q ;
e ls e Jeśli użytkownik wpisał prawidłową datę,
re tu rn S trin g .E m p ty ; metoda DateTime.TryParse() skonwertuje ją
} na obiekt DateTime i zwróci wartość true.
set W przeciwnym razie wartość pola nie zmieni
{ się i będzie wynosić DateTime.MinValue.
DateTime d = DateTim e.M inValue;
bool d a te ls V a lid = D a te T im e .T ry P a rse (v a lu e , out d ) ;
lastU sed = d;
}
}
OnPropertyChanged("DateWarning")
}
}
±
To jest ten sam kod służący do wywoływania zdarzenia PropertyChanged, którego
używałeś we wcześniejszej części rozdziału. Gdybyś jednak skopiował go i wkleił
do klasy Excuse lub ExcuseManager, lecz zapomniał dodać do deklaracji klasy
: INotifyPropertyChanged, to powiązania kontrolek z danymi nie zostałyby
prawidłowo skonfigurowane. Oznaczałoby to, że obiekty wywoływałyby zdarzenia
PropertyChanged, lecz kontrolki na stronie nie próbowałyby ich odbierać, a zatem
powiązanie danych nie działałoby. Taki błąd mógłby być bardzo frustrujący! jesteś tutaj ► 597
598 R o zd ział 11.
12. Obsługa wyjątków
^ Gaszenie pożarów
nie jest już popularne
Program iści nie m a ją być strażak am i. Pracowałeś jak wół, przebrnąłeś przez
dokumentacje techniczne i kilka zajmujących książek Rusz głową!, wspiąłeś się na szczyt
swoich możliwości: jesteś mistrzem programowania. W dalszym ciągu musisz jednak
odrywać się od pracy, ponieważ program wyłącza się lub nie zachowuje się tak,
jak powinien. Nic nie wybija Cię z rytmu tak, jak obowiązek naprawienia dziwnego
błędu... Z obsługą wyjątków możesz jednak napisać kod, który poradzi sobie
z pojawiającymi się problemami. Jest nawet lepiej, możesz bowiem zareagować
na ich pojawienie się i sprawić, że wszystko będzie dalej działało .
to je st n o w y ro z d z ia ł ► 599
M oje program y — moje problemy
Damian uruchomił
genefator wymówek
c
na swoim laptopie.
Damian Samopas
zawsze próbuje znalezć
wymówką, aby wyrwać
sią z pracy.
s* *
Nieobsłużony
wyjątek... musi
być jakiś problem,
którego nie
przewidzieliśmy.
Zaostrz
¿.U U 3U ołówek
V Oto kolejny przykład niesprawnego kodu. Znajduje się w nim pięć różnych wyjątków zgłaszanych
przez program. Komunikaty o błędach wyświetlone są po prawej stronie. Twoim zadaniem jest
połączenie wyjątku z wierszem kodu, który go generuje. Aby uzyskać podpowiedz, przeczytaj
komunikat i spróbuj go zrozumieć.
public s ta tic void BeeProcessor() { W y w o f a n ie d o u b le . P a r s e f 3 2 " l i s k u t k u j e
n a rs o w a n ie m fa ń c u c h a z n ak ó w
object myBee = new HoneyBee(36.5, "Zippo"); f z w r ó c e n ie m w a r t o ś c i t y p u d o u b te ,
flo a t howMuchMoney = (float)myBee; w ty m p r z y p a d k u 3 2 . D ru g i Paramf tr
k onstruktora k la sy H on eyB ee o k reśla
w a rto ść w fa ściw o ści M yN am e.
HoneyBee anotherBee = new HoneyBee(12.5, "Buzzy")
double beeName = double.Parse(anotherBee.MyName);
flo a t f = float.Parse(beesWeCanFeed);
-; • — - - A- ,...... (2 )
int drones = 4; BeeProcessingSystem.exe
int queens = 0; A dditional inform ation: Odwołanie do obiektu nie zostało ustawione na wystąpienie
obiektu J
int dronesPerQueen = drones / queens;
anotherBee = null;
A n unhandled exception o f ty p e 'System .InvalidCastException' occurred in
i f (dronesPerQueen < 10) { BeeProcessingSystem.exe
©
An unhandled exception o f type'S ystem .F orm atE xception' occurred in m sco rlib .d ll
Zaostrz ołówek
Twoim zadaniem było połączenie wiersza kodu
powodującego błąd z wygenerowanym w nim wyjątkiem.
Rozwiązanie
M « ^ 0 ParseO oczekuje
taĄcucha znaków w określonym
f° rmacie. „Buzzy“ nie jest
HoneyBee anotherBee = new HoneyBee(12.5, "Buzzy"); łaAcuchem, który może zostać
w j akiś sp°sób zamieniony
double beeName = double.Parse(anotherBee.MyName); na liczbę. T° dętego zgłaszany
jest wyjątek Forma^ceptim.
in t drones = 4;
in
1 1 1t
W queens
WI I^ = V0;J
Bardzo tatwo uzyskać wyjątek
in t dronesPerQueen = drones / queens; DivideByZeroException. Po prostu
p°dziel dowolną liczbę przez zero.
anotherBee = n u ll;
i f (dronesPerQueen < 10) { Ustawienie referencji anotherBee na wartość
anotherBee.DoMyJob(); . t yCel L imir ::,yiZik«wsk° zuJ’
} NullReferenceException to sposób, w jaki C#
przekazuje tram informację, że nie ma żadnego
obiektu, którego metodę DoMyJob() można by
wywołać.
2^
BeeProcessingSystem.exe
Powstanie powyższego błędu dzielenia przez zero nie powinno mieć miejsca.
W ystarczyłoby dokładnie popatrzeć na kod, aby się zorientować, że coś jest nie tak.
To samo dotyczy innych wyjątków. Takim problemom można zapobiec — im więcej
będziesz w iedział na ich temat, tym łatw iej będzie Ci uchronić program
przed awariami pisanych programów.
Tworząc obiekt, .NET wykonuje dodatkową pracę, ponieważ chce Ci przekazać wszystkie
informacje o sytuacji powodującej dany wyjątek. Możesz napisać kod, który rozwiąże
problem będący jego przyczyną, ale możesz także potrzebować pewnych zmian w obsłudze
określonych sytuacji w programie.
■ Nie .istnieją.
głupie pytania
W porządku, to dobry początek. Dzięki temu wiemy, że pojawiła się jakaś wartość,
która nie mieści się w zakresie. Kliknięcie przycisku B reak powoduje powrót do
debuggera, a wykonywanie programu zostanie wstrzymane na konkretnym wierszu kodu:
Name Value
0 file s.C o u n tQ 0 a
$3 ra n d o m .N ext(0 , file s.C o u n tQ } 0 a
file s [ra n d o m .N e x t(0 ,file s .C o u n t(}}] | 'fiie s[ra n d o m .N e xt(0 , file s.C o u n tQ }]' th re w a n e xcep tio n o f ty p e S ystem -A rg u m en tExcep tio n '
Co się zatem stało? Okazuje się, że wywołanie metody GetFileAsync() obiektu IStorageFolder zwraca
kolekcję IReadOnlyList<IStorageFile>. A podobnie jak inne kolekcje, których już używałeś, także ta
zgłasza wyjątek, jeśli spróbujesz pobrać z niej element, który nie istnieje. Spróbuj pobrać zerowy element
pustej kolekcji, a program zgłosi wyjątek System.ArgumentOutOfRangeException, z komunikatem
„Indeks był spoza zakresu. Musi mieć wartość nieujemną i mniejszą niż rozmiar kolekcji”.
Na szczęście nasz problem można rozwiązać w prosty sposób. Wystarczy przed próbą pobrania pliku
sprawdzić, czy kolekcja zawiera jakiekolwiek elementy.
{
IReadOnlyList<IStorageFile> f ile s = await excuseFolder.GetFilesAsync();
i f (file s .C o u n t() == 0) {
await new MessageDialog("Aktualnie fo ld e r wymowek je s t pusty.").ShowAsync();
re tu rn ;
Sprawdzając pliki
} wymówek w folderze pned,
excuseFile = files[random.Next(0, file s.C o u n t())]; tworzeniem obiektu
await ReadExcuseAsync(); Excuse, możemy zapobiec
zgłoszeniu wyjątku.
Możemy także wyświetlić
okienko dialogowe
z pomocnym komunikatem.
O TAK, JUŻ WIEM. WYJĄTKI NIE ZAWSZE SĄ
TAKIE ZŁE. CZASAMI POMAGAJĄ W IDENTYFIKACJI
BŁĘDU, ALE W WIĘKSZOŚCI PRZYPADKÓW
SUGERUJĄ MI, ŻE WYDARZYŁO SIĘ COŚ,
CZEGO SIĘ NIE SPODZIEWAŁEM.
Exception
ToString() tworzy Message
podsumowanie wszystkich StackTrace_________
informacji zawartych
G e tB a s e E x c e p tio n ()
w polach wyjątku i zwraca je
T o S trin g ()
w postaci łańcucha znaków.
IndexOutOfRange
Exception
Message
StackTrace
G e tB a s e E x c e p tio n ()
T o S trin g ()
c
To naprawdę cenne, że .¡yET
udostępnia tyle różnych typ<Sw
wyjątków, ponieważ każdy z nich
zgłaszany jest w innej sytuacji.
Możesz uzyskać wiele imformacji
na temat niespodziewanych
operacji powodujących
wygenerowanie wyjątku,
po prostu patrząc na jego typ.
B
Kliknij ikonę widoczną z prawej strony paska narzędzi D ebug i wybierz opcję
A d d or R em ove B uttons — pozwoli Ci to poznać wszystkie dostępne operacje związane
z debugowaniem.
Przycisków Continue, Break All oraz Stop Debuaina Przycisk „Refresh Windows app" jest
używany w aplikacjach pisanych w języku
a S Sf a a U Ż .^ IO cC C W S zl^ W U iy^ d ^ ^ m yw an ia JavaScript. W aplikacjach pisanych w C#
jest on niewidoczny.
Ö
A dd or R em ove B u tto n s- ► C on tin u e \ F5
0 Step Into F 11
Używałeś już tych przycisków podczas sekwencyjnego 0 <4 Step Over FIG
wykonywania kolejnych instrukcji programu. Przycisk
Skip Over powoduje przeskoczenie wywołania metody. 0 C? Step Out S h ift+ F 11
Przycisk Step Into pozwala przejść do pierwszej Hex
instrukcji wewnątrz wywoływanej metody, a przycisk
Step Out pozwala dokończyć jej realizację i zatrzymać 0 S h o w T hreads in So urce
się na pierwszej instrukcji za jej wywołaniem.
W in d ow s /S
C ustom ize...
Jeśli włączysz przycisk Hex, przekonasz się, że Reset T oolbar
możesz nim włączać i wyłączać prezentacj ę w tryloie
szesnastkowym. W razie jego włączenia warto^ W tej książce nie poświęcimy
zmiennych całkowitych (takich typów j ak: int, fong zbyt wiele uwagi wątkom, jeśli
lub byte) w oknie Watch lub po wskazaniu ich myszką jednak jesteś nimi zainteresowany,
będą wyświetlane jako wartości szesnastkowe. to pewne informacje na ich
temat możesz znaleźć w dodatku
„Pozostałości", w punkcie 4.
Przedstawiliśmy tu tę samą
$ value 0x3afb83d9 wartość wyświetloną w trybie & value 989561817
szesnastkowym (po lewej) oraz
dziesiętnym (po prawej).
T eraz wskaż folder zawierający jakieś pliki wymówek, ponow nie kliknij przycisk Losow a wymówka
i w ejdź do m etody. Tym razem b lo k instrukcji i f zostanie pom inięty, a realizacja p ro g ra m u przejdzie
do następ n eg o w iersza kodu.
U Ż Y J O K N A W A T C H D O O D T W O R Z E N IA PR O B LEM U .
Widziałeś już, jak wielkie możliwości daje okno Watch. Teraz wykorzystamy je do odtworzenia wyjątku. Przerwij
Planujesz wykonywanie programu, usuń ustawioną wcześniej pułapkę, a następnie dodaj nową, umieszczając ją w drugim
przerwać
wykonywanie wierszu metody OpenRandomExcuseAsync(). Uruchom program, wybierz pusty folder, a następnie kliknij
kodu w drugim przycisk L osow a w ym ów ka. Kiedy debugger przerwie działanie programu, zaznacz wyrażenie file s.C o u n t(),
wierszu, gdyż kliknij je prawym przyciskiem myszy i wybierz opcję ^ Add Watch, by dodać je do okna Watch:
to właśnie
w nim jest
umieszczone W atch 1 ▼n x
odwołanie do Name Value
obiektu plików. 0 files.CountO 0
D O D A J K O L E J N E W Y R A Ż E N IE D O O K N A W A T C H I Z A C Z N I J P O S Z U K IW A N IE P R O B L E M U
Debugowanie przypomina nieco analizę miejsca zbrodni, którym w tym przypadku jest Twój program.
Niekoniecznie będziesz wiedział, czego szukasz, aż to coś znajdziesz; dlatego też będziesz musiał skorzystać
z wszelkich dostępnych narzędzi programistycznego kryminologa, by odnajdywać wskazówki i wyśledzić
winowajcę. Ponieważ przyczyną problemów nie było wywołanie file s.C o u n t(), zajmij się następnym
podejrzanym: zaznacz wywołanie random .N ext(files.C ount()) i dodaj je do okna Watch:
Okno W atch posiada jeszcze inne użyteczne narzędzie — pozwala ono na zmianę wartości wyświetlanych
zmiennych i pól. Możesz nawet dzięki niemu wykonać metody i utworzyć nowe obiekty. Gdy to zrobisz,
wyświetli ono ikonę ponownego obliczenia wartości (Q ), którą możesz kliknąć, aby po raz drugi wykonać daną
instrukcję. Jest to uzasadnione tym, że niektóre metody wykonane powtórnie mogą generować różne wyniki
(na przykład te z obiektu Random).
O D T W Ó R Z P R O B L E M , K T Ó R Y S P O W O D O W A Ł P O W S T A N IE O R Y G IN A L N E G O W Y J Ą T K U
To w tym miejscu debugowanie staje się naprawdę interesujące. Dodaj do debuggera jeden wiersz kodu —
Nawet jeśli już instrukcję, która spowodowała zgłoszenie wyjątku: fileNames[random.Next(0, f ile s .C o u n t ( ) ) ] . Zaraz po jej
rozwiązałeś wpisaniu wartość wyrażenia zostanie obliczona przez okno Watch... i powstanie wyjątek.
problem,
dodając kod
sprawdzający,
czy folder
zawiera jakieś
pliki, to i tak
będziesz mógł
użyć okna
Watch do Kliknij ikonkę ze znakiem „ + ”, by rozwinąć dane wyjątku, a przekonasz się, że właściwość Message ma wartość
wygenerowania „Value does not fall within the expected range”. Teraz już dokładnie wiesz, co powoduje problem i dlaczego
wyjątku.
on występuje. Wywołanie metody G etFilesAsync() zwraca kolekcję typu IReadOnlyList<IStorageFile>,
której długość dla folderu pustego wynosi 0. Jeśli spróbujesz użyć jej indeksatora (file s [0 ]), odwołanie do
niego zgłosi wyjątek ArgumentException.
W yświetl okno Locals i dodaj do niego zm ienną $ e x c e p tio n (m ożesz ją także dodać do
okna W atch). D o kładnie przyjrzyj się składowym o biektu i spróbuj określić, co jest nie tak.
C Z Y J U Ż W I E S Z , D L A C Z E G O P R O G R A M Z G Ł O S IŁ W Y J Ą T E K ?
C Z Y Z G Ł A S Z A N I E W Y J Ą T K U , JE Ś L I P R O G R A M N A P O T K A
N I E P R A W I D Ł O W Y P L IK W Y M Ó W K I , M A S E N S ?
C Z Y P O T R A F IS Z W Y M Y Ś L IĆ , C O Z T Y M M O Ż N A Z R O B IĆ ?
Z A C Z E K A J C H W IL Ę ! O C Z Y W IS T E
JEST, Ż E P R O G R A M N IE BĘDZIE D Z IA Ł A Ł —
P R Z E K A Z A Ł A M M U Z Ł Y P LIK. U Ż Y T K O W N IC Y
C A Ł Y C ZAS COŚ PSUJĄ. N IE SPO DZIEW ASZ SIĘ
C H Y B A , Ż E T O SIĘ Z M IE N I, P R A W D A ?
Odporny, przymiotnik
@ Powiedzmy, że użytkownik do m e to d y
vV.o^nik wptow
korzysta z Twojego
kodu i wprowadza dane
wejściowe, których program
się nie spodziewa.
3 Musisz wiedzieć,
że wykonywana przez
Ciebie metoda jest
niebezpieczna.
J e ś li je s t e ś w sta n ie wym yślić, ja.k
zrobić to samo w mniej ryzykowny klasa, k tó rą napisałeś
sposób i uniknąć m ożliwości z g to sz ^ id u żytko w nik
w yjątku, to będzie to najlepsze możliw e
rozwiązanie! Jednak czasam i po p t t t o me
można uniknąć ryzyka i wtaśnie w takich HEJ, TEN PROGRAM
przypadkach będziemy chcieli z robić to... JEST NAPRAWDĘ STABILNY!
P : K ie d y powinienem u żyw a ć t r y
rozwiązanie. Jeśli jednak program
wyświetli komunikat o błędzie informujący
instrukcję albo wejdziesz do funkcji lub
wyjdziesz z niej. Pozwala to na dokładne
i c a tc h ?
0 problemach z odczytaniem pliku, monitorowanie zmiennych po wykonaniu
O : Za każdym razem, gdy masz do to użytkownik będzie miał szansę domyślić
się, co może być nie tak, i użyć tych
każdego fragmentu kodu, co może
okazać się niezwykle pomocne podczas
czynienia z kodem niebezpiecznym lub
takim, który może zgłaszać wyjątki. Cały informacji w celu naprawienia problemu. wyszukiwania problemu.
problem sprowadza się do określenia, który
kod jest ryzykowny, a który pewny. P : C z y debugger u ż y w a n y je st tylko
Okno Watch pozwala Ci także wpisać
dowolne wyrażenie i obliczyć jego wartość.
do badania w y ją tk ó w ?
Widziałeś już kod, który w przypadku Jeśli wyrażenie to zmienia wartości innych
podania nieprawidłowych danych O : Nie. Debugger jest w zasadzie
pól i zmiennych w programie, także może
zostać wykonane. Dzięki temu możliwa
wejściowych może stać się niebezpieczny. bardzo praktycznym narzędziem, którego
Użytkownicy podają nieprawidłowe pliki, staje się modyfikacja wartości podczas
możesz używać do badania każdego
słowa zamiast liczb, nazwy zamiast dat działania aplikacji. To z kolei może być
napisanego kodu. Czasami pomocne
i klikają wszędzie, gdzie tylko jesteś sobie kolejnym narzędziem pomocnym podczas
okazuje się krokowe wykonanie programu
w stanie wyobrazić. Dobry program powtarzania wyjątków i innych błędów.
1sprawdzenie wartości określonych pól
T
przyjmie takie dane i będzie w dalszym i zmiennych — na przykład wtedy, gdy
ciągu pracował w spokojny, przewidywalny badana metoda jest bardzo złożona
sposób. Najprawdopodobniej użytkownik i chcesz mieć pewność, że działa W szy stkie zmiany wprowadzone
nie otrzyma spodziewanych rezultatów, ale w oknie Watch wpły waj ą na dane
prawidłowo. przechowywane w pam ięci i pozostają
będzie przynajmniej wiedział, że program tam do czasu zak°ńc.zenia pr° gramu-
Zapewne domyśliłeś się, biorąc pod uwagę
znalazł problem i zasugerował rozwiązanie. Uruchom aplikację ponownie, a w ar>oścl
nazwę „debugger", że jest to narzędzie te zostaną u su n ięte.
P : W ja k i sposób program może
używane generalnie do wyszukiwania
i usuwania błędów (ang. bug). Czasami
zasugerow ać ro zw ią za n ie problem u,
je że li w cześn iej naw et o nim nie w ie?
błędy takie przyjmują postać zgłaszanych Blok catch
wyjątków. W większości przypadków
O : To do tego służy blok ca tch . Jest on będziesz jednak używał debuggera do
wykonywany jest
wykonywany tylko wtedy, gdy kod w bloku wyszukiwania usterek innego rodzaju,
na przykład wtedy, gdy kod zwraca
tylko wtedy, gdy
t r y spowoduje powstanie wyjątku.
Masz dzięki niemu szansę powiadomić wartości, których się nie spodziewałeś. kod w bloku try
użytkownika, że coś poszło niezgodnie
z planem, i przekazać mu, że jest P : N ie jestem pew ien, c z y dobrze zgłosi wyjątek.
to sytuacja możliwa do naprawienia.
zro zum iałem to w sz y stk o , co robiłeś
w oknie W a tch . M ożesz p o w tó rzyć,
Masz dzięki
Gdyby program do zarządzania
wymówkami kończył działanie w sposób
po co ono jest? niemu możliwość
natychmiastowy w przypadku otrzymania O : Kiedy debugujesz program, zwykle wyświetlenia
nieprawidłowych danych, nie byłoby zwracasz uwagę na zmianę wartości
to szczególnie użyteczne. Gdyby próbował określonych zmiennych i pól. To właśnie użytkownikowi
w jakiś sposób je odczytać i wyświetlić
śmieci na formularzu, także nie byłoby
do tego służy okno Watch. Jeśli wstawisz
w nie kilka zmiennych, ich wartość będzie
stosownej
to pomocne. Co więcej, niektórzy aktualizowana przez okno za każdym informacji
powiedzieliby nawet, że jest to gorsze razem, gdy wykonasz pojedynczą
pozwalającej
naprawić problem.
jesteś tutaj ► 617
Płyń z prądem
Zacznij debugow ać aplikację i otw órz plik, któ ry nie jest prawidłowym plikiem wymówki
(ale m a ro zszerzenie .xm l). K iedy realizacja p ro g ra m u zatrzym a się n a p u łap ce , pięć razy
kliknij przycisk Step O ver (lub naciśnij klawisz F 1 0 ), aby d o trzeć do instrukcji wywołującej
m eto d ę R e a d O b je c t( ), k tó ra p o d ejm ie p ró b ę deserializacji o b ie k tu E xcuse. W tym
m om encie o k n o deb u g g era pow inno w yglądać następująco:
^ W ykonu j pro gra m dalej in stru kcja po in stru kcji. Z araz po w yko na niu przez debugger in stru kcji
ReadObject() zgłaszany jest w yjątek, a pro gra m p o m ija pozostały ko d i przechodzi bezpośrednio
do p ie rw sze j in s tr u k c ji w b lo k u c a tc h .
[4 W znów w ykonyw anie ko d u za pom ocą przycisku Continue (lub F 5 ). Program rozpocznie
działanie od fragm e ntu ko d u w yróżnionego żółtym kolore m , oznaczającego b lo k następnej
in s tru k c ji — w tym przypadku od b lo k u catch. Spowoduje ona je dyn ie w yśw ietlenie okna
dialogowego, a następnie uda, że nic się nie stało. Jednak aw aria zostanie obsłużona.
Jeśli posiadasz kod, który ZAW SZE musi zostać wykonany, zastosuj finally
K ie d y T w ó j pro gra m zgłasza w yjątek, może się zdarzyć k ilk a rzeczy. Jeśli nie zosta ł on obsłużony, program zakończy
przetw arzanie i nagle się zam knie. Jeżeli w yją te k je s t obsługiwany, w ykonyw anie in s tru k c ji przeniesione zostanie do
b lo k u c a tc h . Co dzieje się w ted y z resztą kod u w b lo k u t r y ? Co w przypadku, gdy zamyka on strum ie ń lub zwalnia
cenne zasoby? K o d ten p o w in ie n zostać u ru ch o m io n y nawet w tedy, gdy wystąpi w yjątek. W przeciw nym razie możem y
doprow adzić do nieokreślonego stanu w program ie. W takich przypadkach należy użyć b lo k u f i n a l l y , k tó ry jest
umieszczany za blo ka m i t r y i c a tc h . B lo k f i n a l l y je s t w yko n yw a n y zawsze, bez w zględu na to, czy w yją te k zostanie
zgłoszony. O to w ja k i sposób m ożna w ykorzystać b lo k f i n a l l y , by m etoda R eadExcuseA sync() zawsze generowała
zdarzenie P rop ertyC h ang ed .
^ .
T e r a z d e b u g u j to !
U ru ch o m aplikację zwyczajnie i up ew nij się, że przycisk otw ierający w ym ów kę działa pra w id ło w o, jeśli
spróbujem y wczytać działający p lik w ym ów ki. D ebugger p o w in ie n przerw ać działanie a p lika cji w wierszu
zawierającym pułapkę. D ebugger p o w in ie n zatrzym ać się na ustaw ionej przez C iebie pułapce.
Przejdź kro ko w o przez pozostałą część m etody i upew nij się, że wszystko działa tak, ja k się tego
spodziewasz. P ow inien zostać dokończony b lo k t r y , b lo k catch po w in ie n zostać o m in ię ty (ponieważ nie
było żadnych w yjątków ), a następnie po w in n y zostać wykonane in stru kcje b lo k u f in a l ly .
A teraz spróbuj otw orzyć n ie p ra w id ło w y p lik w ym ów ki. W ykonyw anie p o w in no się rozpocząć od b lo k u try ,
a po n a po tka niu w yją tku debugger p o w in ie n przeskoczyć do b lo k u catch. Po w yko na niu wszystkich jego
in s tru k c ji po w in n o się rozpocząć w ykonyw anie ko d u z b lo k u fin a lly .
p u b lic c la s s Kangaroo {
Zagadkowy basen
______________ f s ;
T w o im zadaniem jest po bra nie fragm entów i n t c ro c ;
kod u z basenu i wstaw ienie ich w puste i n t dingo 0;
miejsca. M ożesz użyć tego samego
fragm e ntu więcej niż raz i nie p u b lic i n t W om bat(int w a lla b y ) {
musisz wykorzystać ich wszystkich.
Celem jest napisanie program u, try {
k tó ry w yśw ietli ko m u n ika t if ( > 0) {
zaprezentowany poniżej. .O p en W rite ("w o bbieg ong ")
c ro c = 0;
} e ls e i f (___ < 0) {
W ynik: Witaj, bracie! c ro c = 3;
} e ls e {
.OpenRead("wobbiegong")
u s in g System .IO ; cro c 1;
p u b lic s t a t i c v o id M a in () {
}
Kangaroo jo e y = new K a n g a ro o ();
}
i n t k o a la = joey.W om bat( ca tch (IO E x c e p tio n ) {
jo e y.W o m b a t(jo e y.W o m b a t(1 )));
c ro c = -3 ;
try {
}
C o n s o le .W rite L in e ((1 5 / koa la )
ca tch {
+ " j a j na k ilo g ra m " ) ;
cro c 4;
}
c a tch (____________________ }
) {
fin a lly {
C o n s o le .W r ite L in e (" W ita j, b r a c i e ! " ) ;
if ( > 2) {
}
cro c d in g o ;
}
}
Przypominam y: każdy
fra g m e n t kodu z basenu może
zostać użyty w ięcej niż raz!
Zagadkowe baseny robią się coraz trudniejsze, a na zw y stają się coraz bardziej za w iłe , by dawać Ci m niej wskazów ek.
N apraw dę będziesz się m usiał trochę napracować nad tym problem em ! Pamiętaj, że zagadki są opcjonalne, w ięc nie
przejm uj się, jeśli musisz kontynuow ać le ktu rę i w rócić do tej nieco później... Jeśli jednak napraw dę chcesz u trw a lić ten
m a te ria ł w sw oim m ózgu, to ta ła m ig łó w k a na pew no Ci w tym pomoże!
jesteś tutaj ► 623
Usunięcie jednego obiektu może być w ybaw ieniem dla innego
try {
J e ś li w bloku catch określisz konkretny
D o S o m e th in g R is k y () typ wyjątku i nadasz zmiennej nazwę,
to kod będzie mógł uzyskać dos t ęp do
} obiektu tego w yjątku.
c a tc h ( E x c e p t io n ex)
s tr in g m essage = e x .M e s s a g e ;
M e ssa g e B o x.S h o w (m e ssa g e , " W y s t ą p ił b ł ą d . " ) ;
m e s s a ge = ex.Message;
s t r in g
€ E 3x c *
}
626 Rozdział 12.
Obsługa wyjątków
Jedna klasa zgłasza _wyjątek, Oczy w iście jedna metoda w pewnej klasie może
zg fasz ać wy ją tk i, kttSr-e będą przechwytywane
przez inną metodę tej sam ej klasy.
inna klasa go przechwytuje
T w orząc klasę, nie zawsze wiesz, w ja k i sposób będzie ona wykorzystywana. Czasami in n i będą używ ali T w o ich obiektów
w sposób, k tó ry sprowadzi na nich pro blem y, a czasem nawet ściągniesz je na siebie sam! W takich przypadkach pojaw iają
się w yjątki.
G łów nym celem przechw ytyw ania i zgłaszania jest określenie, co m ogłoby pójść niezgodnie z naszymi oczekiwaniam i,
i przygotow anie na podstaw ie tej w iedzy jakiegoś p la n u awaryjnego. Zazwyczaj nie w id u je się m etod, k tó re zgłaszają
w yją tki, a następnie same je przechw ytują. W przeważającej większości przypadków w yjątek jest zgłaszany w jednej
m etodzie, a wychwytywany i obsługiw any w zupełnie innej — zazwyczaj należącej do zupełnie innego obiektu.
Exception
Pszczoły potrzebują wyjątku OutOfHoney
Message
StackTrace
T w oje klasy m ogą zgłaszać własne w yją tki. N a przykład w tedy, gdy w m etodzie otrzym asz param e tr
G e tB a s e E x c e p tio n ()
ustaw iony na n u l l , a spodziewałeś się wartości. Dość powszechną tech niką w ta kich przypadkach jest T o S trin g ()
utw orzyć własną klasę dziedziczącą po E x c e p tio n i zgłaszać ten w yją te k za każdym razem, gdy G e tB a s e E x c e p tio n ()
T o S trin g ()
w ystąpi dany rodzaj błędu.
if (honeyLevel == 0) {
th ro w new 0ut0fH oneyExcept1on("W u lu b r a k ło m io d u ." ) ;
}
^ jest instancja
e ls e {
fo re a c h (Egg egg in Eggs) {
V J e ś li w ulu będzie miód,
... \ towyjątek_nigdy nie zostanie
} zgłoszony i kod ten będzie
wykonywany.
p a r tia l c la s s Form1 : Form {
Magnesiki z w yjątkam i
p u b lic s t a t i c v o id M a in () { Poukładaj magnesy tak, aby aplikacja
C o n so le .W rite ("w h e n i t 1 wypisała wynik na konsolę.
E x T e s tD riv e .Z e ro ("y e s ");
C o n s o le .W rite (" it ");
E x T e s tD riv e .Z e ro ("n o "); Wynik:
C o n s o le .W r ite L in e ( " ." ) ; when it thaws it throws.
p u b lic s t a t i c v o id M a in () {
Magnesiki z w yjątkam i. Rozwiązanie
C o n s o le .W rite ("w h e n i t "); Poukładaj magnesy tak, aby aplikacja
E x T e s tD riv e .Z e ro ("y e s "); wypisała wynik na konsolę.
C o n s o le .W rite (" it ");
E x T e s tD riv e .Z e ro ("n o "); Wynik:
C o n s o le . W r it e L in e ( " ." ) ; — > when it thaws it throws.
}
c la s s M yException : E xce p tio n { }
} f in a lly {
Blok finally dba o to, aby za tiażdym
razem podczas wywołania metody
C o n s o le .W rite ("w" ) ; wypisyw ane było „w“ . „ s “ w ypisyw ane
j e s t poza procedurą obsługi w yjątku,
w ięc także będzie s ię poj awiało
zaw sze.
C o n s o le .W r ite ( " s " ) ;
E X
CELNE SPOSTRZEŻENIA
■ Każda in stru kcja może spowodować zgłoszenie ■ Każde t r y może m ieć w ięcej niż je d n o c a tc h :
w yjątku, je ś li podczas je j w ykonyw ania coś się try { ... }
wydarzy. catch (N u llR efe ren ce Excep tion ex) {
/ / Te i n s t r u k c j e będą wykonywane,
■ A b y obsłużyć w yjątek, używaj b lo k u t r y / c a t c h .
/ / gdy z o s t a n i e z gło sz on y w yjątek
N ieobsłużone w y ją tk i będą pow odow ały
I I N u llR e fe re n c e E x c e p t io n .
natychm iastowe zatrzym anie program u
}
i w yśw ietlenie okna błędu.
catch (O verflow Exception ex) { ... }
■ K ażdy w yjątek w b lo k u ko d u za in stru kcją t r y będzie catch (FileN o tFound Exception) { ... }
po w o dow ał przeniesienie w ykonyw ania do pierwszego catch (ArgumentException ex) { ... }
wiersza w b lo k u kod u po c a tc h . ■ T w ó j ko d może zgłaszać w yją tki, używając th ro w :
■ O b ie k t E x c e p tio n udostępnia C i in fo rm a cje na th ro w new Exception("W iadom ość w y ją t k u " ) ;
te m a t przechwyconego w yjątku. Jeżeli w in stru kcji ■ M ożesz ponownie zgłaszać w yją tki, używając
c a tc h umieścisz zm ienną E x c e p tio n , to będzie ona in s tru k c ji th ro w ; choć m ożna to ro b ić ty lk o w ew nątrz
zaw ierała dane dotyczące w yją tku z b lo k u t r y : b lo k u c a tc h . T a kie zgłaszanie w yjątków zachowuje
try { postać stosu wywołań.
/ / I n s t r u k c j e , k tó r e mogą
■ M ożesz tw orzyć własne w yją tki, dziedzicząc po klasie
/ / z g ła s z a ć w y ją t k i .
bazowej E x c e p tio n :
} catch (IO Exception e x ) {
c la s s Custom Exception : E x c e p tio n ;
/ / J e ż e l i z o s t a ł zgło szony wyjątek,
/ / to ex zawiera info rm acje o nim. ■ W większości przypadków będziesz m usiał zgłaszać
ID is posable j e s t naprawdę
Unikanie wyjątków: zaimplementuj IDisposable, efektywnym sposobem
na uniknięcie pospolitych
aby przeprowadzić własne procedury sprzątania wyjątków i problemów.
Upewnij s ię , ż e używ asz
in strukcji using za każdym
S trum ienie są wspaniałe, poniew aż zaw ierają ju ż ko d napisany do zam ykania ich razem, gdy p ra cu jesz
w m om encie usuwania ob ie ktu . Co jednak, gdy posiadasz swój o b ie k t i m usi on z klasami implementującymi
ten in terfejs.
w m om encie usuwania wykonać pewną czynność? Czy nie byłoby świetnie, gdybyś
m ógł napisać własny ko d uru cha m ian y po użyciu o b ie ktu w in s tru k c ji using?
W in stru kcji using możesz
używ ać tylko tych klas,
C # pozw ala C i to zrob ić za pom ocą in te rfe jsu IDisposable. Z a im p le m e n tu j go które im plementują interfe js
i napisz ko d robiący p o rząd ki w m etodzie Dispose() , ja k pokazaliśm y na poniższym ID isp osa b le. W przeciwnym razie
program nie skom piluje się .
przykładzie.
J e ś li ch cesz u m ieścić sw ó j obiekt w instrukcji
c la s s N e c ta r : I D is p o s a b le { using, m usi on implementować ID isposable.
p r iv a te d o u b le a m o u n t;
p r iv a te B e e H iv e h i v e ;
p r iv a te S tre a m h iv e L o g ;
p u b lic N e c ta r ( d o u b le a m o u n t, B e e H ive h i v e , S tre a m h iv e L o g ) {
t h is . a m o u n t = a m o u n t;
t h is .h iv e = h iv e ;
t niej umieścisz, zostanie wykonane
t h is .h iv e L o g = h iv e L o g ; po zakończeniu instrukcji using...
Sądź gdjfwywotasz ją. ręcznie.
} ¡ r
Ta metoda
p u b lic v o id D is p o s e ( ) { D isp o se()
if (a m o u n t > 0 ) { zo sta ła napisana
w taki sposób,
h iv e . A d d ( a m o u n t ) ; by można ją
h iv e . W r it e L o g ( h iv e L o g , am ount + 11 mg n e k t a r u z o s t a ło d o d a n ych "); było wywoływać
w iele razy, a nie
am ount = 0 ; tylko raz.
}
} Ten konkretny kod dodaje dostępny
} nektar-^ do ula i zap is u je jeg o ilość. J. e s_t Jedną z wytycznych odnośnie do im plem entacji interfejsu ID is p o s a b le
to ważne i m usi zo sta ć wykona
umie ś c i?i ś 'mmus! zo sta ć w,y kon ane' dlateg ° jest to, by istniała możliwość wielokrotnego wywoływania metody
u m ieściliśmy go w metodzie D isp sp oose
se().
() r\ u • j u f i u . •
D is p o s e () bez żadnych efektów ubocznych. Czy jesteś w stanie
powiedzieć, dlaczego to zalecenie je st bardzo ważne?
M ożem y teraz użyć w ie lu in s tru k c ji using. W pierwszej kolejności
skorzystamy z wbudowanego o b ie ktu im plem entującego IDisposable Z agnieżdżone instru kcje using, takie ja k te, będą
— Stream. Będziem y także pracować z naszym rozbudow anym obiektem sto so wane w sytu acjach , gdy w tym samym bloku
kodu będzie sz m usiał zadeklarować dwa obiekty
Nectar, k tó ry rów nież im p le m e n tu je ten interfejs: klas implementujących in terfejs ID isposable
■ Nie .istnieją.
głupie pytania
Przyjrzyj się dokładnie klasie Calculator , k tó ra cały czas zachow uje się dziwnie.
Co się m ogło stać?
p u b lic c la s s C a lc u l a t o r {
p u b lic v o id D i v i d e ( f l o a t d i v id e n d , flo a t d iv is o r ) {
Oto problem. W przypadku
try { dzielenia przez zero otrzy mujemy
D ivid eB yZ eroExce ption.
k r
t h is . q u o t ie n t = d iv id e n d / d iv is o r ;
A le przecież mamy blok catch. Dlaczego
w takim razie dalej otrzym ujem y btędy?
} c a tc h {
Z am iast dodaw ać k om en tarz, m askując w ten sposób w yjątek, pow inieneś go obsłużyć. Pamię taj , je śli Twój kod
Jeśli nie jesteś w stanie obsłużyć problem ów , n ie zostaw iaj p u stych lub wypełnionych nie obstuży wyjątku, to będzi
on wędrowat w górę sto su
kom entarzem bloków c a tc h ! W te n sposób b ard zo u tru d n isz innej osobie o d nalezienie wyw otań. Zezw olenie na
ich źródła. Z nacznie lepiej je st pozostaw ić p ro g ram , który będzie zgłaszał wyjątki, takie p rzekazywanie wyjątku
poniew aż łatwiej p o tem określić, co się dzieje. j e s t catkowicie poprawnym
sposobem jego obstugi.
} c a tc h ( E x c e p t io n ex) {
s w . W r it e L in e ( e x . g e t M e s s a g e ( ) ) ;
};
R O Z U M IE M !
T O P EW IEN SPOSÓB OBSŁUGI
W Y J Ą T K Ó W , K T Ó R Y M O Ż E PO M Ó C
O N A M IE R Z Y Ć PO D EJR ZAN E MIEJSCE.
I najważniejsza ze wszystkich:
Damian w końcu
pojechał na urlop...
Teraz, kie dy D a m ian obsłużył wszystkie
swoje w yją tki, jego praca idzie
znakom icie. M oże w końcu wyjechać
na zasłużony (i zatw ierdzony przez
szefa!) u rlop .
641
Powtórne odegranie kradzieży
Zaostrz
Z.UUDLI ołówek
V Poniżej zaprezentowano kod opisujący szczegółowo walkę pomiędzy Kapitanem Wspaniałym
a Kanciarzem (nie mówiąc o jego armii klonów). Twoje zadanie polega na narysowaniu zmian,
które miały miejsce w pamięci podczas tworzenia instancji klasy F in a l B a t t l e .
}
Pierw szy punkt zrobiliśm y za C iebie. Zadbaj
o poprawne narysowanie linii odzwierciedlających
pow i<ązan ia — narysowaliśm y jedn ą z fabryki
klonów do obiektu Kanciarza, ponieważ posiada
ona do niego referencję (poprzez jego pole
PeopleInFactory).
0 bi&V*
K a n cia rza .
T
Twoim zadaniem j e s t
narysowanie sy tu a cji
istn iejącej w pam ięci dla
tych dwóch fragmentów.
ą,Zaostrz ołówek
Narysuj sytuację istniejącą w pamięci podczas pracy programu FinalBattle.
^ ^ R o z w ią z a n ie
Referencja
escapePlane
wska zu je teraz
na nową
instancję obiektu
Dopóki istn ie je
referencja S w indlersEscapePlane.
do sw indler Jeg o pole P ilo tsS e a t
z escapePlane, odwołuje s ię
obiekt ten nie do obiektu Villain.
zostanie u su nięty » ■ /
'S W in d ^
th is .C lo n e lD = C lo n e lD ;
t h is . L o c a t io n = lo c a tio n ;
}
p u b lic v o id T e llL o c a t io n ( s tr in g lo c a t io n , in t c lo n e lD ) {
C o n s o le . W r it e L in e ( " M ó j num er i d e n t y f i k a c y j n y to {0 } i +
"m o żesz m n ie z n a le ź ć tu ta j: {1 }." , c lo n e lD , lo c a t io n ) ;
}
Z n ak ~ (tylda) infprmuj e, ż e kod z ^ to n ^
wykonany podczas likw idacji obiektu przez
p u b lic v o i d W re a k H a v o c () {...} mechanizm oczyszioza.ma. pam ięci-
To j e s t finalizator.
~ Clo
lo n e ( ) { Przekaz u je on do czarnego
charakteru komunika t
T e llL o c a t io n ( th is . L o c a t io n , t h is .C lo n e lD ) ; z informacją zaw ierającą
C o n s o le . W r it e L i n e ( " { 0 } z o s ta ł u s u n ię ty ." , C lo n e lD ) ; identyfikator i położenie
klona. M e toda ta j e s t
} wywoływana tylko podczas
usuw ania obiektu.
}
M e to d ę fin a liza to ra piszesz ta k ja k
Niektóre z zamieszczonych tu fragmentów kodu zostały
ko n stru kto r. Z am ia st m o d yfika to ra dostępu
przedstawione wyłącznie w celach dydaktycznych,
przed nazwą klasy wstawiasz ~. Inform u jesz
nie po to, by stosować je w rzeczywistych programach.
w ten sposób .NET, że ko d w b lo ku
llu w a l
fin a liz a to ra p o w in ie n być wykonywany W książce wielokrotnie wspominaliśmy o tym, że „kiedyś" obiekty
za każdym razem, gdy o b ie k t zostanie zostaną usunięte z pamięci, nie określiliśmy jednak precyzyjnie, kiedy
ten moment następuje. Pisaliśmy jedynie, że dzieje się to po usunięciu wszystkich
usunięty.
referencji do danego obiektu. Za chwilę przedstawimy kod, który automatycznie
F in a liz a to ry nie m ogą m ieć żadnych uruchamia procedurę oczyszczania pamięci, w y w o łu ją c w tym celu m etodę
pa ram etrów , gdyż .N E T nie m a ob ie kto w i nic G C . C o lle c t ( ) , i w y św ie tla kom un ikat w fin a liz a to rz e obiektu . Takie roz
do pow iedzenia poza „Z a ra z z tobą skończę!” . wiązania ingerują w wewnętrzne sposoby działania CLR. Pokazujemy je tylko
po to, by zadem onstrować Ci, ja k działa mechanizm oczyszczania pamięci.
N igdy nie u ż y w a j ich w program ach innych n iż te sto w e !
646 Rozdział 13.
Śmierć obiektu
N iepo trzebn e o b ie kty zostaną usunięte z p a m ię ci... k ied yś. W większości CLONE1
przypadków n ie zobaczysz okna ko m u n ika tu pro ce d u ry usuwania elem entów
bezużytecznych. T w ó j o b ie kt, co prawda, został ustaw iony na n u l l ,
ale pro ced ura nie została jeszcze uruchom iona. Ó' ek t d d ?
\
\ I / - pyk! —
- pyk! —
/ I \
/ I \
STERTA
Po wywołaniu G C .C ollect() wykonywane s ą
finalizatory w obu obiektach, a one sam e znikają.
Pobaw się p ro g ra m e m . K lik n ij przycisk K lo n 1 . , po tem K lo n 2 . , a następnie G C . Z ró b to k ilk a razy. Czasami jako
pierwszy jest usuwany klo n n u m er 1, innym razem jest to klo n 2. M oże się też zdarzyć, że pro ced ura oczyszczania
pam ięci urucham iana będzie nawet bez jawnego w yw ołania G C .C o lle c t( ) .
jesteś tutaj ► 649
N iestabilne środowisko
[ !> SPRAW, ABY KLASA CLONE ZE STRONY 648 BYŁA ZDOLNA DO SERIALIZACJI.
D o da j po pro stu a tryb u t Serializable na górze klasy, abyśmy m o g li ją zapisać do p liku .
[ S e r ia liz a b le ]
public class Clone IDisposable
ą WYSIL _________
SZARE KOMÓRKI
Ten projekt daje dużo do myślenia. Jak sądzisz, jak może wyglądać pozostała część kodu klasy SuperHero?
Na stronie 642 pokazaliśmy tylko jego fragment. Czy jesteś w stanie napisać jego pozostałą część?
A co ważniejsze — czy pow inieneś to robić?
Bez wątpienia istnieje m ożliw ość , by obiekty same wykonywały swoją serializację w chwili, gdy są usuwane.
Ale czy to dobry pomysł? Czy nie jest to sprzeczne z zasadą separacji zadań? Czy nie prowadzi to do powstawania
kodu trudnego do utrzymania? A jakie inne problemy mogą się z tym wiązać?
Dispose: Finalizator:
Bądźmy szczerzy, jestem trochę zaskoczona, że się tutaj
znalazłam. Myślałam, że świat programistyczny doszedł już
do pewnego konsensusu. Mam na myśli to, że jestem bardziej
wartościowa niż ty. Tak poważnie — jesteś całkiem słaby.
Nie możesz nawet siebie serializować, modyfikować kluczowych
danych. nie możesz nic. Jesteś niestabilny, czyż nie?
Przepraszam. To jest trochę niepoważne. Jestem słaby.
dobra. Nie chciałem tego poruszać, ale skoro schodzimy do
takiego poziomu. Ja przynajmniej nie potrzebuję interfejsu,
aby zaistnieć. Bez IDisposable jesteś po prostu kolejną
bezużyteczną metodą.
Specjalny interfejs istnieje, poniew aż jestem tak ważna.
Fakty są takie, że jestem w nim jedyną metodą!
Dobra, dobra. Możesz sobie tak mówić. Co się jednak
stanie, gdy użytkownik zapomni użyć instrukcji using
podczas tworzenia instancji obiektu? Wtedy nie można
cię odnaleźć.
OK, masz rację, programiści muszą wiedzieć, że mnie potrzebują,
i albo wywołują mnie bezpośrednio, albo używają instrukcji
using, która to zrobi. Wiedzą jednak zawsze, w którym
momencie jestem wywoływana, i mogą zrobić wszystko, czego
s i ę bezpo średnio z Windows/
potrzebują, w celu uprzątnięcia obiektu. Jestem potężna, W zw iązku z tym, że .N ET nie
niezawodna i łatwa w użyciu. Jestem „trzy w jednym". A ty? wie nic o ich istn ien iu , nie może
Nikt dokładnie nie wie, kiedy zostaniesz wykonany i jaki będzie po nich posprzątać.
stan aplikacji, gdy w końcu zdecydujesz się pokazać.
W porządku, ale jeśli potrzebujesz zrobić coś dosłownie
w ostatnim momencie podczas usuwania obiektu, nie
możesz tego zrobić inaczej, jak tylko przeze mnie. Mogę
zwolnić zasoby sieciowe i uchwyty okien, i strumienie,
i wszystko inne, co może mieć wpływ na pozostałą część
programu, jeśli nie posprzątasz prawidłowo. Mogę się
upewnić, że obiekty, z którymi pracujesz, zostaną elegancko
usunięte, i nie masz prawa tym gardzić.
Więc nie ma niczego, co możesz zrobić ty, a czego nie mogę ja.
Myślisz, że wiele znaczysz, tylko dlatego, że jesteś wywoływany
podczas uruchamiania procedury oczyszczania pamięci, ale ja
przynajmniej mogę polegać na innych obiektach.
To prawda, koleżanko — ja zostanę wykonany zawsze;
a ty potrzebujesz kogoś, kto cię wywoła. Ja nie potrzebuję
nikogo i niczego!
P : Czy finalizator może używać wszystkich pól i metod P : Jak często wykonywana jest automatycznie procedura
swojego obiektu? oczyszczania pamięci?
O : Oczywiście. Nie możesz co prawda przekazać parametrów O : Nie ma dobrej odpowiedzi na to pytanie. Nie jest ona uruchamiana
do metody finalizatora, ale możesz używać wszystkich pól w obiekcie w jakimś przewidywalnym cyklu i nie masz nad tym żadnej kontroli. Możesz
albo bezpośrednio, albo korzystając z t h is . Bądź jednak ostrożny, ponieważ być pewny, że zostanie wykonana w momencie zakończenia Twojego
pola te mogą przechowywać referencje do innych obiektów, a te mogą już programu. Jeśli chcesz mieć pewność, że mechanizm zadziała, musisz wywołać
być usunięte. Możesz też oczywiście wywoływać podczas finalizacji obiektu G C .C o lle c t (), ale nawet wtedy czas jego wykonania nie jest określony
inne metody i właściwości (o ile ich działanie nie zależy od innych obiektów).
P : Jak szybko po wywołaniu GC.Collect() .NET wykona
P : Co się dzieje z wyjątkami zgłaszanymi w Analizatorze? procedurę oczyszczania pamięci?
O : Dobre pytanie. Użycie bloku try / c a tc h w finalizatorze jest O : Wywołując G C .C o lle c t (), nakazujesz platformie NET wykonać
całkowicie poprawne. Spróbuj sam. Utwórz wyjątek dzielenia przez zero sprzątanie pamięci tak szybko, jak to tylko możliwe. Oznacza to mniej
wewnątrz bloku t r y programu Clone, który przed chwilą napisaliśmy. więcej moment, w którym NET zakończy swoje aktualne zadania. Zostanie
Wyłap go i wyświetl komunikat „Właśnie wyłapałem wyjątek." tuż przed to zrobione dość szybko, ale nie możesz tego dokładnie kontrolować.
komunikatem „Aaaaaa! Dopadłeś mnie!", który już mamy. Uruchom teraz
program i kliknij pierwszy przycisk, a następnie przycisk GC Zobaczysz P : Jeżeli koniecznie muszę coś wykonać, to wrzucam
zarówno okno wyjątku, jak i okno informujące o usunięciu obiektu. to do finalizatora, prawda?
(Oczywiście wyświetlanie okien MessageBox w finalizatorach obiektów,
które nie są tylko zabawkami, to naprawdę zły pom ysł.. Te okna O : Istnieje możliwość, że finalizator nie zostanie wykonany Jest też możliwe
komunikatów mogą się nigdy nie wyświetlić). zablokowanie finalizacji podczas przeprowadzania procedury oczyszczania
pamięci. Proces może się również zakończyć, zanim cokolwiek zdąży się
wykonać. Jeśli robisz cokolwiek oprócz zwalniania zasobów niezarządzanych,
to niemal zawsze lepszym rozwiązaniem będzie użycie ID isposable
i instrukcji using.
654
Śmierć obiektu
Ten w iersz te m p e ra tu re s .A d d (5 6 .5 D );
u sta w ia referencję te m p e ra tu re s .A d d (2 7 .4 D );
diffe re n tL is t tak, temperatures
aby wskazywała L is t< d o u b le > d if f e r e n t L n s t te m p e ra tu re s ;
na ten sam obiekt d iff e r e n t L is t. A d d ( 6 2 .9 D ) ; differentList
co referencja
Obie referencje ws kaz u ją
tempe ra tu re s. na ten sam ob iekt.
^ < ¿ 0 ^
Z m ia n a o b ie ktu L i s t sprawi, że obie referencje odczują różn icę ...
poniew aż obie na niego wskazują.
W y n ik pokazuje, że d i f f e r e n t L i s t i te m p e ra tu re s
w rzeczywistości wskazują na ten sam obiekt.
te m p e ra tu re s ma 3 , d i f f e r e n t L i s t ma 3 obiektu, na który w sk ^ u je za
— 1 ^ d iffe r e n tL is t, j a k i t e m p e r a tu r e s .
p u b lic v o id Speak() {
C o n s o le .W rite L in e ("W a b ię s ię { 0 } . Moja rasa to { 1 }." , Name, B reed);
}
}
^Zaostrz ołówek
Jak myślisz, co zostanie wypisane w oknie konsoli?
Rozwiązanie Wabię się Szarik._ Moja rasa _to _beagle.
Wabię się Tofik. Moja rasa to pudel.
Tu j e s t wielka różnica. © \
Gdy dodałeś zmienną betty, /
/ Tofik Tofik
Kiedy przypisujesz do jednej utw orzyłeś całkiem nową
stru k tu rę . ,
1
\
pudel pudel
struktury inną, tw orzysz s -
tym samym nową KOPIĘ b e tty ja k e
W związku z tym,
znajdujących się w niej ż e u tw orzyłeś nową
kopię danych, ja k e nie
danych. To dlatego, zos t a ł zmieniony podczas © / \
/ n •
że struktura jest TYPEM modyfikacji pól zmiennej
betty.
Becia
i
Tofik
pudel
W ARTOŚCIOW YM . pitbul v
b e tty ja k e
658 Rozdział 13.
Śmierć obiektu
SpeakThreeTimes(jake);
Bez w ątpienia będziesz chciała zrozum ieć, czym stru ktu ra kopiowana
przez wartość różni się od obiektu kopiowanego przez referencję.
Istn ie ją takie przypadki, w których T w o ja m etoda m usi przyjm ow ać w artości typ u wartościowego
lu b referencyjnego — na przykład m etoda, k tó ra p o tra fi pracow ać ze stru ktu rą Dog lub
obiektem Canine. Jeśli kie d yko lw ie k znajdziesz się w takie j sytuacji, możesz użyć słowa
kluczowego o b je c t:
Po wykonaniu
tego w iersza kodu obj
otrzym ujesz trzecią
kop ię danych Sid
w rtowej stru ktu rze
o nazwie happy. husky
Z a jmuje ona sw o ją
własn ą komórkę na
sto s ie . D o g s id (o p a k o w a n y )
O b o k przedstaw iliśm y k ilk a m etod p u b lic double FeedDog(Canine dogToFeed, Bowl dogBowl) {
zaczerpniętych z sym ulatora psów. do ub le eaten = E at(dogT oF eed.M ealS ize, dogB ow l);
Są one całkiem proste: FeedDog() r e tu r n eaten + .05D; / / Zawsze cos s ię w ysyp ie.
w yw ołuje E a t ( ) , a ta z k o le i w yw ołuje }
C h e ckB o w l().
p u b lic v o id E a t(d o u b le m e a lS ize , Bowl dogBowl) {
Warto, byś pam iętał o terminologii:
d o g B o w l.C a p a city -= m ea lS ize ;
parametrem nazywamy element
deklaracji metody określający C h eckB o w l(d og B ow l.C apa city);
w artość, ja k ie j ta metoda }
potrzebuje; argumen t to z kolei
faktyczna w artość lub referencja
przekazywana do metody p u b lic v o id CheckBowl(double c a p a c ity ) {
w momencie je j wy woły wania. if (c a p a c ity < 12.5D) {
s t r in g message = "Moja m iska je s t p ra w ie p u s ta !" ;
C o n s o le .W rite L in e (m e s s a g e );
}
}
Metoda FeedDog() l Metoda FeedDog() musi Gdy wywołania metod Kiedy zakończy się
w y m a g a przekazania dwóch przekazać d w a argumenty się kumulują, a program realizacja metody Console.
parametrów — referencji do wywołania metody zagłębia się coraz bardziej WriteLine(), jej argumenty
Canine oraz referencji Eat(), a zatem także w wywołania, które zostaną pobrane iusunięte
Bowl.A zatem w momencie ione zostaną umieszczone wywołują kolejne metody ze stosu. Dzięki temu metoda
jej wywoływania na stosie na stosie. wywołujące metody Eat() będzie mogła być dalej
znajdą się d w a przekazane jeszcze dalsze, stos powoli wykonywana, jak gdyby
do niej argumenty. staje się coraz większy nic się nie stało. To właśnie
iwiększy. dlatego stos jest tak bardzo
użyteczny!
T eraz, kie dy ju ż podałeś w artości dwóch p a ram e tró w wyjściowych, uda się skom pilow ać program .
D o d a j przycisk z następującą pro ced urą obsługi:
p r iv a t e v o id b u t to n l_ C lic k ( o b je c t se n d e r, EventArgs e
i n t a;
Czy zw róciłeś uwagę na to, że nie m usia łeś inicjalizować
double b;
b i c ? Nie trzeba inicjalizować zmiennych przed ich
i n t c; zastosowaniem jako parametrów wyjściow ych.
a = R e tu rn T h re e V a lu e s(b , c ):
C o n s o le .W rite L in e ("v a lu e { 0 } , h a lf = { ! } , do ub le = { 2 } " , b , c)
}
W tym projekcie
O rany! P ojaw iły się nowe błędy: A rg u m e n t 1 m u s t be passed w ith th e o u t ke yw o rd 2. Z a każdym używamy aplikacji
razem, gdy wyw ołujesz m etodę posiadającą p a ra m e try wyjściowe, musisz poprzedzić argum enty typu W indows
słowem kluczow ym o u t. O to ja k należy to zrobić: Forms, gdyż dzięki
a = R e tu rn T h re e V a lu e s(o u t b , o u t c ) ; temu ła tw o możesz
klikać przyciski
T eraz ju ż nie będziesz m ia ł p ro b le m ó w ze zbudowaniem program u. K ie d y go uruchom isz, m etoda i obserwować
R e tu rn T h re e V a lu e s ( ) ustawi i zw róci trzy wartości: zm ienna a stanie się w artością w ynikow ą rezultaty
m etody, b zostanie zw rócona ja ko p a ram e tr h a lf , a c ja ko pa ra m e tr tw ic e . wyświetlane
w oknie Output.
1 Przed opuszczeniem bieżącej metody należy przypisać wartość do parametru wyjściowego ‘half’ — przyp. tłum.
2 Pierwszy parametr musi być przekazany z zastosowaniem słowa kluczowego out — przyp. tłum.
C r m m ię t a s z , jak w rozdziale 9. używaliśmy instrukcji switch do konwersji łańcucha „ SH es" na warto ś ć Su it.Spades?
Có ż ... istnieją statyczne m e to d y Enum.Parse() oraz Enum.TryParse(), k t ó re działają analogicznie, lecz operują na
wartościach ty p ó w wyliczeniowych.
W łaśnie taką m ożliw ość zapew niają pa ram e try opcjonalne. Tworzysz je, umieszczając za nazwą pa ra m e tru w deklaracji
m etody znak rów ności i w artość domyślną. Liczba param e tró w opcjonalnych nie jest ograniczona, je d n a k wszystkie one
muszą się znaleźć za pa ram e tra m i wymaganymi.
Poniżej przedstaw iliśm y p rzykład m etody używającej p a ram e tró w opcjonalnych do sprawdzenia, czy ktoś m a podwyższoną
bądź ob niżoną tem peraturę.
Istn ie je także dodatkow a m ożliwość. Jeśli chcesz wykorzystać ty lk o n ie któ re (lecz nie wszystkie) z w artości domyślnych,
to możesz skorzystać z n a zw a n ych a rg u m e n tó w , by okre ślić w artości w ybranych param etrów . W tym celu wystarczy podać
nazwę takiego pa ram e tru , a po niej um ieścić d w ukro pek i przekazywaną wartość. Jeśli w w yw oła niu chcesz podać więcej
niż jeden nazwany argum ent, to nie zapom nij od dzielić ich od siebie przecinkam i.
D o d a j do swojego fo rm u la rza m etodę CheckTemperature(), a następnie przycisk w raz z poniższą m etodą obsługi.
Przetestuj pro gra m w debuggerze i upew nij się, że do kła dnie rozum iesz, ja k on działa.
struct SystemJnt32
Składowymi tej s tru k tu ry są int.Parse() oraz in U T r y P ^ O . Represents a 32-bit signed integet
Poświęć chwilę, by w analogiczny sposób sprawdzić wszystkie ty p y przedstawione na początku rozdziału 4 . Przekonasz się,
że wszystkie one, z w yjątkiem typ u string, są nazwami zast ępczy mi stru ktu r- Ty p string jest nazwą zastępczą klasy
System.String (czyli jest t o ty p referencyjny, a nie wartościowy ) .
665
Zakosztuj solidności
f Z
rób to!
Typy akceptujące wartości puste poprawiają odporność programów
U żytko w n icy w yczyniają przeróżne zwariow ane rzeczy. Sądzisz, że wiesz, ja k będą korzystać Kiedy będziesz dodawał
z pisanego przez C iebie pro gra m u, lecz potem ktoś k lik a przyciski w nieo dpo w iedn ie j kolejności, metodę RobustG uy.
ToStringO, zwróć uwagę
w pisuje 256 spacji w p o lu tekstow ym lu b używa M enedżera zadań, by zam knąć pro gra m w trakcie na to, co w yśw ietli
zapisywania danych do p lik u , i nagle okazuje się, że aplikacja zgłasza dziesiątki różnych błędów. okienko In te lliS e n se
Czy pam iętasz, ja k w rozdziale 12. opisywaliśm y program y, któ re p o tra fią radzić sobie z błędnie podczas wpisywania
8 irthday.Value. Ponieważ
zapisanymi, nieoczekiw anym i lu b dziw nym i danym i wejściowym i? N azw aliśm y je program am i wtaściw o ść Value j e s t
odpornymi . C ó ż... je śli chodzi o przyjm ow anie in fo rm a c ji podawanych przez użytkow ników , ty p u DateTim e, u jrzysz
w n im standardowe
to typy akceptujące w artości puste mogą znacznie po pra w ić odporność program ów . Przekonaj się
składowe tego typu.
o tym sam — u tw ó rz nowy projekt aplikacji konsolowej i dodaj do niej przedstaw ioną poniżej
klasę RobustGuy. łl
Użyj metody ToLongDateString(),
cla ss RobustGuy { by w y św ietlić datę w po sta ci
public DateTime? Birthday { get; p riv a te s e t; ) zrozum iałej dla człowieka.
public in t? Height { get; p riv a te s e t; }
P : D o brze, w róćm y na ch w ilę. P : W ie m , w ja k i sposób mogę pobrać P : Skąd mam w ie d zie ć, kied y
Dlaczego m iałbym się interesow ać św ie ż ą kopię s tr u k tu ry podczas u ż y w a ć s tru k tu ry , a k ie d y k la sy?
stosem? p rzy p isyw a n ia jednej zm iennej do
dru g iej. Dlaczego je st to dla mnie O : W większości przypadków programiści
O : Ponieważ zrozumienie różnicy pomiędzy istotne? używają klas. Struktury mają spore
stosem a stertą pozwala Ci lepiej używać ograniczenia, które naprawdę utrudniają
typów wartościowych i referencyjnych. O : Jedną z operacji, w których może się posługiwanie się nimi w dużych projektach.
Łatwo zapomnieć, że struktury i obiekty to naprawdę przydać, jest hermetyzacja Nie umożliwiają dziedziczenia i nie oferują
działają inaczej — znaku równości można implementacji. Popatrz na pole w klasie, abstrakcji i jedynie ograniczony polimorfizm,
użyć w stosunku do obu tych typów, która przechowuje swoje położenie: a wiesz, jak ważne są te rzeczy dla łatwego
wyglądają także podobnie. Posiadanie p r iv a t e P o in t lo c a t io n ; tworzenia programów.
pewnej wiedzy na temat wewnętrznego Struktury są naprawdę przydatne wtedy,
p u b lic P o in t L o c a tio n {
traktowania ich przez .NET i CLR ułatwia gdy masz mały, ograniczony typ danych,
get { re tu rn lo c a t io n ; }
zrozumienie, dlaczego referencje i typy z którym musisz często pracować. Prostokąty
wartościowe działają inaczej. }
i punkty są tutaj doskonałym przykładem —
Gdyby P o in t była klasą, hermetyzacja nie
P : A opakow yw anie? Dlaczego jest byłaby właściwa. Nie miałoby znaczenia to,
nie mają wielkich możliwości, ale będziesz
ich używał bez przerwy. Struktury okazują
dla mnie w ażne? że lo c a tio n jest private, ponieważ utworzyłeś
się być względnie małe i mają ograniczony
właściwość tylko do odczytu, która zwraca
O : Ponieważ powinieneś rozumieć, referencję. W takim przypadku każdy obiekt
zasięg. Jeśli posiadasz niewielki zbiór różnych
danych, który chcesz przechowywać w polu
kiedy różne dane są umieszczane na mógłby uzyskać dostęp do tej składowej.
klasy lub przekazywać do metody w postaci
stosie, i musisz wiedzieć, kiedy są one
Na szczęście P o in t jest strukturą. Oznacza parametru, jest on dobrym kandydatem
kopiowane w jedną bądź w drugą stronę.
to, że publiczna właściwość Lo c a tio n na strukturę. Jeśli jednak sposób używania
Opakowywanie wymaga dodatkowej
zwraca świeżą kopię punktu. Obiekt, który struktury sprawia, że w większości
ilości pamięci i czasu. Jeżeli robisz to tylko
tej kopii używa, może z nią zrobić cokolwiek przypadków będzie ona opakowywana, to
kilka (lub kilkaset) razy w programie, nie
— żadne operacje nie spowodują zmian najprawdopodobniej lepszym rozwiązaniem
odczujesz różnicy. Przypuśćmy jednak,
w prywatnym polu lo c a t io n . będzie stworzenie klasy
że masz aplikację, w której takie operacje
mają miejsce wielokrotnie, miliony razy na
sekundę. To nie jest wcale takie nierealne.
Na końcu książki napiszesz grę, która może
f
Z a jrz y j ponownie do proje k tu
Struktura m oże
być naprawdę
wykonywać wiele obliczeń na sekundę. Gdy odbijających s ię e ty k ie t
zauważysz, że Twój program pobiera coraz przedstawionego w rozdziale 4 .
więcej pamięci i działa coraz wolniej, istnieje
Z a kulisam i używaliśmy w nim wartościowa, jeżeli
punktów i położenia, a to oznacza,
możliwość zaradzenia tego typu problemom. że nasz kod korzystał z wartości chcesz dobrze ukryć
Możesz ulepszyć aplikację poprzez unikanie przechowywanych w strukturach
opakowywania w tych fragmentach, które (naw et je ś li nie deklarowaliśmy implementację
ich jaw n ie).
są często powtarzane. klasy, ponieważ
Zaostrz ołówek właściwość ty lk o
do o d c z y tu , k tó ra
Ta metoda ma za zadanie usunąć obiekt C lo n e , ale nie działa.
Dlaczego?
zwraca s tru k tu rę ,
p r iv a t e v o id S e tC lo n e T o N u ll(C lo n e c lo n e ) { zawsze tw o rz y
clo n e = n u l l ; jej świeżą kopię.
}
S zy bki kwiz, geniuszu!
° d p owiedź na stron ie 670.
669
Rozszerz to
Czasami musisz rozszerzyć klasę, po k tó re j nie możesz dziedziczyć, na przykład klasę s e a le d (spora
część klas .N E T jest s e a le d , co u n ie m o żliw ia dziedziczenie po nich). C # udostępnia C i do tego potężne
narzędzie: metody rozszerzające . G dy dodajesz do p ro je k tu klasę z ta k im i m etodam i, dodajesz te metody
do klas , któ re ju ż istnieją. M usisz ty lk o utw orzyć statyczną klasę i dodać statyczną m etodę, k tó ra przyjm uje
instancję klasy ja ko pierw szy p a ram e tr przy użyciu słowa kluczowego t h is .
Powiedzm y, że posiadasz klasę O rdinaryH um an oznaczoną m o d yfika to re m s e a le d (co, ja k pamiętasz,
u n ie m o żliw ia dziedziczenie p ° n ie j): Klasa OrdinaryHuman j e s t sealed, _
Rozwiązanie Ta metoda ma za zadanie usunąć obiekt C lone , ale nie działa. Dlaczego?
p r iv a t e v o id S e tC lo n e T o N u ll(C lo n e c lo n e ) {
P a r a m e tr clone znajduje ^
c lo n e = n u l l ;
}
M etoda ta ustawia swój własny parametr na null, ale jest on ty lk o referencją Clone.
.To. tak,, jakbyśmy, przyklejali, do. obiektu, e tykie tę i .zaraz. potem , ją .usuwali:.............................................
■ Nie .istnieją.
głupie pytania
P: : Powiedz mi jeszcze raz, dlaczego nie mogę nowych metod O : Jeśli możesz rozszerzyć klasę, to zapewne właśnie tak
dodać bezpośrednio do kodu klasy i muszę używać metod postąpisz — celem metod rozszerzających nie jest zastępowanie
rozszerzających. dziedziczenia, Bardzo się jednak przydają w przypadkach, gdy nie
AHA, ZAŁAPAŁEM!
Je s z c z e jedna rzecz dotycząca
METOD ROZSZERZAJĄCYCH metod rozszerzających, o jakiej
UŻYWASZ WTEDY, GDY CHCESZ ZWIĘKSZYĆ warto pam iętać: tworząc taką
metodę, nie u zysk u jesz dosfyptu
FUNKCJONALNOŚĆ JEDNEJ Z WBUDOWANYCH do wewnętrznych składowych klasy
KLAS .NET FRAMEWORK, PRAWDA? — wciąż j e s t ona traktowana jako
kod spoza niej!
Spróbuj teraz skom pilow ać swój ko d — I D E w yśw ietli błąd. Spowodow any jest on
tym , że n ie któ re klasy .N E T są s e a le d , co oznacza, że nie m ożna po nich dziedziczyć.
(M ożesz ta k oznaczyć także swoje klasy! Po pro stu dodaj słowo kluczowe s e a le d zaraz
po m od yfikatorze dostępu p u b lic . Ż adn a in n a klasa nie będzie m ogła dziedziczyć
po takie j klasie). M e to d y rozszerzające pozw alają C i rozbudow ać klasę nawet m im o braku
m ożliw ości dziedziczenia.
Magnesy rozszerzające
Poprzestawiaj magnesy tak, aby otrzymać następujący wynik:
----------------------------
Pub l i c s t a t ic v o id S endIt c la s s Program {
J ______________________________
p u b lic s t a t ic s t r in g T oP rice
i f (n == 1)
re tu r n "a buck
]
| p u b lic s t a t ic v o id S e n d lt
( t h is s t r in g s) {
I C o n s o le .W rite (s )
u s in g Upside;
1
1V4I11WJ
namespace Sideways {
pu b lic s t a t i c s t r in g To P ric e ( t h is i n t n) { | _____
if (n == 1) c la s s Program {
e ls e
re tu r n "a buck
I s ta tic void Main (s trin g [] args) {
E r
re tu r n 11 more bu cks":
] C in t i
I
s t r in g s = i.T o P r ic e ( ) ;
if (b == tru e ) | bool b = t r u e ; E L
r e tu r n "b e ";
e ls e 1 ^ b .G re e n () .S e n d It( );
r e tu r n " g e ts " ; I b = f a ls e ;
_x > i— r
1-------1 '
\ i b .G re e n ().S e n d It( ); |
Llt \
To tu taj klasa M argin rozszena. . < ■ * 1
i.T o P r ic e ( ) .S e n d It( ) ;
G reen(). Je ż e li bool j e s t równe tru e,
to Green powinno zw rócić ,t>e“ .
W przeciwnym razie zwraca „ge t s “ . Console.R eadK ey();
gP
674 Rozdział 13.
ODBUDOWALIŚMY KLASĘ SUPERHERO,
ALE W JAKI SPOSÓB PRZYW RÓ CIM Y
KAPITANA?
WSZECHŚWIAT •
H*0E a®B wuwofl mmmmmm
Śmierć nie była końcem!
Grzegorz Kamień
d z ie n n ik a r z w s z e c h ś w ia t a
OBIEKTOWO
do Obiektowa.
k a p i t a l n e j nofatki. ^ deserializacji
i w y m a m r o t a ł : „ R o z d z i a ć 9 ." B l S 3 ™ ^ ^ ^ r a m i o M m i
t a j e m n i c z e j o d p o w i e d z i , ale P ^ z m H że p r e e d ^ e u d tarza w sP r a w ie j e g o
s p ę d z i ł o n d u ż o c z a s u , c z y t a j ą c k s i ą ż k i o r a z st a a n y m P 0 *0 # * * a Kanciarzem
p u b lic s t r in g s t a ir s ;
p u b lic Hinge f l o o r ;
Metoda LampO ustaw ia rożne Maf o iC '
p u b lic v o id S e t(H in g e b) {
string i in t. Jeśli wywołasz J ^ ^ P0'6
Bu/b ustawione zostanie na konkretny
f l o o r = b;
obiekt wskazywany przez Hing .
}
to je st n o w y ro z d z ia ł ► 677
D ia b e ł tk w i w s z c z e g ó ł a c h
Z O B A C Z C IE TEN KUBEK
Z LIMITOWANEJ S E R II
Z NADRUKIEM KAPITANA WSPANIAŁEGO,
Z DRUGIEJ COROCZNEJ KONFERENCJI
W SPAN IAŁO ŚC I, PODPISANY
P R Z E Z PROJEKTANTA I GRAFIKA!
Obw iedziona
ramkę, okfadka
kom iksu „
„Śm ierć o biektu ,
podpisana
przez a u to ra .
W symulatorze pszczoły
znajd°waty się w kolekcji.
C 987 L
CurrentState = MakingHoney \
Bee1-
L
gg^^^rontStat^^lyin^oFlowe^^^J^
y B e e t|i
Bees
[_B = 1982 1
BeeH. CurrentState = GatheringNectar
var beeGroups =
from bee in world.Bees
group bee by bee.CurrentState
ID = 987 I CurrentState = M akingHoney
into beeGroup ID = 1 2 I CurrentState = FlyinqToFlower'
orderby beeGroup.Key
select beeGroup; Baza danych
F .
int[] values = new int[] {0, 12, 44, 36, 92, 54, 13, 8};
Console.WriteLine(i);
Teraz możesz wykonać iteracje
po tablicy wynikowej i wypisać
Console.ReadKey(); każdy element zwrócony
przez LINQ. W ynik:
0, 8, 12, 13, 36
var
z o s * zam m S j S i
j m X E
d y '^ s z e r z a l* num erable^!, M * . « podczas pracy
bardzo często będziesz spotykał się także z t y m interł e|sem.
Z a jrz y j do rozdziału 8 ., by przypom nieć sobie inform acje dotyczące interfejsu IEnumerable<T>.
Oprócz tego dodatkowe dane na jego tem at zn a jd zie sz w punkcie 7 . dodatku „Po zostało ści” .
LINQjest prosty, ale Twoje zapytania wcale takie być nie muszą
Ja n e k sp rzed ał sw oją rozw ijającą się firm ę zajm u jącą się sp rz e d a ż ą aplikacji dla S k lep u W indow s
w ielkiem u inw estorow i i chce część sw oich zysków przeznaczyć n a zakup n ajcenniejszych w ydań
kom iksów o K ap itan ie W spaniałym , jak ie się ukazały. W jak i spo só b L IN Q p o m o że m u p rzeszu k ać
kolekcję i znaleźć te najd ro ższe?
c l a s s Comic {
p u b lic s t r i n g Name { g e t; s e t ; } Nie ma żadnego
p u b lic i n t I s s u e { g e t; s e t ; }
szczególnego
powodu, by
} stosować tu
metodę statyczną,
Ja n e k użył inicjalizatorów obiektów i inicjalizatora kolekcji do u tw o rzen ia sw ojego katalogu: może z wyjątkiem
tego, że dzięki
p riv a te s t a t i c IEnumerable<Comic> B u ild C a ta lo g () temu będzie ją
{ łatwiej wywoływać
r e t u r n new List<Com ic> { z metody punktu
wejścia aplikacji
Pominęliśmy parę new Comic Name "Johnny A m erica v s. th e P in k o ", Is s u e = 6 }, konsolowych.
nawiasów za new Comic Name "Rock and R oll (e d y c ja lim ito w a n a )" , Is s u e = 19 },
inicjalizatorami
kolekcji i obiektów new Comic Name "Woman's Work", Is s u e = 36 },
<Comic>, gdyż ich new Comic Name "H ip p ie Madness ( ź le w ydrukowany)", Is s u e = 5 7 },
nie potrzebujemy. new Comic Name "Revenge o f th e New Wave F reak (u sz k o d z o n y )", Is su e = 68 },
new Comic Name "B lack Monday", Is s u e = 74 },
};
new Comic
new Comic
Name
Name
" T rib a l T a tto o M adness", Is s u e = 83 },
"The Death o f an O b je c t" , Is s u e = 97 } 1
Numer 74. przygód Kapitana
Zajrzyj do punktu 7. dodatku „Pozostałości", by poznać składnię, Wspaniałego nosi tytuł
„Black Monday".
która w takich sytuacjach może być napraw dę przydatna i wygodna.
To doskonała okazja do przeprow adzenia kilku eksperymentów!
N a szczęście istnieje dobrze p ro sp eru jący rynek kom iksów z K ap itan e m W spaniałym , a dane n a jego tem a t
um ieszczone zostały n a liście G rzegorza. Ja n e k wie, że n u m e r 57. Hippie Madness został źle w ydrukow any
i praw ie cały n ak ład został zniszczony przez wydawcę. Z n alazł o statn io n a liście G rzeg o rza rzad k ą kopię
sprzedaw an ą p o 13 525 zł. P o kilku godzinach poszukiw ań utw orzył słownik, który kojarzy nu m ery kom iksów
z w artościam i.
Anatomia zapytania
D ość łatw o b ędzie Jankow i p o b ra ć d an e w jednym zapytaniu L IN Q . F ra z a w h ere określa
elem enty kolekcji, k tó re pow inny znajdow ać się w wyniku. N ie m usi o n a być w cale prostym
porów naniem . M oże zaw ierać dow olne praw idłow e w yrażenie C # — n a przykład używać
słow nika v a lu e s , aby zw racał tylko te kom iksy, k tó re są w arte więcej niż 500 zł. F raz a o rd e r b y
działa w te n sam sposób — n akazuje L IN Q poso rto w ać kom iksy n a podstaw ie ich w artości.
comic.Name, values[comic.Issue]);
\d
przedstaw iać w tym rozdziale
zapytanie zwróci obiekty Ćormc. “ m,c- J /,
d w u kro tn ie : za pierwszym
razem w aplikacji konsolow ej,
by pomóc Ci zrozum ieć, jak
ono działa , następnie
Wynik:
w w iększej aplikacji dla Sklepu
H ip p ie M adness ( ź l e w ydrukow ane) j e s t w a r ty 13 5 2 5 ,0 0 z l
W indow s, byś m ógł przekonać
Jo h n y A m e ric a v s . t h e P in k o j e s t w a r ty 3 6 0 0 ,0 0 z l się, jak zapytania LINQ działają
W oman's Work j e s t w a r ty 6 5 0 ,0 0 z l w kontekście — a to dlatego, że
"W ludzki mózg lepiej zapam iętuje
rzeczy posiadające kontekst.
Komiksy Janka
W yb ie rz zapytanie d o w ykonania
LINQ ułatwia zapytania
■
Proste zapytanie
Drogie komiksy
Komiksy powyżej 500 zł.
Komiksy o wartości przekraczającej 500zł. Janek może użyć tych danych do wybrania najbardziej pożądanych komiksów.
Więcej informacji na tem at sposobów projektow ania nawigacji w aplikacjach dla Sklepu Windows
można znaleźć na stronie: http://msdn.microsoft.com/pl-pl/library/windows/apps/hh761500.aspx.
W klasie App odszukaj m e to d ę O n L a u n c h e d (). Jest o n a w ykonyw ana za każdym razem podczas u ru ch am ian ia aplikacji
i odpow iada za utw o rzen ie i przygotow anie ram ki:
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used when the application is launched to open a specific file, to display
/// search results, and so forth.
¡if < / su mm ar y> Użyj opcji Go To Definition,
¡ ¡ I <param name="args">Details about the launch request and process.</param> by przejść do klas Window
protected override void OnLaunched(LaunchActivatedEventArgs args) lub Frame reprezentujących,
i ' odpowiednio: główne okno
Frame root Frame = Window. Current .Content as Frame \
bieżącej aplikacji oraz ramkę
/ / D o not repeat app initialization when the Window already has content, nawigacyjną.
// just ensure that the window is active
if (rootFrame == null)
i
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame(); To właśnie w tym miejscu
aplikacja tworzy nową
if (args.PreviousExecutionState == ApplicationExecutionState.Terminated) ramkę nawigacyjną, która
{ będzie zawierać wszystkie
//TODO: Load state from previously suspended application
i strony tej aplikacji.
// Place the frame in the current Window K\QĆy usuwasz z proj ektu plik MainPage.xaml i zastępujesz
Window.Current.Content = rootFramej go nowym plikem Basic Page o tej samej nazwie, dodajesz do
} pr°jektu nową klasę Mainpage, ktOra zastępuję poprzednią.
f
Dzięki temu metoda Navigate() może utworzyć instancję tej
if (rootFrame.Content == null) nowej klasy zamiast domyślnej, wygenerowanej wraz z projektem.
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
if (!rootFrame.Navigate(typeof(MainPage), args.Arguments)) To właśnie w ten sposób aplikacja wyświetla
t stronę główną. Metoda F ram e .N a v ig ate ()
throw new Exception ("Failed to create initial page'');
tworzy nową instancję strony i wyświetla jej
}
zawartość. Słowo kluczowe ty p e o f zwraca typ
t
// Ensure the current window is active klasy, dzięki czemu metoda wie, jakiego typu
Window.Current.Activate(); obiekt ma utworzyć.
1
Twoje aplikacje także m ogą korzystać z m etody N a v ig a te ( ) , by poruszać się pom iędzy stronam i. K ażda stro n a X A M L
dysponuje w łaściw ością o nazw ie Frame. Gdybyś m iał dodać do aplikacji kolejną stronę, o nazwie A n o th erP ag e, to mógłbyś
ją wyświetlić, używając poniższego fragm entu kodu. Z w róć uw agę n a a rg u m e n t q u e ry przekazyw any w wywołaniu m etody
N a v ig a te ( ) . T o p a ra m e tr przekazyw any do tw orzonej strony.
Zajrzyj do punktu 5. dodatku
if (th is .F r a m e != n u ll)
Pozostałości, gdzie możesz znaleźć
th is.F ra m e .N a v ig a te (ty p e o f(A n o th e rP a g e ), q u e ry ); dodatkowe informacje dotyczące
słowa kluczowego ty p e o f.
Jeśli dodasz stronę o nazwie AnotherPage, IDE doda do
projektu klasę AnotherPage, a ten kod pozwo^ przejść na
stronę AnotherPage, przekazując tśo niej „ąuery ‘ jako argument. jesteś tutaj ► 687
N o w a a p lik a c ja , z n a n y w z o r z e c
Skorzystaj z szablonu B lank Template, u su ń p lik MainPage.xaml, n a stęp n ie dodaj now ą stronę
u tw orzoną n a podstaw ie szablonu Basic Page i nadaj jej nazw ę MainPage.xaml. N a stęp n ie d o d a j
k o le jn ą s tro n ę Basic Page i n a d a j je j nazw ę QueryDetail.xaml. P rze d przejściem do p u n k tu 2.
nie zapom nij w ybrać z m en u głów nego opcji BUILD/Rebuild Solution .
D o d a j d o p ro je k tu k la s ę Comic .
K lasę Comic poznałeś ju ż kilka stro n w cześniej, a zatem nie w ahaj się i dodaj ją do pro jek tu .
c l a s s Comic {
p u b lic s t r i n g Name { g e t; s e t ; }
p u b lic i n t Is s u e { g e t; s e t ; }
}
D o d a j k la s ę C om icQ uery .
c l a s s ComicQuery {
p u b lic s t r i n g T i t l e { g e t; p riv a te s e t; } Jeśli chcesz, instrukcję using
p u b lic s t r i n g S u b t i t l e { g e t; p r i v a t e s e t ; }
możesz umieścić wewnątrz
deklaracji przestrzeni nazw
p u b lic s t r i n g D e s c rip tio n { g e t; p r i v a t e s e t ; } w pliku .cs.
p u b lic BitmapImage Image { g e t; p r i v a t e s e t ; }
^4 Dodaj klasę menedżera zapytań, aby można było z czymś powiązać kontrolki. ComicQueryManager
AvailableQueries
A plikacja dla Ja n k a będzie działać w edług teg o sam ego schem atu, w edług k tó reg o zostały CurrentQueryResults
napisane dwie p o p rz e d n ie aplikacje dla S klepu W indow s. K lasa Com icQ ueryM anager Title
w ykona wszystkie czynności zw iązane z realizacją zapytań i u d o stę p n i właściwości zaw ierające UpdateAva i lab l eQueries()
zw rócone wyniki. K ażda stro n a X A M L będzie dysponow ać statycznym zasobem zaw ierającym UpdateQueryResultsO
instancję klasy C om icQ ueryM anager, będzie także wywoływać jej m eto d y w celu w ykonania static BuildCatalog()
static GetPrices()
zapytań i zad b a o pow iązanie wyników z k o ntrolkam i.
private LinqM akesQueriesEasy()
f
p u b lic s t r i n S T 1 tle { s e t; } Właściwości CurrentQueryResults eras Title są używane do
wyników zapytania na stronie QueryDetails.
p u b lic ComicQueryM anager() {
U p d a te A v a ila b le Q u e rie s ();
C u rre n tQ u e ry R e su lts = new O b s e rv a b le C o lle c tio n < o b je c t> ();
}
p r i v a t e v o id U p d a te A v a ila b le Q u e rie s () {
A v a ila b le Q u e rie s = new O bservableC o llectio n < C o m icQ u ery > {
new ComicQuery("LINQ u ła tw ia z a p y ta n ia " , " P r o s te z a p y ta n ie " ,
"Pokażmy Jankowi j a k e la s ty c z n a j e s t te c h n o lo g ia LINQ",
Ten inicjalizator C re ate Im ag e F ro m A ssets("p u rp le 2 5 0 x 2 5 0 .jp g " )),
kolekcji tworzy
obiekty ComicQuery,
a przechowywane new C om icQ uery("D rogie kom iksy", "Komiksy powyżej 500 z ł . " ,
w nich informacje "Komiksy o w a rto ś c i p r z e k r a c z a ją c e j 500 z ł . "
zostaną wyświetl°ne + " Ja n e k , może użyć ty c h danych do w yb ran ia n a jb a r d z ie j "
na stronie głównej. + " pożądanych kom iksów .",
C re a te Im a g e F ro m A sse ts("c a p ta in am azing 2 5 0 x 2 5 0 .jp g " )),
};
}
p r i v a t e v o id E xpensiveC om ics() {
Nl LINQ przedstawionych we wcześniejszej części
rozdziału. Zamiast wyświetlać ich wyniki
w oknie wiersza poleceń, są one zapisywane we
IEnumerable<Comic> comics = B u ild C a ta lo g ( );
D ic tio n a r y < in t, d ecim al> v a lu e s = G e tP r ic e s ( ) ; właściwości CurrentQueryResults, w postaci
kolekcji typu ObservableCollection<object>.
v a r m ostE xpensive = from comic in comics Warto jednak dokładniej przyjrzeć się instrukcji
w here v a lu e s [c o m ic .Is s u e ] > 500 new { }. W jakiś sposób słowo kluczowe new
o rd e rb y v a lu e s [c o m ic .Is s u e ] d esc e n d in g zostało zastosowane wewnątrz inicjalizatora
s e l e c t com ic; obiektu. Zazwyczaj po new jest podawana nazwa
typu, jednak w tych instrukcjach została ona
fo re a c h (Comic comic in m ostE xpensive) pominięta, dzięki czemu tworzone są instancje
C u rre n tQ u e ry R e su lts.A d d ( typu anonimowego.
new {
T itle = S trin g.F o rm at("{0 } je s t warty { 1 :c }" ,
comic.Name, values[com ic.Issue]),
Image = CreateImageFromAssets(,,captain_amazing_250x250.jpg"),
}
);
*
690 Rozdział 14.
Przeszukiwanie danych i tworzenie aplikacji przy użyciu LINQ
Poniżej przedstaw iliśm y instrukcję z k o d u zapytania E x p en siv eC o m ics (przedstaw ionego n a p o p rzed n iej stro n ie),
tw orzącą instancje typu anonim ow ego, k tó re n astęp n ie zo stan ą d o d an e do kolekcji przechow yw anej we właściwości
C u rre n tQ u e r y R e s u lts :
new {
Title = String.Format("{0} jest warty {1:c}”,
comic.Name, values[comic.Issue]),
Image = CreateImageFromAssets("captain_amazing_250x250.jpg”),
}
P o uru ch o m ien iu p ro g ra m u m ożesz się p rzek o n ać, że tw orzone w ten sposób obiekty w yglądają zupełnie ta k sam o ja k
wszystkie inne. O to w jak i sposób in stancja typu anonim ow ego będzie p rez en to w a n a w o knie Watch:
v a r myAnonymousObject = new {
Name = "R o b e rt", Więcej informacji na temat typów
Cash = 186.3M, anonimowych możesz znaleźć
Age = 37, w punkcie 9 . dodatku Pozostałości.
};
C onsole.W riteLine(m yA nonym ousO bject.N am e);
Powyższy kod tw orzy instancję typu anonim ow ego, zapisuje referen cję do tego now ego o b iek tu w zm iennej
m yA nonym ousObject i używ a do w yśw ietlenia w artości właściwości Name.
O dszukaj pliki obrazków purple_250x250.jpg o raz captain_amazing_250x250.jpg używ ane w p ro jek cie (są one
d ostępne w przykładach dołączonych do książki, k tó re m ożesz p o b rać z serw era F T P w ydaw nictw a H elion
— ftp:llftp.helion.pllprzykladylcshru3.zip i zapisać je w po danym folderze). N astęp n ie p rzejd ź do o kna Solution
Explorer, kliknij praw ym przyciskiem fo ld er ä , w ybierz z m en u opcję A dd/E xisting Item i dodaj pliki.
A te ra z przyjrzyj się dokładniej m eto dzie C re a te Im a g e F r o m A s s e ts ():
p r i v a t e s t a t i c BitmapImage C re a te Im a g e F ro m A sse ts(strin g im ageFilenam e) {
r e t u r n new Bitmaplmage(new U r i( " m s - a p p x :///A s s e ts /" + im ag e F ilen a m e ));
}
K ażdy plik należący do p ro je k tu m a u n ik ato w ą nazw ę w p rzestrzen i nazw m s-appx. Plik purple_250x250.
jpg um ieszczony w folderze Assets będzie m iał nazw ę ms-appx:///Assets/purple_250x250.jpg. M ożesz użyć tego
identyfikatora, by wczytać zaw artość plik u do o b iek tu B itm apIm age, a już niebaw em p rzek o n asz się,
w jaki sposób m o żn a pow iązać go z k o n tro lk ą <Image> w kodzie X A M L strony.
<G rid G rid.R ow ="l" M argin="120,0" D a ta C o n te x t= "{ S ta tic R e so u rc e ResourceKey=com icQueryM anager}'
< G rid .R o w D e fin itio n s>
< R ow D efinition H eig h t= "A u to "/> < R o w D efin itio n /> Nic nie stoi na przeszkodzie,
< /G rid .R o w D e fin itio n s> *y .w Jednym w'f rszU umi%szczać
<T extB lock S ty le = " { S ta tic R e s o u rc e S u b h e a d e rT e x tS ty le } "
T ext="Q uery r e s u l t s " M a rg in = " 1 0 ,0 ,0 ,2 0 " />
< L istV iew Grid.Row="1" M a rg in = " 0 ,-1 0 ,0 ,0 " Item sS o u rce= "{ B in d in g C u rre n tQ u e ry R e su lts} "
Ite m T e m p la te = "{ S ta tic R e so u rc e S tan d ard 1 3 0 Ite m T em p late} " S electionM ode="N one"/>
< /G rid>
K iedy stro n a głów na wywołuje m e to d ę F r a m e .N a v ig a te ( ) , by p rzejść n a stro n ę szczegółów, p rzek azu je do niej jako
p a ra m e tr o b iek t ComicQuery. D o stęp do tego p a ra m e tru m o żn a uzyskać, przesłan iając w kodzie ukrytym m eto d ę
O n N av ig ated T o () i p o b ierając o b iek t przy użyciu w yrażenia e .P a r a m e te r :
s t r in g [ ]
sandwiches = { "szynka i s e r " , "salami z majonezem",
"indyk i se r s z w a jc a rs k i", " k o t le t z kurczaka" };
var sandwichesOnRye =
from sandwich in sandwiches
fodci^ j ańcUch znaków „na chlebie
se le ct sandwich + 11 na chlebie zbożowym"; zb^oiwym do każdego elementu
wynikowego zapytania.
foreach (var sandwich in sandwichesOnRye)
Console.W riteLine(sandwich); Ta zmiana wprowadzana
jest do elementów
wynikowych zapytania,
Zwróć uwagą na to, ż e na końcu nie do elementów
zwróconych elementów dodano Wynik: w oryginalnej kolekcji
„na chlebie zbożowym szy n k a i s e r n a c h l e b i e zbożowym lub bazie danych.
s a la m i z m ajo n ezem n a c h l e b i e zbożowym
in d y k i s e r s z w a j c a r s k i n a c h l e b i e zbożowym
k o tle t z k u r c z a k a n a c h l e b i e zbożowym
http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b
i Nie. istnieją.
głupie pytania
P: : Za dużo jest tych nowych słów operacji jednocześnie. Przyjrzyjmy się dokładniej łączone, aby Twój kod mógł ich użyć.
kluczowych — from, where, orderby, poniższemu zapytaniu. To dlatego LINQ wygląda nieco dziwnie
select... Wygląda to na całkiem inny język. v a r under10 = — C# musi umieścić wiele możliwości
Dlaczego tak się on różni od pozostałej
from number in num berArray w niewielkim fragmencie kodu.
części C#?
where number < 10
O : Ponieważ inny jest jego cel. Większa s e le c t number; LINQ pozwala Ci
część składni C# została zaprojektowana do
wykonywania jednocześnie małej liczby operacji
Wygląda prosto — nie ma tu wielu rzeczy,
prawda? W rzeczywistości jest to dość
pisać zapytania,
lub obliczeń. Możesz rozpocząć pętlę, ustawić
zmienną, obliczyć matematyczne wyrażenie lub
złożony fragment kodu. Pomyśl, co musi które wykonują
się stać w programie, abyś mógł wybrać
wywołać metodę... Wszystko to są pojedyncze z numberArray wszystkie liczby, które są skomplikowane
operacje. mniejsze niż 10. Po pierwsze, musisz przejrzeć
LINQ wygląda nieco inaczej, ponieważ jego całą tablicę. Potem każda liczba porównywana operacje, używając
pojedyncze zapytanie zwykle wykonuje wiele jest z 10. Na końcu wszystkie te wartości są
niewielkiej ilości kodu.
jesteś tutaj ► 695
Rozszerz aplikację Janka To jest przyktad separacji zagadnień. Możesz
modyfikować zawartość obiektu ComicQueryManager
bez wprowadzania zmian w kodzie XAML oraz
w kodzie_krytym, gdyż kod zapytań LINQ
Dodaj nowe zapytania do aplikacji Janka jest w nim dobrze hermetyzowany.
p r i v a t e v o id U p d a te A v a ila b le Q u e rie s () {
A v a ila b le Q u e rie s = new O bservableC o llectio n < C o m icQ u ery > { 4- ^
new ComicQuery("LINQ u ła tw ia z a p y ta n ia " , " P r o s te z a p y ta n ie " , Z r ó b to !
"Pokażmy Jankowi ja k e la s ty c z n a j e s t te c h n o lo g ia LINQ",
C re a te Im a g e F ro m A sse ts("p u rp le _ 2 5 0 x 2 5 0 .jp g ")),
t *
new C om icQ uery("D rogie kom iksy", "Komiksy powyżej 500 z ł . " ,
"Komiksy o w a rto ś c i p r z e k r a c z a ją c e j 500 z ł . "
+ " Jan ek może użyć ty c h danych do w y b ran ia n a jb a r d z ie j "
+ " pożądanych kom iksów .",
C re a te Im a g e F ro m A sse ts("c a p ta in _ a m a z in g _ 2 5 0 x 2 5 0 .jp g ")),
T eraz musisz zm odyfikować instrukcję s w itc h , żeby wykonywać zapytania, kiedy zostaną zaznaczone w kontrolce L istV iew :
p u b lic v o id U pdateQ ueryR esults(C om icQ uery q u ery ) {
T i t l e = q u e r y .T i t l e ;
sw itc h ( q u e r y .T i tl e ) {
c a se "LINQ u ła tw ia z a p y ta n ia " : L inq M ak esQ u eriesE asy (); b re a k ;
c a se "D rogie kom iksy": E x p en siv eC o m ics(); b re a k ;
case "LINQ je s t wszechstronne 1": L1nqIsV ersat1lel(); break; Dodaj te trzy k/auzu/e case
case "LINQ je s t wszechstronne 2": L1nqIsVersat1le2(); break; do instrukcji switch. Będą
one wykonywane przecz strrnę
case "LINQ je s t wszechstronne 3 ": L1nqIsVersat1le3(); break prezentującą inform aj s.zc.zego^n,
} ; kiedy użytkownik ją wyświet/i.
}
Musisz także dodać trzy poniższe m etody. Porów naj je z zapytaniam i L IN Q przedstaw ionym i n a dwóch poprzednich stronach:
p r i v a t e v o id L in q I s V e r s a tile 1 ( ) {
s t r i n g [ ] sandw iches = { "szynka z se re m ", "salam i z m ajonezem ",
"in d y k z m u s z ta rd ą " , " k o t l e t z k u rczak a" };
v a r sandwichesOnRye =
from sandw ich in sandw iches
s e l e c t sandw ich + " na c h le b ie zbożowym";
p r i v a t e v o id L in q I s V e r s a tile 2 ( ) {
Będziesz musiał znaleźć ten plik
Random random = new Random(); w przykładach dołączonych do książki
L is t< in t> listO fN u m b ers = new L is t < i n t> ( ) ; i skopiować go do folderu Assets.
i n t le n g th = ran d o m .N ex t(5 0 , 150);
f o r ( i n t i = 0 ; i < le n g th ; i++)
list0 fN u m b e rs.A d d (ra n d o m .N e x t(1 0 0 ));
p r i v a t e v o id L in q I s V e r s a tile 3 ( ) {
L is t< in t> listO fN u m b ers = new L is t< in t> ( ) Z r ó b to!
f o r ( i n t i = 1 ; i <= 10 0 0 0 ; i++)
listO fN u m b e rs.A d d (i); Aby powyższy kod zadziałał, musisz dodać do
Ćwiczenie niego jeszcze jedną metodę. Każda z nowych
metod LinqIsV ersatile wywołuje metodę
v a r u n d e r5 0 s o rte d =
o nazwie CreateAnonymousListViewItem(). Jej pierwszym
from number in listO fN u m b ers parametrem jest tytuł, który powinien zostać użyty jako
w here number < 50 wartość właściwości T itle nowego obiektu anonimowego.
o rd e rb y number d esc e n d in g Drugi parametr jest opcjonalny. Jest nim nazwa pliku
s e l e c t number; obrazka, który należy wczytać do właściwości Image
obiektu anonimowego, a jej domyślną wartością ma być
v a r f i r s t F i v e = u n d e r5 0 s o rte d .T a k e (6 ) ; purpie_250x250.jpg. Należy jej użyć, jeśli nie zostanie jawnie
podana w wywołaniu. Czy potrafisz napisać kod tej metody?
L is t< in t> s h o r t L i s t = f i r s t F i v e . T o L i s t ( ) ;
Odpowiedź znajdziesz na następnej stronie.
fo re a c h ( i n t n in s h o r t L i s t ) _______________________
C u rren tQ u e ry R esu lts.A d d (C re a te A n o n y m o u sL istV ie w Ite m (n .T o S trin g () z ll
"b lu e o ra v 250x250
C E L N E S P O S T R Z E Ż E N IA
■ fro m pozw ala Ci w skazać sekw encję ■ s e l e c t ok reśla, co znajdzie się w zbiorze
IE num erable<T >, n a któ rej w ykonujesz wynikowym ( s e l e c t v a lu e ).
zapytanie. P o niej zawsze w ystępuje nazw a ■ T a k e () pozw ala Ci p o b ra ć początkow e elem enty
zm iennej, p o te m 1 n , a n a stęp n ie nazw a sekw encji z w yniku zapytania L IN Q ( r e s u l t . T a k e ( 1 0 ) ) .
(fro m v a lu e in v a lu e s ). L IN Q u d o stę p n ia Ci tak że inne m etody
■ w h ere g en eraln ie łączy się z frazą from . T o w tym dla każdej sekw encji: M in (), M ax(), Sum()
m iejscu używasz zwykłych w arunków C # w celu i A v e ra g e ().
p rzek azan ia L IN Q , k tó re elem en ty pow inny ■ M ożesz użyć s e l e c t w odniesieniu do wszystkiego
zostać p o b ra n e z kolekcji (w here v a lu e < 1 0 ). — nie jesteś ograniczony do w yboru nazwy, którą
■ o rd e rb y pozw ala Ci posortow ać wyniki. nadałeś w e frazie from. O to przykład: jeżeli
Z araz po nim um ieszczasz kryteria używane Tw oje zapytanie L IN Q p o b iera zestaw cen
do sortow ania i opcjonalne słowo descend'jng , z tablicy i n t i w e frazie from nazwiesz je
które nakazuje odw rócić jego p o rząd ek (o rd e rb y v a lu e , m ożesz zwrócić ich kolekcję w postaci
v a lu e d e sc e n d in g ). łańcuchów znaków w sposób następujący:
To jest tak jak z {0:x}, którego utywateś w rozdziale 9. s e l e c t S t r i n g .F o r m a t ( " { 0 : c } " , v a lu e ) .
podczas tworzenia programu do wyświetlania postaci
szesnastkowej pliku. Istnieje także {0:d} i {°-D} dla krótkiego
i długiego formatu daty oraz {°.P} i {°:Pn} do wypisywania
wartości w procentach (z liczbą n miejsc po przecinku).
i Nie. istnieją.
głupie pytania
P Jak działa fraza
: from? Taka pętla foreach tymczasowo tworzy P: : W jaki sposób LINQ decyduje,
co umieścić w zbiorze wynikowym?
O : Zachowuje się ona jak pierwszy wiersz
zmienną o nazwie i , do której kolejno
Magnesy LIN Q
Poprzestawiaj magnesy tak, aby po uruchomieniu
c
programu pokazany został tekst zaprezentowany
na dole strony.
pigeon descending
]
Console.WriteLineÇ'Ruszaj przed siebie drogą numer {0}", \
[ wease1s.Sum() |
weasels.Sum()
1 Po zsumowaniu liczb
45+12+9 = 66
W ynik:
Ruszaj przed s i e b i e drogą numer 66
LINQ może połączyć Twoje wyniki wgrupy Testując aplikację sym ulatora ula, m ożesz
przekonać się, ja k to zapytanie LIN Q działa
w praktyce (a jednocześnie dowiedzieć się
Już wiesz, że m ożesz użyć L IN Q w celu p o g ru p o w an ia wyników, czegoś w ięcej o sposobie działania aplikacji
poniew aż uczyniliśm y to w sym ulatorze ula. Przyjrzyjm y się W in Fo rm s). Sym ulator ula zna jd ziesz
dokładniej tem u zapytaniu i zobaczm y, jak o n o działa. w przykładach dołączonych do książki.
select beeGroup; T eraz, gdy już m am y grupy, m ożem y nim i m anipulow ać.
W związku z tym , że zw racam y kolekcję grup, m ożem y
użyć słowa kluczow ego o rd e r b y , aby poso rto w ać je
w edług w artości typu w yliczeniow ego C u r r e n t S t a t e
( I d l e , F ly in g T o F lo w e r itp.). o rd e rb y beeG roup.K ey
nak azuje up o rząd k o w ać sekw encję gru p n a podstaw ie
N astęp n ie m usim y skorzystać ze słow a kluczow ego ich klucza w p o rz ąd k u rosnącym . Poniew aż
s e l e c t , aby w skazać, co m a zostać zw rócone pogrupow aliśm y pszczoły w edług C u r r e n t S ta t e ,
z zapytania. Z w racam y grupy, w ięc w ybierzem y to w łaśnie to p o le zostanie użyte jak o klucz.
nazw ę grupy: s e le c t b e e G ro u p ; .
eElekcja
5
jesteś tutaj ► 701
K lu c z d o s u k c e s u
(JO K a ż d a g ru p a potrzebuje klu cza — u żyjem y w te* roli typu w yliczeniow ego.
Klucz grupy to coś wspólnego dla wszystkich je j składowych. Może on mieć dowolną postać: łańcucha znaków,
liczby, a nawet refere nq i obiektu. Poszukujemy cen, które Janek uzyskał z listy Grzegorza. Każda grupa zwrócona
przez zapytanie będzie sekwencją numerów komiksów. Jej kluczem będzie typ wyliczeniowy PriceR ange. M etoda
Eval u a te P r i ce () pobiera cenę w postaci param etru i zwraca P ri ceRange:
public enum PriceRange { Cheap, Midrange, Expensive } Spróbuj dodać ten kod do nowej aplikacji
public static PriceRange EvaluatePrice(int price) { konsolowej — przekonajmy się,
if (price < 100M) return PriceRange.Cheap; czy będziesz w stanie go uruchomić!
else if (price <= 1000M) return PriceRange.Midrange; Pod koniec rozdziału dodasz go do
else return PriceRange.Expensive; nowej aplikacji dla Sklepu Windows.
}
M o żem y te ra z p o g ru p o w ać kom iksy n a p o d staw ie kryterium cen o w eg o .
Zapytanie L IN Q zwraca sekw encję sekw encji . Każda grupa w zbiorze wynikowym posiada właściwość Key,
która odpowiada wartości typu P riceR ange zwróconej przez E v a lu a t e P r ic e ( ) . Przypatrz się dokładnie frazie group by
— pobieramy ze słownika pary i używamy nazwy p a i r dla każdej z nich: p a ir.K e y jest numerem komiksu, p a ir .V a lu e
jest ceną z listy Grzegorza. Dodanie group p a ir.K e y nakazuje L IN Q utworzyć kolekcje numerów, a następnie połączyć
je na podstawie kategorii cenowej zwróconej przez E v a lu a t e P r ic e ( ) :
Dictionary<int, decimal> values = GetPrices();
Zapytanie określa, do której grupy
należy dany egzemplarz, przekazująca
var priceGroups =
from pair in values
do EvaluatePrice() j ego cenę. Metoda
group pair.Key by EvaluatePrice(pair.Value)
zwraca typ wyliczeniowy ^ c e R a n ^
in t o priceGroup który jest używany jako klucz grupy.
orderby priceGroup.Key descending
select priceGroup;
Zagadkowy basen
Tw oim z a d a n ie m je st p o b ra n ie fragm entów kodu
z basen u i w staw ienie ich w p u ste m iejsca var
w kodzie. M ożesz użyć tego sam ego from
frag m en tu więcej niż raz i nie musisz
l i n e by l i n e .
w ykorzystać ich wszystkich. C e le m jest
n apisanie k o d u , k tóry wypisze taki oto in to wordGroups
komunikat: o rd e rb y _________
s e l e c t __________
H o r s e s e n j o y e a t i n g c a r r o t s , b u t t h e y lo v e e a t i n g a p p le s .
= w ords. ( 2 );
c l a s s L in e {
p u b lic s t r i n g [ ] Words;
p u b lic i n t V alue; fo re a c h (v a r group in twoGroups)
p u b lic L in e ( s tr i n g [ ] Words, i n t V alue) {
{
th is .W o rd s = Words; th i s .V a lu e = V alue;
I in t i = 0 ;
Podpowiedz: LINQ sortuje łańcuchy
I znaków w porządku alfabetycznym. fo re a c h (_________ in n e r in __ ) {
i++;
L in e[] l i n e s = {
new L in e( new s t r i n g [ ] { " e a t in g " , " c a r r o t s , " , if (i == .Key) {
" b u t" , " e n jo y " , "H orses" }, 1 ), v a r poem =
new L in e( new s t r i n g [ ] { " z e b r a s ? " , "h ay ",
word in
"Cows", " b r i d g e ." , " b o lte d " }, 2 ) ,
new L in e( new s t r i n g [ ] { " f o r k " , "d o g s !" , _ word d e sc e n d in g
"E n g in e", "and" }, 3 ) , word + ;
new L in e( new s t r i n g [ ] { " lo v e " , " th e y " ,
fo re a c h (v a r word in
" a p p l e s ." , " e a tin g " }, 2 ) ,
new L in e( new s t r i n g [ ] { " w h is tle d ." , "Bump" }, 1) }; C o n so le .W rite (w o rd );
Przypominamy:
każdy fragm ent kodu
z basenu może zostać
użyty więcej niż raz.
c la s s Line {
p u b lic s t r i n g [ ] Words;
p u b lic in t V alue;
p u b lic L in e ( s tr in g [ ] Words, i n t V alue) {
th is.W o rd s = Words; th i s .V a lu e = V alue;
}
}
L ine[] lin e s = {
new L ine( new s t r i n g [ ] " e a tin g " , " c a r r o t s , " , " b u t" , " e n jo y " , "H orses" } , 1 ),
new L ine( new s t r i n g [ ] " z e b ra s ? " , "h ay ", "Cows", " b r id g e ." , "b o lte d " } , 2 ) ,
new L ine( new s t r i n g [ ] " f o rk " , " d o g s !", "E n g in e", "and" }, 3 ) ,
new L ine( new s t r i n g [ ] " lo v e " , " th e y " , " a p p l e s ." , " e a tin g " }, 2 ) ,
new L ine( new s t r i n g [ ] " w h is tle d ." , "Bump" }, 1 )
};
v a r words =
from line in lines To_pierwsze zapytanie LINQ dzieli
obiekty Line w tablicy lines!) na grupy
group l i n e by l i n e . Value na podj tawie ich pola Value, w Fosnlcym
in to wordGroups porządku klucza Value. y
ord erb y wordGroups. Key
s e l e c t wordGroups;
i f (i == group.Key) {
v a r poem =
from word in inner. Words
orderby word d escending Czy zwróciłeś uwagę na
select word + " " ; to, że dwie frazy: „Horses
enjoy eating carrots, buf
fo reac h (v a r word in poem) i „they love eating apples",
C o n so le .W rite (w o rd ); posortowane są w kolejności
odwrotnej do alfabetycznej?
}
Wynik: Horses enjoy eating carrots, but they love eating apples.
Q
jest n a podstaw ie wyników pow stałych z p o łączen ia kolekcji. }
P ole to będzie używ ane w e frazie w h ere.
[n N A JP IE R W JA N E K U T W O R Z Y Ł K O L E K C JE D O P O Ł Ą C Z E N IA .
Ja n e k m a już je d n ą kolekcję — użył m eto d y B u ild C a ta lo g ( ) napisanej nieco wcześniej.
T eraz m usi tylko n apisać m eto d ę F in d P u r c h a s e s ( ) , k tó ra utw orzy listę obiektów P u rc h a se .
g T E R A Z M O Ż E JE P O Ł Ą C Z Y Ć !
Już w idziałeś wszystkie części tego zap y ta n ia ... O to i one, tym razem w jednym kawałku.
pieniędzy i jak To zapytanie tworzy listę obiektów Purchase zawierających zakupy Janka i porównujeje z cenami z Listy Grzegorza.
dużo są warte
kupione przez
niego komiksy?
■Nie .istnieją.
głupie pytania
Oto kod, który musisz dodać do aplikacji Janka, by pojawiały się w niej dwa ostatnie zapytania LINQ.
Rozwiązania
ćwiczenia
p r i v a t e v o id U p d a te A v a ila b le Q u e rie s () {
A v a ila b le Q u e rie s = new O bservableC o llectio n < C o m icQ u ery > {
new ComicQuery("LINQ u ła tw ia z a p y ta n ia " , " P r o s te z a p y ta n ie " ,
"Pokażmy Jan k o w i, j a k e la s ty c z n a j e s t te c h n o lo g ia LINQ",
C re a te Im a g e F ro m A sse ts("p u rp le _ 2 5 0 x 2 5 0 .jp g ")),
new C om icQ uery("D rogie kom iksy", "Komiksy powyżej 500 z ł . " ,
"Komiksy o w a rto ś c i p r z e k r a c z a ją c e j 500 z ł . "
+ " Jan ek może użyć ty c h danych do w y b ran ia n a jb a r d z ie j "
+ " pożądanych kom iksów .",
C re a te Im a g e F ro m A sse ts("c a p ta in _ a m a z in g _ 2 5 0 x 2 5 0 .jp g ")),
c l a s s P u rch ase {
p u b lic i n t Is s u e { g e t ; s e t ; }
p u b lic decim al P ric e { g e t ; s e t ; }
p r i v a t e v o id J o in P u rc h a s e s W ith P ric e s () {
IEnumerable<Comic> com ics = B u ild C a ta lo g ( );
D ic tio n a r y < in t, d ecim al> v a lu e s = G e tP r i c e s ( ) ;
IE num erable<P urchase> p u rc h a s e s = P u rc h a s e .F in d P u rc h a s e s ();
var re s u lts =
from comic in com ics
j o i n p u rc h a s e in p u rc h a se s
on c o m ic .Is s u e e q u a ls p u r c h a s e .I s s u e
o rd e rb y c o m ic .Is s u e a sc e n d in g
s e l e c t new {
Comic = com ic,
P ric e = p u r c h a s e .P r ic e ,
T i t l e = comic.Name,
S u b t i t l e = "Numer " + c o m ic .Is s u e ,
D e s c rip tio n = S trin g .F o rm a t("K u p io n y za { 0 :c } " , p u r c h a s e .P r ic e ) ,
Image = C re a te Im a g e F ro m A sse ts("c a p ta in _ a m a z in g _ 2 5 0 x 2 5 0 .jp g "),
};
decim al g re g s L is tV a lu e = 0;
decim al to t a lS p e n t = 0;
fo re a c h (v a r r e s u l t in r e s u l t s ) { ComicQueryManager, a zatem
ten wiersz kodu zmieni go na
g re g s L is tV a lu e += v a lu e s [ r e s u lt .C o m ic .I s s u e ] ; komunikat informujący Janka,
to t a lS p e n t += r e s u l t . P r i c e ; ile pieniędzy wydał oraz ile
C u rre n tQ u e r y R e s u lts .A d d ( re s u lt); są warte kupione przez niego
komiksy.
}
K o ntrolka zoom u
semantycznego © Wszystkie komKX^ kolekcji
e szczegółowe lub listę
pozwala wyświetlać WytonaJ g e s t uszczypnięcia, by wys«n
W ięcej in fo rm acji o w yko rzysta n iu sem antycznego pow iększenia w aplikacjach możesz znaleźć
na stronie: http://m sdn.m icrosoft.com /pl-pl/library/w indow s/apps/hh465319.aspx.
Poniżej przedstaw iliśm y podstaw ow y szablon k o d u X A M L k o n tro lk i pow iększenia sem antycznego.
W w idoku pom niejszonym używ ana je st k o n tro lk a L istV ie w lub G ridV iew , je d n a z tych dwóch
k o n tro lek używ ana je st także do o k reślen ia po staci w idoku pow iększenia.
J D O D A J N O W Y E L E M E N T D O S T R O N Y G Ł Ó W N E J.
Ja n e k p o trzeb u je czegoś, co m ógłby kliknąć; dlatego pierw szą rzeczą, k tó rą zrobisz, b ędzie d o d an ie now ego
e lem en tu , zw racającego w szystkie kom iksy w kolekcji. N a p o czątk u dodaj do klasy Com icQ ueryM anager m etodę,
k tó ra pozw oli wyświetlić w szystkie komiksy:
p r i v a t e v o id A llC o m ics() {
fo re a c h (Comic comic in B u ild C a ta lo g ( )) {
v a r r e s u l t = new {
Image = C re ate Im ag eF ro m A ssets("cap tain _ am azin g _ zo o m _ 2 5 0 x 2 5 0 .jp g "),
T i t l e = comic.Name,
S u b t i t l e = "Numer " + c o m ic .Is s u e ,
D e s c rip tio n = "K ap itan W spaniaJy k o n tra " + c o m ic .M a in V illa in ,
Comic = com ic,
};
C u rre n tQ u e r y R e s u lts .A d d ( re s u lt);
}
}
N astęp n ie do instrukcji s w itc h um ieszczonej w m eto d zie U p d a te Q u e ry R e s u lts () dodaj kolejną klauzulę c a se :
A by zakończyć te n e ta p p rac, dodaj nowy o b iek t Com icQuery do in icjalizatora kolekcji um ieszczonego w m etodzie
U p d a te A v a il a b le Q u e r ie s ( ) . O prócz tego będziesz m usiał d o d ać do fo ld eru Assets/ p lik captain_amazing_
zoom_250x250.jpg.
new C om icQ uery("W szystkie komiksy w k o l e k c j i " , Znaj <iź ten ph'k
"Zobacz w s z y s tk ie komiksy w k o l e k c j i " , w przyktad?ch
dołączonych do książki.
"To z a p y ta n ie zw raca w s z y s tk ie kom iksy",
C reate Im ag eF ro m A sse ts("ca p ta in _ am az in g _ z o o m _ 2 5 0 x 2 5 0 .jp g ")),
[a D O D A J W IĘ C E J W Ł A Ś C IW O Ś C I D O K L A SY C O M IC .
Stosow anie zoom u sem antycznego m a sens wyłącznie w przypadkach, gdy dysponujem y inform acjam i szczegółowymi,
które będziem y mogli wyświetlić w w idoku pow iększenia. T akże w tym w idoku p rezentow ane b ęd ą obiekty Comic
p obierane z kolekcji C o m ic Q u ery M a n a g e r.C u rre n tQ u e ry R esu lts, musim y zatem jedynie dodać odpow iednie
inform acje od klasy Comic i upew nić się, że zostaną one odpow iednio pow iązane w w idoku szczegółów.
u sin g W indow s.U I.X am l.M edia.Im aging;
c l a s s Comic {
p u b lic s t r i n g Name { g e t; s e t ; }
p u b lic i n t Is s u e { g e t; s e t ; }
p u b lic i n t Y ear { g e t ; s e t ; }
p u b lic s t r i n g C o v e rP rice { g e t; s e t ; }
p u b lic s t r i n g S y n o p sis { g e t; s e t ; }
p u b lic s t r i n g M a in V illa in { g e t; s e t ; }
p u b lic BitmapImage Cover { g e t; s e t ; }
}
714 Rozdział 14.
Przeszukiwanie danych i tworzenie aplikacji przy użyciu LINQ
^ D O D A J S Z C Z E G Ó Ł O W E D A N E O K O M IK S A C H .
Z m odyfikuj m eto d ę B u il d C a t a lo g ( ) , do d ając do niej szczegółowe inform acje o wszystkich kom iksach. B ędziesz
także m usiał d odać do fo ld eru Assets o brazki p rzedstaw iające okładki poszczególnych kom iksów . M ożesz je znaleźć
w przykładach dołączonych do książki.
new Comic { Name = "Rock and R o ll (e d ycja lim ito w a n a )", Issu e = 19, Year = 1957, C o verP rice = "10 groszy"
M a in V illa in = "Doctor V o rtra n ", Cover = CreateIm ageFrom Assets("Captain Amazing Issu e 19 c o v e r.p n g "),
Synopsis = "Doktor Vortran s ie j e sp u sto szen ie wśród m łodzieży przy u życiu swego radiowego u rz ą d z e n ia ,1
+ " któ re k o rzy sta z najnowszego tanecznego sza le ń stw a , by wprowadzać fanów r o c k 'n 'r o lla "
+ " w niekontrolowany t r a n s ." } ,
new Comic { Name = "Woman's Work", Issu e = 36, Year = 1968, C o verP rice = "12 g ro sz y ",
M a in V illa in = "H y ste ria n n a ", Cover = CreateIm ageFrom Assets("Captain Amazing Issu e 36 c o v e r.p n g "),
Synopsis = "Kapitan s t a je tw arzą w tw arz ze swym pierwszym wrogiem p łc i ż e ń s k ie j, H y ste ria n n ą ,"
+ " k tó re j niesam ow ite, te le p a ty czn e i te le k in e ty c z n e zdoln o ści pozw alają powołać arm ię"
+ " k o b ie t, j a k i e j nawet Kapitan będzie m iał problemy s p ro s ta ć ." } ,
new Comic { Name = "H ippie Madness (ż le wydrukowany)", Issu e = 57, Year = 1973, C o verP rice = "20 g ro s z y ",
M a in V illa in = "Mayor", Cover = CreateIm ageFrom Assets("Captain Amazing Issu e 57 c o v e r.p n g "),
Synopsis = "A pokalipsa zombie zagraża is t n ie n iu Obiektowa, gdyż Mayor u s ta w ił w ybory,"
+ " wprowadzając agenta zombie do firm y d o s ta rc z a ją c e j papierosy całemu m ia stu ." } ,
new Comic { Name = "Revenge o f the New Wave Freak (uszko d zo n y)", Issu e = 68, Year = 1984,
C o verP rice = "75 g ro s z y ", M a in V illa in = "S w in d le r",
Cover = CreateIm ageFrom Assets("Captain Amazing Issu e 68 c o v e r.p n g "),
Synopsis = "Zanieczyszczony tu sz do powiek zm ienia D r. A lv in a Mudda w nowe nemezis K ap itana, "
+ " wprowadzając postać K an ciarza do komiksów o K a p itan ie Wspaniałym." } ,
new Comic { Name = "B la ck Monday", Issu e = 74, Year = 1986, C o verP rice = "75 g ro s z y ",
M a in V illa in = "Mayor", Cover = CreateIm ageFrom Assets("Captain Amazing Issu e 74 c o v e r.p n g "),
Synopsis = "Mayor w raca, by doprowadzić Obiektowo do finansowego kryzysu przez w yko rzystan ie"
+ " swych mocy do tw o rzenia zombie przeciw ko G ie łd z ie Obiektowa." } ,
new Comic { Name = " T rib a l Tattoo Madness", Issu e = 83, Year = 1996, C o verP rice = "Dwa z ło t e " ,
M a in V illa in = "Mokey Man", Cover = C reateIm ageFrom Assets("Captain Amazing Issu e 83 c o v e r.p n g "),
Synopsis = "Monkey Man - p rz e ra ż a ją c y czło w iek małpa - ucieka ze swego w ię z ie n ia na wyspie i wraz"
+ " z grupą wytatuowanych cyrkowych pomocników s ie j e sp u sto szen ie przy u życiu śm iertelnego "
+ " prom ienia brudu." } ,
new Comic { Name = "The Death o f an O b je c t", Issu e = 97, Year = 2013, C o verP rice = "C zte ry z ło t e " ,
M a in V illa in = "S w in d le r", Cover = CreateIm ageFrom Assets("Captain Amazing Issu e 97 co v e r.p n g "),
Synopsis = "Armia klonów K an ciarza a ta k u je Obiektowo w d e sp e ra ck ie j próbie zła p a n ia i z a b ic ia "
+ " Kapitana Wspaniałego. Czy naukowcom z Obiektowa uda s ię p rzyw ró cić go do ż y c ia ? " } ,
};
} Przewróć kartkę, by dokończyćaplikację ----------------►
jesteś tutaj > 715
Zajm ujem y się szczegółami
O d o d a j p l ik k o d u u k r y t e g o d l a n o w e j s t r o n y .
W pliku QueryDetailZoom.xaml.cs będziesz m usiał um ieścić d okładnie tę sam ą m eto d ę O n N a v ig a te d T o ():
U T W Ó R Z K O D X A M L N O W E J S T R O N Y W ID O K U S Z C Z E G Ó Ł Ó W .
O to kolejna rzecz, któ rą musisz zrobić: utworzyć kod X A M L strony QueryDetailZoom.xaml, zawierający kontrolkę
zoom u sem antycznego wyświetlającego szczegółowe inform acje o komiksach. To największa strona, jaką do tej pory
stworzyłeś, dlatego aby ułatwić Ci zrozum ienie, co się w niej dzieje, zamieściliśmy jej kod aż n a dwóch stronach książki.
W widoku <SemanticZoom.ZoomedOutView>
pomniejszenia <L istV iew Item sS o u rce= "{ B in d in g C u rre n tQ u e ry R e su lts} " M a rg in = " 0 ,0 ,2 0 ,0 "
używamy kontrolki
ListView, dokładnie Ite m T e m p la te = "{ S ta tic R e so u rc e S tan d ard 5 0 0 x l3 0 Item T em p late}"
takiej samej jak na
SelectionM ode="N one" />
stronie QueryDetail.
</SemanticZoom.ZoomedOutView>
<SemanticZoom.ZoomedInView>
<GridView Item sS o u rce= "{ B in d ing C u rre n tQ u e ry R e su lts} "
M a rg in = " 0 ,0 ,2 0 ,0 " SelectionM ode="N one" x :N am e= "detailG ridV iew ">
<G ridV iew .Item T em plate>
<D ataTem plate>
<G rid H eight="780" W idth="600" M argin="10">
W widoku powiększenia Szablon danych kontrolki
została zastosowana < G rid .C o lu m n D e fin itio n s> GridView działa dokładnie
kontrolka GridView. tak samo jak w kontrolce
< C o lu m n D efin itio n W idth="A uto"/> ListView. Jeśli musisz
Jej szablonem
danych jest siatka < C o lu m n D efin itio n /> przypomnieć sobie,
zawierająca kontrolkę jak działają te szablony,
< /G rid .C o lu m n D e fin itio n s > zajrzyj do rozdziału 10.
Image, w której
będzie prezentowana
okładka komiksu oraz
StackPanel zawierająca <Image S o u rce= "{B in d in g C om ic.Cover}" M a rg in = " 0 ,0 ,2 0 ,0 "
kontrolki TextBlock S tre tc h = " U n ifo rm T o F ill" W idth="326" H eight="500"
prezentujące wartośd
właściwości. V e rtic a lA lig n m e n t= "T o p "/>
Przewróć kartkę, aby zobaczyć pozostałą część kodu XAML kontrolki SemanłicZoom.-- - - - - - - - - - - - ►
jesteś tutaj ► 717
J a n e k je s t z a c h w y c o n y
T O JEST N AJLEPSZA R Z E C Z , JA K A
M I SIĘ P R Z Y D A R Z Y Ł A , O D C Z A S U G DY
N A U L IC Z N E J W Y P R Z E D A Ż Y Z N A L A Z Ł E M
E G Z E M P LA R Z K O M IK S U N U M E R 2 3 Z EDYCJI
L IM IT O W A N E J, I T O Z A JEDYN E PIĘĆ Z Ł O T Y C H !
Z m ień nazwę aplikacji n a Komiksy Janka, w prowadzając odpow iednią zm ianę w zasobie AppName.
W projektach utworzonych w edług szablonu Split App zasób AppName je s t zde finiow a ny w p lik u App.xamJ \
Solution Explorer ^ □X
o (2 ’o - ć w & m <> Ap
Search Solution Explorer (Ctrl-*-;} P '
^ Solution KomiksyJankaSplrtApp' (1project)
a |c5] KomiksyJankaSplrtApp
> /■ Properties
> ■■References
> y Assets
> y Common
a y DataModel
D Appjraml
> .Ç) ItemsPage.xaml
ÂÜ KomiksyJankaSplitApp_TemporaryKey.pfx
Kliknij je d e n z elem entów , by przejść n a drugą, dw uczęściow ą stro n ę szczegółów: fel Package.appxmanifest
SplitPage.xaml
p Title: 3
n m U n iK ta n M u r u u p m i i n n i r l t ner
«emvurrt A» ocło <r\ tu * * Cm ponutir. « w
armOKnpKm p o u m iu * » h
Solution Explorer ▼ n x
tŁ T0 - »? Cl fl ;ä ) : 1
[J Co py Ctrl+C
t il Paste Ctrf+V
X Delete Del
£::: Renam e F2
f* Properties
U tw ó rz kla sę ComicQueryM anager. K iedy tworzysz klasę w folderze, ID E autom atycznie w ygeneruje ją i um ieści
w przestrzeni nazw zaw ierającej nazw ę folderu:
O f2t o - i? » □ ® <> P P
Skopiuj zaw artość klasy Com icQ ueryM anager z poprzed n iej,
Search Solution Explorer (C trl+ ;) p -
działającej aplikacji Ja n k a i w klej do nowej klasy, utw orzonej
Egl Solu tion 'K o m iksyJan kaSp lrtA pp ' (1 project)
w folderze DataModel. U pew nij się, że klasa ta należy do
a [c«] K o m ik s y J a n k a S p litA p p
p rzestrzen i nazw K o m ik sy Ja n k a S p litA p p .D a ta M o d e l, nie > Properties
P ric e R a n g e . W szystkie pow inny się znaleźć w folderze > C* C o m icQ u e ry.cs
> S 3 C o m icQ u eryM a n a g er.e s
DataModel, a to oznacza, że tak że o n e pow inny należeć do tej > C* PriceR a n g e.cs
sam ej przestrzen i nazw K o m ik sy Ja n k a S p litA p p .D a ta M o d e l. > C* P u rc h a s e r s
O to w jaki sposób pow inno w yglądać o k n o Solution Explorer po > c * Sam p leD ataSo u rce.es
> D A pp .xam l -
do d an iu wszystkich w ym ienionych plików: ►
r
i używa jej do w ypełnienia słow nika o nazw ie D efa u ltV ie w M o d el.
Więcej na temat
tego, czym jest
ViewModel oraz
jak go tworzyć,
można znaleźć
w rozdziale 16.
O
O
Jest jeszcze jed n a rzecz, k tó rą musisz zrobić. S tro n a szczegółów przesłania m eto d ę S a v e S t a t e ( ) , k tó ra pozw ala jej
zapam iętać, który elem en t został kliknięty. W ygenerow any kod rzutuje wybrany elem en t do typu S am pleD ataItem ;
dlatego, żeby uniknąć wyjątków związanych z rzutow aniem , pow inieneś cały kod tej m etody um ieścić w kom entarzu.
p r o te c te d o v e r r id e v o id S a v e S ta te ( D ic tio n a r y < S tr in g , O b ject> p a g e S ta te ) {
/ / C ały kod t e j metody um ieść w kom entarzu.
}
© a To • ? CS S 'B * [p ]
Search Solution Explorer (Ctrl-*-;} fi ~
P rzejdź do fo ld eru zaw ierającego k o d aplikacji dla Ja n k a napisanej we S c aptain Am azing Issue 36 cover.png
0 captain Am azing Issue 57 cover.png
w cześniejszej części rozdziału, wciśnij klawisz Ctrl i k olejno klikaj obrazki, EH Captain Am azing Issue 6 cover.png
0 C aptain Am azing Issue 68 cover.png
aby je zaznaczyć (wszystkie o p ró cz plików Logo.png , SmallLogo.png , 0 Captain Am azing Issue 74 cover.png
SplashScreen.png o raz StoreLogo.png ). Kliknij przycisk A d d , by dodać 0 C aptain Am azing Issue 83 cover.png
0 Captain Am azing Issue 97 cover.png
wszystkie zaznaczone pliki do fo ld eru Assets pro jek tu . 0 captain amazing 250x250.jpg
0 captain_amazing_zoom_250x250.jpg
E 3 DarkGray.png
0 LightGray.png
E 3 Logo.png
e le m e n tó w w y ś w ie tla z a p y ta n ia d o s tę p n e SmallLogo.png
SplashScreen.png
...a d w u c z ę ś c io w a s tro n a
s z c z e g ó łó w p re z e n tu je
w y n ik i w y b ra n e g o z a p y ta n ia
i p o zw a la w y ś w ie tlić
s z c z e g ó ło w e in fo rm a c je na
te m a t w y b ra n e g o e le m e n tu .
^ó ) Z m odyfikuj kod pliku SplitPage.xaml, by strona pre zen to w ała szczegółowe inform acje o komiksie.
K od X A M L um ieszczony w pliku SplitPage.xaml korzysta z szablonów , aby wyświetlać elem en ty Zastosujemy
te właściwości,
w lewej części strony. Je d n a k do w yśw ietlania szczegółów w ybranego e le m e n tu z praw ej strony 0aby
*. ^fJcZeaófc
szczegółowe
używany je st zwyczajny k o d X A M L w ykorzystujący tech n ik ę w iązania danych. Z aw iera on informacje
ko ntrolkę T e x tB lo c k , p ow iązaną z w łaściwością C o n te n t: o wybranym
komiksie były
< T extB lock Grid.Row="2" G rid.C olum nSpan="2" M a rg in = " 0 ,2 0 ,0 ,0 " wyświetlane
w tym samym
T ex t= "{B in d in g C o n ten t}" S ty le = " { S ta tic R e s o u rc e B o d y T ex tS ty le} "/> miejscu strony.
Przykładow e dane um ieszczone w plik u SampleDataSource.cs u d o stę p n iają właściwość C o n te n t,
k tó ra zaw iera duże bloki tek stu . Jed n a k my byśmy chcieli, żeby n asza aplikacja p rezen to w ała inform acje dotyczące
kom iksów z kolekcji Jan k a. N a szczęście dysponujem y już blokiem k o d u X A M L , k tóry schludnie p re ze n tu je te
inform acje, jeśli zostanie pow iązany z o b iek tem Comic. O d s z u k a j k o n tro lk ę w y ś w ie tla ją c ą w a rto ś ć w ła ściw o ści
C o n te n t i zastą p j ą ko d e m X A M L p re z e n tu ją c y m szczegółowe in fo rm a c je o k o m ik s ie . N ie zapom nij d odać do
zew nętrznej kontro lk i G rid właściwości G rid.R ow , G rid .C o lu m n S p an o raz M argin.
o
Hippie Madness (źle
® Połącz zakupy z... wydrukowany)
N iek tó re zapytania nie zw racają o biektu Comic, a zatem wszelkie p o la pow iązane z jego
w łaściwościami b ęd ą puste. W rozdziale 16. dowiesz się o konw erterach w artości, które
pozw alają n a ukryw anie takich p ó l lub wyświetlanie w nich w artości domyślnych.
Zapytanie
Drogie komiksy
zwraca sekwencję
anonimowych © Drogie komiksy Hippie M adness (ile
wydrukowany) jest warty
obiektów IB WS.OOzł
dysponujących Te kontrolki zostały
jedynie powiązane z właściwościami,
właściwościami które nie są dostępne
Title oraz Image. w kontekście danych i dlatego
na stronie są wyświetlane
jako puste miejsca.
W dalszej części książki
dowiesz się o narzędziach,
których będziesz mógł użyć
do ukrycia takich etykiet lub
wyświetlenia w nich wartości
domyślnych.
Możesz także dodać do swojego projektu now e strony, bazujące na szablonach Items Page oraz Split Page, używając do tego celu
tej samej opcji Add New Item , z której skorzystałeś, by dodać stronę Basic Page. Dostępny jest jeszcze jeden cenny szablon —
Grid App — pozwalający na tw orzenie aplikacji o trójpoziomowej nawigacji. Więcej informacji na tem at szablonów Grid App
oraz Split App można znaleźć na stronie: http://msdn.microsoft.com/pl-pl/library/windows/apps/hh768232.aspx.
to je st n o w y ro z d z ia ł ► 729
N a d a w c o , p o z n a j o d b io r c ę
Kiedy p'Fka
z o s ta je uderzona,
wywofywana Pit,a została uderzona pod kątem
70 stopni do poziomu i po/eci
je s t m etoda
OnBalUnPlayO- na (^/eg/ość 82 metrów.
¿ 1 Chcemy, aby obiekt
B a l l . O n B a l l I n P l a y ( 7 0 , 82) P itc h e r z ta p a t tą pitką-
» o odlegtość.
O
y * BaW i ?
P it c h e r .C a t c h B a ll( )
Zdarzenia w IDE
symbolizują błyskawice.
Ikonę podobną do tej Obiekt Fan nasłuchuje
zobaczysz przy nich pitcher oraz inni zdarzenia polegającego
w IntslliSsnss i w oknach S ę d z ia sp ra w d z a każde
oracze próbują zagranie, aby zadecydować, na tym, że piłka leci
właściwości. wyłapać pitkę- w trybuny.
czy było prawidłowe.
Obserwuje całe zdarzenie.
(2 C o ś inicjuje zd arze n ie .
Piłka zostaje u d erzo n a. N ad szed ł czas, aby o b iek t B a ll wywołał zdarzenie.
Czasami mówimy,
że zdarzenie zostało
wygenerowane,
zainicjowane,
zasygnalizowane —
wszystko to są określenia
jednej rzeczy. Ludzie
po prostu używają
dla niej różnych nazw.
Piłka w yw o łu je zd arze n ie .
T w orzone je st now e zd arzen ie (jak to się dokład n ie dzieje, pow iem y za m inutę).
M a ono kilka arg u m en tó w takich ja k p ręd k o ść piłki i tra je k to ria lotu. Z o stają
o n e z nim pow iązane w instancji klasy E v en tA rg s. Z d a rze n ie rozsyłane je st do
wszystkich, którzy zgłosili się do nasłuchiw ania.
i tra je k to rii.
O b ie k* ^
Za.raz po zajściu zdarzenia
tworzony jest obiekt BallEventArgs,
Procedura obsługi zdarzenia a w nim zapi'sywane są informacje
jest metodą znajdującą się o szybkości i trajektorii piłki. Dzięki
w obiekcie subskrybenta, temu dane mogą zostać przekazane
która jest wywoływana do obiektów subskrybentów.
po jego wygenerowaniu.
Zdarzenia obsługiwane są
według zasady pierwszy
K a żd y obiekt obsług uje zd arze n ie . przyszedł, pierwszy
obsłużony — obiekt,
T eraz obiekty P i t c h e r , Umpire o ra z Fan m ogą obsłużyć zd arzen ie B a ll I n P la y który zaczął nasłuchiwać
n a swój sposób. N ie ro b ią teg o je d n a k w tym sam ym czasie — ich p ro ced u ry jako pierwszy, otrzyma
obsługi zdarzen ia wywoływane są je d n a p o drugiej, otrzym ując referen cję powiadomienie w pierwszej
kolejności.
o b iek tu B a llE v e n tA rg s w p ostaci p ara m e tru .
x m usi pracow ać każdy
Oto coś, z czym _, pow inien
• obiekt o b słu g u jąc y z <
i n - obiektu,
on tak ż e otrzym yw ać re fe re n cją
ktćry z g to sit z d arzen ie.
IllnPlay
Ot>iekt Fan sprawdza
BallEventArgs, aby
przekonać się, czy jest
Obiekt Pitcher sprawdza wystarczająco blisko,
BallEventArgs i j e ś ' pitka
żeby złapać piłkę.
je s t w pobliżu, fapie ją-
-%o P itc ^
O
For,
U ri? '* 0
Obiekt Umpire wszystko obserwuje.
Może oczywiście subskrybować także inne
zdarzenia, na przykład BallFielded lub
BallThrown, aby r-eagować na ich wystąpienie.
jesteś tutaj ► 733
P r z y s z e d łe m t u t a j p o a r g u m e n t
Łącząc punkty
T eraz, kiedy m asz już ogólne pojęcie o działaniu
zdarzeń, przyjrzyjmy się dokładniej połączeniom EventArgs
pom iędzy poszczególnym i elem entam i. N a szczęście
zm ienia się tylko kilka rzeczy. "X
O znacza to, S /
że m ożesz obiekt
3 S T j~ r zrzu to w a ć w górą, gdy
p o trz e b u je sz p rzekazać
- r r **** qo do zdarzenia, które
publicznych składow ych. a k u r a t tego określonego
typu nie o b słu g u je.
© Potrzebujem y obiektu p rzech o w u ją ceg o arg u m en ty z d a rze n ia
Pam iętaj, nasze zdarzenie B a l l l n P l a y p o siad a kilka przekazyw anych A
dalej argum entów . P o trzeb u jem y dla nich p ro steg o obiektu. .N E T p o siada
stan d ard o w ą klasę o nazw ie E v en tA rg s, ale n ie m a o n a ż a d n y c h składow ych. BallEventArgs
Trajectory
Jej głównym zad an iem je st um ożliw ianie przekazyw ania Tw oich obiektów do Distance
p ro c e d u r obsługi zdarzeń, k tó re b ę d ą ich używać. O to dek laracja Twojej klasy:
Zdarzenia zwykle są
publiczne. To zdefiniowane
jest w klasie Ball, ale
chcemy, aby klasy Pitcher,
\
Po stowie kluczowym event umieszczane iest
EventHandler. Nie j est t0 st0wo kluczowe
Umpire i inne mogły z niego
korzystać. Jeśli chcesz, aby — jest ono zdefiniowane
miały do niego dostęp tylko obois- w? . NET. Potrzebne jest do wskazania
pt>iektom subskrybującym zdarzenie, w jaki sposólo
inne instancje tej samej powinny wyglądać ich procedury jego o b j^ s ^
klasy, możesz użyć słowa
kluczowego private.
t
K/asa posiadająca tę okreś/oną pr?cedurę , pu
obsługi zdarzenia ma referencj ę obiektu dypu
Ba// o nazwie ba//, więc nazwa, tej p™ ^
będzie się zaczynać od J5a//— . Da/sza c£ęś
będzie miała postać nazwy zdarzenia, w ty™
przypadku Ba//InP/ay.
Istnieje o grom na liczba zd arzeń , k tó re m ogą zostać w ywołane p rzez o b iek t Page X A M L , a każde z nich
posiad a sw oją w łasną m eto d ę, k tó ra je generuje. M eto d a strony O nD oubleT aped() g en eru je zdarzenie
D oubleT aped i jest to jedyny cel jej istnienia. Z d a rz en ie klasy B a ll będzie się trzym ało tej sam ej konw encji.
U pew nim y się, że w k la s ie z n a jd u je się m e to d a O n B a llI n P la y ( ) , k tó ra p o b ie ra o b iek t B a llE v e n tA rg s
jako p a ram etr. S ym ulator gry w baseball b ędzie wywoływał tę m e to d ę za każdym razem , gdy b ędzie chciał
zasygnalizow ać zdarzen ie B a ll I n P la y . K iedy wykryje u d e rz e n ie kija w piłkę, utw orzy now ą instancję
B a llE v e n tA rg s z jej tra je k to rią i odległością, k tó re zo stan ą p rzek azan e do O n B a llI n P la y ( ) .
P Zaczekaj, „standardowa”
:
subskrybować jedno zdarzenie — łączą
one swoje procedury obsługi zdarzeń jedną
Na
procedura obsługi zdarzenia? po drugiej. Powiemy sobie o tym więcej
za chwilę.
POJEDYNCZE
To istnieją jakieś inne rodzaje takich
funkcji? zdarzenie może
P : To dlatego używałem += podczas
odpowiedzieć
O : Tak! Twoje zdarzenia wcale nie muszą pisania swojej metody obsługi
przekazywać zmiennych typu o b je c t
oraz E ve n tA rg s. W zasadzie mogą
zdarzenia? To tak, jakbym dodawał
nową metodę do już istniejących.
W IELE
przekazywać wszystko... lub nic! Popatrz
O : Dokładnie! Za każdym razem, gdy
obiektów.
na okno InteHiSense na dole poprzedniej
strony. Zwróć uwagę na to, że metoda dodajesz procedurę obsługi zdarzenia,
używasz +=. W ten sposób nie zastąpi
j ) Utwórz n o w ą pustą aplikację dla Sklepu W indows i dodaj klasy Ball oraz BallEventArgs.
Oto klasa Ball :
class Ball {
public event EventHandler BalllnPlay;
public void OnBallInPlay(BallEventArgs e) {
EventHandler balllnPlay = BalllnPlay;
i f (balllnPlay != null)
b allIn P lay(th is, e);
}
}
ball^BalllnPlay += ball_BallInPlayj
N aciśnij klawisz Tab jeszcze raz, aby ID E d odało do klasy Pitcher poniższą m e to d ę obsługi zdarzenia.
ID E zawsze będzie generow ało nazwy w edług konw encji (n a zw a O b ie k tu _Nazw aM etody ()):
Czas wykorzystać nabytą wiedzę w praktyce. Twoim zadaniem jest dokończenie klas B a ll
i P i t c h e r , dodanie klasy Fan oraz zadbanie o to, aby wszystko razem działało jako bardzo prosty
symulator gry w baseball.
Ćwiczenie
c la s s P it c h e r {
p u b lic P it c h e r ( B a ll b a ll) {
b a l l . B a l l l n P l a y += new E v e n tH a n d le r(b a ll B a l lI n P la y ) ;
}
pjtcV '«*
7 Popatrz na wyniki
przeds taw i°ne na kolejnej
stronie, aby zapoznać s ię
z tym, c ° powinno zostać
wypisane.
using System.Collections.ObjectModel;
class BaseballSimulator {
private Ball ball = new B all();
private Pitcher pitcher;
private Fan fan;
public ObservableCollection<string> FanSays { get { return fan.FanSays; } }
public ObservableCollection<string>
PitcherSays { get { return pitcher.PitcherSays; } }
public int Trajectory { get; set; }
public int Distance { get; set; }
public BaseballSimulator() {
pitcher = new P itcher(ball);
fan = new Fan(ball);
}
public void PlayBall() {
BallEventArgs ballEventArgs = new BallEventArgs(Trajectory, Distance);
ball.OnBalllnPlay(ballEventArgs);
}
}
[4 UTW ÓRZ STRONĘ GŁÓWNĄ
Czy potrafisz podać kod X A M L, strony patrząc n a jej
wygląd przedstaw iony z prawej strony? Dwie kontrolki
Sym ulator gry w baseball
TextBox są pow iązane z właściwościami Trajectory oraz Miotacz mówi;
Distance obiektu BaseballSimulator zdefiniowanego Rn/t nr 1: Pokryłem pterywzj ba/ę.
jako zasób statyczny; z kolei wypowiedzi zaw odnika i fana Rzut nr Ł Złapałem piłkę.
Czas wykorzystać nabytą wiedzę w praktyce. Twoim zadaniem jest dokończenie klas Ball i Pitcher, dodanie
klasy Fan oraz zadbanie o to, aby wszystko razem działało jako bardzo prosty symulator gry w baseball.
R.OZwi^Z9flis cla ss Ball
cwiczenia
public event EventHandler BallInPlay;
public void OnBallInPlay(BallEventArgs e) {
EventHandler ballInPlay = BallInPlay;
i f (ballInPlay != null)
b allIn P lay(th is, e);
} Metoda 0n8a//InP/ay0 ty/ko wywołuje
zdarzenie 8 a//JnP/ay — wcześniej musi
} s prawdzić, czy nie je s t ono równe nu//.
J eże/i je s t , zostanie zgłoszony wyjątek.
cla ss BallEventArgs : EventArgs
Automatyczne {
w łaściw ości tylko
do odczytu bardzo
public int Trajectory { get; private set; }
dobrze spraw u ją public int Distance { get; private set; }
s ię jako argumenty public BallEventArgs(int trajectory, int distance) {
w zdarzeniach,
ponieważ funkcje this.T rajectory = trajectory;
ich obsługi tylko this.D istance = distance;
odczytują przekazane
do nich dane. }
}
using System.Collections.ObjectModel;
cla ss Fan {
public ObservableCollection<string> FanSays new ObservableCollection<string>();
private int pitchNumber = G;
Konstruktor obiektu Fan
dołącza s ię do t a ñ e r a
public Fan(Ball ball) j procedur obsługi
zdarzenia Ba//InP/ay .
ball.B allInP lay += i EventHandler(ball_BallInPlay);
j
void ball_BallInPlay(object sender, EventArgs e) j
Metoda obsługi
pitchNumber++;
zdarzenia kibica i f (e is BallEventArgs) j
sprawdza, czy piłka BallEventArgs ballEventArgs = e as BallEventArgs;
leci wysoko i daleko.
i f (ballEventArgs.Distance > 12G && ballEventArgsr.aTjectory > SG)
FanSays.Add("Rzut nr " + pitchNumber
+ Home run! Idę po piTkę!");
e lse
FanSays.Add("Rzut nr " + pitchNumber + ": Jeee! Do boju!");
j
}
Jedynym kodem ukrytym potrzebnym na tej stronie jest procedura obsługi zdarzeń Button_Click():
private void Button_Click(object sender, RoutedEventArgs e) {
baseballSim ulator.PlayBall();
}
-------------------------------------------------------------------s r -------------------------------------------
Oto kod XAML strony. Będziesz musiał dodać do niego także znacznik: <local:BaseballSimulator x:Name="baseballSimulator"/>.
<Grid Grid.Row="1" Margin="120,0" DataContext="{StaticResource ResourceKey=baseballSimulator}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Margin="0,0,40,0">
<TextBlock Text="Trajektoria" Style="{StaticResource GroupHeaderTextStyle}" Margin="0,0,0,20"/>
<TextBox Text="{Binding Trajectory, Mode=TwoWay}" Margin="0,0,0,20"/>
<TextBlock Text="Odległość" Style="{StaticResource GroupHeaderTextStyle}" Margin="0,0,0,20"/>
<TextBox Text="{Binding Distance, Mode=TwoWay}" Margin="0,0,0,20"/>
<Button Content="PiTka w grze!" Click="Button_Click"/>
</StackPanel>
<StackPanel Grid.Column="1">
<TextBlock Text="Miotacz mówi:" Style="{StaticResource GroupHeaderTextStyle}" Margin="0,0,0,20"/>
<ListView ItemsSource="{Binding PitcherSays}" Height="150"/>
<TextBlock Text="Fan mówi:" Style="{StaticResource GroupHeaderTextStyle}" Margin="0,0,0,20"/>
<ListView ItemsSource="{Binding FanSays}" Height="150"/>
</StackPanel>
</Grid>
A poniżej przedstawiliśmy kod klasy Pitcher (powyżej niego musisz dodać instrukcję using System.Collections.ObjectModel;):
class Pitcher {
public ObservableCollection<string> PitcherSays = new ObservableCollection<string>();
private int pitchNumber = 0;
Error List D
T - O l Error | ! 0 Warnings Search Error List
Teraz, gdy już zmieniłeś deklarację zdarzenia, powinieneś także zmodyfikować odwołanie do zdarzenia
umieszczone w klasie B a ll:
ball.B allInP lay += new EventHandler<BallEventArgs> (ball_BallInPlay);
i f (ballInPlay != null)
b allIn P lay(th is, e);
C # stosuje niejawną konwersję, gdy pominiesz słowo kluczowe new oraz typ zdarzenia
Kilka stron wcześniej skorzystałeś z możliwości IDE, by utworzyć następującą procedurę obsługi zdarzeń:
W razie zastosowania takiej składni C # dokonuje niejawnej konwersji i samodzielnie określa typ zdarzenia.
Teraz spróbuj zastąpić fragmenty kodu z klas Pitcher oraz Fan poniższym wierszem:
Twój program wciąż będzie działał doskonale, ponieważ IDE automatycznie wygenerowało kod
wykorzystujący niejawną konwersję. Dzięki temu nie musiałeś modyfikować typu po zmianie typu zdarzenia.
M ożesz zobaczyć
w szystkie zdarzenia
kontro/ki, k/ikając ją ,
a następnie przycisk Przew iń zaw artość w ddf
Events w oknie w poszukiwaniu stówa U ick
w łaściw ości. i kliknij je dwukrotnie.
Z a ra z po tym ID E doda do
formularza nową Pr°^fdur^ H j
obsfuoi zdarzenia, która będzie
wywoływana po każdym kliknięciu
w jego obszarze. Do pliku
forml .Designer zostanie także
ożesz utworzyć zdarzenie, które
idzie sygnalizowane podczas
jżdego kliknięcia formularza.
^ Kliknij dwukrotnie wiersz Click w zakładce zdarzeń. IDE automatycznie doda do formularza
procedurę obsługi zdarzenia o nazwie Form1_Click. Dodaj taki oto wiersz kodu:
Visual Studio zrobiło coś więcej. Oprócz wstawienia deklaracji metody skojarzyło ją
także ze zdarzeniem Click formularza. Otwórz Form1.Designer.cs i użyj opcji
Q u ick F ind (E D IT /F in d a n d R eplace/Q uick F in d ) w celu odnalezienia w projekcie tekstu
„Form1_Click”. Znajdziesz taki oto wiersz kodu:
Zanim przejdziemy dalej, poświęć chwilę i zastanów się, co te dwa przyciski robią. Każdy z nich
dodaje nową procedurę obsługi zdarzenia C lick formularza. W pierwszych trzech krokach wykorzystałeś
IDE, by w zwyczajny sposób dodać procedurę obsługi zdarzenia, która będzie wyświetlać komunikat
za każdym razem, gdy zdarzenie C l i c k zostanie wygenerowane. By to było możliwe, IDE dodawało do
pliku Form1.Designer.cs wiersz kodu, w którym operator += kojarzył zdarzenie z procedurą jego obsługi.
Teraz dodałeś dwa przyciski, które wykorzystują dokładnie tę samą składnię, by wstawić kolejne dwie metody
do łańcucha procedur obsługi zdarzenia C l i c k . A zatem, zanim przejdziesz do dalszej lektury, spróbuj
zgadnąć, co się stanie, gdy uruchomisz program, klikniesz pierwszy przycisk, następnie drugi, a w końcu
klikniesz formularz. Czy potrafisz odpowiedzieć na to pytanie przed uruchomieniem aplikacji?
★ Kliknij formularz — pojawi się komunikat M essageBox o treści „Właśnie kliknąłeś formularz”.
★ Kliknij przycisk pierwszy, a następnie ponownie kliknij formularz. Zobaczysz dwa komunikaty:
„Właśnie kliknąłeś formularz” i „Coś”.
Jednak każde
kliknięcie
przycisku
spowoduje,
Właśnie kliknąłeś formularz
że po kolejnym
kliknięciu
formularza pojawi
się dodatkowy
komunikat.
★ Kliknij dwukrotnie przycisk drugi i jeszcze raz kliknij formularz. Tym razem pojawią się
cztery komunikaty: „Właśnie kliknąłeś formularz”, „Coś”, „Coś innego” oraz „Coś innego”.
Ta sama metoda
'e4t- m oże zo sta ć podpięta
do zdarzenia więcej
niż raz.
[^ S a y S o m e th in g E M r
// ^ Occurs when the application transitions to Suspended state from some other
To zdarzenie jest wywoływane za każdym razem, gdy przełączasz się ze swojej aplikacji na inną. Oznacza to, że za każdym
razem, gdy aplikacja zostaje zawieszona, wywoływana jest metoda OnSuspending() zdefiniowana w pliku A pp.xam l.cs.
I podobnie, za każdym razem gdy aplikacja zostanie uruchomiona, wywoływana jest metoda OnLaunched().
Kiedy aplikacja jest zawieszona, system Windows w każdej chwili może ją zakończyć. A zatem powinna ona działać w taki
sposób, jak gdyby każde zawieszenie miało się zakończyć jej zakończeniem, czyli za każdym razem powinna zapisywać
swój stan. Metoda OnLaunched() może sprawdzać przekazane argumenty i na ich podstawie określać, czy działanie
aplikacji zostało wznowione po wcześniejszym zawieszeniu.
748 Rozdział 15.
Zdarzenia i delegaty
using Windows.Storage;
class SuspensionManager {
public s ta tic string CurrentQuery { get; set; }
Po wybraniu z okienka IntelliSense metody OnNavigatedFrom() IDE dodało do kodu szkielet metody, w którym
jest wywoływana metoda OnNavigatedFrom() klasy bazowej. Dodaj do niej wiersz, który usunie zapamiętaną
nazwę zapytania. Nie zapomnij zrobić tego samego w pliku Q ueryD etailZoom .xam l.cs.
Zastąp wiersz z komentarzem TODO wywołaniem metody SaveAsync(). Nie zapomnij umieścić na początku
deklaracji metody słowa kluczowego async, gdyż w przeciwnym razie nie będziesz mógł użyć wewnątrz niej słowa
await, by wykonać wywołanie asynchroniczne:
i f (rootFrame.Content == null) {
/ / J e śli nie uda s ię odtworzyć sekwencji nawigacji, wracamy na pierwszą
/ / stronę, przy czym konfigurujemy ją , przekazując do niej odpowiednie
/ / informacje w formie parametru
i f (!rootFrame.Navigate(typeof(MainPage), args.Arguments)) {
throw new Exception("Nie udało s ię utworzyć strony początkowej");
}
1 f (!S tr1 n g .Is N u ll0 rE m p ty (S u s p e n s 1 o n M a n a g e r.C u rre n tQ u e ry )) {
v a r currentQ uerySequence =
Przyjrzyj s ię dokładni’e
from qu ery 1n new Com 1cQ ueryM anager().Ava1lableQ uer1es
temu zapytaniu LIN Q .
Dodaj ten kod, by where q u e r y . T lt le == S uspenslonM anager.C urrentQ uery ^ C zy rozumiesz, j ak ono
porównać wcześniej s e le c t q u e ry ; działa?
zap isany stan
ap/ikacji z /istą 1 f (cu rre n tQ u e ryS e q u e n ce .C o u n t() == 1) {
zapytań. Jeś/i ComlcQuery qu ery = c u rre n tQ u e ry S e q u e n c e .F 1 rs t();
odpowiada on 1 f (q u e ry != n u l l ) {
jakiem u ś znanemu 1 f ( q u e r y . T lt le == "W s z y s tk ie kom iksy w k o l e k c ji " )
zapytaniu, to
ap/ikacja może ro o tF ra m e .N a v1 g a te (typ e o f(Q u e ryD e ta 1 lZ o o m ), q u e ry );
w yświet/ić stronę e ls e
szczegółów ro o tF ra m e .N a v 1 g a te (ty p e o f(Q u e ry D e ta 1 l), q u e ry );
prezentującą wyniki }
tego zapytania. }
}
/ / Zapewniamy, że bieżące okno będzie aktywne
Window.Current.Activate();
O b iek t typu RoutedEventArgs p o siad a w łaściwość o nazw ie Handled, której p ro c e d u ra obsługi zd arzen ia m oże użyć, by
zaznaczyć, że zdarzenie zostało obsłużone. Przypisanie jej w artości true pow oduje zatrzymanie propagacji zdarzenia .
W przypadku obu rodzajów zd arzeń , zarów no standardow ych, ja k i trasow anych, p a ra m e tr sender b ędzie zaw ierał
referen cję do obiektu, k tóry wywołał p ro c e d u rę obsługi zdarzenia. A zatem , jeśli zd arzen ie p ro p ag u je z kontro lk i do jej
k o n ten era, takiego ja k Grid , to kiedy k o n tro lk a Grid wywoła swoją p ro c e d u rę obsługi zdarzenia, p a ra m e tr sender będzie
w skazywał n a tę k o n tro lk ę Grid. A co m o żn a zrobić, jeśli będziem y chcieli określić, k tó ra k o n tro lk a wywołała początkow e
zdarzenie? Ż a d e n prob lem . O b iek t RoutedEventArgs p o siad a w łaściwość o nazw ie OriginalSource , zaw ierającą
referen cję do kontrolki, k tó ra wywołała zd arzen ie ja k o pierw sza. Jeśli zarów no właściwość OriginalSource , ja k i p a ra m e tr
sender w skazują ten sam o biekt, to k o n tro lk a, k tó ra w ywołała p ro c e d u rę obsługi, je st tą sam ą, k tó ra zapoczątkow ała
zdarzenie i od której zaczęła się jego propagacja.
Struktura kontrolek,
IsHitTestVisible określa,
k tó re zawierają
czy element jest „widoczny" dla wskaźnika lub myszy
inne k o n tro lk i,
Zazwyczaj dow olny ele m e n t um ieszczony n a stro n ie m oże zostać „trafiony” w skaźnikiem
lub myszką — o ile tylko sp ełn ia pew ne kryteria: m usi być w idoczny (co m o żn a zm ieniać zawierające jeszcze
przy w ykorzystaniu właściwości V i s i b il i t y ), m usi m ieć w łaściwość Background lub F ill inne k o n tro lk i, jest
o w artości różnej o d null (choć m oże być przezroczysty — Transparent ) i w k o ń cu jego
szerokość (width ) i wysokość ( height ) m uszą być w iększe o d zera. Jeśli wszystkie te nazywana drzewem
w arunki są spełnione, to właściwość IsH itT estV isib le zwróci w arto ść tru e , a to z kolei
obiektów , a zdarzenia
sprawi, że k o n tro lk a będzie reagow ać n a zd arzen ia zw iązane ze w skaźnikiem lub myszą.
trasowane propagują
W łaściw ość ta je st szczególnie użyteczna w sytuacjach, gdy chcem y, by nasze zd arzen ia były
„niew idoczne” dla myszy. Jeśli przypiszem y właściwości IsH itT estV isib le w artość f a ls e ,
od elementów
to wszystkie zdarzenia zw iązane z naciśnięciam i lub kliknięciam i b ę d ą przekazywane dalej, poto m nych do ich
nie wywołując żadnej reakcji kontrolki. Jeśli poniżej kontro lk i będzie się znajdow ać inna,
to zdarzenie trafi do niej.
rodziców, aż dotrą
do elementu głównego
Listę zdarzeń trasowanych można znaleźć na tej stronie:
h ttp://m sd n .m icrosoft.co m /pl-pl/libra ry/w in do w s/a pps/hh 7 5 8 28 6 .asp x. na samej górze.
752 Rozdział 15.
Zdarzenia i delegaty
p u b lic M ainPage() {
t h is .In it ia liz e C o m p o n e n t ( ) ;
output.ItemsSource = outputItems;
}
Oto kod ukryty strony. Procedura obsługi zdarzeń P o in te r P re s s e d każdej z kontrolek czyści listę wyników, o ile tylko dana
kontrolka jest oryginalnym źródłem zdarzenia, a następnie dodaje do wyników swój własny komunikat. Jeśli przełącznik
odpowiadający danej kontrolce jest włączony, to podczas obsługi zdarzenia jego właściwość e.H a n d le d jest ustawiana na t r u e .
p r iv a t e v o id E llip s e _ P o in t e r P r e s s e d ( o b je c t s e n d e r, P o in te rR o u te d E ve n tA rg s e)
i f (se n d e r == e .O r ig in a lS o u r c e ) o u t p u t It e m s .C le a r ( ) ;
o u tp u tIte m s .A d d (" N a c iś n ię to k o n tro lk ę E l l i p s e " ) ; procedur ° bsługi nif k« n / ch zdarzeń ,
trasowanych będą przekazywane instancje
if (e llip s e S e t s H a n d le d . IsO n) e . Handled = t r u e ; k/as pochodnych RoutadEw ntA rgs, takich jak
PointerRoutedEventArgs /ub PointerPressed.
p r iv a t e v o id R e c t a n g le _ P o in t e rP r e s s e d (o b je c t s e n d e r, P o in te rR o u te d E ve n tA rg s e)
i f (se n d e r == e .O r ig in a lS o u r c e ) o u t p u t It e m s .C le a r ( ) ;
o u tp u tIte m s .A d d (" N a c iś n ię to k o n tro lk ę R e c t a n g le " ) ;
i f (re c ta n g le S e ts H a n d le d .Is O n ) e.H a n d le d = t r u e ;
p r iv a t e v o id G rid _ P o in t e r P r e s s e d (o b je c t s e n d e r, P o in te rR o u te d E ve n tA rg s e)
i f (se n d e r == e .O r ig in a lS o u r c e ) o u t p u t It e m s .C le a r ( ) ;
o u tp u tIte m s .A d d (" N a c iś n ię to k o n tro lk ę G r id " ) ;
i f (g rid S e ts H a n d le d .Is O n ) e.H a n d le d = t r u e ;
p r iv a t e v o id B o rd e r_ P o in te rP re s s e d (o b je c t s e n d e r, P o in te rR o u te d E ve n tA rg s e)
i f (se n d e r == e .O r ig in a lS o u r c e ) o u t p u t It e m s .C le a r ( ) ;
o u tp u tIte m s .A d d (" N a c iś n ię to k o n tro lk ę B o r d e r " );
i f (b o rd e rS e ts H a n d le d .IsO n ) e.H an d le d = t r u e ;
p r iv a t e v o id S ta c k P a n e l_ P o in t e rP r e s s e d (o b je c t s e n d e r, P o in te rR o u te d E ve n tA rg s e)
i f (se n d e r == e .O r ig in a lS o u r c e ) o u t p u t It e m s .C le a r ( ) ;
o u tp u tIte m s .A d d (" N a c iś n ię to k o n tro lk ę S t a c k P a n e l" ) ;
p r iv a t e v o id U p d a te H itT e s tB u tto n (o b je c t s e n d e r, R outedEventA rgs e) procedura obs ługi zdarzeń Click przycisku
używ a w łaściw ości IsO n kontro/ki
g r a y R e c t a n g le .Is H it T e s t V is ib le = n e w H itT e s t V is ib le V a lu e .Is O n ; przełacznika aby włączać i wyłączać
p .^
Właściwość T .u
Is H.i*T__rt/.-e;bfe
itT e s tV is ib le Imntro/ki
kontrolki
Rectangle.
754 Rozdział 15.
Zdarzenia i delegaty
O
na zdarzenia związane z naciskaniem.
F
Obie'*'
O O b ie ^
.
Przewróć kartkę, by użyć swojej nowej aplikacji do wypróbowania zdarzeń trasowanych. jesteś tutaj ► 755
B ą b e lk i p r o p a g u j ą w p r o s t d o g ło w y
P onow nie kliknij szary p ro sto k ą t — tym razem p o w inna zadziałać pułap k a. Użyj
polecenia Step Over (F10), by wykonać kolejne wiersze kodu jeden po drugim .
N a sam ym p o czątku zostanie w ykonany b lo k i f , k tóry wyczyści zaw artość kolekcji
outputltems (typu O bservableC ollection ), pow iązanej z k o n tro lk ą ListBox .
D zieje się ta k dlatego, że referen cje sender o raz e.O riginalSource w skazują tę sam ą k o n tro lk ę Rectangle ; a w aru n ek
sender == e.O riginalSource jest spełniony wyłącznie w p ro c e d u rze obsługi zd arzeń kontro lk i, k tó ra jak o pierw sza
w ywołała zdarzenie (w tym przy p ad k u będzie to k o n tro lk a, k tó rą kliknąłeś lub dotknąłeś).
K iedy dotrzesz do końca m etody, kontynuuj krokowe wykonywanie programu . Z d arze n ie będzie pro p ag o w ać w górę
drzew a obiektów , u ru ch am iając najpierw p ro ce d u rę obsługi zd arzen ia k o n tro lk i Rectangle , n a stęp n ie k o n tro lk i Grid ,
StackPanel i docierając w k ońcu do p ro ced u ry obsługi zd arzen ia strony LayoutAwarePage, k tó ra jest zdefiniow ana
po za kodem aplikacji, dlatego je st w ykonyw ana zawsze. Poniew aż ża d n a z tych k o n tro le k nie je st początkow ym źródłem
zdarzenia, zatem w żadnej z kolejnych p ro c e d u r obsługi zd arzeń p a ra m e tr sender nie będzie rów ny właściwości
e.O riginalSource , w ięc żad n a z nich nie wyczyści listy z w ynikami.
^ BaW % P itc ^
Bali NIE chce interesować
s ię obiektem Pitcher.
Nie przejmuje s ię tym, j
obiektem pracuje, tan,
Kiedy do projektu dodajesz delegat, to właściwie dodajesz nowy typ. Używając go do utworzenia pola lub zmiennej,
tworzysz instancję tego typu. Utwórz zatem nowy projekt typu Console Application, dodaj do niego nowy plik klasy
i nazwij go C onvertsIntToString.cs . Zamiast wstawiać do niego klasę, wpisz pojedynczy wiersz:
delegate string ConvertsIntToString(int i); — C°nverts I nt ToS tn n g j e s t typem delegatu, który dodałeś
do swojego p rojektu. Teraz m ożesz używ ać go podczas
deklarowania zm iennych, dokładnie tak samo, ja k m ożesz
Następnie dodaj do klasy Program metodę HiThere(): w tym celu używ ać klas oraz interfejsów.
private s ta tic string HiThere(int i) S y gnatura tej metody p a su je do
sy g natury typ u ConvertsIntToString.
{
return "Witamy, towarzyszu nr " + (i * 100);
}
W końcu uzupełnij kod metody Main(): someM ethod j e s t zm ienną typ u ConvertsIntToString.
J e s t ona bardzo podobna do zmiennych
s ta tic void Main(string[] args) refe rencyjnych, je d nak za m ia st przyklejać e tykietkę
obiektowi na s t ercie, przyczepia ją do metody.
{
ConvertslntToString someMethod = new ConvertslntToString(HiThere);
string message = someMethod(5);
Conso! e 'Wr'd;;Lin' ( messag6);
Console.ReadKey O;
}
Zmienna someMethod wskazuje na metodę HiThere(). Kiedy Twój program wykonuje wywołanie someMethod(5),
w rzeczywistości wywołuje metodę HiThere(), przekazując do niej wartość 5. W efekcie zwrócony zostanie łańcuch znaków
o postaci „Witamy, towarzyszu nr 500” — czyli taki sam, jaki uzyskalibyśmy, wywołując metodę HiThere() bezpośrednio.
Poświęć chwilkę, by krok po kroku przetestować ten program w debuggerze, tak byś dokładnie wiedział, co się w nim dzieje.
S Do d aj t e a z Ma s ę d a drug ie g o sz e fa kuch ni
, Am y.
Metody tej klasy działają podobnie: co ufatw ia pokazanie tego,
cla ss Amy { V
public GetSecretIngredient MySecretlngredlentMethod {
Także w tym
get {
przypadku metoda
zwracająca return new GetSecretIngred1ent(AmysSecretIngred1ent);
sekretny składnik }
pobiera wartość }
typu int i zwraca private string AmysSecretIngred1ent(1nt amount) {
łańcuch znaków,
jednak różni się 1f (amount < 10)
on od łańcucha return amount.ToStr1ng() + " puszek sardynek - - potrzebujesz więcej!";
Suzanne. e lse
return amount.ToStr1ng() + " puszek sardynek";
}
}
★ Uruchom program. Kliknij na początku przycisk Pobierz skła d n ik — powinien on wypisać w oknie
konsoli komunikat „Nie mam tajnego składnika!”.
★ Kliknij przycisk Użyj delegatu S u za n n e. Do pola ingredientMethod (które jest
delegatem G etSecretIngredient) zostanie wpisana wartość zwrócona przez właściwość
MySecretIngredientMethod. Zwróci ona nową instancję typu G etSecretIngredient, która
będzie wskazywała na metodę SuzannesSecretIngredient().
★ Kliknij po raz kolejny przycisk Pobierz skła d n ik. Teraz pole ingredientMethod wskazuje na
SuzannesSecretIngredient(). Metoda zostanie wywołana. Przekazana zostanie do niej wartość
z kontrolki NumericUpDown (upewnij się, że nadałeś jej nazwę amount), a całość zostanie wypisana
w oknie konsoli.
★ Kliknij przycisk Użyj delegatu A m y . Korzysta on z właściwości Amy.MySecretIngredientMethod,
aby ustawić pole ingredientMethod formularza tak, by wskazywało ono na metodę
AmysSecretIngredient().
★ Kliknij przycisk Pobierz skła d n ik ponownie. Teraz wywoływana jest metoda klasy Amy.
★ Użyj teraz debuggera, aby dokładnie sprawdzić, co się dzieje. Wstaw pułapkę w pierwszym wierszu
wszystkich trzech metod formularza. Uruchom ponownie program (co ustawi ingredientMethod
na domyślną wartość n u ll) i powtórz pięć powyższych kroków. Użyj Step Into (F11), aby przejść
przez każdy wiersz kodu. Sprawdź, co się stanie, gdy klikniesz Pobierz skła d n ik. Wykonanie
programu przeniesie się bezpośrednio do klasy Suzanne lub Amy w zależności od metody, która
znajduje się w polu ingredientMethod.
Zagadkowy basen
public Form1() I
InitializeComponent();
th is . += new EventHandler(Minivan);
th is . += new EventHandler(______________ ) ;
I
void Towtruck(object sender, EventArgs e) I
Console.Write("zbliża s i ę , " ) ;
Twoim za d a n ie m jest pobranie fragmentów I
kodu z basenu i wstawienie ich w puste miejsca.
void Motorcycle(object sender, EventArgs e) I
Możesz użyć tego samego fragmentu więcej
niż raz i nie musisz wykorzystać ich wszystkich. button1.____________ += new EventHandler(_______________) ;
Celem jest dokończenie kodu formularza, który I
po kliknięciu przycisku buttonl wyświetli
w oknie konsoli poniższy komunikat.
void Bicycle(object sender, EventArgs e) I
Console.WriteLine("aby Cię z ła p a ć !");
I
W ynik:
void ______________ (object sender, EventArgs e) I
Palczasty z b liż a s ię , aby C1ę złapać!
button1.____________ += new EventHandler(Dumptruck);
button1.____________ += new EventHandler(_______________) ;
I
void ______________ (object sender, EventArgs e) I
Console.Write("Palczasty ")
I
Przypominamy: każdy
fragment kodu z basenu
może zostać użyty
więcej niż raz!
Możemy teraz dodać do klasy Ball metodę bat_HitTheBall, która będzie nasłuchiwała
zdarzenia HitTheBall obiektu Bat. Gdy piłka zostanie uderzona, jej własna procedura
obsługi zdarzenia uruchomi OnBallInPlay(), aby wywołać własne zdarzenie BallInPlay.
W tym momencie zaczyna się cała reakcja łańcuchowa. Zawodnicy z pola pilnują baz, kibice Teraz procedura
dopingują, sędziowie krzyczą... mamy piłkę w grze. obsługi' zdarzenia może
pobrać informację
o si/e zamachu, a na
je j podstaw ie okreś/ić
Obiekt Ba// subskrybuje dy s ta ns i trajektorię
Symulator wykrywa zdarzenie HitTheBa//. oraz wywołać zdarzenie
zderzenie kija z pitką, B a//InP/ay.
wywotuje zatem metodą
OnHitTheBaHO obiektu B at.
— ^ „
bat.O nH itTheBall() h
O j, oj i Te pitki
mia/y być użyte jako
rezerwowe, gdyby
pierw sza poleciała
gdzieś poza boisko.
...ale to nie zawsze jest dobre!
W danej chwili w grze może być tylko i wyłącznie jedna piłka, jednak obiekt Bat
i
używa zdarzeń do sygnalizowania faktu uderzenia piłki, a to oznacza, że zdarzenie to
może subskrybować dowolny obiekt B a ll. W ten sposób udało się nam wprowadzić
do programu niewielki, ale nieprzyjemny błąd. Co się stanie, jeżeli programista przez rźofconą przez miotacza, w szystkie
przypadek doda trzy kolejne obiekty Ball ? Pałkarz weźmie zamach, uderzy, a w pole cztery poleciały w pole gry!
polecą cztery różne piłki!
8 at€S&
Zdarzenie HitTheBall
^ Bal\
O b ie k t ^
jesteś tutaj ► 763
F u n k c je z w r o t n e s p i e s z ą z p o m o c ą
| hitBallCallback
Wszystko szło zgodnie z planem, do czasu gdy Heniek przeniósł się do nowej lokalizacji
i spróbował kraba pochwycić. Zdziwił się bardzo, widząc tam walczące o niego pozostałe trzy obiekty
TreasureHunter.
W j a k i sposób in n i poszukiw acze skarbów p o ko n a li H eń ka w walce o kraba?
Konstruktor tworzy
tańcach dwóch
public Form1() { Zagadkowy basen
InitializeComponent();
procedur obsfugi
zdarzenia wczytania^ t h is . Load += new EventHandler(Minivan);
Rozwiązanie
formularza. t h is . Load += new EventHandler(M otorcycle );
Zdarzenie to je s t
sygnalizowane }
bezpośrednio void Towtruck(object sender, EventArgs e) {
po zakończeniu
w czytyw ania.
Console.Write("zbliża s ię , ");
^ Ś m
I
/ w konstruktorze
B
noawy obie kt B a t i używa delegatu
BatCallback w celu przekazania referenc
do sw ojej w łasnej m etody OnBallInPlay()
CenekU+ W n[ektóry ch przypadkach korzystniej nowej in sta n c j B at. To j e s t właśnie
ta metoda, której kij będzie używ ał
^ Z l
lUb se e? *1 p0przez m et0^ pub liczną C w momencie uderzenia piłki.
CELNE SPOSTRZEŻENIA
■ Wszystkie kontrolki okna Toolbox używają zdarzeń,
gdy coś się stanie w programie.
Kiedy do projektu dodajesz delegat, to tworzysz nowy ■ Kiedy jeden obiekt przekazuje referencję metody
typ, który przechowuje referencje do metod. do innego, tak aby mógł uzyskać za jej pomocą
Zdarzenia używają delegatów do powiadamiania informacje zwrotne (tylko on sam), nazywamy
obiektów o swojej aktywności. to funkcją zwrotną.
Obiekty subskrybują zdarzenia innych obiektów, ■ Zdarzenia pozwalają metodom anonimowo podłączać
jeśli chcą w jakiś sposób na nie zareagować. się pod nie, natomiast funkcje zwrotne pozwalają Ci
uzyskać lepszą kontrolę nad delegatami, które przyjmują.
EventHandler jest rodzajem delegatu szczególnie
popularnego w pracy ze zdarzeniami. ■ Zarówno funkcje zwrotne, jak i zdarzenia używają
delegatów do przechowywania i wywoływania metod
Możesz dołączyć do jednego zdarzenia kilka procedur w innych obiektach.
jego obsługi. To dlatego do ich przypisania używasz +=.
■ Debugger jest naprawdę dobrym narzędziem,
Przed użyciem zdarzenia lub delegatu zawsze sprawdzaj, które często okazuje się pomocne. Możesz go użyć
czy nie są one równe n u ll. Unikniesz w ten sposób dla lepszego zrozumienia działania zdarzeń, delegatów
wyjątku NullReferenceException. oraz funkcji zwrotnych. Korzystaj z niego!
P : F u n k cja zw ro tn a nie je st w takim ra z ie typem C#? O : Zdarzenie wygląda podobnie do funkcji zwrotnej dlatego,
że oba mechanizmy używają de legatów . To, że są one
O : Nie. Jest ona wzorcem — to po prostu nowatorski sposób
wykorzystywane w obu przypadkach, ma głębszy sens, gdyż jest to
użycia istniejących typów, słów kluczowych i innych narzędzi
sposób, w jaki C# pozwala jednemu obiektowi przekazać do innego
udostępnianych przez C#. Cofnij się nieco i jeszcze raz spójrz na
referencję do jednej ze swych metod.
kod klas B a ll i B a t. Czy widzisz jakiekolwiek słowa kluczowe,
których wcześniej nie używaliśmy? Nie! Użyliśmy w nim jednak Wielką różnicą pomiędzy funkcjami zwrotnymi a zdarzeniami jest to,
delegatu, a on jest typem .NET. że zdarzenia powiadamiają wszystkich o tym, że coś się stało. Funkcja
,, , .................................... ,. , . , , zwrotna nigdy nie jest udostępniana. Jest ustawiona jako prywatna
Tak się składa że istnieje wiele wzorców, których możesz używać. test i sprawuje ścisłą kontrolę nad tym, jaka metoda zostanie wywołana.
nawet cały obszar programowania zwany wzorcam i projektow ym i.
Sprawdź książkę W zorce projektow e. Rusz g ło w ą ! na w itryn ie w ydaw nictw a Helion. To doskonałe źródło różnych
wzorców, które możesz wykorzystać w swoich programach. Pierwszym z nich jest wzorzec obserwatora, który będzie
wyglądał znajomo. Jeden obiekt udostępnia informacje, a drugi nasłuchuje. Hm... http://helion.pl/ksiazki/w zorrg.htm .
W zrabowanym zbiorze diagramów klas odkrył, że obiekt G oldenC rab sygnalizuje zdarzenie
R unF orC over za każdym razem, gdy k to ś się do niego zbliży. Jest nawet lepiej, bo zdarzenie posiada
param etr N ew L o ca tio n A rg s, który zawiera szczegółowe inform acje na tem at położenia kraba. Ż aden
z pozostałych obiektów łow ców skarbów o tym nie wie, więc H en iek jest ju ż pewny, że pieniądze są jego.
c la ss GoldenCrab {
p u b lic delegate void Escape(NewLocationArgs e ) ;
p u b lic event Escape RunForCover;
p u b lic void SomeoneNearby() {
Escape runForCover = RunForCover;
i f (runForCover != n u ll)
ru n F o rC o v e r(th is, new NewLocationArgs("Pod s k a łą " ) ;
}
} £ razern' 9dy ktoś zbliża
c la s s NewLocationArgs { się do ztotego kraba, metoda
p u b lic N ewLocationArgs(H idingPlace newLocation) { SomeoneNearbyO w ygotuje
th is.n e w L o c a tio n = new Location; zdarzenie RunforCover, które
} w yszukuje m iejsce do ukrycia.
p riv a te H idin g Place newLocation;
p u b lic H idin g Place NewLocation { get { re tu rn new Location; } }
}
To wyjaśnia, dlaczego plan Heńka się nie powiódł. Kiedy do konstruktora TreasureHunter dodał on
procedurę obsługi zdarzenia, nieumyślnie zro b ił to sam o dla w szystkich czterech poszukiw aczy skarbów!
W ten sposób każda ich funkcja obsługi zdarzenia została podłączona do łańcucha wywołań zdarzenia
RunForCover. Kiedy złoty skorupiak uciekał do kryjówki, każdy był o tym informowany. Wszystko byłoby
dobrze, gdyby Heniek był pierwszym, który otrzyma powiadomienie. Nie mógł on jednak wiedzieć, czy
inni poszukiwacze skarbów już je otrzymali. Zależało to od tego, czy zgłosili chęć subskrypcji przed nim.
Wtedy zostaliby poinformowani w pierwszej kolejności.
jesteś tutaj ► 769
J a k fu n k c jo n a ln e
Otwórz plik MainPage.xaml.cs i dodaj do niego przedstawione poniżej instrukcje using oraz jeden wiersz kodu do konstruktora:
using Mindows.UI.ApplicationSettings;
using Mindows.UI.Popups; S e ttln g sP a g e jest klasą statyczną pozwalającą
aplikacji dodawać i usuwać polecenia z panelu
/ / / <summary> Ustawienia. Należy ona do przestrzeni nazw
I I I Prosta strona udostępniająca możliwości wspólne dla wszystkich a p lik a c ji. M in d o w s .U I.A p p lic a tio n S e ttin g s . Trzeba
/ / / </summary> zauważyć, że umieszczenie kodu dodającego
p ublic sealed p a rtia l c la ss MainPage : KomiksyJanka.Common.LayoutAwarePage opcję „O aplikacji" do panelu Ustawienia
{ w konstruktorze strony głównej aplikacji
s t a t ic bool aboutCommandAdded = fa ls e ; powoduje, że każde ponowne wejście na stronę
public MainPage() { głów ną spowoduje dodanie do panelu kolejnej,
th is .In itia liz e C o m p o n e n t(); takiej samej opcji. Aby tem u zapobiec używamy
i f (¡aboutCommandAdded) { pola aboutCommandAdded. W arto umieścić
SettingsPane.GetForCurrentView().CommandsRequested += w komentarzu instrukcję przypisującą temu polu
MainPage_CommandsRequested; wartość true i odwiedzić kilka stron aplikacji,
aboutCommandAdded = tru e; by przekonać się, że faktycznie w panelu
} Ustawienia pojawi się kilka opcji „O aplikacji".
}
}
Po wpisaniu += IDE zaoferuje automatyczne wygenerowanie szkieletu procedury obsługi zdarzeń. Poniżej pokazaliśmy,
co należy umieścić w tej procedurze obsługi. Używa ona delegatu o nazwie UICommandInvokedHandler, dodajmy zatem
metodę o nazwie AboutInvokeHandler(). Oto kod metody wywoływanej przez polecenie O aplikacji.
Teraz uruchom aplikację. Wyświetl panel funkcji, kliknij lub dotknij pozycję U stawienia , a następnie wybierz opcję
O aplikacji. Aplikacja wywoła metodę AboutInvokedHandler(), która wyświetli okienko MessageDialog.
A teraz skorzystaj z IDE, aby zobaczyć, jak to działa. Zatrzymaj program i użyj polecenia Go To D efinition,
by przejść do definicji klasy SettingsCommand pobranej z metadanych. Powinna wyglądać następująco:
Udostępnianie
Teraz ponownie użyj tego samego polecenia, by wyświetlić definicję typu UICommandlnvokedHandler:
CommandsRequested.
★ Procedura obsługi zdarzenia ma argument typu SettingsPaneCommandsRequestedEventsArgs.
Wyświetl jego definicję, by zobaczyć obiekt Request, używany w trzecim wierszu procedury obsługi.
★ Klasa Request definiuje jedną właściwość: kolekcję o nazwie ApplicationCommands, zawierającą
obiekty klasy SettingsCommand.
★ Ponownie wróć do kodu procedury obsługi zdarzeń, gdyż teraz już rozumiesz, jak ona działa. Kiedy Panel U staw ienia
użytkownik kliknie panel Ustawienia, wywołuje on swoje zdarzenie CommandsRequested, by zapytać się możesz w yśw ietlić
aplikację o polecenia, które należy w nim wyświetlić, oraz ich funkcje zwrotne. Podłączyłeś do tego naciskając
jednocześnie klaw isz
zdarzenia procedurę obsługi, która zwraca obiekt SettingsCommand definiujący polecenie O aplikacji Windows + C
oraz delegat wskazujący na metodę wyświetlającą okienko MessageDialog. Po kliknięciu O aplikacji
panel użyje delegatu, by wywołać metodę AboutInvokedHandler() .
★ Wciąż nie do końca to rozumiesz? Nie przejmuj się, skorzystaj z przycisków nawigacyjnych 0 * 0
umieszczonych na pasku narzędzi, by zmieniać prezentowane definicje. Spróbuj umieścić pułapki
w konstruktorze oraz obu przedstawionych wcześniej metodach. Czasami, zanim całość rozwiązania
„zaskoczy” Ci w głowie, będziesz musiał kilka razy wyświetlić definicje wszystkich używanych klas i metod.
Aplikacje mogą współdziałać także z panelami Wyszukiwanie i Udostępnianie! Zajrzyj do punktu 1.
dodatku „Pozostałości” , aby dowiedzieć się, gdzie możesz znaleźć informacje na ten temat.
jesteś tutaj ► 771
772 R o zd ział 1S.
16. Projektowanie aplikacji wedłuę wzorca
Świetne aplikacje
od zewnątrz i od środka
Twoje aplikacje m u szą być nie tylko w sp an iałe w izualnie. Kiedy mówimy o projekcie,
co Ci przychodzi do głowy? Przykład jakiejś wspaniałej architektury budowlanej? Doskonale rozpla
nowana strona WWW? Produkt zarówno estetyczny, jak i dobrze zaprojektowany? Dokładnie te
same zasady odnoszą się do aplikacji. W tym rozdziale poznasz wzorzec Model-View-ViewModel
(MVVM, model-widok-widok modelu) i dowiesz się, jak używać go do tworzenia dobrze zapro
jektowanych aplikacji o luźnych powiązaniach. Przy okazji poznasz animacje, szablony kontrolek
używane do projektowania wyglądu aplikacji, nauczysz się stosować konwertery, by ułatwiać
korzystanie z techniki wiązania danych, a w końcu zobaczysz, jak połączyć te wszystkie elementy,
by stworzyć solidne podstawy do tworzenia dowolnych aplikacji w języku C#.
to je st n o w y ro z d z ia ł ► 773
D a m i a n i K u b a z a c z y n a j ą s i ę k łó c i ć
Wspaniali Bombiarze
Na parkiecie Na parkiecie
U» IV4i Orni m 11
Hnvytrttl Ifuttm ii
takub«4 Cyilyn»rt6
lidanrtt Mtortn 0
Cmn* Ult IV41
Na ławce Na ławce
(<te*IVii firn*o ■
r
Na tej stronie znajdują s ię cztery
odrębne kontrolki L istV iew , zatem
każda z nich potrzebuje odrębnej
kolekcji ObservableCollection, którą
można by powiązać z je j właściwością
Item sS o u rce.
Kuba: Tak, rozumiem, co mówisz. Ale to ty mnie nie słuchasz. Ty myślisz o modelu danych.
Kuba: Nie skończyłem. A co z całą resztą aplikacji? Będą w niej kontrolki ListView
i TextBox, w których te dane trzeba będzie jakoś wyświetlać. Jeśli nie użyjemy kolekcji,
z którymi te kontrolki będziemy mogli powiązać, to nic z tego nie wyjdzie.
Damian: H m .
Kuba: Ano właśnie. Czyli będziemy chyba musieli podjąć kilka, h m ., strategicznych
decyzji związanych z modelem obiektów.
Damian: Masz na myśli to, że będziemy musieli dojść do kompromisu i stworzyć marny
model obiektów, którego nie da się łatwo używać, i to tylko dlatego, żebyś miał coś,
do czego będziesz mógł powiązać kontrolki?
WYSIL
SZARE KOMÓRKI
Jak można by stworzyć klasy, które zapewnią łatwość wiązania danych z kontrolkami,
a jednocześnie pozwolą utworzyć model obiektów gwarantujący łatwość zarządzania danymi?
Czy projektujesz pod kątem wiązania danych, czy łatwości pracy z danymi?
Już wiesz, jak ważne jest, by tworzyć model obiektów zapewniający łatwość pracy z danymi. Co jednak zrobić w przypadku,
gdy obiekty mają służyć do dwóch różnych rzeczy? To jeden z najczęstszych problemów, z jakimi będziesz się stykał
jako projektant aplikacji. Twoje obiekty muszą posiadać publiczne właściwości oraz kolekcje O bservableCollections
umożliwiające powiązanie danych z kontrolkami XAML. Jednak czasami składowe te utrudniają operowanie na danych,
gdyż zmuszają nas do tworzenia nieintuicyjnego modelu obiektów, z którego korzystanie nie jest wygodne.
var benchPlayers =
from player in _roster.Players
where player.Starter == false
select player;
i
... je ś li je d n o c z e ś n ie m usisz
Player Roster League
z a p e w n ić m o ż liw o ś ć w ią z a n ia Name: string TeamName: string JimmysTeam: Roster
ty c h d a n ych z k o n tro lk a m i Number: int Starters: BriansTeam: Roster
ObservableCollection
X A M L na s tro n a c h a p lik a c ji. Bench:
ObservableCollection Przydałyby się
tu ta j ja k ie ś
POWIĄZANIE prywatne metody
generujące dane
I t e m s S o u r c e = " {B i n d i n g } testo w e.
/ek t
© W PROJEKCIE UTW ÓRZ FOLDERY DLA WIDOKU, MODELU ORAZ MODELU WIDOKU
Kliknij prawym przyciskiem myszy nazwę projektu widoczną w oknie Solution Explorer i z wyświetlonego menu
wybierz opcję A d d /N e w Folder:
}
Teraz folder M odel powinien mieć następującą zawartość: J Model
> c * Player, cs
Jak widać, nazwa pola _players rozpoczyna się > c* Ro5ter.cs
od znaku podkreślenia. Dodawanie tego znaku do
nazw pól prywatnych jest bardzo często stosowaną
konwencją nazewniczą. Będziemy jej używali w tym Na następnej stronie doda» widok.
rozdziale, więc pewnie się do niej przyzwyczaisz.
jesteś tutaj ► 779
Przejmij kontrolę nad kontrolkami
Dwukrotnie kliknij błąd, by przejść do problematycznego miejsca w kodzie, gdzie po usunięciu pliku
M ainPage.xaml pojawił się błąd:
Chwileczkę, już wiesz, co ten kod robi! Modyfikowałeś go podczas pisania aplikacji dla Kuby. Szuka on
klasy MainPage, żeby do niej przejść podczas uruchamiania aplikacji. Jednak przed chwilą usunąłeś plik
XAML definiujący tę klasę. Nie ma problemu! Wystarczy podać klasę, którą chcesz uruchomić:
Hm... to dziwne. Przecież dodałeś do projektu klasę LeaguePage, jednak najwyraźniej IDE jej nie
rozpoznaje. Wszystko przez to, że dodałeś ją do folderu, dlatego IDE umieściło ją w przestrzeni nazw
View. A zatem rozwiązanie problemu sprowadza się do określenia przestrzeni nazw w odwołaniu do klasy:
if (!rootFrame.Navigate(typeof(View.LeaguePage ), args.Arguments))
throw new Exception("Failed to create initial page");
<UserControl
x:Class="RozgrywkiLigiKoszykowki.View.RosterControl"
xmlns="http://schem as.m icrosoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:RozgrywkiLigiKoszykowki.View"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" W iesz, że kontrolki zm ieniają sw oje wymiary na podstaw ie właściwości Me'?11*
d:De s i gnWidth ="400"> oraz Width. M ożesz zm ieniać te wartości, b y okre ś|ić, jaka ma t>yć w ielkość
J kontrolki w yśw ietlanej w oknie D esigner IDE, podczas je j projektowania.
<UserControl.Resources>
<DataTemplate x:Key="PlayerItemTemplate">
<TextBlock Style="{StaticResource ItemTextStyle}"> Oto szablon elementów wyświetlanych
<Run Text="{Binding Name}"/> w kontrolkach L istV iew . Każdy w iersz
<Run Text=" nr "/> składa s ię z jednej kontrolki TextBlock
<Run Text="{Binding Number}"/> oraz trzech elementów Run wyświetlających
imię zawodnika i jego numer.
</TextBlock>
</DataTemplate>
</UserControl.Resources>
<Grid> A by kontrolka Border miała zaokrąglone
<Grid.RowDefinitions> wierzchołki, należy zastosow ać
właściwość CornerRadius.
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions> V
<Border BorderThickness="2" BorderBrush "Blue" CornerRadius="6" Margin="0,0,40,0">
<StackPanel Margin="20">
<TextBlock Text="{Binding TeamName}"
Style="{StaticResource HeaderTextStyle}"/>
<TextBlock Text="Starting Players" Margin="0,20,0,0"
Style="{StaticResource GroupHeaderTextStyle}" />
<ListView ItemsSource="{Binding Starters}" Margin="0,20,0,0M
_ -----------------^ ItemTemplate="{StaticResource PlayerItemTemplate}
Obie kontrolki L istV iew <TextBlock Text="Bench Players" Margin="0,20,0,0"
korzystają z tego samego Style="{StaticResource GroupHeaderTextStyle}" />
szablonu zd efiniowanego <ListView ItemsSource="{Binding Bench}" Margin="0,20,0,0"
jako zasób statyczny.
ItemTemplate="{StaticResource PlayerItemTemplate}
</StackPanel>
</Border>
</Grid>
</UserControl>
Napisz model widoku aplikacji do zarządzania ligą koszykówki. W tym celu przeanalizuj
obiekty modelu i powiązania używane w widoku i na ich podstawie określ, jakiego
systemu „kanalizacyjnego" potrzebuje aplikacja, żeby je ze sobą połączyć.
Ćwiczenie
Jeśli w oknie projektanta XAML IDE w yśw ietli komunikat, że w przestrzeni nazw ViewModel nie
istnieje klasa LeagueViewModel, a Ty jesteś na 100% pewny, że dodałeś ją do właściwego folderu,
to spróbuj kliknąć projekt prawym przyciskiem myszy i wybrać z menu opcję Unload Project,
następnie kliknij go ponownie prawym przyciskiem myszy i wybierz opcję Reload Project.
jesteś tutaj ► 783
Rozwiązanie ćwiczenia
A
g e s tia :
Model widoku aplikacji do zarządzania ligą koszykówki składa się z trzech klas: LeagueV iew M odel,
P la yerV iew M o de l oraz R o s te rV ie w M o d e l. Wszystkie trzy znajdują się w folderze ViewModei
Ćwiczenie
Rozwiązanie namespace RozgrywkiLigiKoszykowki.ViewModel { Jeśli pominiesz wiersz u s in g M o d e l; , to
using Model;
using System .Collections.ObjectM odel;
wszędzie w kodzie, zamiast R o ste r , będziesz
musiał używać nazwy M o d e l.R o s te r .
Klasa c la ss LeagueViewModel {
T public RosterViewModel BriansTeam { get; p riv a te s e t; }
Ud0 stępnia 0bie kty public RosterViewModel JimmysTeam { get; p riv a te se t;
RosterViewModel,
których kontrolki public LeagueViewModel () {
RosterControl mogą Roster briansRoster = new Roster("Bombiarze G etBom berPlayers());
używ ać jako swojego BriansTeam = new RosterViewM odel(briansRoster);
kontekstu danych.
Tworzony j e s t także Roster jimmysRoster = new R o ster("Tw ard ziele", G etAm azinPlayers());
obiekt modelu, JimmysTeam = new RosterViewModel(jimmysRoster);
Roster, którego }
może używ ać obiekt
RosterViewModel. p riv a te IEnumerable<Player> GetBomberPlayers() {
List<Player> bomberPlayers = new List< Player> () {
Ta prywatna new Player( Damian", 31, tru e ),
metoda generuje new Player( Ludwik", 23, tru e ),
przykładowe new Player( Krystian 6, tru e ), Dane przykładowe są zazwyczaj generowane
dane o drużynie new Player( Maciek",, 0, tru e ), przez klasy modelu widoku, gdyż stan
„Bombiarze', new Player( Janek" 42, tru e ), aplikacji tworzonych w oparciu o wzorzec
tworząc w tym new Player( Hubert ,3 2 , fa ls e ) , MVVM jest zarządzany przy użyciu klas
celu listę new Player( Ferdek ,8 , f a ls e ) , modelu, które z kolei są hermetyzowane
obiektów Player. }; przez obiekty modelu widoku.
return bomberPlayers;
c la ss RosterViewModel : INotifyPropertyChanged {
public ObservableCollection<PlayerViewModel> S ta rte rs { get; p riv a te se t; }
public ObservableCollection<PlayerViewModel> Bench { get; p riv a te s e t; )
var benchPlayers =
from player in _ro s te r.P la y e rs
where p la y e r.S ta rte r == fa lse To je s t podobne zapytanie
s e le ct p laye r; LINQ, które odnajduje graczy
B e n ch .C le a r(); s iedzących na ławce.
foreach (P layer player in benchPlayers)
Bench.Add(new PlayerViewModel(player.Name, player.Num ber));
C Z Y K O N T R O L K I U Ż Y T K O W N IK A N IE SĄ
W RZEC ZYW ISTO ŚC I JEDYN IE SPOSOBEM
R O Z D Z IE L E N IA K O D U X A M L N A K IL K A P L IK Ó W ?
C H W IL A ... TE C A Ł E R O Z W A Ż A N IA ^ -----
O H E R M E T Y Z A C JI I U M IE S Z C Z A N IU O B IE K T Ó W
W R Ó Ż N Y C H W A R S T W A C H B R Z M IĄ BAR D ZO Z N A J O M O . C Z Y NIE
O M A J Ą O N E CZEGOŚ W SPÓ LN EG O Z S EP A R A C JĄ Z A G A D N IEŃ ?
cla ss StopwatchModel { Z ró b to ! - Ą r
{
priiv a te DateTime? sta rte d ;
Te dwa pola
prywatne pri vate TimeSpan? _previousElapsedTime; Upewnij się, że utw orzyłeś tę klasę w folderze Model.
przechowują Pominęliśmy dodatkowe w iersze określające przestrzeń
stan aplikacji public bool Running { nazw, gdyż w iesz, jak mają wyglądać.
stopera. Oba get { return _started .H asV alue; } Moglibyśmy użyć dodatkowego pola logicznego, by śledzić,
akceptują } czy sto p er działa, czy j e s t zatrzym any, jednak przyjmowałoby
wartość null.
_ ono wartość true wyłącznie w przypadku, gdy pole _ sta rte d
publi ' c Ti meSpan? Elapsed { miałoby ja ką ś wartość. Czy nie lepiej będzie zatem zwracać
get { wartość _started.H asV alue?
i f (_started.H asValue) {
i f (_previousElapsedTime.HasValue)
return CalculateTim eElapsedSinceStarted() + _previousElapsedTime;
else
return C alculateTim eElapsedSinceStarted();
} Ta właściwość, przeznaczona tylko do
else odczytu, wylicza zm ierzony czas, używając
return _previousElapsedTime; do tego dwóch pól prywatnych . Przyjrzyj s ię
je j dokładnie. Czy rozum iesz, ja k ona dz'iała?
}
t
p riv a te TimeSpan CalculateTim eElapsedSinceStarted() { Oto podpowiedz: Kiedy dodajesz lub
return DateTime.Now - _s ta rte d .V a lu e ; odejm ujesz dane typu DateTime lub
} TimeSpan, za w sze u zy sk u je sz wynik
typu TimeSpan.
public void S t a r t () {
_sta rte d = DateTime.Now;
i f (!_previousElapsedTime.HasValue) Inne fragmenty aplikacji m u szą mieć
_previousElapsedTime = new TimeSpan(O); możliwość uruchamiania i zatrzym ywania
s t opera, dlatego model udostępnia
}
m etody s łużące do tego celu.
public void Stop() {
i f (_started.H asValue)
Z
Wyzerowanie _previousElapsedTime += DateTime.Now sta rte d .V a lu e ;
stanu _sta rte d = n u ll;
oznacza }
przypisanie
polom public void Reset() {
wartości null _previousElapsedTime = n u ll; S truktury TimeSpan oraz DateTime.
_sta rte d = n u ll;
} Istnieją dwie sfruk-but-y, k t óre są niezwykle przydatne podczas
operowania na czasie. Struktu rę DateTime, służącą do przechOWy Wania
public StopwatchModel() { daty i godziny, poznałes już wcześniej. Struktura TimeSpan reprezen tuje
R e s e t(); natom iast okres czasu. O k res ten m ierzony jest w ta k zwanych ta k tach
}
(an3; tlck) , p rzy czym jeden ta k t odpowiada jednej dziesięciomilionowej
?
Ta metoda inicjalizuje każdą
części sekundy/ czy |i na jedną milisekundę przypada 10 tysięcy
ta k tó w . Struktura TimeSpan udostępnia zatem m e to d y pozwalające
nową instancję StopwatchModel, konw ertow ać ta k ty na sekundy, milisekundy, dni itd.
tak by sto p er był wyzerowany
i zatrzym any.
}
referencji do
jakichkolwiek
p u b lic TimeSpan? LapTime { g e t; p r iv a t e s e t; } klas spoza
zupełności w ystarczy użyć właściwości modelu. Taki
Metoda p u b lic v o id Lap() { autom atycznej. Prywatne pole wewnętrzne
Lap()
LapTime = Elapsed; nie j e s t potrzebne, gdyż w tym przypadku model jest łatw iej
aktualizuje
właściwość OnLapTimeUpdated(LapTime)
nie trzeba hermetyzować żadnyclt otiltczeń. stw o rzyć, gdyż
i wywołuje
zdarzenie. } jest niezależny
p u b lic eve nt E ventH andler<LapEventArgs> LapTimeUpdated; od pozostałych
p r iv a t e v o id OnLapTimeUpdated(TimeSpan? lapT im e) {
warstw aplikacji
E ventH andler<LapEventArgs> lapTim eUpdated = LapTimeUpdated M VVM.
if (lapTim eU pdated != n u ll) {
la p T im e U p d a te d (th is , new L a p E v e n tA rg s (la p T im e ));
} t
Bardzo miłym efektem ubocznym
} To j e s t zw yczajny kod do oddzielonych od siebie warstw jest
wywoływania zdarzeń. możliwość zbudowania projektu
bezpośrednio po napisaniu klas modelu.
Jeśli IDE stwierdzi, że nie może znaleźć klasy StopwatchViewModel w przestrzeni nazw ViewModel,
792 Rozdział 16. choć Ty jesteś absolutnie pewny, że ją tam umieściłeś, to spróbuj skorzystać z opcji Unload Project iReload Project.
Tworzenie aplikacji według wzorca MVVM
Zachowaj naprawdę dużą ostrożność inie zakładaj, że IDE się myli. Czasami błąd w kodzie X A M L jednej strony
(taki jak nieprawidłowa właściwość xmlns) może spowodować problemy w e wszystkich oknach Designer. jesteś tutaj ► 793
N a p is z m o d e l w id o k u
%
Dodaj model widoku aplikacji stopera
Poniżej przedstawiony został kod klasy stanowiącej model widoku aplikacji stopera.
Upewnij się, że zostanie ona umieszczona w przestrzeni nazw ViewModel.
cla ss StopwatchViewModel : INotifyPropertyChanged {
p riv a te StopwatchModel _stopwatchModel = new StopwatchModel();
W łaściwość
p riv a te DispatcherTimer _tim er = new D ispatcherTim er(); Running odwołuje Te instrukcje using
się do modelu, będą konieczne do
public bool Running { get { return _stopwatchModel.Running; ) ) by sprawdzić czy skompilowania klasy.
stoper je s t aktualnie
public StopwatchViewModel() {
_ tim e r.In te rv a l = TimeSpan.FromMilliseconds(50);
_ tim e r.T ic k += Tim erTick;
_ t im e r .S t a r t ();
S ta rt();
uruchomiony.
using Model;
c
using System.ComponentModel;
_stopwatchModel.LapTimeUpdated += LapTimeUpdatedEventHandler;
using Windows.UI.Xaml;
}
public void S t a r t () {
_stopw atchM odel.Start();
}
Działanie metod Start(), Stop()
°raz Lap() sprowadza się
public void Stop() {
do wywołania analogicznych
_stopwatchModel.Stop();
metod modelu.
}
in t _lastH o u rs;
in t _lastM in utes;
decimal _lastSeconds;
void Tim erTick(object sender, object e) { Każde zdarzenie zgłoszone przez
i f (_lastH ours != Hours) { DispatcherTimer powoduje sprawdzenie,
_lastH ours = Hours; czy nie uległy zmianie właściwości
OnPropertyChanged("Hours"); określające liczbę zmierzonych sekund,
) minut i godzin. Je śli któraś z nich
i f (_lastM inutes != Minutes) { zmieniła wartość, to wywoływane je st
_lastM inutes = Minutes; odpowiednie zdarzenie PropertyChanged,
OnPropertyChanged("Minutes") pozwalając na aktualizację widoku.
)
i f (_lastSeconds != Seconds) {
_lastSeconds = Seconds; Składnia ? : pozwala zapisać w jednym wierszu
OnPropertyChanged("Seconds") wyrażenie warunkowe działające tak samo jak
)
) instrukcja i f . Więcej informacji na jego tem at możesz
znaleźć w punkcie 2. dodatku „Pozostałości".
public in t Hours {
get { 7 ^
return _stopwatchModel.Elapsed, HasValue ? _stopwatchModel.Elapsed.Value.Hours : 0;
)
)
public in t Minutes {
get { return _stopwatchModel.Elapsed.HasValue ? _stopwatchModel.Elapsed.Value.Minutes : 0; }
} N---------------------------------- ^
Właściwość Elapsed.Value zwraca daną typu TimeSpan,
publlgce tde{clmal Seconds { a jej właściwość Minutes zwraca liczbę typu int. P
if (_stopwatchModel.Elapsed.HasValue) {
return (decimal)_stopwatchModel.Elapsed.Value.Seconds
+ (_stopwatchM odel.Elapsed.Value.M illiseconds * .001M);
}
else Właściwość Seconds zwraca liczbę sekund oraz setnych
return 0 . 0M; sekundy w postaci liczby typu decimal. Ustaw tu
} pułapkę i skorzystaj z debuggera, aby przekonać się,
jak ta właściwość działa.
public in t LapHours {
get { return _stopwatchModel.LapTime.HasValue ? _stopwatchModel.LapTime.Value.Hours : 0; }
public in t LapMinutes {
get { return _stopwatchModel.LapTime.HasValue ? _stopwatchModel.LapTime.Value.Minutes : 0; }
}
in t _lastLapH ours;
in t _lastLapM inutes;
decimal _lastLapSeconds;
p riv a te void LapTimeUpdatedEventHandler(object sender, LapEventArgs e) {
i f (_lastLapHours != LapHours) {
}
_lastLapHours = LapHours;
OnPropertyChanged("LapHours"); V To je s t procedura obsługi zdarzeń LapTimeUpdated
i f (_lastLapM inutes != LapMinutes) { wywoływanych przez model. Działa bardzo podobnie
_lastLapM inutes = LapMinutes; do procedury obsługi zdarzeń wywoływanych przez
OnPropertyChanged("LapMinutes"); obiekt DispatcherTimer, czyli sprawdza właściwości
} przechowujące zmierzony czas i wywołuje zdarzenie
i f (_lastLapSeconds != LapSeconds) { PropertyChanged, je śli wartość którejś z nich uległa
_lastLapSeconds = LapSeconds;
OnPropertyChanged("LapSeconds");
}
}
W pierw szej kolejności ponownie otwórz plik B asicStopw atch.xam l.cs i dodaj do niego następ u jące
p ro ced u ry obsługi zdarzeń:
Całe zachowanie
t2 N astęp n ie usuń plik M a in P a g e.x a m l i za stą p go nową stroną typu B a sic P a g e, dokład n ie ta k sam o
zostało zdefiniowane
jak to robiłeś w p o p rzed n ich p ro jek tach (nie zapom nij tak że o ponow nym zbudow aniu aplikacji). w kontro/ce
użytkownika, d/atego
(3 O tw órz nowy plik M ainPage.xaml i do jego znacznika głów nego dodaj poniższą p rzestrzeń nazw: w kodzie ukrytym
strony głównej nie
xmlns:view="using:Stoper.View" znajdziemy nawet
jednego w iersza
związanego
Z m odyfikuj zasób AppName zdefiniow any w plik u M ainPage.xaml i podaj w nim nazwę aplikacji
z obsługą kontro/ki.
<Page.Resources>
<x:String x:Key="AppName">Stoper</x:String>
Solution Explorer ^ □ X
</Page.Resources> « û t0 - if # > [p]
Search Solution Explorer (Ctrl-«-;) p-
(5 Dodaj kontrolkę BasicStopwatch do kodu XAML strony MainPage.xaml: |rj| Solution 'Stoper' (1 project}
[c*l Stoper
<view:BasicStopwatch Grid.Row="1" Margin="120,0"/> >y Properties
> References
> £ Assets
T eraz aplikacja pow inna już działać. Kliknij przyciski: Start, Stop, Zeruj |> ■ Common
a Model
o raz O krążenie, by p rzek o n ać się, czy dobrze działają. |> c# LapEventArgs.es
CK StopwatchModel.es
a it View
a ,Q BasicStopwatch-xaml
Stoper a
> "l I BasicStopwatchjcaml.es
ViewModel
> c# StopwatchViewModel.es
> ,.Q App.xaml
Zmierzonyczas:0:0:14,335 a P| MainPagejcaml
Czasokrążenia:0:0:9.063 P ^ MainPage.xaml.es
He I Package.appxmanifest
J13 Stopwatch_TemporaryKey.pfx
Na samej górze klas dodaj instrukcję using Windows.UI.Xaml.Data;, a następnie zmień definicję klasy tak, by
implementowała interfejs IValueConverter. Skorzystaj z możliwości IDE, aby automatycznie zaimplementować
ten interfejs. Spowoduje to dodanie do klasy szkieletu dwóch metod: Convert() oraz ConvertBack().
using Windows.UI.Xaml.Data;
c l a s s TimeNumberFormatConverter : IVal ueConverter {
p u b l i c object Convert(object value, Type targetType,
object parameter, s t r i n g language) {
Ten konwerter wie,
i f ( v a l u e i s decimal)
jak konwertować r eturn ( ( d e c i m a l ) v a l u e ) . T o S t r i n g ( " 0 0 . 0 0 " ) ;
wartości typów else i f (value i s int) {
decimal oraz int.
W przypadku i f (parameter == n u l l )
zastosowania r eturn ( ( i n t ) v a l u e ) . T o S t r i n g ( " d 1 " ) ;
wartości typu
else
int można podać
opcjonalny r eturn ( ( i n t ) v a l u e ) . T o S t r i n g ( p a r a m e t e r . T o S t r i n g ( ) ) ;
parametr. } M etoda ConvertBack() j e s t używana w przy p adku s tosowanią powiązań
r eturn value; dwukierunkowych. W tym projekcie m e będz'\emy ich używ ać, więc
pozostaw im y szk ie le t m etody w niezm ien ionej p o sta ci.
}
Czy pozostawianie w kodzie w yjątku N otIm plem entedE xception jest dobrym pomysłem? W przypadku
tego projektu ten kod nigdy nie powinien zostać wykonany. Gdyby jednak był wykonywany, to czy lepiej
będzie, by przechodził niezauważony i użytkow nik nigdy się o nim nie dowiadyw ał? A może jednak lepiej
zgłaszać ten wyjątek, aby ła tw ie j było wyśledzić ew entualny problem? Które z tych rozwiązań pozwoli
stworzyć solidniejszą aplikację? Nikt nie tw ie rdzi, że istnieje tylko jedna właściwa odpowiedź na to pytanie.
798 Rozdział 16.
Tworzenie aplikacji według wzorca MVVM
Stoper
Z a n im w artości tra fią do k o n tro le k TextB ox,
Zm ierzony czas: 0 : 0 0 :1 1 ,55
zostaną n a jp ie rw przekazane do konw ertera, dzięki Czas okrążenia: 0 : 00 : 08.11
Bardzo często będziesz chciał w yśw ietla ć i ukrywać kontrolki, w zależności od właściw ości logicznych dostępnych
w kontekście danych. W łaściwość V i s i b i l i t y kon tro lki możesz jednak powiązać w yłącznie z właściw ością docelow ą typu
V i s i b i l i t y (co oznacza, że będzie ona zwracać w artości takie jak V is i b il it y . C o l la p s e d ) . Dodamy do projektu stopera
konw erter o nazwie B o o le a n V is ib ility C o n v e r te r , który pozw oli powiązać w łaściwość V i s i b i l i t y kontrolki z logiczną
właściw ością docelową, dzięki czemu kontrolkę będzie można w yśw ietla ć i ukrywać na podstaw ie w artości logicznej.
i n t _ la s tH o u rs ;
i n t _ la s tM in u te s ; Do procedury
decim al _ la s tS e c o n d s ; obsługi zdarzeń
bool _ la s tR u n n in g ; zegara doda/iśmy
sprawdzanie
v o id T im e rT ic k (o b je c t se n d e r, o b je c t e) { właściwości
i f (_ la s tR u n n in g != Running) { Running.
_ la s tR u n n in g = Running; Czy /epszym
O nP ropertyC hangedC 'R unning"); rozwiązaniem
byłoby
} ^ wywoływanie przez
if (_ la s tH o u rs != Hours) { mode/ zdarzenia?
_ la s tH o u rs = Hours;
O nP ro pertyC h an ged ("H o urs");
Theme Light *■
}
if (_ la s tM in u te s != M in u te s) { Jeś/i masz Default
prob/emy
_ la s tM in u te s = M in u te s ; Dark
}z rozpoznaniem
O n P ro p e rtyC h a n g e d ("M in u te s"); kontro/ki Light
} użytkownika
High Contrast (default)
if (_la stS e co n d s != Seconds) { w oknie Designer,
to możesz High Contrast White
_la stS e co n d s = Seconds;
spróbować High Contrast Black
O nPropertyC hanged("S econds"); wybrać w oknie
} Deive inny motyw High Contrast #1
} ko/orystyczny. High Contrast#?
c la ss BooleanNotConverter : IValueConverter {
p ublic object Convert(object value, Type targetType, object parameter, s trin g language) {
i f ((va lu e is bool) && ((b o o l)value ) == fa lse )
return tru e ;
else
return f a ls e ;
}
p ublic object ConvertBack(object valu e , Type targetType, object parameter, s trin g language) {
throw new NotImplementedException();
}
}
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
Stoper
Zmierzony czas: 0 :0 0 : 1242
Czas okrążenia: 0 :0 0 ; 00.00
© ®Stop
©Zeruj
®
O krążenie
Stoper działa
W rozdziale 11. dowiedziałeś się, że istnieje zasób statyczny o nazwie A p p B a rB u tto n S ty le ; został on zdefiniowany
w p lik u StandardStyles.xaml, któ ry jest dodawany do pro je ktu podczas tworzenia w nim nowej strony typu Basic
Page. A le o co w tym wszystkim chodzi? Jak to zawsze bywa w aplikacjach C # , nie ma w tym nic magicznego
i wszystko ma swoje wyjaśnienie: przyciski umieszczane na pasku aplikacji używają stylów oraz szablonów
k o n tro le k , które pozwalają zdefiniować ich wygląd tylko jeden raz, a stosować w ielokrotnie.
< R e s o u rc e D ic tio n a ry
x m ln s= "h ttp ://s c h e m a s .m ic ro s o ft.c o m /w in fx /2 0 0 6 /x a m l/p re s e n ta tio n "
x m ln s :x = "h ttp ://s c h e m a s .m ic ro s o ft.c o m /w in fx /2 0 0 6 /x a m l">
W yszukaj łańcuch A p p B a rB u tto n S ty le , by znaleźć zasób statyczny, którego użyłeś w przyciskach. Przekonasz
się, że został on zdefiniow any p rzy użyciu znacznika < S ty le > . S tyl zawiera grupę znaczników, któ re u s ta w ia ją
w a rto ś c i w ła ściw o ści d o w o ln e j k o n tro lk i, w k tó re j z o sta n ie o n zastosow any. W łaściwość T a rg e tT y p e
Te znaczniki stylu określa typ k o n tro lk i, w któ re j może on być stosowany — w tym przypadku jest to B u tto n B a se , czyli
okreś/ają ko/or,
wyrównanie klasa bazowa wszystkich k o n tro le k B u tto n . Styl zaw iera znaczniki < S e tte r> , określające w artości k o n tro le k,
oraz czcionkę, w których został on zastosowany.
która będzie
używana we
< S ty le x:K e y = "A p p B a rB u tto n S ty le " T arge tT yp e= "B utton B ase">
wszystkich
przyciskach, r .< S e tte r P ro p e rty= "F o re g ro u n d "
w których
został V a lu e = "{S ta tic R e s o u rc e A ppB arItem ForegroundThem eBrush}"/>
zastosowany < S e tte r P ro p e rty = " V e rtic a lA lig n m e n t" V a lu e = "S tre tc h "/> Ten zasób statyczny okreś/a ja sny
ten sty/.
< S e tte r P ro p e rty = "F o n tF a m ily " Value="Segoe UI S ym bol"/> bądź ciemny ko/or, za/eżnie od iw ty w u
ko/orystycznego zastosowanego do
Inne kontro/ki mogą < S e tte r P ro p e rty = "F o n tW e ig h t" V a lu e = "N o rm a l"/> wyświet/enia kontro/ki .
używać tej właściwości,
by dowiedzieć się, że < S e tte r P ro p e rty = "F o n tS iz e " V a lu e = "2 0 "/>
dana kontro/ka je s t — < S e tte r P ro p e rty = "A u to m a tio n P ro p e rtie s .Ite m T y p e " Value="App Bar B u tto n "/>
przyciskiem paska
ap/ikacji.
K o le jn y znacznik < S e tte r> określa wartość właściwości T em plate; będzie nią: < C o n tro lT e m p la te > . Z nacznik
ten definiuje szablon k o n tro lk i. K ie d y przycisk jest rysowany na stronie, system W indows szuka szablonu
k o n tro lk i, żeby dowiedzieć się, ja k ją należy przedstawić, i wyświetla wszystkie k o n tro lk i umieszczone w szablonie.
C H W IL E C Z K Ę ,
T O M I W Y G L Ą D A Z N A J O M O . C Z Y S Z A B LO N Ó W
C O N T R O L T E M P L A T E N IE U Ż Y W A L IŚ M Y C Z A S E M W R O Z D Z IA L E 1.?
Szablon rysuje przycisk, używając w tym celu k o n tro lk i StackPanel, zawierającej k o n tro lk ę Grid oraz TextBlock .
K o n tro lk a Grid nie zawiera żadnych wierszy ani ko lu m n — w ykorzystuje zasadę, że k o n tro lk i w kom órce są
rysowane je dn a na drugiej (o czym miałeś okazję się przekonać w p o prze dnim rozdziale p rzy oka zji prezentacji
zdarzeń trasowanych). U żyw a dwóch znaków czcionki Segoe U I Symbol, by narysować okrą gły przycisk: znak
 przedstaw ia w yp ełn io ny okrąg, a znak  — pusty okrąg. (Sprawdź to samemu w program ie
Tablica znaków). Ponad znakam i w yśw ietlana jest k o n tro lk a ContentPresenter. W m om encie tw orzenia
o b ie ktu Button k o n tro lk a ta jest zastępowana dow olną treścią umieszczoną pom iędzy znacznikiem otw ierającym
i zamykającym przycisku, lu b podaną w jego właściwości Content — w dosłownym znaczeniu tego słowa,
przedstaw ia ona zawartość.
<ControlTemplate TargetType="ButtonBase">
/Grid> Na poprzedniej s t ronie urnteścifeś w przycisku znak E103 „pauza", więc to właśnie
on z os! ani e u, mief zcz° ny w k°nfrolce TextBlock. Znaczniki <Setter> przedstawione
w kroku 2. określają, że w k° ntr° lce będzie używana czcionka Segoe U I Symbol.
<TextBlock
x:Name="TextLabel" Text= "{ TemplateBinding AutomationProperties.Name)
Foreground="{StaticResource AppBarItemForegroundThemeBrush)1
M argin="0,0,2,0" FontSize="12" TextAlignment="Center"
Width="88" MaxHeight="32" TextTrimming="WordEllipsis"
Style= "{StaticR eso urce B asicTe xtS tyle)"/>
</StackPanel>
O statnie dwie k o n tro lk i w yśw ietlają obram ow anie w o k ó ł całej k o n tro lk i. Jedna z nich nosi nazwę
F o c u s V is u a lW h ite i jest prezentow ana ja k o lin ia przerywana. A druga nosi nazwę F o c u s V is u a lB la c k i także
jest prezentow ana ja ko lin ia przerywana, je d n a k m a m niejsze kreseczki. Możesz używać tych pro sto kątów
podczas korzystania ze stopera do w yró żnia nia wybranego przycisku p rzy użyciu klawisza Tab .
<R ectangle
x:N am e="FocusV isualW hite" I s H itT e s tV is ib le = " F a ls e "
S tro k e = "{S ta tic R e s o u rc e F ocusV isualW hiteS trokeT hem eB rush}"
S trokeEndLineC ap="Square" S tro ke D a sh A rra y= "1 ,1 "
O p a c ity = "0 " S tro k e D a s h O ffs e t= "1 .5 "/>
<R ectangle
x:N am e="F ocusV isualB lack I s H itT e s tV is ib le = " F a ls e "
S tro k e = "{S ta tic R e s o u rc e FocusV isualB lackS trokeT hem eB rush}"
S trokeEndLineC ap="Square" S tro ke D a sh A rra y= "1 ,1 "
O p a c ity = "0 " S tro k e D a s h O ffs e t= "0 .5 "/>
Styl został dodany ja ko zasób statyczny i przypisano m u klucz A p p B a rB u tto n S ty le , aby można go było stosować w przyciskach
(oraz wszelkich innych klasach dziedziczących po B utton B ase ) przy użyciu właściwości S ty le . A co by się stało, gdybyśmy
p o m in ę li określenie klucza? W takim przypadku styl zostanie a u to m a tyczn ie zastosow any we w szystkich kla sa ch
p a sujących do k la s y po da ne j we w łaściw ości T a rg e tT yp e . Zastosujm y zatem ten styl, aby przyjrzeć m u się w działaniu.
O tw ó rz p lik BasicStopwatch.xaml i zm ień sekcję < U s e rC o n tro l.R e s o u rc e s > , dodając styl w fo rm ie zasobu
statycznego i przypisując jego właściwości T a rg e tT y p e w artość T e x tB lo c k . Styl p o w in ie n określać wielkość
oraz grubość czcionki:
K o n tro lk i oraz szablony k o n tro le k używają ta k zwanych grup stanu w izualnego do zm iany wyglądu
oraz zachowania k o n tro le k w zależności od stanu, w ja k im się one znajdują. Przyciski dysponują grupą
stanów o nazwie CommonStates obejm ującą stany: Normal, PointerOver (gdy w skaźnik myszy znajduje
się nad przyciskiem ), Pressed (kie d y u żytko w n ik naciska przycisk) oraz Disabled (kie dy przycisk
jest wyłączony). Szablon k o n tro lk i w stylu AppBarButtonStyle zaw iera sekcję <VisualStateGroup>
określającą, ja k zm ieniają się właściwości przycisku, gdy znajduje się on w jednym z tych stanów.
Używaj DoubleAnimation,
by animować wartości zmiennoprzecinkowe
K ie d y w kodzie X A M L podajesz w artości właściwości takich ja k W id th lu b H e ig h t, to pow oduje to określenie
w artości właściwości typ u d o u b le . Klasa D o u b le A n im a tio n pozwala na sto p n io w e z m ie n ia n ie w ła ś c iw o ś c i ty p u
d o u b le od je d n e j w a rto ś c i do d ru g ie j, w za d a n ym o kre sie czasu, i jest często stosowana do m odyfikow ania
postaci k o n tro lk i, gdy przechodzi ona do jakiegoś stanu w izualnego. Szablon k o n tro lk i w stylu A p p B a rB u tto n S ty le
używa klasy D o u b le A n im a tio n do w ykonania anim acji wybranego przycisku, polegającej na zm ianie przezroczystości
dwóch pro sto kątów wyświetlanych w o k ó ł k o n tro lk i od w artości 0 (przezroczysta) do 1 (nieprzezroczysta).
O kres, w ja k im będzie w ykonywana ta anim acja, wynosi 0, co oznacza, że zm iana zajdzie natychm iast:
< V is u a lS ta te x:Name="Focused">
< S to rybo ard>
<DoubleAnim ation
Animowana je s t S toryb oard .T arg etN a m e= "F ocusV isua lW h ite"
kontrolka o nazwie
S to ry b o a rd .T a rg e tP ro p e rty = "O p a c ity " Kiedy p rzycisk wychodzi ze stanu Focused,
FocusVisualW hite.
To="1" obiekt Storyboard j e s t przywracany do
D u ra tio n = "0 "/> stanu początkowego, a animacje powrócą
<DoubleAnim ation do sw ych w artości początkowych; w naszym
Anim acja zmienia
S to ryb o a rd .T a rg e tN a m e = "F o cu sV isu a lB la ck" przypadku oznacza to, że w łaściw ość
w artość w łaściw ości
O pacity przyjm ie w artość 0.
O pacity od je j S to ry b o a rd .T a rg e tP ro p e rty = "O p a c ity "
w artości domyślnej To="1"
do w artości 1 . D u ra tio n = "0 "/>
< /S to ry b o a rd >
< /V is u a lS ta te >
Poeksperym entuj nieco z anim acjam i, abyś trochę po zna ł ich działanie. W tym celu skopiujesz cały styl do swojej k o n tro lk i
użytko w nika, zmienisz przyciski tak, by korzystały z nowego stylu, a następnie wprowadzisz k ilk a zm ian w anim acji:
S kop iu j cały styl, zaczynając od < S ty le x :K e y = "A p p B a rB u tto n S ty le " T a rg e tT y p e = "B u tto n B a s e "> , a kończąc
na < /S ty le > . W k le j go do s e k c ji < U s e rC o n tro l.R e s o u rc e s > w p lik u BasicStopwatch.xam l, bezpośrednio poniżej
kon w e rte rów , a następnie zm ie ń w łaściw ość x :K e y z A p p B a rB u tto n S ty le na S to p w a tc h B u tto n S ty le .
Z m o d y fik u j wszystkie cztery przyciski tak, by korzystały z nowego stylu. W tym celu w każdym z nich
Przyciski
zm ień właściwość S t y le na S t y le = " { S t a tic R e s o u r c e S to p w a tc h B u tto n S ty le } " . — jeszcze nie
będą wyglądały
Z m o d y fik u j znaczniki D o u b le A n im a tio n w stanie w izualnym Focused tak, by zm iana nie zachodziła inaczej, gdyż
nowy styl,
natychm iast, lecz by trw ała pięć sekund. Czas trw an ia anim acji zawsze jest zapisywany w postaci: który dodałeś
g o d z in y :m in u ty :se k u n d y , a zatem użyj właściwości D u r a t io n = " 0 : 0 : 5 " (w prow adź tę zmianę jako zasób
statyczny, je s t
w obu anim acjach, by działały tak samo zarówno w jasnym, jak i ciem nym m otyw ie kolorystycznym ).
skopiowanym
stylem, który był
U ru ch o m program , następnie u ż y j k la w is z a Tab , aby zm ieniać aktu aln ie w ybrany przycisk. w nich używany
Teraz przeryw ana lin ia po w in na pojaw iać się i zanikać p o w o li, w czasie p ię ciu sekund. ju ż wcześniej.
Ponow nie zm od yfikuj anim acje; tym razem użyj w nich następujących właściwości: D u r a t io n = " 0 : 0 : 0 . 5 "
A u to R e v e rs e = "tru e " R e p e a tB e h a v io r= "F o re v e r".
3 Ponow nie uru cho m aplikację. Teraz przeryw any pro sto kąt na wybranym przycisku pulsuje — p o ja w ia się płynn ie
w czasie p ó ł sekundy, a następnie p łynn ie zanika w czasie kolejnej p o ło w y sekundy.
D ziałanie anim acji ram k i kluczowej (ang. key fra m e anim ations) p o leg a n a tw orzeniu ra m e k klu c z o w y c h . R a m k a kluczow a
jest dyskretnym zdarzeniem , zachodzącym w anim acji w określonym czasie. M ożesz się p rzek o n ać, ja k one działają,
d o d a ją c jeszcze je d n ą , trz e c ią a n im a c ję do w iz u a ln e g o s ta n u P re sse d . U m ieść ją b ezp o śred n io p rzez zamykającym
znacznikiem < /S to ry b o a rd > :
Ponow nie uru ch o m aplikację. K iedy tera z naciśniesz przycisk, znak zapisany w jego właściwości C o n te n t zacznie błyskać.
Czy zauw ażyłeś, że anim acja zostanie zatrzym ana w połow ie, jeśli p rzestan iesz naciskać przycisk? D zieje się tak, poniew aż
stan przycisku zm ienił się n a N o rm al , a zatem anim acja pow róciła do p u n k tu początkow ego.
using Windows.UI.Xaml.Data;
cla ss AngleConverter : IValueConverter {
public object Convert(object valu e , Type targetType, object parameter, s trin g language) {
double parsedValue;
i f ((va lu e != n u ll)
&& d o u b le .T ry P a rse (v a lu e .T o S trin g (), out parsedValue)
&& (parameter != n u ll))
Wartości godzin należą do zakresu od
switch (param eter.To Strin g ()) { 0 do 11, a zatem, aby skonw&rtować
case "Hours": je na kąt, wystarczy je pomnożyć
return parsedValue * 30;
przez 30.
case "Minutes":
case "Seconds": Minuty i sekundy przyjmują wartości
return parsedValue * 6; < z zakresu od 0 do 59, a zatem w ich
} przypadku konwersja polega na pomnożeniu
return 0; ich przez 6.
public object ConvertBack(object valu e, Type targetType, object parameter, s trin g language) {
throw new NotImplementedException();
<ResourceDictionary
xmlns="http://schem as.m icrosoft.com /winfx/2006/xam l/presentation"
xm lns:x="http://schem as.m icrosoft.com /winfx/2006/xam l"
xmlns:local="using:Stopwatch.View">
</Style>
</ResoucreDictionary>
N astępnie zm od yfikuj p lik A pp.xam l i dodaj sło w n ik do zasobów ap lika cji. K ie d y tworzysz nową aplikację dla
Sklepu W indow s, I D E tw orzy p lik App.xam l zawierający jeden znacznik <Application.Resources> i to właśnie
dzięki niem u aplikacja w ie o istn ie n iu p lik u StandardStyles.com . Z m o d y fik u j ten znacznik, dodając do niego
in fo rm a cje o nowym sło w niku zasobów:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--
S ty le s that define common aspects of the platform look and feel
Required by Visual Studio p roject and item templates
-- >
<ResourceDictionary Source="Common/StandardStyles.xaml"/>
<ResourceDictionary Source="View/StopwatchStyles.xaml"/>
</ResourceDictionary.M ergedDictionaries> Kiedy dodasz ten wiersz kodu do pliku A pp.xamh
style z nowego pliku Stopwatch S ty le s xaml
</ResourceDictionary> zostaną dołączone d° zasobów apHkacj i .
</Application.Resources>
Podoba nam
się wygląd T eraz możesz ju ż dodać przyciski. W ystarczy, że skopiujesz całą k o n tro lk ę StackPanel w raz z zawartością
aplikacji i umieścisz ją w nowej kon tro lce .
z przyciskami l^idaj wyrównanie w pionie,
widocznymi <StackPanel O rientation= "H orizontal" VerticalAlignment="Bottom"> ^—' by um ieścić przyciski na dole .
na tle tarczy <Button Style= "{StaticR esource StopwatchButtonStyle)"
stopera. IsEnabled="{Binding Running, Converter={StaticResource notConverter}}"
Możesz je AutomationProperties.Name="Start" Click="StartButton_Click"></Button>
także dodać <Button Style= "{StaticR esource StopwatchButtonStyle)" IsEnabled="{Binding Running)"
w drugim AutomationProperties.Name="Stop" Click="StopButton_Click"></Button>
wierszu <Button Style= "{StaticR esource StopwatchButtonStyle)" Click= "ResetButton_Click"
siatki, by były AutomationProperties.Name="Zeruj"></Button>
wyświetlone <Button Style= "{StaticR esource StopwatchButtonStyle)" IsEnabled="{Binding Running)"
poniżej tarczy. AutomationProperties.Name="Okrążenie" Click="LapButton_Click"></Button>
</StackPanel>
Stoper
Zm ierzon y czas: 0 : 2 1 : 12.95
Czas okrążenia: 0 : 2 0 : 51.06
N AJPR AW D O PO D O BN IEJ W ŁA Ś N IE
T O PR Z Y C H O D ZI M I DO G ŁO W Y,
KIEDY M YŚLĘ O ANIMACJI...
s to ry b o a rd .R e p e a tB e h a v io r = R e p e a tB e h a vio r.F o re ve r;
sto ry b o a rd .A u to R e v e rs e = t r u e ;
s to ry b o a rd .C h ild re n .A d d (a n im a tio n ); Kiedy obiekt S to ry b o a rd został już skonfigurowany,
s to ry b o a rd .B e g in ( ); a animacje dodane do jego kolekcji C h ild re n , możesz
} rozpocząć animację, w yw ołując jego metodę B e g in ().
U l 1a
Kontrolka Animatedlmage jest niewidoczna aż do momentu,
gdy zostanie w yw ołana jej metoda S ta r tA n im a tio n ( ) ,
dlatego też kontrolki w yśw ietlone w ew nątrz kontrolki
Canvas będą pokazane w form ie zarysu. Można je wybrać,
korzystając z okna Document Outline. Spróbuj poprzeciągać
je w różne miejsca kontrolki Canvas, sprawdzając przy tym
__ 1
wartości właściwości C a n v a s .L e ft oraz Canvas.Top.
Teraz możesz zm odyfikow ać ko n stru kto r umieszczony w p lik u FlyingBees.xaml.cs, aby u rucham iał animację
pszczoły. D o datkow o utw ó rz animację typu D o u b le A n im a tio n , k tó ra będzie m odyfikow ać w artości właściwości
C a n v a s .L e ft. Porównaj kod odpow iedzialny za utw orzenie obiektów S to ry b o a rd i o b ie ktu anim acji, z kodem
X A M L używającym znacznika < D o u b le A n im a tio n > przedstawionym we wcześniejszej części rozdziału.
p u b lic F ly in g B e e s () {
t h is . I n itia liz e C o m p o n e n t ( ) ;
{
S to ry b o a rd .S e tT a rg e t(a n im a tio n , f ir s t B e e ) ; z pamięci po zakończeniu odtwarzania
we wcześniejszej
części rozdziału, S to ry b o a rd .S e tT a rg e tP ro p e rty (a n im a tio n , " ( C a n v a s .L e ft) 1 animacji. Możesz się
teraz samemu an im a tio n .F ro m = 50; o tym przekonać, korzystając
utworzysz obiekty a n im a tio n .T o = 450; z opcji M ake O bje ct ID, by zacząć
Storyboard
i DoubleAnimation a n im a tio n .D u ra tio n = Tim eSpan.FromSeconds(3); obserwować obiekt, i klikając O , by
i ustaw isz wartości a n im a tio n .R e p e a tB e h a v io r = R e p e a tB e h a vio r.F o re ve r; odświeżyć go po zakończeniu animacji,
ich właściwości. a n im a tio n .A u to R e ve rse = t r u e ; ale najpierw zamień w yw ołanie
s to ry b o a rd .C h ild re n .A d d (a n im a tio n ); RepeatBehavior na RepeatBehavior(2),
s to ry b o a rd .B e g in () ; w przeciwnym przypadku animacja
nigdy się nie zakończy!
W tym projekcie coś jest nie w porządku. Czy potrafisz wskazać, gdzie tk w i błąd?
jesteś tutaj ► 819
Pamiętaj — MVVM jest wzorcem
? ? ?
* * *
T O PROSTE. W Y S T A R C Z Y DO D AĆ KO LEKCJĘ T Y P U
O B S E R V A B LE C O LLE C T IO N , W KTÓ R EJ BĘDZIEM Y
O P R Z E C H O W Y W A L I K O N T R O L K I, I P O W IĄ Z A Ć JĄ
Z W ŁA Ś C IW O Ś C IĄ C H ILD R EN K O N T R O L K I CANVAS.
C Z E M U ROBICIE Z TEG O T A K I W IE L K I PROBLEM?
< / I t e m s C o n t r o l. I t e m s P a n e l>
< / I t e m s C o n t r o l> W momencie tw orzenia kontrolki Item sTem plate tw o rzy ona Panel,
którego będzie używała do prezentowania swoich elementów,
a do określenia ich postaci używa szablonu Item sPanelTem plate.
s t a t i c c la s s BeeHelper {
p u b lic s t a t i c Animatedlmage B eeF actory(
do ub le w id th , double h e ig h t, TimeSpan f la p I n t e r v a l ) {
Z 1
Ta metoda L is t< s t r in g > imageNames = new L is t < s t r in g > ( ) ;
wytwórcza tworzy imageNames.Add("Bee a n im a tio n l.p n g " )
kontrolki B e e .
imageNames.Add("Bee a n im a tio n 2 .p n g ")
U m ieszczenie je j
w w arstw ie widoku imageNames.Add("Bee a n im a tio n 3 .p n g ")
j e s t sensownym imageNames.Add("Bee a n im a tio n 4 .p n g ")
rozwiązaniem,
gdyż cały ten kod
AnimatedImage bee = new AnimatedImage(imageNames, f la p I n t e r v a l ) ;
j e s t związany
z interfejsem be e.W idth = w id th ;
użytkownika. Kiedy jakiś często używany fragm ent kodu umieścisz w odrębnej (często statycznej)
b e e .H e ig h t = h e ig h t;
metodzie, to taka metoda jest zazwyczaj nazywana metodą pomocniczą.
r e tu r n bee;
Sensownym rozwiązaniem, które ułatw ia analizę kodu, jest gromadzenie takich
}
metod w klasie statycznej, której nazwa kończy się słowem „Helper".
Te informacje Wszystkie kontrolki XAML dziedziczą po klasie bazowej UIElement, zdefiniowanej w przestrzeni nazw W indows.UI.Xaml.
przydadzą Ci W tym przypadku celowo podaliśmy przestrzeń nazw (W indows.UI.Xam l.U IElem ent) w kodzie klasy, a nie w instrukcji
s ię w ostatnim
laboratorium. using, aby ograniczyć ilość kodu związanego z interfejsem użytkownika, dodawanego do klasy modelu widoku.
Zastosowaliśmy klasę UIElement, gdyż jest to najbardziej abstrakcyjna z klas, po której dziedziczą wszystkie sprajty.
W niektórych projektach bardziej odpowiednim rozwiązaniem mogłoby być zastosowanie klasy FrameworkElement,
gdyż to w łaśnie ona definiuje w iele właściwości takich jak: H e ig h t, W idth, O p a city, H o r iz o n ta lA lig n itd.
c l a s s BeeViewModel {
p r i v a t e re a d o n ly O b servableC ollection< W indow s.U I.X am l.U IElem ent>
_ s p r it e s = new O b serv ab leC o llectio n < W in d o w s.U I.X am l.U IE lem en t> ();
p u b lic IN o tify C o lle c tio n C h a n g e d S p r ite s { g e t { r e tu r n _ s p r i t e s ; } }
Hermetyzacja właściwości
p u b lic BeeViewModel() { S p rite s je s t przeprowadzana
AnimatedIm age f i r s t B e e = w dwóch etapach. Pierwszym
B e e H e lp e r.B e e F a c to ry (5 0 , 50, z nich je s t dodanie do definicji
T im e S p a n .F ro m M illise c o n d s(5 0 )); po/a wewnętrznego modyfikatora
Sprajt (ang. readon/y, dzięki czemu jego
sprite) to term i n s p r ite s .A d d ( fir s tB e e );
początkowej wartości nie
określający wsze l k i e będzie można później zmienić.
AnimatedIm age secondBee = Dodatkowo właściwość je s t
dw uw ym iarow e
obrazki lub
B e e H e lp e r.B e e F a c to ry (2 0 0 , 200, T im e S p a n .F ro m M illise c o n d s(1 0 )); typu JNotifyCo//ectionChanged,
_ s p rite s .A d d (s e c o n d B e e ); dzięki czemu inne k/asy mogą
animacje, stosowa ne j ą jedynie obserwować, /ecz nie
w dużych grach mogą je j zmieniać.
kom puterowy ch
AnimatedIm age th ird B e e =
lub większych
B e e H e lp e r.B e e F a c to ry (3 0 0 , 125, T im e S p a n .F ro m M illise co n d s(1 0 0 ));
_ s p r ite s .A d d ( th ir d B e e ) ;
animacjach.
Zmieniasz właściwości kontrolek
B eeH elper.M akeB eeM ove(firstB ee, 50, 450, 40) i dodajesz do nich animacje już
B e e H e lp e r.S e tB e e L o c a tio n (se c o n d B e e, 8 0 , 260) po dodaniu kontrolek do kolekcji
B e e H e lp e r.S e tB e e L o c a tio n (th ird B e e , 230, 100)
O b s e r v a b le C o lle c t io n . Zatem
dlaczego ten kod działa?
Długie ćwiczenie
To już ostatnie ćwiczenie w tej książce. Twoim zadaniem będzie napisanie programu, który animuje pszczoły
i gwiazdy. Trzeba będzie napisać sporo kodu, jednak jesteś do tego dobrze przygotowany... a kiedy już się
uporasz z tym zadaniem, będziesz dysponował wszystkimi narzędziami niezbędnymi do napisania gry
wideo. (Czy potrafisz odgadnąć, co będzie tematem trzeciego Laboratorium?).
Pszczoły latają po
niebie, udając się
w losowo wybrane
m iejsca. Kiedy
w m i ary .nie ba c Gwiazdy przygasają
(kontrolki Canvas) i rozbłyskują.
ulegną zmianie,
pszczoły polecą A
w nowe miejsca.
Visual Studio d ostarcza fantastycznego narzęd zia do przeprow adzania eksperym entów z przeróżnym i
kształtam i! Uruchom program Blend for V isual Studio 2012 i sko rzystaj z pióra, ołów ka i przybornika,
824 b y tw o rz y ć k szta łty X A M L, które następnie będziesz mógł kopiować i u ży w ać w swoich projektach C # .
Tworzenie aplikacji według wzorca MVVM
K tóu przeds t awi°n eg ° w Irnliu 4. nie uda. się skom pilować aż do momentu
dodania w kroku 9. i w ^ ś a ^ ś d play A re a S iz e . Na razie m ożesz użyć ID E do
wyge nerowania szk ieletu te j w ła ściw ości. .
^ DODAJ KOD UKRYTY STRONY ORAZ APLIKACJI.
D o p lik u BeesOnAStarryNight.xaml w folderze View dodaj procedurę obsługi zdarzeń
S izeC hanged:
p r iv a t e v o id S ize C h a n g e d H a n d le r(o b je ct sen de r, SizeChangedEventArgs e) {
v ie w M o d e l.P la yA re a S ize = new S iz e (e .N e w S iz e .W id th , e .N e w S iz e .H e ig h t);
Oprócz elips, prostokątów i w ielo kątów istnieją także inne kształty; możesz się o nich
dowiedzieć na stronie: http://msdn.microsoft.com/library/windows/apps/hh465055.aspx.
jesteś tutaj ► 825
Ach moje gwiazdy
Długie ćwiczenie
Rozwiązanie DODAJ DO W ARSTW Y WIDOKU KLASĘ BeeS TARHelper.
#6using Windows.UI.Xaml;
Poniżej przedstaw iliśm y ko d użytecznej klasy pom ocniczej. Z aw iera
m etody, k tó re ju ż znasz, oraz k ilk a nowych. U m ieść ją w folderze View.
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Shapes;
s t a t ic c la ss BeeStarHelper {
public s t a t ic AnimatedImage BeeFactory (double width, double height, TimeSpan fla p In te rv a l) {
List< string> imageNames = new L is t< s trin g > ();
imageNames.Add("Bee animation 1.png");
imageNames.Add("Bee animation 2.png");
imageNames.Add("Bee animation 3.png");
imageNames.Add("Bee animation 4.png");
u s in g W indow s.Foundation;
c la s s Bee {
p u b lic P o in t L o c a tio n { g e t; s e t; }
p u b lic S ize S ize { g e t; s e t; }
p u b lic Rect P o s itio n { g e t { r e tu r n new R e c t(L o c a tio n , S iz e ); } }
p u b lic do ub le W idth { g e t { re tu r n P o s itio n .W id th ; }}
u s in g W indow s.Foundation;
p u b lic do ub le H e igh t { g e t { r e tu r n P o s itio n .H e ig h t; } }
c la s s S ta r {
p u b lic B e e (P o in t lo c a t io n , S ize s iz e ) {
p u b lic P o in t L o c a tio n {
L o c a tio n = lo c a t io n ; g e t; s e t;
S ize = s iz e ; }
}
} p u b lic S ta r ( P o in t lo c a t io n ) {
L o c a tio n = lo c a t io n ;
u s in g W indow s.F oundation; }
c la s s BeeMovedEventArgs : EventArgs { } Kiedy ju ż uda Ci się uruchomić tę
p u b lic Bee BeeThatMoved { g e t; p r iv a t e s e t; } aplikację, spróbu j dodać do klasy
S ta r logiczną w łaściw ość Rotating
p u b lic double X { g e t; p r iv a t e s e t; }
i użyj je j do powolnego obracania
p u b lic double Y { g e t; p r iv a t e s e t; } niektórych gwiazd.
M odel będzie wywoływał zdarzenia używ ające tej klasy EventA rgs, S tru k tu ra R ect udostępnia
by informować model widoku o zachodzących zmianach. kilka przeciążonych
konstruktorów i metod,
u s in g W indow s.F oundation; pozwalających na pobieranie
c la s s StarChangedEventArgs : EventArgs { jego szero kości, w ysokości,
w ielkości i położenia
p u b lic S ta r StarThatChanged { g e t; p r iv a t e s e t; }
(w formie danych typu
p u b lic bool Removed { g e t; p r iv a t e s e t; } Point lub pojedynczych
współrzędnych X i Y ).
p u b lic S tarC h an ge dE ven tA rgs(S tar starT hatC hanged, bool removed) {
StarThatChanged = starT hatC hanged;
Removed = removed;
} S truktury Point, Size oraz Rect.
_____________________________________ W przestrzeni Windows.Foundation dostępnych jest kilka przydatnych struktur.
Struktura Point używa właściwości X i Y typu double do przechowywania
Właściwość Points ----- zest awu współrzędnych. Także struktura Size ma dwfe whśdwośd typu doub|e
kontrolki Polygon je s t — W idth oraz Height — oraz trzecią, specjalną właściwość Emp iy Struktura
kolekcją stru k tu r Point.
Rect przechowuje dwa zestawy współrzędnych, ° kreś|ające położenie teweg°
górnego oraz prawego dolnego wierzchołka p to s to l^ta . Udostępnia także wie|e
użytecznych metod, na przykład do określania jego szerokości, wysokości, części
wspólnej z innymi prostokątami i tak dalej. 827
Bzzz, bzzz, bzzz
class BeeStarViewModel {
private readonly ObservableCollection<UIElement>
_sprites = new ObservableCollection<UIElement>();
public INotifyCollectionChanged Sprites { get { return _sprites; } }
public BeeStarViewModel () {
I I O k r e ś l p r o c e d u r y o b s łu g i z d a r z e ń BeeM oved o r a z S ta rC h a n g e d k l a s y B e e S t a r M o d e l, a n a s t ę p n ie
I I uruchom z e g a r , b y z g ł a s z a ł z d a r z e n ie c o d w ie s e k u n d y .
}
void timer_Tick(object sender, object e) {
I I U ramach o b s łu g i k a żd e g o z d a r z e n ia T i c k z e g a r a z n a jd ź w s z y s t k i e r e f e r e n c j e S t a r C o n t r o l w k o l e k c j i
II _ f a d e d S t a r s i usuh k a żd ą z n ic h z k o l e k c j i _ s p r i t e s , n a s t ę p n ie w yw o ła j m etod ę U p d a te () k l a s y
I l B e e V ie w M o d e l, a b y j ą p o in fo rm o w a ć o k o n i e c z n o ś c i a k t u a l i z a c j i .
}
void BeeMovedHandler(object sender, BeeMovedEventArgs e) {
I I S ło w n ik _ b e e s o d w zo ro w u je o b i e k t y B ee przech o w y w a n e p r z e z m odel na k o n t r o l k i A n im a te d lm a g e używ ane w w id o k u .
I I U p rz y p a d k u p r z e s u w a n ia p s z c z o ł y o b ie k t B e e S ta rM o d e l w y w o łu je z d a r z e n ie BeeM oved, b y p o in fo rm o w a ć w s z y s t k ic h
I I z a in t e r e s o w a n y c h , k t ó r a p s z c z o ła z m ie n ił a p o ł o ż e n ie i g d z ie s i ę t e r a z z n a j d u je . J e ś l i s ł o w n i k _ b e e s
I I j e s z c z e n i e z a w ie r a ż a d n y ch k o n t r o l e k A n im a te d lm a g e r e p r e z e n t u ją c y c h p s z c z o ł y , t o n a l e ż y u t w o r z y ć ta k ą
I I k o n t r o l k ę , o k r e ś l i ć j e j p o ł o ż e n ie w o b s z a r z e k o n t r o l k i C a nva s o r a z z a k t u a liz o w a ć k o l e k c j e _ b e e s i _ s p r i t e s .
I I J e ś l i s ło w n i k _ b e e s z a w ie r a j u ż j a k i e ś k o n t r o l k i , t o n a l e ż y j e d y n i e o d s z u k a ć o d p o w ia d a ją c e im k o n t r o l k i
I I A n im a te d lm a g e i p r z e s u n ą ć j e w nowe m i e j s c e , u ż y w a ją c p r z y tym a n i m a c ji.
}
void StarChangedHandler(object sender, StarChangedEventArgs e) {
I I S ło w n ik _ s t a r s d z i a ł a p o d o b n ie j a k _ b e e s , z t ą r ó ż n i c ą , ż e o d w zo ro w u je o b i e k t y S t a r na o d p o w ia d a ją c e im
I I k o n t r o l k i S t a r C o n t r o l . P a ra m e try E v e n tA r g s z a w ie r a ją r e f e r e n c j e do o b ie k tó w S t a r ( k t ó r e d y s p o n u ją w ła ś c iw o ś c ią
I l L o c a tio n ) o ra z w ła ś c iw o ś ć lo g ic z n ą in fo rm u ją c ą , c z y dana gw iazda z o s t a ł a u s u n ię t a . J e ś l i gw iazda z o s t a ł a u s u n ię t a ,
I I to chcem y j ą p ł y n n ie u k r y ć - w tym c e l u n a l e ż y u su n ą ć j ą z k o l e k c j i _ s t a r s , p r z e n i e ś ć do k o l e k c j i
I I _ f a d e d S t a r s , a n a s t ę p n ie w yw ołać m eto d ę F a d e O u t() ( k o n t r o l k a z o s t a n i e u s u n ię t a z k o l e k c j i _ s p r i t e s
I I p o d c z a s n a s tę p n e g o w yw o ła n ia m e to d y U p d a te () - t o w ła ś n ie z te g o powodu c z ę s t o t l i w o ś ć
I I z g ł a s z a n i a z d a r z e ń T i c k z e g a r a j e s t w ię k s z a od c z a s u tr w a n ia a n im a c ji w y g a sz a n ia g w ia z d y .
//
I I J e ś l i g w ia z d a n i e z o s t a j e u s u n ię t a , to sp raw d za m y, c z y z n a jd u je s i ę ona w k o l e k c j i _ s t a r s - j e ś l i t a k , to
I I p o b ie ra m y r e f e r e n c j ę S t a r C o n t r o l ; j e ś l i j e j n i e ma, t o t r z e b a u tw o r z y ć nową k o n t r o lk ę S t a r C o n t r o l , p ł y n n i e j ą
I I w y ś w i e t l i ć , d o d a ć do k o l e k c j i _ s p r i t e s , a n a s t ę p n ie z m ie n ić w a r t o ś ć z - i n d e x , b y k o n t r o l k i p s z c z ó ł
I I b y ł y w y ś w ie t la n e n a d n i ą . Na samym k o h cu n a l e ż y o k r e ś l i ć p o ł o ż e n ie g w ia z d y w o b s z a r z e k o n t r o l k i C a n v a s.
}
Kiedy określasz nowe położenie w obszarze kontrolki Canvas, powoduje to
aktualizację kontrolki — nawet je ś li była ju ż wyświetlona. To właśnie w ten sposób
gwiazdy poruszają s ię w oknie aplikacji, kiedy zmienia się obszar pola gry.
jesteś tutaj ► 829
Rozwiązanie ćwiczenia
Długie ćwiczenie
Rozwiązanie
h Poniżej p rze dstaw iliśm y uzupełnione m etody klasy BeeS tarM odel.
using Windows.Foundation;
c la ss BeeStarModel {
public s t a t ic readonly S ize S ta rS iz e = new S ize(1 5 0 , 100);
public BeeStarModel () {
_playAreaSize = Size.Em pty; O to m etody umieszczone w kodzie
} ukrytym k o n tro lki S ta r C o n tr o l:
p u b lic v o id F a d e In () {
public void Update () { Te w ła ściw ości i metody
przedstaw iliśm y ju ż w cześnie j. fa d e In S to ry b o a rd .B e g in ();
MoveOneBee();
AddOrRemoveAStar(); }
}
p u b lic v o id FadeOut() {
p riv a te s t a t ic bool RectsOverlap(Rect r1 , Rect r2) { fa d e O u tS to ry b o a rd .B e g in ();
r 1 .In t e r s e c t (r 2 );
i f (r1.W idth > 0 || r1.H eight > 0) }
return tru e;
return fa ls e ;
}
Długie ćwiczenie
Rozwiązanie Rzuć monetą, wybierając losowo wartość 0
J f e O to kilka osta tn ich składow ych klasy B e e S ta rM o d e l. gdy ™h tą c z rn T m m ^ rn ż T f usuwał
1Lgdy j e s t ich więcej niż 20. ' j'
p riv a te void AddOrRemoveAStar () {
i f (((_random .Next(2) == 0) || (_stars.C o u n t <= 5 )) && (_stars.C o u n t < 20 ))
C rea teA Sta r();
else {
S ta r starToRemove = _sta rs.K e y s .T o L is t()[_ ra n d o m .N e x t(_ sta rs.C o u n t)];
_stars.Remove(starToRemove);
OnStarChanged(starToRemove, tru e );
Podczas każdego wywołania metody Update()
chcemy usunąć lub dodać gwiazdę. Gwiazdy
}
} tworzy ju ż metoda CreateAStar(). Je śli chcemy
usunąć gwiazdę, to wystarczy usunąć ją
public event EventHandler<BeeMovedEventArgs> BeeMoved ; z kolekcji _ s ta r s i wywołać zdarzenie
StarChanged.
p riv a te void OnBeeMoved (Bee beeThatMoved, double x , double y)
{
EventHandler<BeeMovedEventArgs> beeMoved = BeeMoved;
i f (beeMoved != n u ll)
{
beeMoved(this, new BeeMovedEventArgs(beeThatMoved, x , y ) ) ;
}
}
To typowe procedury
public event EventHandler<StarChangedEventArgs> StarChanged; obsługi zdarzeń
oraz metody do ich
p riv a te void OnStarChanged(S ta r starThatChanged, bool removed) wywoływania.
{
EventHandler<StarChangedEventArgs> starChanged = StarChanged;
i f (starChanged != n u ll)
{
starChanged(this, new StarChangedEventArgs(starThatChanged, removed));
}
}
Poniżej p rze dstaw iliśm y uzupełnione m etody klasy B eeS tarV iew M odel.
using View;
using Model;
using System .Collections.ObjectM odel;
using S yste m .C o lle ctio n s.S p e cialize d ;
using Windows.Foundation;
using DispatcherTimer = Windows.UI.Xaml.DispatcherTimer;
using UIElement = Windows.UI.Xaml.UIElement;
Ten kod przedstawiliśmy
c la ss BeeStarViewModel { ju ż wcześniej.
p riv a te readonly ObservableCollection<UIElement>
_sprites = new ObservableCollection<UIElement>();
public INotifyCollectionChanged Sprites { get { return _ s p r it e s ; } }
BeeStarFlelper.SetCanvasLocation(
newStar, e.StarThatChanged.Location.X, e.StarThatChanged.Location.Y);
N ie m n ie j je d n a k wciąż jest jeszcze k ilk a spraw, k tó ry m i pow inieneś się zająć, zanim przejdziesz do ostatniego
la b o ra to riu m , je ś li chcesz, by in fo rm acje , k tó re ud ało C i się w tłoczyć do swojego m ózgu, pozostały w nim .
działa,
Odpocznij. A może nawet lepiej
gdyż zo sta ł — utnij sobie drzemkę.
T w ój mózg przysw oił bardzo dużo in fo rm a c ji, a czasami
odpowiednio najlepszym , co możesz zrobić, aby tę całą wiedzę
„zatrzym ać” , jest przespać się. Istn ie je bardzo dużo
napisany, a każdy badań nad pracą m ózgu, k tó re w ykazują, że przyswajanie
fragm ent kodu in fo rm a c ji znacząco popra w ia się po d o b rze p rze spa nej
nocy. D lateg o zapew nij swojem u m ózgow i dobrze
można zrozum ieć. zasłużony odpoczynek!
K
.■.choć kod można
tatw'iej zrozumieć,
je śli programista
wykorzystał odpowiednie
wzorce projektowe
i zasady programowania
obiektowego.
Invaders
To laboratorium zawiera specyfikację opisującą program, który
musisz napisać, wykorzystując wiedzę zdobytą podczas lektury
tej książki.
Ten projekt jest większy niż te, które widziałeś do tej pory.
Przeczytaj więc uważnie wszystko, zanim przystąpisz do pisania,
i przeznacz na to trochę czasu. Jeśli wykonałeś wszystkie
ćwiczenia zamieszczone w książce, to dysponujesz wszystkimi
narzędziami niezbędnymi do napisania tego laboratorium.
Uzupełniliśmy część detali projektu za Ciebie i upewniliśmy się,
że masz wszystkie potrzebne elementy... i nic więcej.
Do Ciebie należy ukończenie pracy. Możesz pobrać naszą
wersję gotowej gry ze Sklepu Windows. Jest ona dostępna
w formie projektu otwartego, zatem jest dostępny także jej
kod ź ró d ło w y . niemniej jednak nauczysz się znacznie więcej,
jeśli spróbujesz napisać ją samodzielnie!
Więcej informacji na ten tem at można znaleźć w witrynie
poświęconej książce: http://www. headfirstlabs.com/hfcsharp.
L A
Laboratorium C# 835
Invaders
S ta tk i obcych
są animowane
i mają duże
pikse/e
przypominające
grafikę z /a t 80.
ubiegłego wieku.
Obszar gry ma
proporcje 4:3,
podobnie ja k
stare automaty
do gier;
dodatkowo, aby
gra wyg/ądała
autentycznie,
w je j obszarze
widać
symu/owane /inie
znane ze starych
monitorów.
836
Invaders
S3 120 30 40
Pierwsza fala
najeźdźców może
jednocześnie
strzelać dwoma
pociskami — będą
oni wstrzymywać
ogień, gdy na
ekranie będą dwa Gra powinna
pociski lub więcej. przechowywać
Następna fala informację o wszystkich
może wystrzelić naciśnięciach
j ednocześnie trzy Mawiszy. Naciśnięcie
Gracz może strzelać, pociski, kolejna strzałki w prawo
dotykając ekranu lub cztery i tak daty. i spacji spowoduje
naciskając klawisz spacji. przemieszczenie
Na ekranie w danej cnwili s ię statku w prawo
mogą znajdować się jednak Je ś li pocisk i wystrzelenie pocisku
tylko trzy pociski gracza. uderzy wroga, (chyba że na ekranie
Gdy pocisk w coś uderzy to oba obiekty znajdują s ię ju ż trzy).
lub zniknie, to może zostać znikają.
wystrzelony kolejny. W przeciwnym
razie pocisk znika
dopiero wtedy,
S P A C fi gdy wychodzi
poza ekran.
837
Invaders
838
Invaders
Wszystkie s ta tk i najeźdźców
wyświet/one na ekranie są
przechowywane w obiekcie List.
Kiedy statek zostaje zniszczony,
je s t usuwany z /is ty.
°6,eki ?\cff
Obiekt reprezentujący statek
przechowuje informacje o jego
położeniu i sam się przesuwa
w /ewo i prawo, dbając
jednocześnie o to, by nie
przekroczyć krawędzi po/a gry.
Gra przechowuje
/istę obiektów Shot
reprezentujących
strzały, i to zarówno
tych oddanych
przez gracza do
najeźdźców, jak
i strza ły oddawane
do gracza. Za
każdym razem,
gdy s trz a ł zostaje
dodany, przesunięty
/ub usunięty, obiekt
InvadersMode/
O biekt S tars dysponuje /is tą (typu L is t)
zgłasza zdarzenie
s tru k tu r Point, przy czym każdej gwieździe
ShotMoved.
migającej w t/e odpowiada jedna struktura .
Obiekt InvadersMode/ zgłasza zdarzenia
StarChanged, aby dodawać i usuwać
gwiazdy, dzięki czemu będą one spraw iały
'/s f < P o ^ wrażenie migotania.
839
Invaders
Player Invader
static PlayerSize: Size static InvaderSize: Size
InvaderType: InvaderType
Score: int
Move(Direction) Move(Direction)
ctor: InvaderType, Point,
Size
Możesz
class S h o t {
przyspieszyć
publ ic const double Sh o t P i x e l s P e r S e c o n d = 95;
lub zwolnić
strzał,
publ ic Point Location { get; pr ivate set; } zmieniając
publ ic static Size S h ot Si ze = new Size(2, 10); tę wielkość
przesunięcia,
pr iv at e Dire ct io n _d ir ection; wyrażoną T a k la s a Shot b ę d z ie C i p o t r z e b n a .
publ ic Di rection Di re ct io n { get; pr iv at e set; } w pikselach. M o d e l używa jej do śledzenia strzałów oddanych
przez gracza do statków najeźdźców, ja k
pr iv at e D a te Ti me _l astMoved;
rów nież strzałów oddanych przez najeźdźców
publ ic S h ot (P oi nt location, Dire ct io n direction ) { do gracza. Przyjrzyj się dokładniej je j m etodzie
Location = location; Move(): używa ona prywatnego po la typu
_ d ir ec ti on = direction; DateTime, przechowującego inform ację o tym
_ l a s t M o v e d = DateTime.Now; kiedy nastąpiło ostatnie przesunięcie strzału.
} Podczas każdego w yw ołania m etody położenie
strzału (określane przez właściwość Location)
publ ic void Move () {
Time Sp an ti m e S i n c e L a s t M o v e d = Da te T i m e . N o w - _l astMoved; jest przesuwane w górę lub w d ó ł z szybkością
doub le d i st an ce = ti me Si nc eL a s t M o v e d . M i l l i s e c o n d s 95 pikseli na sekundę.
* Sh ot Pi xe l s P e r S e c o n d / 1000; Oprócz tego będziesz także potrzebował
if (Direction == Direction.Up) dist an ce *= -1;
przedstawionych obok trzech klas EventArgs,
Location = new Po int(Location.X, Location.Y + distance);
lastMoved = DateTime.Now;
używanych przez m odel do inform ow ania
m odelu w idoku o pojaw ianiu się lub znikaniu
gwiazd, przesunięciach, pojaw ianiu się lub
znikaniu strzałów oraz przesunięciach lub
class S t a r C h a n g e d E v e n t A r g s : EventArgs { zniszczeniach statków. K iedy gracz lub jakiś
public Point Point { get; pr iv at e set; }
statek najeźdźcy oddadzą strzał, m odel
public bool D i sa pp ea re d { get; pr iv at e set; }
utworzy obiekt Shot, a następnie wygeneruje
public S t a r C h a n g e d E v e n t A r g s (Point point,
zdarzenie ShotMoved. O bie kt m odelu
bool disappeared) {
Point = point; w idoku obsłuży to zdarzenie i zaktualizuje
Di sa p p e a r e d = disappeared; swoją kolekcję Sprites , która z kolei
} poin fo rm uje o zmianach obiekt widoku.
}
Ten typ
class S h o t M o v e d E v e n t A r g s : EventArgs {
publ ic Shot Shot { get; pr iv at e set; }
wyliczeniowy
określa
publ ic bool D i sa pp ea re d { get; pr ivate set; }
rodzaj sta tk u
pilotowanego
publ ic S h o t M o v e d E v e n t A r g s (Shot shot, bool disappeared) { przez
Shot = shot; najeźdźcę.
D i sa pp ea re d = disappeared;
} enum InvaderType
Bug,
Saucer,
S a t e llit e ,
class S h i p C h a n g e d E v e n t A r g s : Ev entArgs { Spaceship,
public Ship Sh ip U p d a t e d { get; pr iv at e set; } S ta r,
public bool K i l l e d { get; private set; } }
841
Invaders
c la s s InvadersM odel j
p u b lic re a d o n ly s t a t i c S ize P la yA rea S ize = new S iz e (4 0 0 , S00);
p u b lic co n st i n t MaximumPlayerShots = S;
p u b lic co n st i n t I n it ia lS t a r C o u n t = 50;
p u b lic i n t Score { g e t; p r iv a t e s e t ; }
p u b lic i n t Wave { g e t; p r iv a t e s e t; }
Kiedy gracz ginie, obiekt modelu widoku
p u b lic i n t L ive s { g e t; p r iv a t e s e t ; } sprawia, że jego statek będzie migotał przez
2,5 &ekundu. Model używa prywatnego pola
p u b lic bool GameOver { g e t; p r iv a t e s e t ; } typu Date Time?, by zapamiętać, kiedy to
nastąpiło, i uniemożliwić przesuwanie statków
i strzałów w czasie, gdy gracz umiera.
p r iv a t e DateTime? _ p la y e rD ie d = n u l l ;
p u b lic bool P la ye rD yin g { g e t { r e tu r n p la y e rD ie d .H a s V a lu e ; } }
p r iv a t e P la y e r _ p la y e r ;
p r iv a t e D ir e c tio n _ in v a d e r D ir e c tio n = D ir e c t io n . L e f t ;
p r iv a t e bool _justMovedDown = f a ls e ;
p u b lic v o id EndGame() {
GameOver = t r u e ;
}
// B ę d z ie s z m u s ia ł d o k o ń c z y ć r e s z t ę k o d u k l a s y In v a d e r s M o d e l.
a42
Invaders
O m e to d a startgame O r o z p o c z y n a g r ę .
T a m etoda przypisuje właściwościGameOver wartość fa lse . Następnie usuwa wszystkie statki najeźdźców z kolekcji
_invaders _playerShots oraz _invaderShots (jednak zanim to zrobi, dla każdej z nich
oraz strzały z kolekcji
generuje zdarzenia ShipChanged oraz ShotMoved). Następnie m etoda czyści istniejące gwiazdy (generując dla każdej
z nich zdarzenie StarChanged) i tw orzy nowe. W końcu m etoda tw orzy nowy ob ie kt Player (generując zdarzenie
ShipChanged), przypisuje właściwości Lives wartość 2, właściwości Wave wartość 0 i dodaje pierwszą falę najeźdźców.
S m e to d a UPDATE() p o d t r z y m u je g r ę .
O b ie k t m od elu w id o k u używa zegara, by wyw oływ ać m etodę Update() w iele razy na sekundę, o ile tylko gra
jeszcze nie została zakończona — to właśnie dzięki tem u gra może się toczyć. W pierwszej kolejności m etoda
ta sprawdza, czy gra nie została wstrzym ana. Jeśli nie została, to m etoda w ykonuje następujące czynności
(bo gw iazdam i m ruga zawsze, niezależnie od tego czy gra się toczy, czy jest wstrzym ana):
★ Jeśli gracz nie zginął, to przesuwa wszystkie statki najeźdźców (więcej in fo rm a c ji na ten tem at podam y na
następnej stronie).
★ N astępnie aktu alizuje wszystkie strzały (jeśli gracz nie zginął). G ra m usi zaktualizow ać w p ę tli obie kolekcje
strzałów, w yw ołując m etodę Move() każdego z nich. Jeśli k tó ry k o lw ie k strzał przekroczył granice obszaru gry,
zostanie usunięty, a m etoda zgłasza zdarzenie ShotMoved.
★ Najeźdźcy odpow iadają ogniem (więcej na ten te m a t napiszem y na następnej stronie).
★ W końcu m etoda sprawdza ko lizje : w pierwszej kolejności szukając strzałów, k tó re tra fiły w statki najeźdźców
(i usuwa oba z odpow iednich k o le k c ji), a następnie szukając strzałów, k tó re tra fiły w statek gracza. T o
w tym m iejscu bardzo się przyda właściwość Rect klasy bazowej Ship — możesz skorzystać z przedstaw ionej
w rozdziale 16. m etody sprawdzającej, czy p ro sto kąty zachodzą na siebie, aby wykryw ać ko liz je (więcej na ten
tem at napiszemy na następnej stronie).
O to podpow iedź: Jeśli spróbujesz usunąć ob ie kt z kolekcji podczas przeglądania jej zaw artości w pętli
fo re a c h , kolekcja zgłosi w yjątek. Jednak możesz skorzystać z m etody rozszerzenia LINQ T o L i s t ( ) ,
aby n a jp ie rw zrobić kopię kolekcji, a dopiero później w ykonać pętlę.
843
Invaders
844
Invaders
J S P R A W D Z IĆ , C Z Y F O R M A C J A N A J E Ź D Ź C Ó W O S IĄ G N Ę Ł A K R A W Ę D Ź P O L A W A L K I .
Najeźdźcy muszą zmieniać kierunek za każdym razem, gdy jeden z nich znajduje się w odległości podwojonego
jednokrotnego przesunięcia od krawędzi pola walki. Gdy poruszają się w prawo i są ju ż bliscy osiągnięcia prawej krawędzi,
gra musi nakazać im przesunąć się w dół i rozpocząć poruszanie się w lewo. Kiedy eskadra porusza się w tę stronę,
gra musi sprawdzać, czy nie została osiągnięta lewa krawędź. A by wykonywać taką procedurę, dodaj prywatną metodę
M o v e In v a d e rs (), która będzie wywoływana przez U p d a te (). Pierwszym zadaniem jest obliczenie czasu, ja k i upłynął od
ostatniego przesunięcia, co można zrobić, używając pola _ la s tU p d a te d . Jeśli nie upłynęło dostatecznie dużo czasu, nic nie
należy robić. Jeśli najeźdźcy poruszają się w prawo, to metoda M o ve In vad ers() powinna użyć L IN Q w celu przeszukania
kolekcji _ in v a d e rs i odnalezienia tego z nich, którego współrzędna X znajduje się w obszarze prawej krawędzi. Gdy taki
najeźdźca zostanie znaleziony, formacja powinna zostać przesunięta w dół, a pole in v a d e rD ire c tio n powinno zostać
ustawione na D ir e c t io n . L e f t . W przeciwnym razie grupa powinna kontynuować poruszanie się w prawo. Z drugiej
strony, jeśli najeźdźcy poruszają się w lewo, powinna zostać wykonana operacja przeciwna. Kolejne zapytanie L IN Q
powinno wtedy sprawdzić, czy jakiś najeźdźca znajduje się blisko lewej krawędzi. Jeśli tak, form aqa także musi przesunąć
się w dół i zmienić kierunek ruchu. Można przy tym używać prywatnego pola _justMovedDown, by przechowywać
inform ację o tym, kiedy formacja została przesunięta w dół i zmieniła kierunek.
S P R A W D Z IĆ Z D E R Z E N IA G R A C Z A I N A J E Ź D Ź C Ó W .
Będziesz chciał utworzyć metodę do badania kolizji. Istnieją trzy rodzaje zderzeń, jakie możesz badać, a bardzo Ci się
w tym przyda metoda do odnajdywania zachodzących na siebie prostokątów, przedstawiona w rozdziale 16.
★ Użyj L IN Q w celu odnalezienia nieżywych najeźdźców, wykonując iterację po wszystkich pociskach na liście gracza
i wybierając tego wroga, którego właściwość Area zawiera położenie strzału. Usuń najeźdźcę i strzał.
★ Dodaj zapytanie, aby sprawdzić, czy jakiś najeźdźca nie osiągnął dolnej krawędzi ekranu — jeśli tak, zakończ grę.
★ Nie potrzebujesz L IN Q do wyszukiwania strzałów, które trafiają w gracza. W ykonaj pętlę, która porówna
położenie strzałów wroga i właściwość Area statku. (Pamiętaj, nie możesz m odyfikow ać k o le k c ji wewnątrz
p ę tli fo re a ch . Jeżeli to zrobisz, otrzymasz wyjątek In v a lid O p e ra tio n E x c e p tio n z kom unikatem , że kolekcja
została zmodyfikowana. Być może będziesz musiał utworzyć tymczasową listę obiektów do usunięcia, bądź użyć
metody rozszerzenia T o L is t( ) , by najpierw skopiować kolekqę).
845
Invaders
W s z y s tk ie a k c je s ą o b s łu g iw a n e
przy u życiu w ią z a n ia .
Statki najeźdźców, statek gracza, strzały i gwiazdy,
a nawet lin ie symulujące stare m o n ito ry... — wszystko to
są ko n tro lk i dodawane do kolekcji ObservableControls
obiektu m odelu w idoku. O prócz tego będziesz także
potrzebował k o n tro lk i T e x tB lo c k z napisem „G ra
skończona” , której właściwość V is ib ilit y zostanie
powiązana z właściwością GameOver, oraz drugiej
ko n tro lk i tego samego typu z tekstem „G ra wstrzymana” ,
powiązanej z właściwością Paused.
W y n ik oraz d o d a tk o w e
s ta tk i g ra c z a s ą o so b n ym i
Invaders k o n tro lk a m i.
W praw ym górnym rogu gry znajduje się
k o n tro lk a StackPanel, w któ re j została
<*> <*> j a j <a > »*> j a > «a ; 'a j
um ieszczona k o n tro lk a TextBlock
/Xv /Xv !*v /Tv /X\ /xv /Xv Jxv /xv /Xv /Xv
'fi. % 'fi % % % ■»_ powiązana z właściwością Score,
oraz k o n tro lk a GridView powiązana
^ ^^ v^v z właściwością Lives . K o n tro lk a
'i' + . + GridView w yśw ietla statki gracza, gdyż
je j sza b lo n d a n ych (D a ta T e m p la te )
je s t k o n tr o lk ą Image; dlatego też, aby
k o n tro lk a ta pozw alała na dodawanie
W y n ik oraz d o d a tk o w e s ta tk i
g ra c z a s ą o so b n ym i k o n tro lk a m i.
G łów ny obszar gry to kon tro lka Border
z zaokrąglonymi rogami, zawierająca kontrolkę
ItemsControl, której właściwość ItemsSource
została powiązana z właściwością Sprites i której
właściwość ItemsPanel jest kon tro lką Canvas
o białym tle. N a następnej stronie przedstawimy
kod, któ ry zadba o aktualizację ich marginesów
tak, by obszar wewnątrz zawsze m ia ł p ro p o rc je 4:3
— właściwość Margin k o n tro lk i Border zapewni,
że właściwość Height będzie równa 4/3 wartości
właściwości Width, i to nawet w przypadku zmiany
orientacji ekranu lub jego wielkości.
846
Invaders
S1zeChanged="pageRoot SizeChanged"
M a n ip u la tio n M o d e = "T ra n sla te X " M a n ip u la tio n D e lta = "p a g e R o o t_ M a n ip u la tio n D e lta "
M an ip ula tion C om p lete d= "pa geR oo t M a n ip u la tio n C o m p le te d " Tapped="pageRoot Tapped"
< B order x:N am e="playA rea" B o rd e rB ru sh = "B lu e " B o rd e rT h ickn e ss= "2 " C ornerR adius="10"
B ackground="B lack" M a rg in = "5 " G rid.R ow = "1" Loaded="playArea_Loaded">
< Ite m sC o n tro l
O to ko d ukryty, k tó ry dba o zachowanie p ro p o rc ji 4:3 obszaru gry, dodając lu b usuwając m arginesy odpow iednio:
lewy i praw y oraz górny i dolny.
p r iv a t e v o id playA re a_Lo ad ed(o b je c t sen de r, RoutedEventArgs e) {
U p d a te P la y A re a S iz e (p la y A re a .R e n d e rS iz e );
}
p u b lic v o id In v a d e rS h o t() {
in v a d e rS h o tS to ry b o a rd .B e g in ();
}
p u b lic v o id S ta r tF la s h in g ( ) {
fla s h S to ry b o a r d .B e g in ( );
}
p u b lic v o id S to p F la s h in g O {
f la s h S to r y b o a r d .S to p ( ) ;
}
849
Invaders
W rozdziale 15. dowiedziałeś się, ja k dodawać w yw ołanie zw rotne, pozwalające w yśw ietlić okienko
z p o zio m u pa ne lu Ustawienia. T w o im zadaniem jest dowiedzieć się, w ja k i sposób można um ieścić na
stronie k o n tro lk ę Popup. Poniżej przedstaw iliśm y ko d ukryty, um o żliw ia jący w yśw ietlenie je j z poziom u
panelu Ustawienia; dodatkow o zaw iera on także pro ced urę obsługi zdarzeń obsługujących użycie
przycisku rozpoczynającego grę, k tó ry także będziesz m usiał dodać: ____________
SettingsCommand aboutCommand = new SettingsCommand( © 2013 A n drew Stellm an o raz Jenn ifer G reene
;
„O a p l i k a c j i " , „O a p l i k a c j i " , in v o k e d H a n d le r);
args.R equest.ApplicationC om m ands.Add(aboutC om m and);
} Oto okienko, które
p r iv a t e v o id AboutInvokedH andler(IU IC om m and command) { przygotowaliśmy — możesz
viewM odel.Paused = t r u e ; w nim użyć kontrolek
StackPanel oraz Grid, by
aboutPopup.IsO pen = t r u e ; wyśw ietlić w nim inne,
} d°wolnie wybrane kontrolki.
p r iv a t e v o id C lo se P o p u p (o b je ct se n d e r, RoutedEventArgs e) { ^ Przycisk o postaci strzałki,
zamykający okienko, je s t
aboutPopup.IsO pen = f a ls e ; skojarzony z procedurą
viewM odel.Paused = f a ls e ; dbsługi zdarzeń ClosePopup.
}
p r iv a t e v o id S ta rtB u tto n C l1 c k (o b je c t sen de r, RoutedEventArgs e) {
aboutPopup.IsO pen = f a ls e ;
vie w M o d e l.S ta rtG a m e ();
}
I jeszcze jedno: uzyskasz znacznie ładniejszy efekt, jeśli okienko będzie się pojawiało stopniowo. Przekonaj się, czy będziesz
wiedział, ja k skorzystać z kolekcji T r a n s itio n s w oknie P r o p e r t ie s , by dodać do okienka efekt E n tra n ce T h e m e T ra n sitio n .
Transitions (Collection)
850
Invaders
O to sam początek klasy In va d e rsV ie w M o d e l , k tó ry pom oże C i rozpocząć pracę nad nią:
p u b lic S ize P la yA rea S ize { Pole P la yA rea S ize udostępnia jedynie akcesor set i jest
set { aktualizowane przez obiekt w idoku za każdym razem, gdy
S cale = v a lu e .W id th / 405; wielkość obszaru gry ulegnie zmianie. Kiedy wartość właściwości
m o d e l.U p d a te A llS h ip s A n d S ta rs (): P la yA rea S ize zostanie określona, akcesor set oblicza nową wartość
R e c re a te S c a n L in e s ();
mnożnika Scale, a następnie inform uje model, że powinien zgłosić
}
zdarzenia, by zaktualizować wszystkie statki i gwiazdy.
}
Przy okazji odtwarzane są także linie symulujące stare monitory.
851
Invaders
iM^ a "w ^ 'k U. Uu^ w ;a,J pi:!d^ ':UIi! l,ul^ a l^ .,D .,.T ir . , , by przechowywai
p r iv a t e DateTime? le f t A c t io n = n u l l ;
p r iv a t e DateTime? r ig h t A c t io n = n u ll
852
Invaders
EndGame();
Zgłaszanie zdarzenia zegara co 100 milisekund
} sprawi, że widok będzie aktualizowany 10 razy na
? sekundę. Liczba ta nie odpowiada liczbie ramek
Kończymy grę, przez co
rozpocznie się ona od wyświetlanych w ciągu sekundy, gdyż sprajty są
wyświetlenia napisu przesuwane po ekranie przy wykorzystaniu animacji.
„Gra skończona“ .
853
Invaders
v o id T im e rT ic k E v e n tH a n d le r(o b je c t sen de r, o b je c t e) {
if (_la stP a u se d != Paused)
{
Użyj p o la _la stP a u se d , by z g ła sz a ć zd a rz e n ie PropertyChanged za każdym razem,
gdy zm ieni s i ę w artość w ła ściw ości Paused.
}
if (!Paused)
{
J e ś l i o b ie w ła ściw o ści, J L e ftA c tio n oraz _ r ig h t A c t io n , mają w artość,
oznacza to , t e użytkownik n a cisn ą ł Jed n o cześn ie dwa k la w isze lub n a cisn ą ł
k la w isz i wykonał g e s t k iw n ię c ia . W takim przypadku do o k re śle n ia kierunku
przesuw ania s ta tk u gracza w ybierz p ó ź n ie js z e z d a rz e n ie . J e ś l i ty lk o Jedna
z ty ch w ła ściw ości ma w a rto ść, w ybierz J ą i p rzekaż w wywołaniu metody
_m odel.M oveP layer().
}
Zażądaj a k t u a liz a c ji modelu widoku; n a stęp n ie sprawdź w łaściw ość S c o re . J e ś l i j e j
w a rto ść n ie odpowiada w ła ściw ości _m o d el.S co re , zmień J ą i wygeneruj zd a rzen ie
PropertyChanged.
Z a k tu a liz u j w arto ść w ła ściw o ści L iv e s , by odpowiadała w ła ściw o ści _m o d e l.L iv e s ;
w tym c e lu usuń o b ie k t lub dodaj go, używając new o b je c t ( ) .
{
Każdy k lu c z słow nika _sh o t!n v a d e rs J e s t ko n tro lk ą Antmatedlmage, a Jego
w artość o k re śla cz a s , w którym dany s t a te k najeźdźców z o s t a ł z n isz cz o n y .
Pełne zakończenie anim acji zn iszczon ego s ta tk u n a je ź d ź cy zajm uje p ó ł sekundy,
a zatem każdy s ta te k , k tó ry u le g ł z n is z c z e n iu w c ze śn ie j n iż p ó ł sekundy temu,
powinien z o s ta ć u su n ię ty z e słowników _ s p r i t e s oraz _sh o t!n v a d e rs.
854
Invaders
}
}
855
Invaders
{
J e ś l i s t r z a ł n ie J e s t kluczem w słow niku _ s h o t s , to używając Jego metody w ytw órczej,
utwórz nową k o n tro lk ę s t r z a ł u , a n a stęp n ie dodaj J ą do słowników _s h o ts oraz _ s p r i t e s .
J e ś l i s t r z a ł J e s t dostępny w słowniku _ s h o t s , oznacza to , t e Ju ż J e s t um ieszczony na
e k ra n ie ; w tym przypadku odszukaj Jego k o n tro lk ę i s k o r z y s ta j z metody pom ocn iczej,
by Ją przesu n ą ć na ek ra n ie , używając p rz y tym w ła ściw ości Lo ca tio n .
} e ls e {
S t r z a ł z n ik n ą ł, a zatem sprawdź słow n ik _ s h o ts , by zobaczyć, c z y J e s t w nim o b iek t
s t r z a ł u . J e ś l i J e s t , to usuft k o n tro lk ę s t r z a łu ze słow nika _ s p r i t e s , a o b ie k t s t r z a łu
ze słow nika s h o ts .
}
}
{
Odszukaj gwiazdę w słow niku _ s t a r s i usuft j e j k o n tro lk ę z e słow nika _ s p r i t e s .
} e ls e {
if (! s ta rs .C o n ta in s K e y (e .P o in t))
{
U żyj metody w ytw órczej, by utw orzyć nową k o n tro lk ę , n a stęp n ie dodaj J ą do słownika
_ s t a r s (używając Jako k lu cza w ła ściw o ści e .P o in t przekazanego argumentu z d a rz e n ia ).
} e ls e {
Gwiazdy zazw yczaj n ie zm ien ia ją p o ło że n ia , dlatego ta kla u zu la e ls e r a c z e j n ie
z o s ta n ie wykonana; możesz z n i e j Jednak s k o rz y s ta ć , by dodać do g ry s t r z e l a ją c e
gwiazdy. Odszukaj k o n tro lk ę gwiazdy w słowniku _ s t a r s i u ż y j metody pom ocn iczej,
by przesu n ą ć J ą w nowe m ie js c e .
856
Invaders
Dodaj dźwięk
Znacznik XAML MediaElement pozwala dodawać do aplikacji dźwięki. Czy potrafisz dowiedzieć
się, jak go wykorzystać, by dodać dźwięki maszerujących najeźdźców, strzałów oddawanych przez
gracza oraz niszczonych statków kosmicznych? Wszystkiego dowiesz się na tej stronie:
http://m sdn.m icrosoft.com/pl-pl/library/windows/apps/xaml/hh465160.aspx
To jest Twoja szansa na wybicie się! Czy utworzyłeś nową, lepszą wersję gry?
Opublikuj swoją wersję gry na CodePlex lub innej witrynie do publikacji projektów
informatycznych i skorzystaj z możliwości pokazania swoich możliwości na
forum Head First C#: www.headfirstlabs.com/books/hfcsharp/.
857
Invaders
858 Laborotorium C#
17. Projekt dodatkowy!
Klasy, obiekty, XAML, hermetyzacja, dziedziczenie, polimorfizm, LINQ, MVVM... dysponujesz juz
wszystkimi narzędziami niezbędnymi do pisania wspaniałych aplikacji dla Sklepu Windows oraz
tradycyjnych aplikacji okienkowych. Czy jednak wiesz, ze tych samych narzędzi możesz użyć do
pisania aplikacji dla W indows Phone ? Tak, to prawda! W tym dodatkowym projekcie poznasz
proces tworzenia gry dla systemu Windows Phone. Jeśli nie posiadasz odpowiedniego urządzenia,
to i tak nie masz się czym przejmować — będziesz mógł w nią grać na em ulatorze W indow s
Phone. A zatem zaczynajmy!
Atak pszczół!
W tym rozdziale napiszesz grę o nazw ie A tak pszczół przeznaczoną
dla system u W indow s P h o n e 8 . W grze pojaw i się ul rozw ścieczonych
pszczół, a jedynym sposobem ich u sp o k o jen ia będzie użycie bardzo
sm akow itego kw iatka. Im więcej pszczół u d a Ci się złapać przy jego
użyciu, tym wyższy uzyskasz wynik.
Zanim zaczniesz...
A by w ykonać ten p ro jek t, będziesz m usiał zainstalować Visual Studio 2012 fo r Windows Phone .
W ersję E xpress tej w ersji V isual S tu d io m o żn a p o b ra ć ze strony:
http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-for-windows-phone
★ fo ld e r Assets;
U K R Y J P A N E L T Y T U Ł U N A S T R O N IE G Ł Ó W N E J .
S tro n a głów na, MainPage.xaml, pow in n a ju ż być o tw o rzo n a w ID E (jeśli nie jest, to ją otw órz).
T o jest głów na stro n a Twojej aplikacji. Przyjrzyj się jej kodow i X A M L , a n astęp n ie odszukaj
w nim k o n tro lk ę StackPanel o nazw ie T itle P a n e l .
< !--T itle P a n e l contains the name o f the a p p lic a tio n and page t i t l e —>
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
<TextBlock Text="page name" M argin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
IDE utworzyło plik.
MainPage.xaml
* awifrający tą kontrolką
otackPanel o nazwie
Zm odyfikuj kod X A M L: dodaj do elementu StackPanel właściwość V is ib ilit y = M
CollapsedM,
ta k by zniknęła k o n tro lk a TextBox z tytułem aplikacji.
/— Dodaj tą właściwość
< !--T itle P a n e l contains the name o f the a p p lic a tio n and page t i t l e —>
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28" Visibility=MCollapsedM>
<TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
<TextBlock Text="page name" M argin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
0
MY APPLICATION
page name
£>
Poniżej przedstaw iliśm y k o d klasy BeeAttackModel. D efiniuje o n a właściwości do p o b iera n ia liczby niezłapanych
pszczół, wyniku o raz czasu pom iędzy w ypuszczaniem kolejnych pszczół, jak rów nież m eto d ę do rozpoczynania gry.
O prócz tego u d o stę p n ia o n a m etody do info rm o w an ia m o d elu o tym , że użytkow nik p rzesu n ął kw iatek, że pszczoła
wylądow ała oraz m eto d ę zw racającą p o ło żen ie ula, używ aną pod czas w ypuszczania kolejnej pszczoły.
using Windows.Foundation;
Model widoku będzie używat tych
cla ss BeeAttackModel { właściwości, by aktualizować wynik
public in t MissesLeft { get; p riv a te s e t; } oraz liczbę niezłapanych pszczół.
public in t Score { get; p riv a te s e t; }
public TimeSpan TimeBetweenBees {
get {
double m illisecon ds = 500;
m illiseco n ds = Math.Max(milliseconds - Score * 2 .5 , 100);
return Tim eSpan.From M illiseconds(m illi seconds);
}
} Wraz z łapaniem przez gracza kolejnych
pszczół gra przyspiesza. Ta właściwość
p riv a te double _flowerW idth;
p riv a te double _beeWidth;
wylicza czas pomiędzy pojawianiem się
p riv a te double _flo w e rL e ft; kolejnych pszczół, zależny od wyniku
p riv a te double _playAreaWidth; uzyskanego przez gracza.
p riv a te double _hiveW idth;
p riv a te double _lastH ive Lo ca tio n ;
p riv a te bool _gameOver;
p riv a te readonly Random _random = new Random()
public void StartGame (double flowerWidth, double beeWidth double playAreaWidth. double hiveWidth) {
_flowerWidth = flowerWidth;
_beeWidth = beeWidth;
_playAreaWidth = playAreaWidth; \ t ?
_hiveWidth = hiveWidth; Metoda StartGame() przywraca grę do stanu
_lastH iveLo catio n = playAreaWidth / 2; początkowego. Model widoku przekaże do niej
M issesLeft = 5; szer°k°ść kwiatka, pszczoły oraz obszaru gry
Score = 0; i ula, które będą następnie używane podczas
_gameOver = f a ls e ; prowadzenia rozgrywki.
OnPlayerScored();
}
U T W Ó R Z F O L D E R V I E W I K O N T R O L K Ę B EEC o n t r o l .
W arstw a w idoku pisanej aplikacji sk ład a się z dw óch k o n tro lek użytkow nika. Pierw sza z nich nosi
nazw ę BeeControl i je st anim ow aną, rysunkow ą pszczołą, k tó ra b ędzie się p o ru szać w dół ekranu.
Z acznij o d u tw o rzen ia fo ld eru View . N astę p n ie kliknij go w o knie Solution Explorer praw ym przyciskiem
myszy i w ybierz opcję Add/N ew Item. N astęp n ie d o d a j n o w ą k o n tro lk ę Windows Phone UserControl i zapisz ją
w p lik u BeeControl.xaml.
> 0
A ssets
Tile s
D
A lig n m e n tG rid .p n g
ftp:lljip.helion.pllprzykladylcshru3.zip E 3 A p p lica tio n lco n .p n g
a Bee a n im a tio n l.p n g
N astęp n ie kliknij praw ym przyciskiem myszy Bee a n im a tio n 2.png
using M icrosoft.Phone.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
W arstw a m odelu w idoku naszej aplikacji b ędzie się składać z jed n ej klasy O (2) b - d « gl o p
Search Solution Explorer (Ctrl+;) f-
— BeeAttackViewModel — k tó ra b ędzie korzystać z m eto d , właściwości
Solution 'A takPszczo l' (1 project)
o raz zdarzeń m odelu, by aktualizow ać w idok. Utwórz folder ViewModel,
a [c*] A ta k P s z c z o l
a następnie dodaj do niego klasę BeeAttackViewModel. Poniżej > P Properties
public BeeAttackViewModel() {
Obiekt modelu widoku przechowujereferencję
_model.Missed += MissedEventHandler;
do instancji modelu w polu pryw‘atnym-
_model.GameOver += GameOverEventHandler; To właśnie w tym miejscu określane są
_model.PlayerScored += PlayerScoredEventHandler; procedury obsługi zdo-rzeń modelu.
_ tim e r.T ic k += HiveTim erTick;
GameOver = V is ib ili t y .V is ib l e ;
OnPropertyChanged("GameOver");
}
Oloiekt modelu widoku tworzy
public void StartGame (S ize flo w erS ize, S ize h iv e S ize , S ize playAreaSize) { każdą kontrolkę BeeControl
_flo w erS ize = flo w erS ize; i określa jej szerokość
_h iv e S ize = h ive Size ; i wysokość, musi w tym celu
_playAreaSize = playA reaSize;
dysponować odpowiednimi
informacjami.
_beeSize = new Size(playA reaSize.W idth / 10 playAreaSize.W idth / 10);
_model.StartGam e(flowerSize.W idth, _beeSize.WWidth, playAreaSize.W idth,
h ive Size .W id th );
OnPropertyChanged("MissesLeft");
_ tim e r.In te rv a l = _model.TimeBetweenBees; Widok może rozpocząć grę, wywołując metodę
_ t im e r .S t a r t (); StartGame klasy modelu widoku i przekazując do niej
wielkość kwiatka, ula oraz obszaru gry. Obiekt modelu
GameOver = V is ib ility .C o lla p s e d ;
widoku ustawia wartości swoich pól prywatnych,
OnPropertyChanged("GameOver"); uruchamia zegar i aktualizuje właściwość GameOver.
}
<Image x:Name="flower"
Source="/Assets/Flower.png" Właściwość GameOver obiektu modelu widoku
Grid.Row="2" zwraca wartości typu w yliczeniow y Visibility,
HorizontalAlignment="Left" można ją zatem bezpośrednio powiązaC w k°dzie
Margin="{Binding FlowerMargin}"/> XAML z właściwością Visibility.
Aby móc skorzystać
z tego wiersza,
<StackPanel Grid.Row="1" VerticalAlignm ent="Center"
potrzebujesz
określenia HorizontalAlignment="Center" V is ib ility = "{B in d in g GameOver}">
przestrzeni nazw <StackPanel O rientation= "H orizontal" HorizontalAlignment="Center">
XML xmlns:view <TextBlock Foreground="Yellow"
przedstawionego Style= "{StaticR esource JumpListAlphabetSmallStyle}">Atak</TextBlock>
na następnej <view:BeeControl Width="75" Height="75"/>
stronie. Pozwala <TextBlock Foreground="Black"
on wyświetlić na Style= "{StaticR esource JumpListAlphabetSmallStyle}">pszcz6T</TextBlock>
stronie pszczołę </StackPanel>
machającą <Button Click="Button_Click">Rozpocznij nową grę</Button> Procedura obsługi kliknięć tego przycisku
skrzydełkami. /StackPanel> ^ ______ została przedstawiona na następnej stronie.
</Grid>
xmlns:view=""
http://sch e m as.m icro so ft.co m /clie n t/2 0 0 7
http://schem as.m icrosoft.com /expression/blend/2008
http://schem as.m icrosoft.com /w infx/2006/xam l Z wyświetlonego
h ttp ://schem as.openxm lform ats.org/m arkup-com patibility/2006 okienka wybierz
AtakPszczol (AtakPszczol) przestrzeń nazw
View. Jeśli swojemu
AtakPszczol.Model (AtakPszczol)
projektowi nadałeś inną
AtakPszczol.Resources (AtakPszczol) nazwę, to zobaczysz
AtakPszczol.View (AtakPszczol) ją w okienku zamiast
AtakPszczol.ViewModel (AtakPszczol)
" nazwy AtakPszczol.
Ponow nie naciśnij klawisz Enter i zacznij wpisywać ManipulationDelta="" , by d odać p ro ced u rę
obsługi zdarzeń. ID E wyświetli o k ienko IntelliSense, pozw alające d odać do k o d u u krytego strony
now ą p ro c e d u rę obsługi zdarzeń:
xmlns:view="clr-nam espace:AtakPszczol.View”
K iedy zakończysz w prow adzanie tych zm ian, w otw ierającym znaczniku <UserControl>
p ojaw ią się dwa dodatkow e w iersze: jeśli IDE z jakiegoś powodu nie
xmlns:view="clr-namespace:AtakPszczol.View" \wyświetli °kienkafatelh^iine, ^
to mozesz te wiersze w całości
ManipulationDelta="UserControl_ManipulationDelta" ' wpisać ręcznie.
using ViewModel;
O Z A K T U A L IZ U J S T R O N Ę G Ł Ó W N Ą — D O D A J D O N IE J K O N T R O L K Ę G R Y .
T eraz, kiedy u d ało się n am herm etyzow ać całą grę w ew nątrz kontro lk i BeeAttackGameControl,
w ystarczy dodać ją do strony głównej aplikacji. O tw órz p lik MainPage.xaml i do otwierającego
znacznika <phone:PhoneApplicationPage> dodaj przestrzeń nazw xmlns:view , ta k sam o jak
zrobiłeś w pliku BeeAttackGameControl.xaml.
Także w tym przypadku, jeśli swojemu
xmlns:view="clr-namespace:AtakPszczol.View" ^ ------- pr°jektowi nadałeś inną nazwę, te ta
przestrzeń nazw będzie jej odpowiadać.
N astęp n ie odszukaj k o n tro lk ę G rid o nazw ie ContentPanel i dodaj do niej swoją kon tro lk ę
BeeAttackGameControl. U m ieść k u rso r p om iędzy znacznikiem otw ierającym i zamykającym,
a następ n ie zacznij wpisywać <view: — ID E wyświetli okien k o IntelliSense, z k tó reg o pow inieneś
wybrać kon tro lk ę BeeAttackGameControl :
to je st n o w y ro z d z ia ł ► 873
M ic ro s o ft Ci p o m o ż e
http://www. microsoft.com/en-us/download/details.aspx?id=29854
Po jego zainstalow aniu n a Tw oim k o m p u terze pojaw i się zbiór stro n W W W , plików
m ultim edialnych i p rezen tacji, jak rów nież d o k u m en tacja i kody źródłow e przykładów.
Stanow i on doskonały kolejny k ro k n a d rodze do dalszego p oznaw ania języka C # .
"Windows Camp in a box" is an off-line version of the resources we use for our camps. This kit includes the hands-on-labs, presentations,
samples (with source), and links to additional resources.
Once you have completed and polished your app, attend an application excellence lab to get a developer token to submit your app to
the Windows store.
874 D odatek A
Pozostałości
{
I*
* Zwróć uwagę, że Name i Age są właściwościami, którym towarzyszą pola wewnętrzne
* przeznaczone ty lk o do odczytu. Zastosowanie modyfikatora readonly oznacza,
* że wartość tych pól może być określona wyłącznie w momencie in ic ja liz a c ji
* obiektu (w ich d e k la ra c ji lub w konstruktorze).
*I
/ / / <summary>
I I I Wewnętrzne pole ty lk o do odczytu dla właściwości Name Tworzenie pól tylko do odczytu
poprawia hermetyczność klasy, gdyż
/ / / </summary> po utworzeniu obiektu ich wartości
p riv a te readonly s trin g name; j uż nigdy me będzie można zmienić.
/ / / <summary>
I I I Imię faceta
/ / / </summary>
I
Słowo kluczowe readonly poznałeś
w rozdziale 16., jednak chcieliśmy
p u b lic s trin g Name { get { return name; } } o nim napisać także tutaj, na wypadek
gdybyś zajrzał do tego dodatku, zanim
/ / / <summary> przeczytasz rozdział I6-
I I I Wewnętrzne pole ty lk o do odczytu dla właściwości Age
/ / / </summary>
p riv a te readonly in t age;
/ / / <summary>
I I I Wiek faceta
/ / / </summary>
p u b lic in t Age { get { return age; } }
/*
* Właściwość Cash nie je s t ty lk o do odczytu, gdyż je j wartość może się zmieniać
* podczas is tn ie n ia obiektu Guy.
*/
/ / / <summary>
I I I Ilo ś ć gotówki w posiadaniu faceta
/ / / </summary>
p u b lic in t Cash { get; p riva te se t; }
876 Dodatek A
Pozostałości
p u b l i c o v e r r i d e s t r i n g T o S t r in g ( ) {
r e t u r n S t r i n g . F o r m a t ( " { 0 } ma {1 } l a t a i {2} z f g o t ó w k i . " , Name, Age, Cash);
1
W t ym miejscu przesłaniamy
W <summary> metodę ToStringC). Rozwi a nie to
III B ie r z e fa c e to w i p ie n ią d z e z p o r t f e l a . opisa/iśmy w rozdzia/e 8.
I I I <Isummary>
I I I <param name="amount">Odbierana kwota pie nię d zy</pa ram >
I I I < re tu rn s > W ie lk o ś ó oddanej kwoty lu b 0, j e ś l i f a c e t n i e ma w y s ta rc z a ją c o dużo g o t ó w k i < / r e t u r n s >
p u b l i c i n t G iv e C a s h ( in t amount) {
i f (amount <= Cash && amount > 0)
{
Cash -= amount;
r e t u r n amount;
}
e ls e
{
r e t u r n 0;
}
}
I I I <summary>
I I I Dodaje gotówkę do p o r t f e l a f a c e t a .
I I I <Isummary>
I l l <param name="amount">Otrzymana kwota</param>
I I I <returns>Wysokośó otrzym anej kwoty lu b 0, j e ś l i fa c e t n ic nie otrz y m a f< /re tu rn s >
p u b l i c i n t R e ce iv e C a s h (in t amount) {
i f (amount > 0)
{
Cash += amount;
r e t u r n amount;
}
C o n s o le . W r it e L i n e ( " { 0 } mówi: {1 } z f n i e j e s t kwotą, j a k ą bym p r z y j ą f . " , Name, amount);
r e t u r n 0;
}
Więcej podstaw...
U cząc się now ego języka prog ram o w an ia, łatw o m o żn a poczuć się przytłoczonym . C # nie je st p o d tym w zględem
żadnym w yjątkiem . T o w łaśnie z teg o p o w o d u skoncentrow aliśm y się n a tych jego asp ek tach , k tó re, ja k w ynika z naszych
dośw iadczeń, są najistotniejsze i najczęściej stosow ane p rzez początkujących i śred n io zaaw ansow anych program istów .
Jed n ak C # o raz .N E T p o siad ają tak ie konstrukcje składniow e, k tó re znacznie łatwiej m o żn a opisać w łasnym i słowami,
kiedy już zdobędzie się pew ne dośw iadczenie. N a kilku kolejnych stro n ach przedstaw iliśm y przykładow ą aplikację
konsolow ą, k tó ra je dem onstruje.
/*
* Oto dwa przydatne słowa kluczowe używane w pętlach. Słowo "contlnue"
Wiele osób * nakazuje rozpoczęcie następnej ite r a c ji p ę t li, natomiast "break" powoduje
uważa, że * natychmiastowe przerwanie je j wykonywania.
*
instrukcje
skoków * In stru kcje break, continue, throw oraz return są nazywane Instrukcjam i skoków,
są złym * gdyż ich wykonanie powoduje przeskoczenie do innego miejsca programu. (Poznałeś
rozwiązaniem. * in s tru k c ję break wraz z in s tru k c ją switch/case w rozdziale 8 ., a throw
Zazwyczaj * w rozdziale 10.). Is tn ie je jeszcze jedna in s tru k c ja skoku - goto. Umożliwia
istnieją inne * ona przeskoczenie do miejsca opatrzonego e ty k ie tą . (Na pewno poznasz te e ty k ie ty ,
sposoby * bo mają podobną składnię do tych stosowanych w in s tru k c ji case).
umożliwiające *
uzyskanie * Poniższą p ętlę bez żadnego problemu mógłbyś napisać bez używania in s tru k c ji
tych samych * continue i break. Stanowi ona doskonały przykład pokazujący, ja k C# pozwala zrobić
efektów. * to samo na w iele różnych sposobów. To właśnie dlatego nie musiałeś używać
Warto jednak, * in s tru k c ji break, continue ani innych słów kluczowych i operatorów do pisania
byś wiedział, * programów przedstawionych w te j książce.
jak one *
działają, jeśli * In stru kcja break je s t także stosowana wraz z case, o czym mogłeś się przekonać
kiedyś się na * w rozdziale 8.
nie natkniesz. */
w hile (true) {
in t amountToGive = random.Next(20);
Console.WriteLine("Bob dał Joemu {0} z ło tych , Joe ma {1} zło ty c h , a Bob ma {2} z ło ty c h .",
amountToGive, joe.Cash, bob.Cash);
}
Console.WriteLine("Bobowi zostało {0} z ło ty c h .", bob.Cash);
878 Dodatek A
Pozostałości
/*
Wi e l u z n a s , myśl ąc o programowaniu, myśl i o zerach i jedynkach, a do
sprawdzania tych z e r i jed ynek s ł u ż ą o p e r a t o ry logiczne.
/
// W k s i ą ż c e do ł ą c z e n i a ł a ń c u c h ó w znaków uż y wa l i ś my o p e r a t o r a + i w s z y s t k o
// d z i a ł a ł o d o s k o n a l e . J e dnak w i e l e osób u ni ka s t o s o w a n i a t e g o o p e r a t o r a w p ę t l a c h ,
// k t ó r e b ę d ą w i e l o k r o t n i e wykonywane, gd yż ka ż d e j e g o u ż y c i e p o wodu j e u t w o r z e n i e na
// s t e r c i e nowego o b i e k t u , k t ó r y pot em b ę d z i e m u s i a ł z o s t a ć u s u n i ę t y . W ł a ś n i e z t e g o
// w z g l ę d u .NET u d o s t ę p n i a k l a s ę StringBuilder, k t ó r a d o s k o n a l e n a d a j e s i ę do
// e f e k t y w n e g o t w o r z e n i a i ł ą c z e n i a ł a ń c u c h ó w . J e j metoda Append() d o d a j e ł a ń c u c h na
// k ońc u, AppendFormat() d o d a j e ł a ń c u c h s f o r ma t o w a n y ( u ż y w a j ą c p r z y t ym {0} o r a z
// {1}, t a k j a k r o b i ą t o me t o d y S t r i n g . F o r m a t ( ) o r a z C o n s o l e . W r i t e L i n e ( ) ) , a me t oda
// AppendLine() d o d a j e l i n i j k ę z e z n a k i e m nowego w i e r s z a na k o ń c u . Aby u z y s k a ć
// o s t a t e c z n y ł a ń c u c h , w y s t a r c z y wywoł ać me t o d ę T o S t r i n g ( ) .
S t r i n g B u i l d e r S t r i n g B u i l d e r = new S t r i n g B u i l d e r ( " C z e ś ć ");
stringBuilder.Append("kolego, ");
stringBuilder.AppendFormat("wita {0}-letni facet imi eni em {1}. " , j o e . A g e , joe.Name);
s t r i n g B u i l d e r . A p p e n d t i n e ( " J a k ą z w s p a n i a ł ą pogodę d z i ś mamy."); Jedna uwaga: W tym konkretnym przypadku
Console.W ritetine(stringBuilder.ToStringO); wydajn°ść °biektu StrimgBuilder będzie mniejsza
. niż wydajn°ść operatora +, gdyż ten drugi
Obiektu StringBuilder- u j M s'.ę wstępnie dolicza długość łańcucha i dokładnie
Console.ReadKey(); zazwyczaj w sy^a^ac^ gdy n\e określa, ile pamięci należy dla niego przydzielić
wiadomo, ile operacji łączenia
łańcuchów trzeba będzie wykonać.
/*
* To w y s t a r c z a j ą c e i n f o r m a c j e , by z a c z ą ć p r ogr amowa ć w C#, j e d n a k w żadnym wypadku
* nie są one kompletne ani wyczerpujące. Na s z c z ę ś c i e M i c r o s o f t u d o s t ę p n i a
* d o k u m e n t a c j ę z a w i e r a j ą c ą wykaz w s z y s t k i c h o p e r a t o r ó w , s ł ó w k l uc z owyc h o r a z i nn y c h
* c e c h t e g o j ę z y k a . P r z e j r z y j j ą - j e ś l i d o p i e r o z a c z y n a s z poznawać C#, n i e p r z e j m u j
* s i ę t ym, ż e może s i ę ona wydawać t r u d n a do z r o z u m i e n i a . W i t r y n a MSDN j e s t
* w s p a n i a ł y m ź r ó d ł e m i n f o r m a c j i , choć n i e z o s t a ł a p o m y ś l a n a j a k o c e n t r u m do n a u k i .
*
* d o k u m e n t a c j a j ę z y k a C#: h t t p : / / m s d n . m i c r o s o f t . c o m / p l - p l / l i b r a r y / 6 1 8 a y h y 6 . a s p x
* o p e r a t o r y C#: h t t p : / / m s d n . m i c r o s o f t . c o m / p l - p l / l i b r a r y / 6 a 7 1 f 4 5 d . a s p x
* s ł o wa k l u c z o we C#: h t t p : / / m s d n . m i c r o s o f t . c o m / p l - p l / l i b r a r y / x 5 3 a 0 6 b b . a s p x
*/
namespace H e a d f i r s t . C s h a r p . L e f t o v e r 3
{ Biblioteki można także tworzyć w Visual Studio for Windows 8.
c la s s Guy Poprosiliśmy Cię jednak, żebyś utworzył ten projekt w Visual Studio
{ for Windows Desktop, gdyż dodaje ona odwołania do wszystkich
} złożeń .NET Framewrok, dzięki czemu okno „Reference Manager"
} przedstawione na następnej stronie będzie puste.
Z w róć uw agę, jak V isual Studio dostosow ało nazw ę p rzestrzen i nazw do nazwy biblioteki. T o stan d ard o w e rozw iązanie.
T eraz m ożesz uzupełnić zawartość klasy Guy — wstaw do niej k o d p rzedstaw iony w drugim p unkcie teg o d o d atk u ; już
niedługo będziem y go p o trzebow ać. N a stęp n ie dodaj do biblioteki dwie kolejne klasy: H i T h e r e W r i t e r o ra z L i n e W r i t e r .
Poniżej przedstaw iliśm y k o d pierw szej z nich.
namespace H e a d f i r s t . C s h a r p . L e f t o v e r 3
{
p u b l i c s t a t i c c la s s H iT h e r e W rite r
{
p u b l i c s t a t i c v o id H i T h e r e ( s t r i n g name)
{
MessageBox.Show("Siemka! Mam na im ię " + name);
}
}
}
i n t e r n a l s t a t i c c la s s L i n e W r it e r {
p u b l i c s t a t i c v o i d W r i t e A L i n e ( s t r i n g message)
{
C o n so le .W rite L in e (m e ssa g e ); Naszej bibliotece nadaliśmy nazwę Headf1rst.Csharp.Leftover3,
}
gdyż jest to standardowy sposób nazywania złożeń.
Więcej informacji na ten temat możesz znaleźć na stronie
}
http://msdn.microsoft.com/pl-pl/library/ms229048.aspx.
882 D odatek A
Pozostałości
Error List
Chwila! N asza biblio tek a w ciąż nie chce się skom pilow ać! D ziw na spraw a. Czy podczas w pisyw ania powyższej instrukcji
u s in g zauw ażyłeś, że o k ienko IntelliSense p rzestało w yświetlać podpow iedzi, gdy d o tarłeś do „using System .W in” ? D zieje
się tak dlatego, że w projekcie nie ma odwołania do złożenia System.Windows.Forms.
R ozw iążm y zatem te n p ro b lem , do d ając n iezb ęd n e odw ołanie. P rzejdź do o k n a Solution Explorer i rozw iń opcję References.
K liknij ją praw ym przyciskiem myszy i w ybierz opcję A d d Reference...; n a ek ran ie pojaw i się poniższe o k n o dialogowe.
To co ja właściwie zrobiłem?
Przyjrzyj się dokładnie deklaracjom klas L in e W r ite r o ra z H iT h e re W rite r:
p u b llc c l a s s H iT h ereW riter
In te rn al s t a t i c c l a s s L in e W rite r
W deklaracjach tych zastosowaliśmy m odyfikatory dostępu : klasa H iT h e re W rite r zo stała p o p rze d zo n a m odyfikatorem
p u b lic , n ato m iast L in e W r ite r m o dyfikatorem in t e r n a l . Już za chwilę napiszem y aplikację konsolow ą, k tó ra korzysta
z naszej nowej biblioteki. P ro g ram m oże odwoływać się do klas publicznych biblioteki jedynie bezpośrednio (choć klasy
m ogą być także używ ane p o śred n io , n a przykład gdy je d n a m e to d a wywołuje in n ą lub zw raca o biekt klasy w ew nętrznej
im plem entującej jakiś publiczny interfejs).
Poniew aż nie p o d a n o w niej żadnego m odyfikatora d o stęp u , dom yślnie został użyty m ody fik ato r i n t e r n a l .
B ędziem y chcieli używać klasy Guy w innych p ro g ram ach , zatem zamieńmy ją n a klasę publiczną:
p u b llc c l a s s Guy
A te ra z spróbuj uru ch o m ić p ro g ram w debuggerze. N a ek ran ie pojaw i się następ u jący kom unikat:
K iedy się n ad tym zastanow isz, dojdziesz do w niosku, że to całkiem logiczne — w k ońcu b ib lio tek a klas nie m a żadnego
p u n k tu w ejścia. T o jedynie g ru p a klas, których m ogą używać in n e program y. A zatem dodajm y p ro g ram wykonywalny,
który będzie używ ał naszej biblioteki — dzięki tem u d ebugger będzie m ógł coś u ruchom ić. V isual S tu d io u d o stęp n ia
napraw dę w ygodne narzęd zie, z k tó reg o m ożem y skorzystać przy takich okazjach: pozw ala dodaw ać w iele pro jek tó w do
jednego rozw iązania. K likn ij prawym przyciskiem myszy nazwę rozwiązania w oknie Solution Explorer i wybierz opcję
Add/New Project; n a ek ran ie pojaw i się o k n o dialogow e Add Project. D odaj now ą aplikację konsolow ą o nazw ie MyProgram.
Kiedy już dodasz nowy program , jego p ro jek t pow inien pojaw ić się w oknie Solution Explorer poniżej biblioteki klas. Prawym
przyciskiem myszy kliknij opcję References n ależącą do p ro jek tu MyProgram i wybierz A d d Reference, następnie rozw iń opcję
■* Solution i kliknij Projects. Pow inieneś n a niej zobaczyć utw orzoną wcześniej bibliotekę klas IfTMUffOBTOlfimiSlWWBBl
— zaznacz ją i kliknij przycisk OK . B iblioteka pow inna się teraz pojaw ić w oknie Solution Explorer.
Przejdź n a sam p o czątek pliku Program.cs p ro je k tu MyProgram i zacznij wpisywać instrukcję u sin g :
884 Dodatek A
T eraz m ożem y napisać now y pro g ram . Zacznij o d w pisania Guy. O bserw uj podpow iedzi Pozostałości
i inform acje w yśw ietlane n a ekranie:
W tekście książki niejednokrotnie
piszemy o kompilacji kodu. Kiedy
to robisz, kod jest kompilowany do
Common Intermediate Language (IL)
W o k ienku IntelliSense pojaw i się p e łn a nazw a przestrzen i nazw, do jakiej należy klasa — języka niskiego poziomu używanego
Guy, dzięki czem u m ożesz się p rzek o n ać, że używasz klasy zdefiniow anej w zupełnie przez .NET. Jest to asembler, którego
innym złożeniu. D o k o ń cz p ro g ram : kod nadaje się do analizy i pisania przez
człowieka i do którego kompilowane są
s t a t i c v o id M a i n ( s t r i n g [ ] args) wszystkie programy pisane na platformie
{ .NET (w tym także te w C # i Visual
Guy guy = new G uy("Ja ne k ", 43, 125); Basicu). Kod IL jest z kolei kompilowany
H iT h e re W rite r.H iT h e re (g u y .N a m e );
do kodu maszynowego w momencie
} uruchamiania programu przez CLR.
A te ra z go uru chom . O rany! Z n o w u pojaw ił się te n sam k o m u n ik at co wcześniej, Służy do tego kompilator JIT (ang. just
inform ujący o tym , że nie m o żn a u ruchom ić biblioteki klas! N ie m a p ro b lem u . Kliknij in-time). Jego nazwa wzięła się stąd,
projekt MyProgram praw ym przyciskiem myszy i w ybierz o p cję O Set 35 startup Project, że kod IL jest kompilowany w ramach
wykonywania programu, a nie wcześniej
W skład rozw iązania m oże w chodzić w iele różnych projektów , a w ten sposób
— na etapie jego przygotowywania do
inform ujesz ID E , który z nich należy w ykonać podczas u ru ch am ian ia debuggera. wykonania.
Jeszcze raz spróbuj u ru ch o m ić p ro g ram — te ra z w k ońcu się udało!
Oznacza to, że pliki EXE i DLL
Zbuduj program „W itaj, świecie" z poziomu wiersza poleceń zawierają kod IL, a nie kod maszynowy.
Ma to duże znaczenie, gdyż dzięki
N au k a p ro gram ow an ia w iąże się z n ap isan iem tradycyjnego ju ż p ro g ra m u Witaj, świecie: temu wiele języków może kompilować
p ro steg o p ro g ram u , k tóry wyświetla pojedynczy w iersz tek stu (w łaśnie: „W itaj, św iecie”). kod źródłowy do IL, zapewniając
Jest to zazwyczaj pierw szy p ro g ram pisany w poznaw anym języku, gdyż um iejętność tym samym możliwość wykonywania
programów przez CLR. Należą do
jego napisan ia pokazuje, że dysponujem y w ystarczającym i u m iejętnościam i, by zabrać
nich między innymi: Visual Basic
się za pisan ie bardziej złożonych program ów . P ro g ram Developer C om m and Prom pt jest .NET, F # , J # zarządzane C ++/C L I,
instalow any w raz z V isual S tudio 2012, a kiedy go uruchom im y, k o m p ilato r C # , csc.exe, JScript .NET, Windows PowerShell,
będzie dostępny. U ru ch o m zatem Developer Command Prompt i skorzystaj z N o tatn ik a, IronPython, IronRuby. Jest to bardzo
by napisać p ro g ram WitajSwiecie.cs, n a stęp n ie skom piluj go do po staci p ro g ram u użyteczne rozwiązanie: kod VB.NET
jest kompilowany do IL, zatem istnieje
w ykonyw alnego, używ ając csc.exe, i wykonaj:
możliwość napisania złożenia w C #
D eveloper C om m and Prom pt fo r VS2012
i wykorzystania go w programie pisanym
w Visual Basic.NET (i na odwrót).
c :\Users\Public\Documents>type HelloWorld.es
using System,; Jeśli używasz Macintosha lub systemu
elass HelloWorld {
public static void Main(string[] args) { Linux, to wypróbuj Mono. Jest to
Console.WriteLine("Witaj, t'wiecie! "); ogólnie dostępna implementacja IL
ł
pozwalająca na wykonywanie programów
c :\Users\Public\I>ocuments>csc HelloWorld.es
Kompilator Microsoft (R) Visual C# w wersji 4.0.30319.33440
EXE napisanych w systemie Windows
dla programu Microsoft (R) .MET Framework 4.5 (wystarczy wydać polecenie mono
Copyright (C) Microsoft Corporation. Wszelkie prawa zastrzeżone.
MyProgram.exe, choć zazwyczaj działa
ono tylko z niektórymi złożeniami .NET).
c :\Users\Public\Documents>HelloWorld.exe
Witajj świecie1 Nie będziemy już więcej pisać o Mono,
gdyż nasza książka koncentruje się na
c :\Users\Public\Documents>-
technologiach Microsoftu. Musimy
jednak przyznać, że oglądanie symulatora
ula lub gry w karty na Macu lub Linuksie
jest naprawdę fascynujące!
To tylko jedno spośród wielu zagadnień związanych z tworzeniem i stosowaniem złożeń. Jest ich znacznie więcej
(w tym także i te związane z określaniem ich wersji oraz ich podpisywaniem w celu zapewnienia bezpieczeństwa).
Więcej informacji o złożeniach możesz znaleźć na stronie http://msdn.microsoft.com/pl-pl/library/k3677y81.aspx.
O to prosty projekt, który pom oże Ci zrozum ieć, ja k działa kom ponent BackgroundWorker. Zacznij o d utw orzenia formularza.
U m ieść na nim pole w yboru (nadaj m u nazwę useBackgroundWorkerCheckbox), dwa przyciski ( goButton oraz cancel Button )
oraz kom ponent ProgressBar (o nazwie progressBarl ). N astępnie przeciągnij n a form ularz kom ponent BackgroundWorker.
Pojawi się on u dołu okna projek tan ta formularzy. Zachowaj jego oryginalną nazwę — backgroundWorkerl — a właściwościom
WorkerReportsProgress oraz WorkerSupportsCancelation przypisz wartości tru e .
Oto komponent
BackgroundWorker. Zwróć
uwagę, że ma on bardzo
niewiele właściwości,
które możesz ustawiać.
Z aznacz k o m p o n en t BackgroundWorker i przejdź n a stro n ę Events w o knie Properties (w tym celu kliknij przycisk z ikoną
błyskawicy). P ojaw ią się trzy d o stęp n e zdarzenia: DoWork, ProgressChanged o ra z RunWorkerCompleted. D w ukrotnie
kliknij każde z nich, by d odać o d pow iednie p ro ced u ry obsługi.
886 Dodatek A
Pozostałości
I I I <summary>
I I I Zużywamy cykle procesora zwalniając d zia ła n ie programu poprzez wykonywanie operacji
I I I przez 100 ms.
I I I <|summary>
p riv a te void WasteCPUCycles() { Metoda WasteCPUCycles()
DateTime startTim e = DateTime.Now wykonuje całą masę złożonych
double value = Math.E; operacji matematycznych, zajmując
w hile (DateTime.Now < startTime.AddMilliseconds(lOO)) procesorowi aż 1 0 0 milisekund;
value /= Math.PI; później jej działanie się kończy.
value *= M ath.S qrt(2);
}
}
I I I <summary>
I I I K lik n ię c ie przycisku Jazda! rozpoczyna zużywanie c y k li procesora przez 10 sekund.
I I I <Isummary>
p riv a te void goButton_Click(object sender, EventArgs e) {
goButton.Enabled = fa lse ;
i f (¡useBackgroundWorkerCheckbox.Checked) {
/ / J e ś li nie używamy wątku roboczego, zaczynamy marnować cykle procesora.
fo r ( in t i = 1; i <= 100; i + + ) Kiedy użytkownik kliknie przycisk Jazdal, m^oda
WasteCPUCycles(); obsługująca zdarzenie sprawdzi, czy zostało zaznaczone
progressBarl.Value = i pole wyboru „Użyj BackgroundWorker“. Jeśli nie,
Jeśli formularz to formularz będzie zajmował proces°r przez W sekun<d.
używa komponentu }
goButton.Enabled tru e ; Jeśli jednak zaznaczono pole, to wywołana zostanie
BackgroundWorker, metoda RunWorkerAsync() komponentu Backgr°undWorker,
uaktywniamy } else {
cancelButton.Enabled = tru e ; nakazując mu wykonywanie tych samych operacji w tle.
przycisk Anufaj *_
I I I <summary>
I I I Kiedy użyt kowni k k l i k n i e p r z y c i s k An u l u j , z o s t a n i e wywoTana met oda Backgr oundWor ker . Cance l Async( ) ,
I I I co spowoduje z g ł o s z e n i e komuni kat u o anul owani u wąt ku.
I / / < Isummary> , , Jeśli ut y kownik Miknie przycisk Anuluj,
p r i v a t e voi d c a n c e l B u t t o n _ C l i c k ( o b j e c t s e n d e r , EventArgs e) { spowoduje to wywołanie metody CancelAsync()
backgroundWorkerl.CancelAsync(); ^ komponentu BackgroundWorker, która
} przekazuj e komunikat o przerwaniu pracy.
Kiedy już zakończysz pisanie formularza, uruchom program. Bez trudu zauważysz, że wykorzystanie komponentu BackgroundWorker
znacznie poprawia reakcje programu na poczynania użytkownika.
-# Upewnij się, że pole wyboru Użyj BackgroundWorker nie zostało zaznaczone, a następnie kliknij przycisk Jazda! Zobaczysz,
jak pasek postępu powoli się wypełnia. Spróbuj przeciągnąć okno formularza — nie uda Ci się. Formularz będzie zablokowany.
Jeśli będziesz mieć szczęście, to po jakimś czasie przeskoczy w inne miejsce, odpowiadając na Twoje próby przeciągania.
-# Kiedy operacje zostaną zakończone, zaznacz pole wyboru Użyj BackgroundWorker i ponownie kliknij przycisk Jazda! Teraz
program będzie reagował znacznie lepiej. Bez żadnych opóźnień będziesz mógł go przeciągać, a nawet zamknąć. Kiedy prace
zostaną zakończone, procedura obsługi zdarzenia RunWorkerCompleted przywróci przyciski do stanu początkowego.
-# Podczas działania programu (z użyciem komponentu BackgroundWorker) kliknij przycisk Anuluj. Spowoduje to zmianę wartości
właściwości C ancellatio n P en d in g , co z kolei nakaże programowi przerwać pętlę.
Zastanawiasz się zapewne, dlaczego należy używać metody R e p o rtP ro g re ss(), a nie bezpośrednio ustawiać wartość właściwości Value
komponentu ProgressB ar. Spróbuj tak zrobić. Dodaj poniższy wiersz do metody obsługi zdarzenia DoWork:
progressB arl.V alue = 10;
Następnie ponownie uruchom program. Gdy tylko dotrze on do nowego wiersza kodu, zostanie zgłoszony wyjątek
In validO perationE xcep tio n z informacją: „Cross-thread operation not valid: Control ‘progressBarl’ accessed from a thread other
than the thread it was created on.”2. Jest on zgłaszany, ponieważ komponent BackgroundWorker uruchamia osobny wątek i w nim
wykonuje metodę DoWork(). A zatem w programie istnieją dwa wątki: ten, w którym działa formularz ze swoim graficznym interfejsem
użytkownika, oraz drugi wątek działający w tle. Według zasad działania .NET jedynie wątek GUI może aktualizować kontrolki
formularza; w każdym innym przypadku zostanie zgłoszony wyjątek.
To jedynie jeden z wielu problemów, jakie mogą przydarzyć się początkującemu programiście próbującemu używać wątków — i to właśnie z tego
powodu całkowicie pominęliśmy w tej książce związane z nimi zagadnienia. Jeśli jednak chcesz się z nimi zapoznać, gorąco polecamy doskonały
e-book Joego Albahari w całości poświęcony wykorzystaniu w ątków w C# i .NET. Znajdziesz go na stronie http://www.albahari.com/threading.
2 Niedozwolona operacja pomiędzy wątkami: komponent progressBarl został użyty z innego wątku niż ten,
w którym go utworzono — przyp. tłum.
888 Dodatek A
Pozostałości
Type n e s t e d Cl a s s T y p e = t y p e o f ( N e s t e d C l a s s . D o u b l e N e s t e d C l a s s ) ;
Co n s o l e . Wr i t e L i n e ( n e s t e d C l a s s T y p e . F u l l N a m e ) ;
Gdy pobierzesz obiekt Type d/a
/ / wy ni ki : TypeExampl es . Pr ogr am+Nes t e dCl ass +Doubl eNes t edCl as s
typu generycznego, jego nazwą
będzie nazwa typu z dodanym
List<Guy> g u y L i s t = new Li s t <Guy>( ) ;
odwrotnym apostrofem oraz /iczbą
C o n s o l e . W r i t e L i n e ( g u y L i s t . G e t T y p e ( ) .Name); jego generycznych parametrów.
/ / wy ni ki : L i s t ' 1
C o n s o l e . Wr i t e L i n e ( 1 2 3 4 5 . Ge t T y p e ( ) . F u l l N a me ) ;
¡ I wy ni ki : S y s t e m . I n t 3 2 IS
Także /iterały mają swoj e typy.1 Możesz j e
Cons o l e . Re a dKe y( ) ; pobrać, używając metody GetType().
Jednak to dopiero początek. Platforma .NET udostępnia wbudowany interfejs o nazwie IEquatable<T> , który umożliwia dodanie
do obiektów kodu służącego do porównywania ich i określania, czy są równe innym obiektom. Obiekt implementujący go wie,
jak ma porównywać swoje wartości z wartościami obiektów typu T. Interfejs ten posiada tylko jedną metodę — E q u a ls() — a jego
implementacja polega na napisaniu kodu porównującego zawartość danego obiektu z innym obiektem. W witrynie MSDN dostępna jest
strona zawierająca więcej informacji na ten temat (http:llmsdn.microsoft.comlpl-plllibrarylms131190.aspx). Oto jej fragment:
Jeśli tego Jeśli implementujesz metodę Equals(), powinieneś także przesłonić pochodzące z klasy bazowej implementacje metod
nie zrobisz,
Object.Equals(Object) oraz GetHashCode(), tak by ich działanie było spójne z metodą IEquatable<T>.Equals(). Jeśli przesłonisz
kompilator
wyświetli metodę Object.Equals(Object), to twoja implementacja będzie także wywoływana w ramach wykonywania na rzecz obiektów twojej
ostrzeżenie. klasy statycznej metody Equals(System.Object, System.Object). W ten sposób mamy gwarancję, że wszystkie wywołania metody
Equals() będą zwracać spójne wyniki, co demonstruje przedstawiony przykład.
Poniżej przedstawiliśmy klasę o nazwie EquatableGuy , która rozszerza Guy i implementuje interfejs IEquatable<Guy> .
I I I <summary>
I I I Facet, który w ie, ja k porównywać s ię z innymi facetami
I I I <|summary>
cla ss EquatableGuy : Guy, IEquatable<Guy> {
Metoda Equals() porównuje faktyczne
public EquatableGuy(string name, in t age, in t cash) wartości składowych przekazanego obiektu
: base(name, age, cash) { } z polami Name, Age oraz Cash obiektu
bieżącego, sprawdzając, czy są takie same;
I I I <summary> jeśli są, zwraca true.
I I I Porównuje ten obiekt z innym obiektem EquatableGuy.
I I I <Isummary>
I I I <param name="other">Obiekt EquatableGuy, z którym należy s ię porównaó</param>
I I I <returns>True, j e ś l i obiekty mają te same w a rto ści, fa ls e w przeciwnym przypadku</returns>
public bool Equals(Guy other) {
i f (R eferen ceEq uals(n ull, other)) return fa ls e ;
i f (R efe ren ceEq u als(th is, other)) return tru e ;
return Equals(other.Name, Name) &! . other.Age == Age other.Cash == Cash;
}
890 Dodatek A
Pozostałości
III <summary>
III Przesłania metodę Equals() i wywołuje w n ie j Equals(Guy).
III <Isummary>
III <param name="obj">Obiekt, z którym porównujemy</param>
III <returns>True, je ś l i wartość przekazanego obiektu je s t ró wartości tego obiektu</returns>
publ ic override bool Equals(object obj) {
i f (!(o b j is Guy)) return fa ls e ; Przesłaniamy także metodę
return Equals((Guy)obj); Equals() odziedziczoną po
obiekcie Object oraz metodę
} Ponieważ nasza metoda Equals() porównuje już GetHashCode() (ze względu na
obiekty Guy, po prostu ją wywołujemy. kontrakt, o którym wspominał
III <summary> artykuł z witryny MSDN).
III Elementem kontraktu na przesłonięcie metody Equals() je s t konieczność
III przesłonięcia także metody GetHashCode(). Powinna ona porównać wartości
III i zwrócić tru e , j e ś l i są one sobie równe.
To standardowy sposób działania
III <Isummary> metody GetHashCode(). Zwróć uwagę
III <returns></returns> na zastosowanie bitowego operatora
publ ic override in t GetHashCode() { XOR O , liczby pierwszej oraz
const in t prime = 397; operatora warunkowego (?:).
in t re s u lt = Age;
re s u lt = (re s u lt * prime) ^ (Name != n u ll ? Name.GetHashCode() 0) ;
re s u lt = (re s u lt * prime) ^ Cash;
return re s u lt;
T eraz, kiedy m etody Equals() i GetHashCode() zostały zaim p lem en to w an e tak, by spraw dzać w artości p ó l i właściwości
obiektów , p opraw nie zacznie działać także m eto d a L is t.C o n ta in s () . O to lista List<Guy> zaw ierająca kilka obiektów
Guy, w tym także nowy o b iek t EquatableGuy z takim i sam ym i w artościam i co o b iek t zapisany w zm iennej j o e l :
Jeśli spróbujesz porów n ać dwie referen cje do obiektów E q u a tab leG u y przy użyciu o p erato ró w == o raz !=, to ich działanie
ograniczy się do spraw dzenia, czy o bie w skazują n a ten sam o b iek t lub czy obie m ają w artość n u l i . A co zrobić, gdybyś
chciał, by porów nyw ały o n e faktyczne w artości składowych o b u obiektów ? O k azuje się, że m ożesz przeciążyć operator
— przedefiniow ać go tak , by wykonywał w skazane czynności, jeśli zostanie użyty do p o ró w n an ia referen cji określonego
typu. M ożesz przeanalizow ać tak ie rozw iązanie n a przykładzie klasy E q u a tab le G u y W ith O v erlo a d , k tó ra rozszerza
E q u atab leG u y i dodatkow o p rzeciąża o p e ra to ry == i !=:
/ / / <summary>
/ / / F a c e t, k tó ry w ie, j a k s i ę porównywać z innymi face ta m i
/ / / </summary>
c l a s s E quatableG uyW ithO verload : E quatableG uy
{
p u b lic E q u a ta b leG u y W ith O v e rlo a d (strin g name, i n t a g e , i n t cash )
: base(nam e, a g e , cash ) { }
{ i f ( O b je c t.R e f e r e n c e E q u a ls ( le f t, n u l i ) ) re tu rn f a ls e ; fy n ^ a tr ^ ™ ™
e ls e re tu rn le f t.E q u a ls ( r i g h t) ; ReferenceEqua/sO, zostałby zg^ szebu
} wyj ątek StackOverf/owException Czy
p°trafisz powiedzieć d/aczego?
Ponieważ juz p u b lic s t a t i c bool o p e r a to r != (E quatableG uyW ithO verload l e f t ,
zdefiniowa/iśmy E quatableG uyW ithO verload r i g h t )
działanie {
operatora = = __ r e t u r n ¡ ( l e f t == r i g h t ) ; Gdybyśmy nie przesłoni/i metod £qua/sO
tutaj wystarczy } oraz GetHashCodeO, IDE wyświet/ił°by
zwrócić wartość ostrzeżenie: ,/Equatab/eGuyWithOver/oad
przeciwną. defines operator == or operator 1= but does
p u b lic o v e r r id e bool E q u a ls ( o b je c t o b j) not override Object.GetHashCodeO’ 3.
r e t u r n b a s e .E q u a ls ( o b j) ;
}
p u b lic o v e r r id e i n t GetHashCodeQ
r e t u r n b ase.G e tH ash C o d e ();
{
1
Ponieważ k/asa
Equatab/eGuyWithOver/oad działa
} tak samo jak k/asy Equatab/eGuy
oraz Guy, wystarczy, że wywołamy
metodę k/asy bazowej.
A o to frag m en t k o d u używ ającego obiektów E q u a ta b le G u y W ith O v e rlo a d :
Chwi/eczkę! Co tu się stało?
j o e l = new E q u atab leG u y W ith O v erlo ad (jo el.N am e, jo e l.A g e , jo e l .C a s h ) ; Zostały tu zastosowane
jo e 2 = new E q u atab leG u y W ith O v erlo ad (jo el.N am e, jo e l.A g e , jo e l .C a s h ) ; operatory == i != k/asy
C o n s o le .W r ite L in e (jo e l == jo e 2 ) ; / / fa ls e Guy. Aby użyć właściwych,
nowych operatorów,
C o n s o le .W r ite L in e (jo e l j o e 2 ): / / tr u e zastosuj rzutowanie na
Equatab/eGuyWithOver/oad.
C o n so le .W rite L in e ((E q u a ta b le G u y W ith O v e rlo a d )jo e l ==
(E q u a ta b le G u y W ith 0 v e rlo a d )jo e 2 ); / / tr u e
C o n so le .W rite L in e ((E q u a ta b le G u y W ith O v e rlo a d )jo e l !=
(E q u a ta b le G u y W ith 0 v e rlo a d )jo e 2 ); // fa ls e
jo e 2 .R e c e iv e C a s h (2 5 );
C o n so le .W rite L in e ((E q u a ta b le G u y W ith O v e rlo a d )jo e l ==
(E q u a ta b le G u y W ith 0 v e rlo a d )jo e 2 ); // fa ls e
C o n so le .W rite L in e ((E q u a ta b le G u y W ith O v e rlo a d )jo e l !=
(E q u a ta b le G u y W ith 0 v e rlo a d )jo e 2 ); / / tr u e
3 Klasa EquatableGuyWithOverload definiuje operator = = lub !=, lecz nie przesłania metody Object.GetHashCode() — przyp. tłum.
892 Dodatek A
Pozostałości
enum Sport
{
F o o t b a l l , B a s e b a ll,
B a s k e t b a l l, Hockey,
Boxing, Rugby, Fencing,
}
Mógłbyś sam odzielnie zaim plem entow ać interfejs ¡ E n u m e ra b le , tw orząc w pew nej klasie właściwość C u r r e n t
o raz m eto d ę M o v e N e x t( ) :
Poniżej pokazaliśm y p ę tlę f o r e a c h w yśw ietlającą kolejne elem en ty kolekcji M a n u a lS p o r tE n u m e ra to r . Z w raca o n a sporty
w określonej kolejności (F o o tb all, B aseball, B asketball, H ockey, Boxing, R ugby, Fencing):
C o n s o le . W rit e L in e ( " Z a w a rto ś ć k o l e k c j i S p o r t C o l l e c t i o n : " ) ;
S p o r t C o l l e c t i o n S p o r t C o l l e c t i o n = new S p o r t C o l l e c t i o n ( ) ;
fore a c h (S p o rt s p o r t in S p o r t C o l l e c t i o n )
C o n s o le .W rite L in e (s p o rt.T o S trin g ());
S tw orzenie e n u m e ra to ra w ym aga sp o ro p racy — m usi o n przechow yw ać swój stan i pam iętać, jakie w artości zostały
już zw rócone. N a szczęście C # u d o stę p n ia n ap raw d ę użyteczne narzędzie, k tó re znacznie to ułatw ia. Je st nim instrukcja
y i e l d r e t u r n , a dow iesz się o niej w ięcej, gdy przew rócisz kartkę.
In stru k cja y ie ld re tu rn je st pew nego ro d zaju kom pletnym narzędziem do tw orzenia e n u m erato ró w . P o k azan a poniżej
klasa S p o rtC o lle c tio n zapew nia d okładnie te sam e m ożliwości co klasa p rzed staw io n a n a p o p rzed n iej stro n ie, je d n a k jej
e n u m e ra to r składa się jedynie z trzech w ierszy kodu.
C hoć te n kod w ydaje się nieco dziwny, to jeśli prześledzisz jego działanie w d ebuggerze, n a pew no zo rien tu jesz się,
co się w nim dzieje. G dy k o m p ilato r znajdzie m e to d ę zaw ierającą instrukcję y i e l d re tu rn , k tó ra zw raca w artość typu
IEnumerator lub IEnumerator<T> , to automatycznie doda do klasy metodę MoveNext() oraz właściwość C urrent .
W m om encie w yw ołania tej m etody pierw sza w ykonana instru k cja y ie ld re tu rn spow oduje zw rócenie pierw szej w artości
do pętli foreach . G dy działanie pętli będzie kontynuow ane (poprzez w ywołanie m eto d y MoveNext() ), m eto d a wznowi
działanie bezpośrednio po w ykonanej w cześniej instrukcji y ie ld re tu rn . M eto d a MoveNext() zwróci w arto ść fa ls e
p o zakończeniu działania m eto d y e n u m e rato ra . Z ro zu m ien ie teg o rozw iązania n a podstaw ie o pisu słow nego m oże być
dosyć tru d n e. Z nacznie łatwiej b ędzie je pojąć, u ru ch am iając p ro g ram w debuggerze i śledząc jego działanie k ro k p o kroku
(przy użyciu opcji Step Into lub klaw isza F l l ) . A by nieco ułatw ić Ci zadanie, przedstaw iliśm y poniżej nap raw d ę prosty
e n u m e ra to r o nazw ie NameEnumerator, który zw raca kolejno cztery im iona.
A to p ę tla foreach op e ru ją c a n a tym en u m era to rz e . Skorzystaj z opcji Step Into (F11), by dokład n ie prześledzić,
co się w niej dzieje:
894 Dodatek A
Pozostałości
A o to klasa IEnumerable<Guy> zaw ierająca dane kilku facetów o ra z in d e k sa to r pozw alający n a ustaw ianie
lub p o b ieran ie ich wieku:
in t count = 0;
foreach (s trin g name in namesAndAges.Keys) {
in t cashForGuy =
(++count < namesAndAges.Count) ? random.Next(125) : pileOfCash;
pileOfCash -= cashForGuy; tS
y ie ld return new Guy(name, namesAndAges[name], cashForGuy); \
^ Tutaj tworzymy obiekty Guy z /osową i/ością posiadanej gotówta..
} Robimy to, by pokazać, że enumerator może tworzyć °biekty na żądan'e
podczas wykonywania pęt/i foreach.
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return GetEnumerator();
}
/ / / <summary>
/ / / Indeksator pobiera lub ustawia wiek faceta
/ / / </summary>
/ / / <param name="name">Imię faceta</param>
/ / / <returns>Wiek faceta</returns> Zazwyczaj gdy do indeksatora
p ub lic in t th is [s tr in g name] { zostanie przekazany niewłaściwy
get { indeks, zgłosi on wyjątek
i f (namesAndAges.ContainsKey(name)) I ndexOutOfRangeException.
return namesAndAges[name];
throw new KeyNotFoundException("Nie znaleziono faceta o imieniu " + name);
}
set {
i f (namesAndAges.ContainsKey(name))
namesAndAges[name] = value; Ten indeksator posiada akcesor set,
else który a/bo aktua/izuje wiek faceta,
namesAndAges.Add(name, value); a/bo dodaje nowy obiekt Guy do
słownika.
}
}
}
Poniżej pokazaliśm y kod, który używa in d ek sato ra , by zaktualizow ać w iek jed n eg o faceta, a n a stęp n ie dodaje
dwóch kolejnych, by n a k ońcu wyświetlić wszystkich w pętli.
R efaktoryzacja oznacza zm ianę stru k tu ry k o d u p ro g ram u bez zm iany jego zachow ania. Z aw sze w tedy, gdy piszesz
skom plikow aną m eto d ę , pow inieneś pośw ięcić chwilę, by pow rócić do n apisanego k o d u i pom yśleć, w jak i sposób mógłbyś
go zm ienić, aby był łatwiejszy do zrozum ienia. N a szczęście ID E p o sia d a kilka w budow anych n arzęd zi do refaktoryzacji.
D aje o n a w iele m ożliwości — o to kilka najczęściej używanych.
Wyciągnij metody
W dodatkow ych p ro jek tach sym u lato ra u la, dostępnych w przykładach dołączonych do książki, um ieściliśm y
początkow o kod prezen tu jący w irtualny św iat przy użyciu poniższych p ę tli f o r e a c h :
Z robiliśm y p o tem tę sam ą rzecz dla drugiego bloku składającego się z czterech wierszy. U tw orzyliśm y n a ich podstaw ie
m eto d ę M o v e B e e F r o m H iv e T o F ie ld ( ) . O to efe k t m odyfikacji p ętli f o r e a c h — jest znacznie łatw iejsza do czytania:
896 Dodatek A
Pozostałości
Z robiliśm y to w przy p ad k u b e e C o n t r o l w kodzie sym ulatora. W yśw ietliło się n astęp u jące okno:
To okno
umożliwia wybór IDE wykonuje naprawdę
nowej nazwy ciężką pracę podczas
dla elementu. zamiany nazw. Gdy
Gdybyśmy nazwali zmieniasz nazwę klasy,
go, powiedzmy, musi zostać zmodyfikowana
„Sambo", IDE każda instrukcja, która
przeszukałoby tworzy jej instancję lub
cały kod jej używa. Możesz kliknąć
i zamieniło każde dowolne wystąpienie nazwy
j ego wystąpienie w kodzie. IDE wprowadzi
na „Sambo". zmianę w każdym miejscu
w programie.
p r i v a t e v o id b u t t o n l _ C l i c k ( o b j e c t sender, EventArgs e) {
i n t va lu e = 5;
s t r i n g t e x t = " W i t a j c i e wszyscy";
if (v a lu e = = 3 6 || te x t.C o n ta in s ("w s z y s c y ")) Dodatkowo IDE
zauważy, że powinno
MessageBox.Show("Bum!"); utworzyć metodę
} statyczną, gdyż
wewnątrz niej nie są
używane żadne pola
W ybierz całą zaw artość instrukcji if: v a l u e = = 3 6 | | t e x t . C o n t a i n s ( " w s z y s c y " ) . Kliknij ją obiektów.
praw ym przyciskiem myszy i użyj Refactor/Extract M ethod... W yświetli się n astęp u jące okno:
Wyrażenie używa dwóch
zmiennych o nazwach
value i text. IDE dodało
Każde wyrażenie zatem do metody dwa
warunkowe przyjmuje parametry i nadało im
wartość logiczną. IDE takie same nazwy.
utworzy zatem metodę,
która zwraca bool, T
i zamieni to wyrażenie Dzięki takiemu rozwiązaniu
na jej wywołanie uzyskujesz nie tylko łatwiejszy
w czytaniu kod. Posiadasz
także nową metodę, która
może zostać ponownie użyta
w dowolnym miejscu!
class Program {
delegate void M yIntAndString(int i , s trin g s);
delegate in t CombineTwoInts(int x, in t y );
/*
* W rozdziale 15. dowiedziałeś s ię , ja k używać delegatów, by utworzyć
* referencje do metod. We wszystkich przedstawionych przykładach
* ich zastosowania przypisywaliśmy do nich is tn ie ją c e metody.
*
* Metody anonimowe to metody deklarowane w in s tru k c ji - używane są przy
* tym nawiasy klamrowe { } podobnie ja k w przypadku anonimowych typów.
*
* Więcej inform acji na temat anonimowych metod znajdziesz na s tron ie
* h ttp ://m sd n .m icro so ft.co m /p l-p l/lib ra ry /0 y w 3 tz 5 k .a s p x .
*/
898 Dodatek A
Pozostałości
/*
* Wyrażenia lambda są sp ecja ln ym rodzajem anonimowych metod, k t ó r e
* k o r z y s t a j ą z o p e r a to r a =>. Nosi on nazwę o p e ra to ra lambda, je d n a k
* w przypadku ty c h wyrażeń, c z y t a ją c go, zazwyczaj używamy
* o k r e ś l e n i a "p rz e c h o d z i d o ". Oto p r o s t e w yrażenie lambda:
*
* (a, b) => { r e t u r n a + b; }
*
* Można j e p r z e c z y ta ć j a k o "a i b prz e cho dzi do a p lu s b" - j e s t t o
* metoda anonimowa dodająca dwie w a r t o ś c i . Można wyobrażać s ob ie wyrażenia
* lambda ja k o metody anonimowe p o b i e r a j ą c e pa ram e try i zwracają ce w a r t o ś c i .
*
* Więcej i n f o r m a c j i na temat wyrażeń lambda z n a jd z ie s z na s t r o n i e
* h ttp ://m s d n .m ic ro s o ft.c o m /p l-p l/lib ra ry /b b 3 9 7 6 8 7 .a s p x .
*/
Console.ReadKey();
jesteś tutaj ► 899
J e s t t a k d u ż o L IN Q
900 Dodatek A
Pozostałości
v a r zipcodeGroups = from item in do c.D esce nd ants("p erson " E/ementO zwraca obiekt
group it e m . E l e m e n t ( " f a v o u r i t e D r i n k " ) . V a l u e XE/ement. Możesz
użyć jego właściwości
by it e m . E l e m e n t ( " p e r s o n a l I n f o " ) . E l e m e n t ( " z i p " ) . V a l u e
w ce/u sprawdzenia
i n t o zipcodeGroup
okreś/onych pó/
s e l e c t zip codeGroup;
w dokumencie XML.
fore a c h (v a r group in zipcodeGroups)
C o n s o le . W r it e L i n e ( " { 0 } u lu b io n y c h napojów w { 1 } " ,
g r o u p . D i s t i n c t ( ) . C o u n t ( ) , g r o u p .K e y ) ;
Istnieje jeszcze jedna technologia służąca do tworzenia aplikaqi „okienkowych”, której można używać w Visual Studio 2012 for
Windows Desktop. Nosi ona nazwę Windows Presentation Foundation (w skrócie: W PF). Podobnie jak aplikaqe dla Sklepu
Windows, także aplikaqe W PF bazują na języku XAM L i korzystają z wielu identycznych konstrukqi i składni: kontrolek Grid,
StackPanel , T e xtB lo ck , zasobów statycznych, wiązania danych itd.
Bardzo byśmy chcieli opisać aplikaqe W PF w tej książce, jednak nie mamy na to miejsca.
902 Dodatek A
Pozostałości
NIE M.AŁEM
O TYM POJĘCIA! SKĄD
MOGĘ DOWIEDZIEĆ SIĘ
WIĘCEJ?
Joseph Albahari
bardzo nam pomógł
podczas prac
nad pierwszym
wydaniem tej
książki —
przeprowadził jej
bardzo dokładną
korektę techniczną.
Dziękujemy
za pomoc, Joe!
(U7*5.( )
Istnieje wspaniała ksiqżka, która to wszystko opisuje!
IN A NUTSHELL Jej tytuł to C # 5 .0 in a Nutshell. A u to ram i pozycji są Jo sep h
A lb a h a ri i B en A lbahari. Jest to kom pleksow y p rzew odnik
The Definitive Nefetvnce p o m ożliw ościach C # . Poznasz zaaw ansow ane techniki
teg o języka i zobaczysz wszystkie kluczow e klasy i narzędzia
.N E T F ram ew ork. D ow iesz się, co nap raw d ę dzieje się
Ja są ib A lixih a ri w ew nątrz C # .
O RE ILLY* i - fit’ll AM xiimri
Spraw dź na http://www.oreiHy.com/.
906 Dodatek
Skorowidz
w idoku, 797, 820 C om boB ox, 321, 371, 375, 423, 543 L IN Q , 25, 680, 681, 685, 694, 695, 698,
W indow s.Storage, 570 C o n te n tC o n tro l, 65 701, 845, 898
zagnieżdżanie, 402, 632, 889 dodaw anie, 62, 63 L IN Q to X M L , 900, 901
klaw iatura, 848, 852 G rid , 52, 65, 528, 532, 535, 536, L IN Q P ad , 718
klucz, 702, 707 538, 544, 545, 555, 753, 814 Linux, 885
R ejestru system ow ego, 883 G ridV iew , 713, 821 lista, 393, 394, 395, 395, 396, 397, 398,
klucz-w artość, 413 in terfejsu użytkow nika, 814 401, 422, 439
kod k o n tek st danych, 542 A rrayL ist, 401
IL , 885 k o n te n era , 59 niegeneryczna, Patrz: lista
maszynowy, 885 kopiow anie, 281 A rrayL ist
ukryty, 543 ListBox, 216, 543, 821 sortow anie, 404, 408, 681
znacznikowy, 543 ListView, 692, 713, 777, 821 stała, 401
źródłowy, 885 niew izualna, Patrz: ko m p o n en t wyliczeniowa, 387
kolejka, 435, 436 N u m ericU p D o w n , 148 literał, 183, 189, 211, 240, 241, 242,
kolekcja, 18, 543, 556, 680, 684, 694 N u m ericU p D o w n , 281 243, 889
C hildren, 545, 814, 817, 820, 821 O pen F ileD ialo g , 454
generyczna, 398, 401, 435 PictureB ox, 138, 228, 230, 232, Ł
inicjalizator, 402 289, 498 ładow anie b ezp o śred n ie, 91
ObservableCollections, 776, 787, 823 Polygon, 825 łańcuch
kolizja, 840, 845 P o p u p , 838, 850 strum ieni, 450
kolor, 140 P rogressB ar, 52, 66, 692, 886, Patrz znaków , 110, 147, 205, 243, 386,
k om entarz, 102 , 11 1 też: p asek p o stęp u 411, 457, 483, 549
kom pilator, 98, 269, 682, 885 ScrollV iew er, 532, 533, 535, 542, dzielenie n a fragm enty, 471
k o m p o n en t, Patrz: ko n tro lk a 543, 544, 572 k o n k aten acja, 188, 411
k onkatenacja, Patrz: łańcuch znaków S tackP anel, 52, 65, 539, 555, 753, konw ersja n a tablicę bajtów , 457
k onkatenacja 777, 814
konsola, 262 S tatusS trip, 216 M
k o n stru k to r, 265, 266, 310, 386, 619 szablon, Patrz: szablon k o n tro lek M acin to sh , 885
bezargum entow y, 267, 553, 817 T extB lock, 63, 65, 67, 117, 542, m an ag e d resources, Patrz: zasoby
klasy 543, 544, 545 zarządzane
bazow ej, 311 TextB ox, 148, 281, 459, 532, 542, m anifest
p o to m n ej, 311 543, 547, 551 aplikacji, 53
przeciążony, 471, 817 T im er, 215, 216, 231 p ak ietu , 580, 586
S tream W riter, 445 ToggleSw itch, 753 m aszyna w irtualna, 99, 210
k o n tek st danych, 542, 547, 550, 551, tw orzenie, 781 m eto d a, 11, 73, 102, 103, 211, 267, 351
552, 553 użytkow nika, 781, 786, 817 abstrakcyjna, 356, 359
k o n tra k t danych, Patrz: d an e k o n tra k t z zaw artością, 544 anonim ow a, 768, 898, 899
serializacja, Patrz: serializacja zagnieżdżanie, 545 A ppen d A llT ex t, 456
k o n trak tu danych zoom sem antyczny, 712, 714 A p p en d L in es, 570
k o ntrolka, 52, 66, 96, 99, 115, 132, 219, ko n w erter, 27, 773, 798, 800 A ppendL inesA sync, 570
454, 530 B o o lean N o tC o n v erter, 800 A p p licatio n .D o E v en ts, 140, 377,
A n im ated lm ag e, 825, 849 B ooleanV isibilityC onverter, 800 382, 886,
A ppB ar, 572 w artości, 798 arg u m en t, 188, Patrz też: m eto d a
B ackgroundW ork er, 886 kow ariancja, 414 p a ra m e tr
B o rd er, 544, 572, 753 nazw any, 664
B u tto n , 532 L przekazyw any p rzez referen cję,
C anvas, 63, 65, 66, 85, 545, 814, library assem bly, Patrz: złożenie 656, 657, 660, 661, 662, 663
818, 826 biblioteczne przekazyw any p rzez w artość, 656,
C heckBox, 125, 281 licznik czasu, 53, 80, 82 657, 660, 661, 662, 663
908 Dodatek
Skorowidz
910 Dodatek
Skorowidz
912 Dodatek