You are on page 1of 350

Reiter Istvn

C#
2009, 0.91 verzi

Tartalomjegyzk
1. Bevezet .......8 1.1. A jegyzet jellsei8 1.2. Jogi felttelek8 2. Microsoft .NET Framework...9 2.1. A .NET platform.9 2.1.1. MSIL/CIL...9 2.1.2. Fordts/futtats.9 2.1.3. BCL. 10 2.2. A C# programozsi nyelv.10 2.3. Alternatv megoldsok10 2.3.1. SSCLI10 2.3.2. Mono10 2.3.3. DotGNU..10 3. Fejleszt i krnyezetek..12 3.1. Microsoft Visual Studio12 3.2. SharpDevelop.13 3.3. MonoDevelop..14 4. Hello C#!..16 4.1. A C# szintaktikja..17 4.1.1. Kulcsszavak.17 4.1.2. Megjegyzsek..18 4.2. Nvterek...18 5. Vltozk...20 5.1. Tpusok.20 5.2. Loklis vltozk.21 5.3. Referencia- s rtktpusok21 5.4. Boxing s Unboxing.22 5.5. Konstansok.23 5.6. A felsorolt tpus..23 5.7. Null tpusok..24 6. Opertorok..25 6.1. Opertor precedencia...25 6.2. rtkad opertor..25 6.3. Matematikai opertorok26 6.4. Relcis opertorok..26 6.5. Logikai/feltteles opertorok..27 6.6. Bit opertorok.30 6.7. Rvid forma.32 6.8. Egyb opertorok..33 7. Vezrlsi szerkezetek...34 7.1. Szekvencia...34 7.2. Elgazs34 7.3. Ciklus.36 7.3.1. Yield.39 7.4. Gyakorl feladatok.39 8. Tmbk.41 8.1. Tbbdimenzis tmbk42

3 8.2. ArrayList...44 8.3. Gyakorl feladatok.46 9. Stringek47 9.1. Metdusok47 9.2. StringBuilder49 10. Tpuskonverzik.51 10.1. Ellen rztt konverzik51 10.2. Is s as.52 10.3. Karakterkonverzik..52 11. Gyakorl feladatok I...54 12. Objektum-orientlt programozs elmlet.54 12.1. UML..56 12.2. Osztly.56 12.3. Adattag s metdus.56 12.4. Lthatsg..57 12.5. Egysgbezrs..57 12.6. rkl ds...58 13. Osztlyok..58 13.1. Konstruktorok.......59 13.2. Adattagok62 13.3. Lthatsgi mdostk...62 13.4. Parcilis osztlyok...62 13.5. Begyazott osztlyok..63 13.6. Objektum inicializlk.64 13.7. Destruktorok..64 14. Metdusok67 14.1. Paramterek...68 14.2. Parancssori paramterek...71 14.3. Kiterjesztett metdusok..71 14.3.1. Gyakorl feladatok72 15. Statikus tagok.73 15.1. Statikus adattagok73 15.2. Statikus metdusok.73 15.3. Statikus tulajdonsgok...74 15.4. Statikus konstruktor74 15.5. Statikus osztlyok74 15.6. Gyakorl feladatok...75 16. Tulajdonsgok76 17. Indexel k..79 18. Gyakorl feladatok II.80 19. rkl ds.81 19.1. Konstruktorok...82 19.2. Polimorfizmus...82 19.3. Virtulis metdusok.83 19.4. Lezrt osztlyok85 19.5. Absztrakt osztlyok.86 20. Interfszek88 20.1. Explicit interfszimplementci...90 20.2. Virtulis tagok91 20.3. Gyakorlati plda 1.92

4 20.4. Gyakorlati plda 2.94 21. Opertor tlterhels...96 21.1. Egyenl sg opertorok..97 21.2. Relcis opertorok.98 21.3. Konverzis opertorok98 21.4. Kompatibilits ms nyelvekkel.99 21.5. Gyakorlati plda..100 22. Struktrk...102 23. Kivtelkezels103 23.1. Kivtel hierarchia104 23.2. Sajt kivtel ksztse...105 23.3. Kivtel tovbbadsa..105 23.4. Finally blokk.105 24. Generikusok..107 24.1. Generikus metdusok...107 24.2. Generikus osztlyok..108 24.3. Generikus megszortsok110 24.4. rkl ds.111 24.5. Statikus tagok.....111 24.6. Generikus gy jtemnyek.112 24.7. Generikus interfszek...113 25. Delegate ek..114 25.1. Tbbszrs delegate ek.115 25.2. Paramter s visszatrsi rtk.116 25.3. Nvtelen metdusok..117 26. Esemnyek.118 26.1. Gyakorl feladatok.119 27. Lambda kifejezsek.120 27.1. Generikus kifejezsek...120 27.2. Kifejezsfk..121 27.3. Lambda kifejezsek vltozinak hatkre..121 27.4. Esemnykezel k....122 28. Attribtumok..123 29. Az el fordt..125 30. Unsafe kd.126 30.1. Fix objektumok...127 31. Tbbszl alkalmazsok...128 31.1. Application Domain ek130 31.2. Szlak131 31.3. Aszinkron delegate ek131 31.4. Szlak ltrehozsa.135 31.5. Foreground s background szlak...136 31.6. Szinkronizci.136 32. Reflection142 33. llomnykezels..144 33.1. Olvass/rs file bl/file ba..144 33.2. Knyvtrstruktra kezelse147 33.3. In memory streamek..149 34. Grafikus fellet alkalmazsok ksztse.151 34.1. Windows Forms Bevezet ...151

5 34.2. Windows Presentation Foundation - Bevezet .....153 35. Windows Forms...156 36. Windows Forms Vezrl k..........................................160 36.1. Form.........................................160 36.2. Button.......................................161 36.3. Label.........................................162 36.4. TextBox....162 36.5. ListBox..164 36.6. CheckBox.167 36.7. CheckedListBox.168 36.8. RadioButton....169 36.9. ComboBox...171 36.10. TreeView....172 36.11. DomainUpDown...175 36.12. NumericUpDown..176 36.13. ProgressBar..176 36.14. TrackBar.177 36.15. PictureBox.177 36.16. RichTextBox.178 36.17. DateTimePicker....181 36.18. MenuStrip..183 36.19. ltalnos prbeszdablakok186 36.20. TabControl.192 36.21. ContextMenuStrip192 36.22. Gyakorl feladatok..192 37. Windows Forms Komponensek....193 37.1. Timer..193 37.2. ErrorProvider...193 37.3. BackGroundWorker...194 37.4. Process.196 37.5. ImageList..196 37.6. Gyakorl feladatok.197 38. Windows Forms - j vezrl k ltrehozsa....198 38.1. Szrmaztats...198 38.2. UserControl -ok..200 39. Rajzols: GDI+..203 39.1. Kpek kezelse...205 40. Drag and Drop...206 41. Windows Presentation Foundation.208 41.1. A WPF Architektra...209 41.2. A WPF osztlyhierarchija...209 41.3. XAML.210 41.3.1. Fordts211 41.3.2. Logikai- s Vizulis fa..211 41.3.3. Felpts...212 42. WPF Esemnyek s tulajdonsgok215 43. WPF Vezrl k elrendezse221 43.1. StackPanel...221 43.2. WrapPanel223 43.3. DockPanel224

6 43.4. Canvas..225 43.5. UniformGrid.226 43.6. Grid.227 44. WPF Vezrl k231 44.1. Megjelens s szvegformzs.231 44.2. Vezrl k....236 44.2.1. TextBlock s Label.236 44.2.2. CheckBox s Radiobutton...238 44.2.3. TextBox, PasswordBox s RichTextBox.240 44.2.4. ProgressBar, ScrollBar s Slider...243 44.2.5. ComboBox s ListBox..247 44.2.6. TreeView...250 44.2.7. Menu..251 44.2.8. Expander s TabControl..252 44.2.9. Image s MediaElement...254 45. Er forrsok...256 45.1. Assembly resource256 45.2. Object resource..256 46. Stlusok..260 46.1. Triggerek262 46.1.1. Trigger...262 46.1.2. MultiTrigger..263 46.1.3. EventTrigger263 47. Sablonok265 48. Commanding....267 49. Animcik s transzformcik272 49.1. Transzformcik.272 49.1.1. MatrixTransform.273 49.1.2. RotateTransform.277 49.1.3. TranslateTransform...279 49.1.4. ScaleTransform..280 49.1.5. SkewTransform...282 49.1.6. TransformGroup.283 49.2. Animcik.285 49.2.1. From-to-by animcik...286 49.2.2. Key Frame animcik290 49.2.3. Spline animcik291 49.2.4. Animcik s transzformcik...292 50. Vezrl k ksztse.294 50.1. UserControl.295 51. ADO.NET.305 51.1. MS SQL Server 2005/2008 Express...305 52. SQL Alapok...307 52.1. Adatbzis ltrehozsa...307 52.2. Tblk ltrehozsa.308 52.3. Adatok beszrsa tblba...309 52.4. Oszlop trlse tblbl.309 52.5. Kivlaszts...309 52.6. Sz rs...310 52.7. Rendezs..311

7 52.8. Adatok mdostsa312 52.9. Relcik312 52.10. Join.314 53. Kapcsolds az adatbzishoz.314 53.1. Kapcsolds Access adatbzishoz..315 53.2. Kapcsolds Oracle adatbzishoz....316 54. Kapcsolat nkli rteg...317 54.1. DataTable..317 54.2. DataGridView...321 54.3. DataView...329 54.4. DataSet..330 54.4.1. Relcik tblk kztt331 54.4.2. Tpusos DataSet -ek...333 55. Adatbzis adatainak lekrdezse s mdostsa..335 55.1. Connected s Disconnected kzti kapcsolat.. ..336 56. XML.342 56.1. XML file ok kezelse343 56.2. XML DOM..346 56.3. XML szerializci347 56.4. XML s a kapcsolat nlkli rteg. .349

1. Bevezet
Napjainkban egyre nagyobb teret nyer a .NET Framework s egyik f nyelve a C#. Ez a jegyzet abbl a clbl szletett, hogy megismertesse az olvasval ezt a nagyszer technolgit. A jegyzet a C# 2.0 s 3.0 verzijval is foglalkozik, ha nincs kln feltntetve akkor az adott nyelvi elem gond nlkl hasznlhat a korbbi verziban. Nhny fejezet felttelez olyan tudst amely alapjt egy ks bbi fejezet kpezi, ezrt ne essen ktsgbe a kedves olvas, ha valamit nem rt, egyszer en olvasson tovbb s trjen vissza a krdses fejezethez, ha rtallt a vlaszra. Brmilyen krst, javaslatot illetve hibajavtst szvesen vrok a reiteristvan@gmail.com email cmre.

1.1 A jegyzet jellsei


Forrskd: szrke alapon, kerettel Megjegyzs: fehr alap, kerettel

1.2 Jogi felttelek

A jegyzet teljes tartalma a Creative Commons Nevezd meg!-Ne add el! 2.5 Magyarorszg liszensze al tartozik. Szabadon mdosthat s terjeszthet , a forrs feltntetsvel. A jegyzet ingyenes, mindennem rtkestsi ksrlet tiltott s a szerz beleegyezse nlkl trtnik. A mindenkori legfrissebb vltozat letlthet a kvetkez oldalakrl: http://people.inf.elte.hu/reiter_i/sharp.html http://devportal.hu/groups/fejlesztk/media/p/1122.aspx

A kvetkez frissts id pontja: 2009. prilis 12.

2. Microsoft .NET Framework


A kilencvenes vek kzepn a Sun MicroSystems kiadta a Java platform els nyilvnos vltozatt. Az addigi programnyelvek/platformok klnbz okokbl nem tudtk felvenni a Java val a versenyt, gy szmtalan fejleszt dnttt gy, hogy a knyelmesebb s sokoldalbb Java t vlasztja. Rszben a piac visszaszerzsnek rdekben a Microsoft a kilencvenes vek vgn elindtotta a Next Generation Windows Services fed nev projektet, amelyb l aztn megszletett a .NET.

2.1 A .NET platform


Maga a .NET platform a Microsoft, a Hewlett Packard, az Intel s msok kzrem kdsvel megfogalmazott CLI (Common Language Infrastructure) egy implementcija. A CLI egy szablyrendszer, amely maga is tbb rszre oszlik: A CTS (Common Type System) az adatok kezelst, a memriban val megjelenst, az egymssal val interakcit, stb. rja le. A CLS (Common Language Specification) a CLI kompatibilis nyelvekkel kapcsolatos elvrsokat tartalmazza. A VES (Virtual Execution System) a futsi krnyezetet specifiklja, nevezik CLR -nek (Common Language Runtime) is. A .NET nem egy programozsi nyelv, hanem egy krnyezet. Gyakorlatilag brmilyen programozsi nyelvnek lehet .NET implementcija. Jelenleg kb. 50 nyelvnek ltezik hivatalosan .NET megfelel je, nem beszlve a szmtalan hobbifejlesztsr l.

2.1.1 MSIL/CIL
A hagyomnyos programnyelveken mint pl. a C++ - megrt programok n. natv kdra fordulnak le, vagyis a processzor szmra kis tlzssal azonnal rtelmezhet ek. Ezeknek a nyelveknek az el nye a htrnya is egyben. Br gyorsak, de rengeteg hibalehet sg rejt zik a felgyelet nlkli (unmanaged) vgrehajtsban. A .NET (akrcsak a Java) ms ton jr, a fordt egy kztes nyelvre (Intermediate Language) fordtja le a forrskdot. Ez a nyelv a .NET vilgban az MSIL illetve a szabvnyosts utn CIL (Microsoft/Common IL) klnbsg csak az elnevezsben van.

2.1.2 Fordts/futtats
A natv programok n. gpi kdra fordulnak le, mg a .NET forrskdokbl egy CIL nyelv futtathat llomny keletkezik. Ez a kd a felteleptett .NET Framework nek szl utastsokat tartalmaz. Amikor futtatjuk ezeket az llomnyokat el szr az n. JIT (justintime) fordt veszi kezelsbe s lefordtja ket gpi kdra, amit a processzor mr kpes kezelni. Amikor el szr lefordtjuk a programunkat akkor egy n. Assembly (vagy szerelvny) keletkezik. Ez tartalmazza a felhasznlt illetve megvalstott tpusok adatait (ez az n. Metadata) amelyeket a futtat krnyezet fel tud hasznlni a futtatshoz (az osztlyok neve, metdusai, stb). Egy Assembly egy vagy tbb file bl is llhat.

10

2.1.3 BCL
A .NET Framework teleptsvel a szmtgpre kerl tbbek kzt a BCL (Base Class Library), ami az alapvet feladatok (file olvass/ rs, adatbzis kezels, adatszerkezetek, stb) elvgzshez szksges eszkzket tartalmazza. Az sszes tbbi knyvtr (ADO.NET, WCF, stb) ezekre a knyvtrakra pl.

2.2 A C# programozsi nyelv


A C# (ejtsd: Sz-srp) a Visual Basic mellett a .NET f programozsi nyelve. 1999 ben Anders Hejlsberg vezetsvel kezdtk meg a fejlesztst. A C# tisztn objektumorientlt, tpusbiztos, ltalnos felhasznls nyelv. A tervezsnl a lehet legnagyobb produktivits elrst tartottk szem el tt. A nyelv elmletileg platformfggetlen (ltezik Linux s Mac fordt is), de napjainkban a legnagyobb hatkonysgot a Microsoft implementcija biztostja.

2.3 Alternatv megoldsok


A Microsoft .NET Framework jelen pillanatban csak s kizrlag Microsoft Windows opercis rendszerek alatt elrhet . Ugyanakkor a szabvnyosts utn a CLI specifikci nyilvnos s brki szmra elrhet lett, ezen ismeretek birtokban pedig tbb fggetlen csapat vagy cg is ltrehozta a sajt CLI implementcijt, br eddig mg nem sikerlt teljes mrtkben reproduklni az eredetit. Ezt a clt nehezti, hogy a Microsoft id kzben szmos a specifikciban nem szerepl vltoztatst vgzett a keretrendszeren.

2.3.1 SSCLI
Az SSCLI (Shared Source Common Language Infrastructure) vagy korbbi nevn Rotor a Microsoft ltal fejlesztett nylt forrs, keresztplatformos vltozata a .NET Frameworknek (teht nem az eredeti lebuttott vltozata). Az SSCLI Windows, FreeBSD s Mac OSX rendszereken fut. Az SSCLI t kimondottan tanulsi clra ksztette a Microsoft, ezrt a liszensze engedlyez mindenfajta mdostst, egyedl a piaci rtkestst tiltja meg. Ez a rendszer nem szolgltatja az eredeti keretrendszer teljes funkcionalitst, jelen pillanatban valamivel a .NET 2.0 mgtt jr. Az SSCLI project jelen pillanatban megsz nni vagy legalbbis id legesen lellni ltszik. Ett l fggetlenl a forrskd s a hozz tartoz dokumentcik rendelkezsre llnak, letlthet ek a kvetkez webhelyr l: http://www.microsoft.com/downloads/details.aspx?FamilyId=8C09FD61-3F264555-AE17-3121B4F51D4D&displaylang=en

2.3.2 Mono
A Mono projekt szl atyja Miguel de Icaza 2000 ben kezdte meg a fejlesztst s egy vvel ks bb mutatta be ez els kezdetleges C# fordtt. A Ximian (amelyet Icaza s Nat Friedman alaptott) felkarolta az tletet s 2001 jliusban hivatalosan

11 is elkezd dtt a Mono fejlesztse. 2003 ban a Novell felvsrolta a Ximian t, az 1.0 verzi mr Novell termkknt kszlt el, egy vvel ks bb. A legutols verzi (2.0.1) amely 2008 oktberben ltott napvilgot. Ez a vltozat mr a teljes .NET 2.0 t magba foglalja, illetve a C# 3.0 kpessgeit is rszlegesen tmogatja (pl. van LINQ to XML illetve LINQ to Objects). Ugyanez a verzi tartalmazza a Novell SilverLight alternatvjt, amit MoonLight ra kereszteltek, ennek fejlesztsben a Novell segtsgre van a Microsoft is. A Mono sajt fejleszt eszkzzel is rendelkezik a MonoDeveloppal. Ezzel a vltozattal prhuzamosan fejleszts alatt ll a .NET 3.0 t is tmogat rendszer, a project kdneve Olive (http://www.mono-project.com/Olive). A Mono Windows, Linux, UNIX, BSD, Mac OSX s Solaris rendszereken elrhet . Napjainkban a Mono mutatja a leggretesebb fejl dst, mint a Microsoft .NET jv beli ellenfele illetve keresztplatformos trsa. A Mono emblmja egy majmot brzol, a sz ugyanis spanyolul majmot jelent. A Mono hivatalos oldala: http://www.mono-project.com/Main_Page

2.3.3 DotGNU
A DotGNU a GNU projekt rsze, amelynek clja egy ingyenes s nylt alternatvt nyjtani a Microsoft implementci helyett. Ez a projekt szemben a Mono val nem a Microsoft BCL el val kompatibilitst helyezi el trbe, hanem az eredeti szabvny pontos s tkletes implementcijnak a ltrehozst. A DotGNU sajt CLI megvalstsnak a Portable .NET nevet adta. A jegyzet rsnak idejn a projekt lellni ltszik. A DotGNU hivatalos oldala: http://www.gnu.org/software/dotgnu/

12

3. Fejleszt i krnyezetek
Ebben a fejezetben nhny IDE t (Integrated Development Environment), azaz Integrlt Fejleszt i Krnyezetet vizsgluk meg. Termszetesen ezek nlkl is lehetsges fejleszteni, azonban egy j fejleszt eszkz hinya jelent sen meggtolja a gyors s produktv programozst. Ugyanakkor a jegyzet els felben egy hagyomnyos szvegszerkeszt vel s a parancssorral is lehet gond nlkl haladni.

3.1 Microsoft Visual Studio


Valszn leg a legelterjedtebb IDE a .NET programozshoz. A Visual Studio termkcsald rsze az Express sorozat, amely teljesen ingyenes mindenki szmra, egy Express eszkzzel ksztett program el is adhat, anlkl, hogy brmifle liszenszet vsrolnnk. Ez a termkvonal nmileg kevesebb funkcionalitssal rendelkezik, mint nagyobb testvre, ugyanakkor kivlan alkalmas hobbifejlesztsre (de akr rendes kereskedelmi program ksztsre is). A jegyzet a grafikus fellet alkalmazsok ksztsnl Visual Studio t hasznl majd, ezrt rdemes tjkozdni az egyes fejleszt eszkzk klnbsgeir l. A kpen a Visual Studio 2008 fellete lthat:

Jobb oldalon fll a Solution Explorer lthat, ez a projektnk file jait tartalmazza. Alatta a Properties ablak, ez f knt a grafikus designer ablaknl hasznlhat, az egyes vezrl elemek tulajdonsgait (nv, szlessg, magasssg, stb) llthatjuk be. Vgl a legnagyobb terletet a kdszerkeszt ablak foglalja el.

13 A Visual Studio tartalmazza az n. IntelliSense rendszert, amely automatikusan felajnlja az elkezdett metdusok/vltozk/osztlyok/stb. nevnek kiegsztst. j projekt ltrehozshoz Kattintsunk a File menre, vlasszuk ki a New menpontot azon bell pedig a Projekt et (vagy hasznljuk a Ctrl+Shift+N billenty kombincit). Ha nem az lenne megnyitva, akkor kattintsunk a bal oldali listban a C# elemre. Grafikus fellet alkalmazs ksztshez vlasszuk ki a Windows Application vagy a WPF Application sablont. Konzolalkalmazs ksztshez (a jegyzet els rszben f knt ilyet runk majd) a Console Application sablonra lesz szksgnk. Miel tt lefordtjuk a programunkat, kivlaszthatjuk, hogy Debug vagy Release mdban fordtsunk, el bbi tartalmaz nhny plusz informcit a hibakeress el segtshez (gy egy kicsit lassabb), utbbi a mr ksz programot jelenti. Lefordtani a programot a Build menpont Build Solution parancsval (vagy az F6 gyorsbillenty vel) tudjuk, futtatni pedig a Debug men Start Debugging (vagy az F5 billenty lenyomsval illetve a kis zld hromszgre val kattintssal) parancsval. Ez utbbi automatikusan lefordtja a projektet, ha a legutols fordts utn megvltozott valamelyik file.

3.2 SharpDevelop
A SharpDevelop egy nylt forrskd ingyenes alternatva Windows opercis rendszer alatti .NET fejlesztshez.

A legfrisebb verzit idn februrban adtk ki, ez mr tmogatja a C# 3.0 verzijt, valamint a Visual Basic.NET, Boo, IronPython s F# programozsi nyelveket is.

14 A SharpDevelop hivatalos oldala: http://www.sharpdevelop.com/OpenSource/SD/ rdekessg, hogy a fejleszt k egy knyvet is rtak a fejleszts menetr l s ez a knyv ingyenesen letlthet . Eredetileg a Wrox kiad oldalrl lehetett letlteni, de annak megsz nse utn tkerlt az Apress kiadhoz. Sajnos gy t nik, hogy az Apress nem kvnja nylvnossgra hozni a knyvet, de termszetesen az interneten brmit meg lehet tallni, gy a kvetkez webhelyr l ez is letlthet : http://www.mediafire.com/?thnognidwyj A knyv letltse nem szmt illeglis cselekmnynek!

3.3 MonoDevelop
A MonoDevelop els sorban a Mono hoz kszlt nylt forrskd s ingyenes fejleszt eszkz. Eredetileg Linux oprcis rendszer al szntk, de van mr Windows, Mac OSX, OpenSolaris s FreeBSD alatt fut vltozata is.

A MonoDevelop a kvetkez programozsi nyelveket tmogatja: C#, Java, Visual Basic.NET, Nemerle, Boo, CIL, C, C++. A MD eredetileg a SharpDevelop bl szrmazott el, de a mai vltozatok mr kevss hasonltanak az sre. A MonoDevelop ot akrcsak a Mono t a Novell (is) fejleszti.

15 A MonoDevelop hivatalos oldala: http://www.monodevelop.com/ A MonoDevelop egyetlen nagy htult vel rendelkezik: Windows opercis rendszer al nem ltezik hozz telept szoftver, a felhasznlnak magnak kell lefordtania a forrskdot.

16

4. Hello C#!
A hres-hrhedt Hello World! program els knt Dennis Ritchie s Brian Kerningham A C programozsi nyelv cm knyvben jelent meg, s azta szinte hagyomny, hogy egy programozsi nyelv bevezet jeknt ezt a programot mutatjk be. Semmi mst nem csinl, mint kirja a kperny re az dvzletet. Mi itt most nem a vilgot, hanem a C# nyelvet dvzljk, ezrt ennek megfelel en mdostsuk a forrskdot:
using System; class HelloWorld { static public void Main() { Console.WriteLine("Hello C#!"); Console.ReadKey(); } }

Miel tt lefordtjuk tegynk pr lpst a parancssorbl val fordts el segtsre. Ahhoz, hogy gy is le tudjunk fordtani egy forrsfile t, vagy meg kell adnunk a fordtprogram teljes elrsi tjt (ez a mi esetnkben elg hossz) vagy a fordtprogram knyvtrt fel kell venni a PATH krnyezeti vltozba. Ez utbbihoz Vezrl pult/Rendszer -> Specilis fl/Krnyezeti vltozk. A rendszervltozk listjbl keressk ki a Path t s kattintsunk a Szerkeszts gombra. Most nyissuk meg a Sajtgpet, C meghajt, Windows mappa, azon bell Microsoft.NET/Framework. Nyissuk meg vagy a v2.0 vagy a v3.5.. kezdet mappt (attl fgg en, hogy C# 2.0 vagy 3.0 kell). Msoljuk ki a cmsorbl ezt a szp hossz elrst. Vissza a Path hoz. A vltoz rtke sorban navigljunk el a vgre, rjunk egy pontosvessz t (;) s illesszk be az elrsi utat. Mindent OK zunk le s ksz. Ha van megnyitva konzol vagy PowerShell azt indtsuk jra, utna rjuk be, hogy csc. Azt kell ltnunk, hogy Microsoft Visual C# 2008 Compiler Version 3.5 Itt az vszm s verzi vltozhat, ez a C# 3.0 zenete. Most mr fordthatunk a csc filenev.cs paranccsal (termszetesen a szveges file kiterjesztse .txt, gy nevezzk t mivel a C# forrskdot tartalmaz file -ok kiterjesztse .cs). Az els sor megmondja a fordtnak, hogy hasznlja a System nvteret. Ezutn ltrehozunk egy osztlyt, mivel a C# teljesen objektumorientlt ezrt utastst csakis osztlyon bell adhatunk. A HelloWorld osztlyon bell definilunk egy Main nev statikus fggvnyt, ami a programunk belpsi pontja lesz. Minden egyes C# program a Main fggvnnyel kezd dik, ezt mindenkppen ltre kell hoznunk. Vgl meghvjuk a Console osztlyban lv WriteLine() s ReadKey() fggvnyeket. El bbi kirja a kperny re a paramtert, utbbi vr egy billenty letst.

17 Ebben a bekezdsben szerepel nhny (sok) kifejezs ami ismeretlen lehet, a jegyzet ks bbi fejezeteiben mindenre fny derl majd.

4.1 A C# szintaktikja
Amikor egy programozsi nyelv szintaktikjrl beszlnk, akkor azokra a szablyokra gondolunk, amelyek megszabjk a forrskd felptst. Ez azrt fontos, mert az egyes fordtprogramok az ezekkel a szablyokkal ltrehozott kdot tudjk rtelmezni. A C# C-stlus szintaxissal rendelkezik (azaz a C programozsi nyelv szintaxishoz hasonlt), ez hrom fontos kittelt von maga utn: - Az egyes utastsok vgn pontosvessz (;) ll - A kis- s nagybet k klnbz jelent sggel brnak, azaz a program s Program azonostk klnbznek. Ha a fenti kdban Console.WriteLine() helyett console.writeline() t rnnk a program nem fordulna le. - A program egysgeit (osztlyok, metdusok, stb.) n. blokkokkal jelljk ki, a kapcsos zrjelek ({, }) segtsgvel.

4.1.1 Kulcsszavak
Szinte minden programnyelv definil kulcsszavakat, amelyek specilis jelent sggel brnak a fordt szmra. Ezeket az azonostkat a sajt meghatrozott jelentskn kv l nem lehet msra hasznlni, ellenkez esetben a fordt hibt jelez. Vegynk pldul egy vltozt, aminek az int nevet akarjuk adni. Az int egy beptett tpus neve is, azaz kulcssz, ezrt nem fog lefordulni a program:
int int; //hiba

A legtbb fejleszt eszkz megsznezi a kulcsszavakat (is), ezrt knny elkerlni a fenti hibt. A C# 77 darab kulcsszt ismer: abstract as base bool break byte case catch char checked class const continue decimal default delegate do double else enum event explicit extern false finally fixed float for foreach goto if implicit in int interface internal is lock long namespace new null object operator out override params private protected public readonly ref return sbyte sealed short sizeof stackalloc static string struct switch this throw true try typeof uint ulong unchecked unsafe ushort using virtual volatile void while

18 Ezeken kv l ltezik mg 23 darab azonost, amelyeket a nyelv nem tart fenn specilis hasznlatra, de klnleges jelentssel brnak. Amennyiben lehetsges kerljk a hasznlatukat hagyomnyos vltozk, metdusok, osztlyok ltrehozsnl: add ascending by descending equals from get global group in into join let on orderby partial remove var select where set yield value

Nhnyuk a krnyezett l fgg en ms-ms jelentssel is brhat, a megfelel fejezet b vebb informcit ad majd ezekr l az esetekr l.

4.1.2 Megjegyzsek
A forrskdba megjegyzseket tehetnk. Ezzel egyrszt zeneteket hagyhatunk (pl. egy metdus lersa) magunknak, vagy a tbbi fejleszt nek, msrszt a kommentek segtsgvel dokumentcit tudunk generlni, ami szintn az els clt szolglja csak ppen lvezhet bb formban. Megjegyzseket a kvetkez kppen hagyhatunk:
using System; class HelloWorld { static public void Main() { Console.WriteLine("Hello C#"); // Ez egy egysoros komment Console.ReadKey();
/* Ez egy tbbsoros komment */

} }

Az egysoros komment a sajt sora legvgig tart, mg a tbbsoros a kt /* on bell rvnyes. Utbiakat nem lehet egymsba gyazni:
/* /* */

*/

Ez a kd nem fordul le. A kommenteket a fordt nem veszi figyelembe, tulajdonkppen a fordtprogram els lpse, hogy a forrskdbl eltvolt minden megjegyzst.

4.2 Nvterek
A .NET Framework osztlyknyvtrai szerny becsls szerint is legalbb tzezer nevet, azonostt tartalmaznak. Ilyen nagysgrenddel elkerlhetetlen, hogy a nevek ismtl djenek. Ekkor egyrszt nehz eligazodni kztk, msrszt a fordt is

19 megzavarodhat. Ennek a problmnak a kikszblsre hoztk ltre a nvterek fogalmt. Egy nvtr tulajdonkppen egy virtulis doboz, amelyben a logikailag sszefgg osztlyok, metdusok, stb vannak. Nylvn knyebb megtallni az adatbziskezelshez szksges osztlyokat, ha valamilyen kifejez nev nvtrtben vannak (System.Data). Nvteret magunk is definilhatunk, a namespace kulcsszval:
namespace MyNameSpace { }

Ezutn a nvterre vagy a program elejn a using al, vagy az azonost el rt teljes elrssel hivatkozhatunk:
using MyNameSpace;
//vagy

MyNameSpace.Valami

A jegyzet els felben f leg a System nvteret fogjuk hasznlni, ahol ms is kell ott jelezni fogom.

20

5. Vltozk
Amikor programot runk, akkor szksg lehet trolkra, ahov az adatainkat ideiglenesen eltroljuk. Ezeket a trolkat vltozknak nevezzk. A vltozk a memria egy(vagy tbb) celljra hivatkoz lerk. Egy vltozt a kvetkez mdon hozhatunk ltre C# nyelven: Tpus vltoznv; A vltoznv els karaktere csak bet vagy alulvons jel (_) lehet, a tbbi karakter szm is. Lehet leg kerljk az kezetes karakterek hasznlatt.

5.1 Tpusok
A C# er sen (statikusan) tpusos nyelv, ami azt jelenti, hogy minden egyes vltoz tpusnak ismertnek kell lennie fordtsi id ben. A tpus hatrozza meg, hogy egy vltoz milyen rtkeket tartalmazhat illetve mekkora helyet foglal a memriban. A kvetkez tblzat a C# beptett tpusait tartalmazza, mellettk ott a .NET megfelel jk, a mretk s egy rvid lers: C# tpus byte char bool sbyte short ushort int uint float double decimal long ulong string object .NET tpus Mret (byte) System.Byte 1 System.Char 1 System.Boolean 1 1 2 2 4 4 4 8 8 8 8 NA NA Lers El jel nlkli 8 bites egsz szm (0..255) Egy Unicode karakter Logikai tpus, rtke igaz(1) vagy hamis(0) El jeles 8 bites egsz szm (-128..127) El jeles 16 bites egsz szm (32768..32767 El jel nlkli 16 bites egsz szm (0..65535) El jeles 32 bites egsz szm ( 2147483647.. 2147483647). El jel nlli 32 bites egsz szm (0..4294967295) Egyszeres pontossg lebeg pontos szm Ktszeres pontossg lebeg pontos szm Fix pontossg 28+1 jegy szm El jeles 64 bites egsz szm El jel nlkli 64 bites egsz szm Unicode karakterek szekvencija Minden ms tpus se

System.SByte System.Int16 System.Uint16 System.Int32 System.Uint32 System.Single System.Double System.Decimal System.Int64 System.Uint64 System.String System.Object

A forrskdban teljesen mindegy, hogy a rendes vagy a .NET nven hivatkozunk egy tpusra.

21 Alaktsuk t a Hello C# programot gy, hogy a kirand szveget egy vltozba tesszk:
using System; class HelloWorld { static public void Main() {
//string tpusu vltoz deklarcija, benne a kirand szveg

string message = "Hello C#"; Console.WriteLine(message); Console.ReadKey(); } }

A C# 3.0 lehet v teszi, hogy egy metdus hatkrben deklarlt vltoz tpusnak meghatrozst a fordtra bzzuk. Ezt az akcit a var szval kivitelezhetjk. Ez termszetesen nem jelenti azt, hogy gy hasznlhatjuk a nyelvet, mint egy tpustalan krnyezetet, abban a pillanatban, hogy rtket rendeltnk a vltozhoz (ezt azonnal meg kell tennnk), az gy fog viselkedni mint az ekvivalens tpus. A ilyen vltozk tpusa nem vltoztathat meg, de a megfelel tpuskonverzik vgrehajthatak.
int x = 10; // int tpus vltoz var z = 10; // int tpus vltoz z = "string"; // fordtsi hiba var w; //fordtsi hiba

Nhny specilis esett l eltekintve a var hasznlata nem ajnlott, mivel nehezen olvashatv teszi a forrskdot. A kt leggyakoribb felhasznlsi terlete a nvtelen tpusok s a lekrdezs-kifejezsek.

5.2 Loklis vltozk


Egy blokkon bell deklarlt vltoz loklis lesz a blokkra nzve, vagyis a program tbbi rszb l nem lthat (gy is mondhatjuk, hogy a vltoz hatkre a blokkra terjed ki). A fenti pldban a message egy loklis vltoz, ha egy msik fggvnyb l vagy osztlybl prblnnk meg elrni, akkor a program nem fordulna le.

5.3 Referencia- s rtktpusok


A .NET minden tpusa a System.Object nev tpusbl szrmazik, s ezen bell sztoszlik rtk- s referenciatpusokra. A kett kzti klnbsg leginkbb a memriban val elhelyezkedsben jelenik meg. A CLR kt helyre tud adatokat pakolni, az egyik a verem (stack) a msik a halom (heap). A verem egy n. LIFO (last-in-first-out) adattr, vagyis az az elem amit utljra berakunk az lesz a tetejn, kivenni pedig csak a legfels elemet tudjuk. A halom nem adatszerkezet, hanem a program ltal lefoglalt nyers memria, amit a CLR tetszs szerint hasznlhat. Minden m velet a vermet hasznlja, pl. ha ssze akarunk adni kt szmot akkor a CLR lerakja mindkett t a verembe s meghvja a

22 megfelel utastst ami kiveszi a verem legfels vgeredmnyt pedig visszateszi a verembe:
int x = 10; int y = 11; x+y A verem: |11| |10| -- sszeads m velet-- |21|

kt elemt sszeadja

ket, a

Ez azt is jelenti egyben, hogy fggetlenl attl, hogy rtkr l vagy referencirl van sz, valamilyen mdon mindkett t be kell tudnunk rakni a verembe. Az rtktpusok teljes valjukban a veremben vannak, mg a referencik a halomban jnnek ltre s a verembe egy rjuk hivatkoz referencia kerl. De mirt van ez gy? ltalban egy rtktpus csak egy-ngy bytenyi helyet foglal el, ezrt knyelmesen kezelhetjk a vermen keresztl. Ezzel szemben egy referenciatpus sokkal nagyobb szokott lenni s a memriban val megjelense is sszetettebb, ezrt hatkonyabb a halomban eltrolni. A forrskdban jl megklnbztethet a kett , mg referenciatpust a new opertor segtsgvel hozunk ltre, addig egy rtktpusnl erre nincs felttlenl szksg. Ez all a szably all kivtelt kpez a string tpus.

5.4 Boxing s unboxing


Boxing nak (bedobozols) azt a folyamatot nevezzk, amely megengedi egy rtktpusnak, hogy gy viselkedjen, mint egy referenciatpus. Mivel minden tpus (rtk s referencia is) a System.Object tpusbl szrmazik, ezrt egy rtktpust rtkl adhatunk egy object tpusnak. Csakhogy az object maga is referenciatpus, ezrt az rtkadskor ltrejn a memriban (a halomban, nem a veremben) egy referenciatpus karakterisztikjval rendelkez rtktpus. Ennek el nye, hogy olyan helyen is hasznlhatunk rtktpust, ahol egybknt nem lehetne. Vegyk a kvetkez pldt:
int x = 10; Console.WriteLine("X erteke: {0}", x);

Els re semmi klns, de elrulom, hogy a Console.WriteLine() metdus ebben a formjban msodik paramter l egy object tpus vltozt vr. Vagyis ebben a pillanatban a CLR automatikusan bedobozolja az x vltozt. A kvetkez forrskd megmutatja, hogyan tudunk kzzel dobozolni:
int x = 10; object boxObject = x; //bedobozolva Console.WriteLine("X erteke: {0}", boxObject);

Most nem volt szksg a CLR re.

23 Az unboxing (vagy kidobozols) a boxing ellentte, vagyis a bedobozolt rtktpusunkbl kivarzsoljuk az eredeti rtkt:
int x = 0; object obj = x; //bedobozolva int y = (int)obj; //kidobozolva

Az object tpuson egy explicit tpuskonverzit hajtottunk vgre (err l hamarosan), gy visszanyertk az eredeti rtket.

5.5 Konstansok
A const tpusmdost segtsgvel egy vltozt konstanss tehetnk. A konstansoknak egyetlen egyszer adhatunk (s ekkor kell is adnunk) rtket, mgpedig a deklarcinl. Brmely ks bbi prblkozs fordtsi hibt okoz.
const int x; //Hiba const int x = 10; //Ez j x = 11; //Hiba

A konstans vltozknak adott rtket/kifejezst fordtsi id ben ki kell tudnia rtkelni a fordtnak.
Console.WriteLine("Adjon meg egy szamot: "); int x = int.Parse(Console.ReadLine()); const y = x; //Ez nem j, x nem ismert fordtsi id ben

5.6 A felsorolt tpus


A felsorolt tpus olyan adatszerkezet, amely meghatrozott rtkek nvvel elltott halmazt kpviseli. Felsorolt tpust az enum kulcssz segtsgvel deklarlunk:
enum Animal { Cat, Dog, Tiger, Wolf };

Ezutn gy hasznlhatjuk:
Animal a = Animal.Tiger; if(a == Animal.Tiger) //Ha a egy tigris { Console.WriteLine("a egy tigris..."); }

A felsorols minden tagjnak megfeleltethetnk egy egsz rtket. Ha mst nem adunk meg, akkor az alaprelmezs szerint a szmozs nulltl kezd dik s deklarci szerinti sorrendben (rtsd: balrl jobbra) eggyel nvekszik.

24
enum Animal { Cat, Dog, Tiger, Wolf } Animal a = Animal.Cat; int x = (int)a; //x == 0 a = Animal.Wolf; x = (int)a; //x == 3

Magunk is megadhatjuk az rtkeket:


enum Animal { Cat = 1, Dog = 3, Tiger, Wolf }

Azok a nevek amelyekhez nem rendeltnk rtket explicit mdon az ket megel z nv rtkt l szmtva kapjk meg azt. gy a a fenti pldban Tiger rtke ngy lesz.

5.7 Null tpusok


A referenciatpusok az inicializls el tt nullrtket vesznek fel, illetve mi magunk is jellhetjk ket belltatlannak:
class RefType { } RefType rt = null;

Ugyanez az rtktpusoknl mr nem m kdik:


int vt = null; //ez le sem fordul

Ez azrt van, mert a referenciatpusok rengeteg plusz informcit tartalmaznak, mg az inicializls el tt is, mg az rtktpusok memriban elfoglalt helye a deklarci pillanatban automatikusan feltlt dik nulla rtkekkel. Ahhoz, hogy meg tudjuk llaptani, hogy egy rtktpus mg nem inicializlt egy specilis tpust a nullable tpust kell hasznlnunk, amit a rendes tpus utn rt krd jellel (?) jelznk:
int? i = null; //ez mr m kdik

Egy nullable tpusra val konverzi implicit (kln krs nlkl) megy vgbe, mg az ellenkez irnyba explicit konverzira lesz szksgnk (vagyis ezt tudatnunk kell a fordtval):
int y = 10; int? x = y; //implicit konverzi y = (int)x; //explicit konverzi

25

6. Opertorok
Amikor programozunk utastsokat adunk a szmtgpnek. Ezek az utastsok kifejezsekb l llnak, a kifejezsek pedig opertorokbl s operandusokbl illetve ezek kombincijbl jnnek ltre: i = x + y; Ebben a pldban egy utastst adunk, mgpedig azt, hogy i nek rtkl adjuk x s y sszegt. Kt kifejezs is van az utastsban: 1. x + y > ezt az rtket jelljk * -al 2. i = * -> i nek rtkl adjuk a * -ot Az els esetben x s y operandusok, a + jel pedig az sszeads m velet opertora. Ugyangy a msodik pontban i s * (vagyis x+y) az operandusok az rtkads m velet (=) pedig az opertor. Egy opertornak nem csak kt operandusa lehet. A C# nyelv egy- (unris) s hromoperandusu (ternris) opertorokkal is rendelkezik. A kvetkez nhny fejezetben tvessznk nhny opertort, de nem az sszeset. Ennek oka, hogy bizonyos opertorok nmagukban nem hordoznak jelentst, egy specilis rszterlet kapcsoldik hozzjuk, ezrt ezeket az opertorokat majd a megfelel helyen ismerjk meg. (Pl. az indexel opertor most kimarad, els knt a tmbknl tallkozhat vele az olvas.)

6.1 Opertor precedencia


Amikor tbb opertor is szerepel egy kifejezsben a fordtnak muszj valamilyen sorrendet (precedencit) fllltani kztk, hiszen az eredmny ett l is fgg. Pl.: 10 * 5 + 1 Sorrendt l fgg en az eredmny lehet 51, vagy 60. A j megolds az elbbi, az opertorok vgrehajtsnak sorrendjben a szorzs s oszts el nyt lvez. A legels helyen szerepelnek pl. a zrjeles kifejezsek, utolsn pedig az rtkad opertor. Ha bizonytalanok vagyunk a vgrehajts sorrendjben mindig hasznljunk zrjeleket, ez a vgleges programra semmilyen hatssal sincs. A fenti kifejezs teht gy nzzen ki: (10 * 5) + 1

6.2 rtkad opertor


Az egyik legltalnosabb m velet amit elvgezhetnk az az, hogy egy vltoznak rtket adunk. A C# nyelvben ezt az egyenl sgjel segtsgvel tehetjk meg:
int x = 10;

26 Ltrehoztunk egy int tpus vltozt, elneveztk x nek s kezd rtknek 10 et adtunk. Termszetesen nem ktelez a deklarcinl (amikor tjkoztatjuk a fordtt, hogy van egy valamilyen tpus, adott nev vltoz) megadni a defincit (amikor meghatrozzuk, hogy a vltoz milyen rtket kapjon), ezt el lehet halasztani:
int x; x = 10;

Ett l fggetlenl a legtbb esetben ajnlott akkor rtket adni egy vltoznak amikor deklarljuk. Egy vltoznak nem csak konstans rtket, de egy msik vltozt is rtkl adhatunk, de csak abban az esetben, ha a kt vltoz azonos tpus, illetve ha ltezik a megfelel konverzi (a tpuskonverzikkal egy ks bbi fejezet foglalkozik).
int x = 10; int y = x; //y rtke most 10

6.3 Matematikai opertorok


A kvetkez pldban a matematikai opertorok hasznlatt mutatjuk meg:
using System; public class Operators { static public void Main() { int x = 10; int y = 3; int z = x + y; //sszeads: z = 10 + 3 Console.WriteLine(z); //Kirja az eredmnyt: 13 z = x - y; //Kivons: z = 10 - 3 Console.WriteLine(z); // 7 z = x * y; //Szorzs: z = 10 * 3 Console.WriteLine(z); //30 z = x / y; //Maradk nlkli oszts: z = 10 / 3; Console.WriteLine(z); // 3 z = x % y; //Maradkos oszts: z = 10 % 3 Console.WriteLine(z); // Az oszts maradkt rja ki: 1 Console.ReadKey(); //Vr egy billenty letst } }

6.4 Relcis opertorok


A relcis opertorok segtsgvel egy adott rtkszlet elemei kzti viszonyt tudjuk lekrdezni. A numerikus tpusokon rtelmezve van egy rendezs relci:

27
using System; public class RelOp { static public void Main() { int x = 10; int y = 23; Console.WriteLine(x > y); //Kirja az eredmnyt: false Console.WriteLine(x == y); //false Console.WriteLine(x != y); //x nem egyenl y al: true Console.WriteLine(x <= y); //x kisebb-egyenl mint y: true Console.ReadKey(); } }

Az els sor egyrtelm , a msodikban az egyenl sget vizsgljuk a kett s egyenl sgjellel. Ilyen esetekben figyelni kell, mert egy elts is nehezen kiderthet hibt okoz, amikor egyenl sg helyett az rtkad opertort hasznljuk. Az esetek tbbsgben ugyanis gy is le fog fordulni a program, m kdni viszont valszn leg rosszul fog. Minden ilyen opertor logikai tpussal tr vissza. A relcis opertorok sszefoglalsa: x>y x >= y x<y x <= y x == y x != y x nagyobb mint y x nagyobb vagy egyenl mint y x kisebb mint y x kisebb vagy egyenl mint y x egyenl y -al x nem egyenl y -al

6.5 Logikai/feltteles opertorok


Akrcsak a C++, a C# sem rendelkezik igazi logikai tpussal, ehelyett 1 s 0 jelzi az igaz s hamis rtkeket:
using System; public class RelOp { static public void Main() { bool l = true; bool k = false; if(l == true && k == false) { Console.WriteLine("Igaz"); } Console.ReadKey(); } }

28

El szr felvettnk kt logikai (bool) vltozt, az els nek igaz a msodiknak hamis rtket adtunk. Ezutn egy elgazs kvetkezik, err l b vebben egy ks bbi fejezetben lehet olvasni, a lnyege az, hogy ha a felttel igaz, akkor vgrehajt egy bizonyos utasts(oka)t. A fenti pldban az s (&&) opertort hasznltuk, ez kt operandust vr s akkor ad vissza igaz rtket, ha mindkt operandusa igaz vagy nullnl nagyobb rtket kpvisel. Ebb l kvetkezik az is, hogy akr az el z fejezetben megismert relcis opertorokbl felptett kifejezsek, vagy matematikai formulk is lehetnek operandusok. A program maga kirja, hogy Igaz. Nzzk az s igazsgtblzatt: A hamis hamis igaz igaz A msodik opertor a vagy:
using System; public class RelOp { static public void Main() { bool l = true; bool k = false; if(l == true || k == true) { Console.WriteLine("Igaz"); } Console.ReadKey(); } }

B hamis igaz hamis igaz

Eredmny hamis hamis hamis igaz

A vagy (||) opertor akkor trt vissza igaz rtket, ha az operandusai kzl valamelyik igaz vagy nagyobb mint nulla. Ez a program is ugyanazt csinlja, mint az el z , a klnbsg a felttelben van, k biztosan nem igaz (hiszen pp el tte kapott hamis rtket). A vagy igazsgtblzata: A hamis hamis igaz igaz B hamis igaz hamis igaz Eredmny hamis igaz igaz igaz

Az eredmny kirtkelse az n. lusta kirtkels (vagy rvidzr) mdszervel trtnik, azaz a program csak addig vizsglja a felttelt amg muszj. Pl. a vagy

29 pldban a k soha nem fog kirtkel dni, mivel l van az els helyen (balrl jobbra haladunk) s igaz, vagyis a felttel k rtkt l fggetlenl mindenkppen teljesl. A harmadik a tagads (!()):
using System; public class RelOp { static public void Main() { int x = 10; if(!(x == 11)) { Console.WriteLine("X nem egyenlo 11 -el!"); } Console.ReadKey(); } }

Ennek az opertornak egy operandusa van, akkor ad vissza igaz rtket, ha az operandusban megfogalmazott felttel hamis vagy egyenl nullval. A tagads (negci) igazsgtblja: A Eredmny hamis igaz igaz hamis Ez a hrom opertor n. feltteles opertor, kzlk az s s vagy opertoroknak ltezik a csonkolt logikai prja is. A klnbsg annyi, hogy a logikai opertorok az eredmnyt l fggetlenl kirtkelik a teljes kifejezst, nem lnek a lusta kirtkelssel. A logikai vagy m velet:

if(l == true | k == true) { Console.WriteLine("Igaz"); }

A logikai s:
if(l == true & k == true) { Console.WriteLine("Igaz"); }

A logikai opertorok csaldjhoz tartozik (ha nem is szorosan) a feltteles opertor. Ez az egyetlen hromoperandusu opertor, a kvetkez kppen m kdik:

30

felttel ? igaz-g : hamis-g;


using System; public class RelOp { static public void Main() { int x = 10; int y = 10; Console.WriteLine( (x == y) ? "Egyenlo" : "Nem egyenlo"); Console.ReadKey(); } }

6.6 Bit opertorok


Az el z fejezetben emltett logikai opertorok bitenknti m veletek elvgzsre is alkalmasak numerikus tpusokon. A szmtgp az adatokat kettes szmrendszer beli alakban trolja, gy pl. ha van egy byte tpus vltoznk (ami egy byte azaz 8 bit hossz) aminek a 2 rtket adjuk, akkor az a kvetkez kppen jelenik meg a memriban: 2 00000010

A bit opertorok ezzel a formval dolgoznak. Az eddig megismert kett mell mg jn ngy msik opertor is. A m veletek: Bitenknti s: veszi a kt operandus binris alakjt s a megfelel bitprokon elvgzi az s m veletet azaz ha mindkt bit 1 llsban van akkor az adott helyen az eredmnyben is az lesz, egybknt 0: 01101101 00010001 AND 00000001 Plda:
using System; class Program { static public void Main() { int x = 10; Console.WriteLine(x & 2);
//1010 & 0010 = 0010 = 2

Console.ReadKey(); } }

31

Bitenknti vagy: hasonlan m kdik mint az s, de a vgeredmnyben egy bit rtke akkor lesz 1, ha a kt operandus adott bitje kzl az egyik is az: 01101101 00010001 OR 01111101
using System; class Program { static public void Main() { int x = 10; Console.WriteLine(x | 2);
//1010 | 0010 = 1010 = 10

Console.ReadKey(); } }

Biteltols balra: a kettes szmrendszerbeli alak fels bitjt eltoljuk s a jobb oldalon keletkez res bitet nullra lltjuk. Az opertor: <<: 10001111 LEFT SHIFT 100011110
using System; class Program { static public void Main() { int x = 143; Console.WriteLine(x << 1);
//10001111 (=143) << 1 = 100011110 = 286

Console.ReadKey(); } }

32 Biteltols jobbra: most az als bitet toljuk el s fell ptoljuk a hinyt. Az opertor: >>:
using System; class Program { static public void Main() { int x = 143; Console.WriteLine(x >> 1); Console.ReadKey(); } }

6.7 Rvid forma


Vegyk a kvetkez pldt:
x = x + 10;

Az x nev vltozt megnveltk tzzel. Csakhogy van egy kis baj: ez a megolds nem tl hatkony. Mi trtnik valjban? Els knt rtelmezni kell a jobb oldalt, azaz ki kell rtkelni x et, hozz kell adni tzet s eltrolni a veremben. Ezutn ismt kirtkeljk x et, ezttal a bal oldalon. Szerencsre van megolds, mgpedig az n. rvid forma. A fenti sorbl ez lesz:
x += 10;

Rvidebb, szebb s hatkonyabb. Az sszes aritmetikai opertornak ltezik rvid formja. A problma ugyanaz, de a megolds ms a kvetkez esetben:
x = x + 1;

Szemmel lthatan ugyanaz a baj, azonban az eggyel val nvelsre-cskkentsre van nll opertorunk:
++x/--x; x++/x--;

Ebb l az opertorbl rgtn kt verzit is kapunk, prefixes (++/-- el l) s postfixes formt. A prefixes alak pontosan azt teszi amit elvrunk t le, azaz megnveli(cskkenti) az operandust egyel. A postfixes forma egy kicsit bonyolultabb, els knt ltrehoz egy tmeneti vltozt, amiben eltrolja az operandusa rtkt, majd megnveli eggyel az operandust, vgl visszaadja az tmeneti vltozt. Ez els re taln nem t nik hasznosnak, de vannak helyzetek amikor lnyegesen megknnyti az letnket a hasznla. Attl fgg en, hogy nveljk vagy cskkentjk az operandust inkrementlis illetve dekrementl opertorrl beszlnk.

33

6.8 Egyb opertorok


Unris -/+: az adott szm pozitv illetve negatv rtkt jelezzk vele. Csakis el jeles tpusokon m kdik. Typeof: az operandusa tpust adja vissza:
using System; class Program { static public void Main() { int x = 143; if(typeof(int) == x.GetType()) { Console.WriteLine("X tipusa int"); } Console.ReadKey(); } }

A vltozn meghvott GetType() metdus (amit mellesleg minden tpus a System.Object t l rkl) a vltoz tpust adja vissza.

34

7. Vezrlsi szerkezetek
Vezrlsi szerkezetnek konstrukcikat nevezzk. a program utastsainak sorrendisgt szablyoz

7.1 Szekvencia
A legegyszer bb vezrlsi szerkezet a szekvencia. Ez tulajdonkppen egyms utn megszabott sorrendben vgrehajtott utastsokbl ll.

7.2 Elgazs
Gyakran el fordul, hogy meg kell vizsglnunk egy lltst, s attl fgg en, hogy igaz vagy hamis ms-ms utastst kell vgrehajtanunk. Ilyen esetekben elgazst hasznlunk:
using System; class Conditional { static public void Main() { int x = 10; if(x == 10) //Ha x == 10 { Console.WriteLine("X == 10"); } Console.ReadKey(); } }

Mi van akkor, ha azt is jelezni akarjuk, hogy x nem egyenl tzzel? Erre val az else g, ez akkor hajtdik vgre, ha a program nem tall ms felttelt. Ezt az gat nem ktelez megadni:
using System; class Conditional { static public void Main() { int x = 10; if(x == 10) //Ha x == 10 { Console.WriteLine("X == 10"); } else //De ha nem annyi { Console.WriteLine("X nem egyenlo tizzel!"); } Console.ReadKey(); } }

35

Arra is van lehet sgnk, hogy tbb felttelt is megvizsgljunk, ekkor else-if et hasznlunk:
using System; class Conditional { static public void Main() { int x = 12; if(x == 10) //Ha x == 10 { Console.WriteLine("X == 10"); } else if(x == 12) //Vagy x == 12 { Console.WriteLine("X == 12"); } else //De ha nem annyi { Console.WriteLine("X nem egyenlo tizzel s tizenkettovel sem!"); } Console.ReadKey(); } }

A program az els olyan gat fogja vgrehajtani aminek a felttele teljesl (termszetesen el fordulhat, hogy egyik sem lesz j, ekkor nem trtnik semmi). Egy elgazsban pontosan egy darab if brmennyi else-if s pontosan egy else g lehet. Egy elgazson bell is rhatunk elgazst. Az utols pldban egy darab vltoz rtkt vizsgljuk. Ilyen esetekben azonban van egy egyszer bb s elegnsabb megolds, mgpedig a switch-case szerkezet. Ezt akkor hasznljuk, ha egy vltoz tbb lehetsges llapott akarjuk vizsglni:
using System; class Switch { static public void Main() { int x = 10; switch(x) { case 10: Console.WriteLine("X == 10"); break; default: Console.WriteLine("Default"); break; }

36
Console.ReadKey(); } }

A switch en bell els knt megvizsgljuk, hogy x egyenl e tzzel. Ha igen, kirjuk s a break kel kiugrunk a switch b l (egybknt a break minden ilyen esetben alkalmazhat, pl. megszaktjuk egy elgazs egy gnak vgrehajtst). Ha viszont x nem egyenl tzzel, akkor a default ra ugrunk, ami gyakorlatilag megfelel egy else gnak. Az egyes esetek utastsai utn meg kell adni, hogy hol folytassa a program a vgrehajtst. Ezt vagy a break kel tesszk a mr ltott mdon, vagy valamilyen ugr utastssal (jump, return, goto, stb):
using System; class Program { static public void Main() { Console.WriteLine("Adjon meg egy szamot: "); int x = int.Parse(Console.ReadLine()); switch(x) { case 12: Console.WriteLine("X == 12"); break; case 10: goto default; default: Console.WriteLine("Default"); break; } Console.ReadKey(); } }

Bekrnk egy szmot a felhasznltl, s eltroljuk egy vltozban. Ezt a Console.ReadLine() fggvnyen meghvott int.Parse() metdussal tesszk, elbbi beolvassa a standard inputot az els sorvge jelig, utbbi pedig numerikuss konvertlja az eredmnyt (ha tudja, ha pedig nem nos, err l egy ks bbi fejezetben lesz sz). Most jn a switch. Az els esetet mr lttuk, a msodikban pedig a default cmkre ugrunk a goto utastssal.

7.3 Ciklus
Amikor egy adott utastssorozatot egyms utn tbbszr kell vgrehajtanunk, akkor cilust hasznlunk. A C# ngyfle ciklust biztost szmunkra. Az els az n. szmlls ciklus (nevezzk for-ciklusnak):

37
using System; class Program { static public void Main() { for(int i = 0;i < 10;++i) { Console.WriteLine("X == {0}", i); } Console.ReadKey(); } }

Miel tt kiveszzk a ciklust nzzk meg a ciklusmagot:


Console.WriteLine("I == {0}", i);

Ismerkedjnk meg az n. formtumstringgel. A ciklusunk kirja a szmokat nulltl kilencig, de szeretnnk egy kicsit kicsinostani, ezrt a szm el rjuk, hogy I == . Ehhez a szveghez akarjuk hozzadni a vltoz rtkt s ezt is tesszk a {0} jellssel. Ez a formtumstring utni els (illetve nulladik, hiszen onnan kezdjk a szmozst) paramtert helyettesti be a szvegbe. Most nzzk a ciklusfejet:
for(int i = 0;i < 10;++i)

Els knt ltrehozunk egy ciklusvltozt (i) amivel nyilvntartjuk, hogy ppen hol tart a ciklus. Mivel ez egy szmlls ciklus ezrt i valamikor elri (j esetben) azt az rtket, ahol a ciklus befejez dik. Ezt a vgpontot (alias ciklusfelttel) adjuk meg a msodik helyen, teht addig megy a ciklus amg i kisebb tznl. Vgl gondoskodnunk kell arrl, hogy nveljk(cskkentsk, mikor mi kell) a ciklusvltozt, ezt a mr ismert inkrementlis opertorral tesszk, mgpedig a prefix formval mivel nincs szksgnk az tmeneti vltozra, csak a memrit foglaln. Termszetesen itt brmilyen ms kifejezst is hasznlhatunk, ami hatssal van a ciklusvltoz rtkre (pl. nem eggyel, hanem ket vel nveljk az rtkt). Msodik kliensnk az el ltesztel s ciklus (mostantl hvjuk while-ciklusnak), ami onnan kapta a nevt, hogy a ciklusmag vgrehajtsa el tt ellen rzi a ciklusfelttelt ezrt el fordulhat az is, hogy a ciklus egyszer sem fut le:
using System; class Program { static public void Main() { int i = 0; //kezd rtk while(i < 10) //ciklusfelttel

38
{ Console.WriteLine("I == {0}", i); ++i; //ciklusvltoz nvelse } Console.ReadKey(); } }

A program ugyanazt csinlja mint az el z , viszont itt jl lthatan elklnlnek a ciklusfelttelrt felel s utastsok (kezd rtk, ciklusfelttel, nvel/cskkent). A harmadik versenyz kvetkezik, t htultesztel s ciklusnak hvjk (legyen dowhile), nem nehz kitallni, hogy azrt kapta ezt a nevet mert a ciklusmag vgrehajtsa utn ellen rzi a felttelt, gy legalbb egyszer biztosan lefut:
using System; class Program { static public void Main() { int i = 0; do { Console.WriteLine("I == {0}", i); ++i; } while(i < 10); Console.ReadKey(); } }

Vgl, de nem utolssorban a foreach (neki nincs kln neve) ciklus kvetkezik. Ezzel a ciklussal vgigiterlhatunk egy tmbn vagy gy jtemnyen, illetve minden olyan objektumon, ami megvalstja az IEnumerable s IEnumerator interfszeket (interfszekr l egy ks bbi fejezet fog beszmolni, ott lesz sz err l a kett r l is). A pldnk most nem a mr megszokott szmoljunk el kilencig lesz, helyette vgigmegynk egy string en:
using System; class Program { static public void Main() { string str = "abcdefghijklmnopqrstuvwxyz"; foreach(char ch in str) { Console.Write(ch); } Console.ReadKey();

39
} }

A cilusfejben felvesznk egy char tpus vltozt (egy string karakterekb l ll), utna az in kulcssz kvetkezik, amivel kijelljk, hogy min megynk t. A foreach pontos m kdsvel az interfszekr l szl fejezet foglalkozik, tbbek kztt megvalstunk egy osztlyt, amelyen a foreach kpes vgigiterlni (azaz megvalstjuk a fentebb emltett kt interfszt).

7.3.1 Yield
A yield kifejezs lehet v teszi, hogy egy metdust hasznljunk (metdusokrl egy ksbbi fejezet szl) foreach ciklussal:
using System; using System.Collections; class Program { static public IEnumerable EnumerableMethod(int max) { for(int i = 0;i < max;++i) { yield return i; } } static public void Main() { foreach(int x in EnumerableMethod(10)) { Console.WriteLine(x); } Console.ReadKey(); } }

7.4 Gyakorl feladatok


1. Ksztsnk szmkitall jtkot, amely lehet sget ad kivlasztani, hogy a felhasznl prblja kitallni a program ltal sorsolt szmot, vagy fordtva. A kitallt szm legyen pl. 1 s 100 kztt. t prblkozsa lehet a jtkosnak, minden tipp utn rjuk ki, hogy a tippelt szm nagyobb, vagy kisebb mint a kitallt szm. Ha a gpen van a sor, akkor hasznljuk a kvetkez fejezet elejn bevezetett vletlenszmgenerl objektumot (Random), a szm ltrehozsra. A gpi jtkos gy tallja ki a szmot, hogy mindig felezi az intervallumot (pl.el szr 50 t tippel, ha a kitallt szm nagyobb, akkor 75 jn, s gy tovbb). A felhasznl egy jtk vgn rgtnk kezdhessen jabbat.

40 2. Ksztsnk egy egyszer szmolgpet! A program indtsakor jelenjen meg egy men, ahol kivlaszthatjuk a m veletet, ezutn bekrjk a m velet tagjait (az egyszer sg kedvrt legyen csak kt szm). 3. Ksztsnk k -papr-oll jtkot! Ebben az esetben is hasznljuk a vletlenszmgenertort. A jtk folyamatos legyen, vagyis addig tart, amg a felhasznl kilp (neheztskppen lehet egy karakter, amelyre a jtk vgetr). Tartsuk nylvn a jtk llst s minden fordul utn rjuk ki.

41

8. Tmbk
Gyakran van szksgnk arra, hogy tbb azonos tpus objektumot troljunk el. Ilyenkor knyelmetlen lenne mindegyiknek vltozt foglalnunk (kpzeljnk el 30 darab int tpus rtket, mg lerni is egy rkkvalsg lenne), de ezt nem kell megtennnk, hiszen rendelkezsnkre ll a tmb adatszerkezet. A tmb meghatrozott szm, azonos tpus elemek halmaza. Minden elemre egyrtelm en mutat egy index (egsz szm). A C# mindig folytonos memriablokkokban helyezi el egy tmb elemeit. Tmbdeklarci:
int[] x = new int[10];

Ltrehoztunk egy 10 darab int tpus elemeket tartalmaz tmbt. A tmb deklarcija utn az egyes indexeken lv elemek automatikusan a megfelel nullrtkre inicializldnak (ebben az esetben 10 darab nullt fog tartalmazni a tmbnk). Ez a szably referenciatpusoknl (osztlyok) kiss mshogy m kdik. Ekkor a tmbelemek null -ra inicializldnak. Ez nagy klnbsg, mivel rtktpusok esetben szimpla nullt kapnnk vissza az ltalunk nem belltott indexre hivatkozva, mg referenciatpusoknl ugyanez a m velet NullReferenceException kivtelt fog generlni. Az egyes elemekre az indexel opertorral (szgeletes zrjel -> [ ]) s az elem indexvel(sorszmval) hivatkozunk. A szmozs mindig nulltl kezd dik, gy a legutols elem indexe az elemek szma mnusz egy. A kvetkez pldban feltltnk egy tmbt vletlen szmokkal s kiratjuk a tartalmt szmlls s foreach (ugyanis minden tmb implementlja az IEnumerator s IEnumerable interfszeket) ciklussal:
using System; class Program { static public void Main() { int[] array = new int[10]; Random r = new Random(); for(int i = 0;i < array.Length;++i) { array[i] = r.Next(); } for(int i = 0;i < array.Length;++i) { Console.WriteLine("{0}, ", array[i]); } Console.WriteLine(); foreach(int i in array) { Console.WriteLine("{0}, ", i); } Console.ReadKey();

42
} }

A pldnkban egy vletlenszmgenertor objektumot (Random r) hasznltunk, ami a nevhez hven vletlenszmokat tud visszaadni, az objektumon meghvott Next() metdussal. A pldaprogram valszn leg elg nagy szmokat fog produklni, ezt a Next() metdus paramtereknt megadott fels hatrral reduklhatjuk:
array[i] = r.Next(100);

Ekkor maximum 99, minimum 0 lehet a vletlenszm. Egy dolog van mg ami ismeretlen, a tmb Length tulajdonsga, ez a tmb elemeinek szmt adja vissza. Tulajdonsgokkal egy ks bbi fejezet foglalkozik rszletesen. Lthat az indexel opertor hasznlata is, az array[i] az array i edik elemt jelenti. Az indexelssel vigyzni kell, ugyanis a fordt nem ellen rzi fordtsi id ben az indexek helyessgt, viszont helytelen indexels esetn futs id ben System.IndexOutOfRangeException kivtelt fog dobni a program. Kivtelkezelsr l is egy ks bbi fejezet szl. Egy tmbt akr a deklarci pillanatban is feltlthetnk a neknk megfelel rtkekkel:
char[] chararray = new char[] { 'b', 'd', 'a', 'c' };

Ekkor az elemek szma a megadott rtkekt l fgg. Egy tmb elemeinek szma a deklarcival meghatrozott, azon a ks bbiekben nem lehet vltoztatni. Dinamikusan b vthet adatszerkezetekr l a Gy jtemnyek cm fejezet szl. Minden tmb a System.Array osztlybl szrmazik, ezrt nhny hasznos m velet azonnal rendelkezsnkre ll (pl. rendezhetnk egy tmbt a Sort() metdussal):
chararray.Sort(); //tmb rendezse

8.1 Tbbdimenzis tmbk


Eddig az n. egydimenzis tmbt, vagy vektort hasznltuk. Lehet sgnk van azonban tbbdimenzis tmbk ltrehozsra is, ekkor nem egy indexxel hivatkozunk egy elemre hanem annyival ahny dimenzis. Vegyk pldul a matematikbl mr ismert mtrixot: 12, 23, 2 A = [ 13, 67, 52 ] 45, 55, 1 Ez egy ktdimenzis tmbnek (mtrix a neve ki hinn?) felel meg, az egyes elemekre kt indexxel hivatkozunk, els helyen a sor ll utna az oszlop. gy a 45 indexe: [3, 0] (ne feledjk, mg mindig nulltl indexelnk). Multidimenzis tmbt a kvetkez mdon hozunk ltre C# nyelven:

43
int[,] matrix = new int[3, 3];

Ez itt egy 3x3 as mtrix, olyan mint a fent lthat. Itt is sszekthetjk az elemek megadst a deklarcival, br egy kicsit trkksebb a dolog:
int[,] matrix = new int[,] { {12, 23, 2}, {13, 67, 52}, {45, 55, 1} };

Ez a mtrix mr pontosan olyan mint amit mr lttunk. Az elemszm most is meghatrozott, nem vltoztathat. Nylvn nem akarjuk mindig kzzel feltlteni a tmbket, a mr ismert vletlenszmgenertoros plda mdostott vltozata jn:
using System; class Program { static public void Main() { int[,] matrix = new int[3,3]; Random r = new Random(); for(int i = 0;i < matrix.GetLength(0);++i) { for(int j = 0;j < matrix.GetLength(1);++j) { matrix[i, j] = r.Next(); } } Console.ReadKey(); } }

Most nem rjuk ki a szmokat, ezt nem okozhat gondot megrni. A tmbk GetLength() metdusa a paramterknt megadott dimenzi hosszt adja vissza (nulltl szmozva), teht a pldban az els esetben a sor, a msodikban az oszlop hosszt adjuk meg a ciklusfelttelben. A tbbdimenzis tmbk egy varinsa az n. egyenetlen(jagged) tmb. Ekkor legalbb egy dimenzi hosszt meg kell adnunk, ez konstans marad, viszont a bels tmbk hossza tetszs szerint megadhat:
int[][] jarray = new int[3][];

Ksztettnk egy hrom sorral rendelkez tmbt, azonban a sorok hosszt (az egyes sorok nylvn nll vektorok) rrnk ks bb megadni s nem kell ugyanolyannak lennik:

44

using System; class Program { static public void Main() { int[][] jarray = new int[3][]; Random r = new Random(); for(int i = 0;i < jarray.Length;++i) { jarray[i] = new int[r.next(1, 5)]; for(int j = 0;j < jarray[i].Length;++j) { jarray[i][j] = r.next(100); } } Console.ReadKey(); } }

Vletlenszmgenertorral adjuk meg a bels tmbk hosszt, persze rtelmes kereteken bell. A bels ciklusban jl lthat, hogy a tmb elemei valban tmbk, hiszen hasznltuk a Length tulajdonsgot. Az inicializls a kvetkez kppen alakul ebben az esetben:
int[][] jarray = new int[][] { new int[] {1, 2}, new int[] {3, 4} };

A C# 3.0 megengedi a var kulcssz hasznlatt is, az el bbi plda gy nz ki ekkor:


var jarray = new int[][] { new int[] {1, 2}, new int[] {3, 4} };

A fordt fordtskor dnti el a pontos tpust, azon nem lehet vltoztatni.

8.2 ArrayList
A fejezet olvassa el tt ajnlott elolvasni az rkl ds s Tpuskonverzik cm fejezetet.

Az ArrayList kt problmra, a fix mretre s a meghatrozott tpusra nyjt megoldst. Azt mr tudjuk, hogy minden osztly a System.Object b l szrmazik, gy

45 ha van egy olyan trolnk, amely ilyentpus objektumokat trol, akkor nyert gynk van. Hogy ez mirt van gy, arrl az rkl ds cm fejezet nyjt informcit. Az ArrayList hasonlan m kdik mint egy tmb, kt klnbsg van, az egyik, hogy nem adunk meg mretet a deklarcinl, a msik pedig, hogy tpuskonverzit kell alkalmaznunk, amikor hivatkozunk egy elemre. Miel tt hasznlnnk egy ArrayList et, a programban hivatkoznunk kell a System.Collections nvtrre.
using System; using System.Collections; class Program { static public void Main() { ArrayList list = new ArrayList(); Random r = new Random(); for(int i = 0;i < 10;++i) { list.Add(i); } for(int i = 0;i < list.Count;++i) { Console.WriteLine("{0}, ", list[i]); } Console.ReadKey(); } }

Az Add() metdus segtsgvel tudunk elemeket betenni a listba. Br azt mondtam, hogy a kivtelnl konverzi szksges, most nem csinltunk ilyet. Ez azrt van gy, mert a Console.WriteLine nak van olyan tlterhelt vltozata, amely egy System.Object tpus elemet vr. Egsz ms a helyzet, ha konkrt tpus vltoznak akarom rtkl adni az adott indexen lv elemet:
int x = list[4];

Felttelezzk, hogy int tpus elemeket troltunk el, s van elem a negyedik indexen. A fenti sor mr a fordtson fennakad a Cannot implicitly convert type object to int kezdet hibazenettel . Egy Object tpus elemet ugyanis nem ksztettek fel implicit konvertlsra, ez a programoz dolga. A fenti sor gy nz ki helyesen:
int x = (int)list[4];

Ez egy n. explicit tpuskonverzi, b vebben lsd a Tpuskonverzik cm fejezetet. Az ArrayList a C# 1.0 ban jelent meg, a 2.0 bevezette a tpusos vltoztait a generikus adatszerkezetekkel. Egy ArrayList deklarcijnl nem adjuk meg, hny elemet fogunk eltrolni. Egy normlis tmb kivtelt dob, ha tbb elemet prblunk elhelyezni benne, mint

46 amennyit megadtunk. Akkor honnan tudja az ArrayList, hogy mennyi helyre van szksge? A vlasz egyszer , van neki egy alaprtelmezett kapacitsa (ezt a Capacity tulajdonsggal krdezhetjk le). Ez a kezdet kezdetn 16, de ez nem mindig elg, gy miel tt elhelyeznnk a 17 ik elemet, a kapcits 32 re ugrik (s gy tovbb). A kapacitst kzzel is bellthatjuk a mr emltett tulajdonsg segtsgvel, ekkor figyeljnk arra, hogy az aktulis elemek szmnl nem kisebb rtket adjunk meg, egybknt ArgumentOutOfRange kivtel dobdik. Az ArrayList en kv l szmos adatszerkezetet is szolgltat neknk a C#, de helyettk rdemesebb a nekik megfelel generikus vltozatokat hasznlni, ezekr l b vebben a generikus gy jtemnyekr l szl fejezetben olvashatunk.

8.3 Gyakorl feladatok


1. Tltsnk fel egy tmbt vletlenszmokkal s vlasszuk ki kzlk a legnagyobbat (az mindegy, ha tbb egyenl legnagyobb szm van). 2. Tltsnk fel egy tmbt vletlenszmokkal s dntsk el, hogy hny darab pros szm van benne.

47

9. Stringek
A C# beptett karaktertpusa (char) egy Unicode karaktert kpes eltrolni kt byte on. A szintn beptett string tpus ilyen karakterekb l ll (teht char knt hivatkozhatunk az egyes bet ire).
using System; class Program { static public void Main() { string s = "ezegystring"; Console.WriteLine(s); Console.ReadKey(); } }

Nem gy t nik, de a string referenciatpus, ennek ellenre nem kell hasznlnunk a new opertort. Egy string egyes bet ire az indexel opertorral hivatkozhatunk:
using System; class Program { static public void Main() { string s = "ezegystring"; Console.WriteLine(s[0]); //e Console.ReadKey(); } }

Ekkor a visszaadott tpus char lesz. A foreach ciklussal indexel opertor nlkl is vgigiterlhatunk a karaktersorozaton:
foreach(char ch in s) { Console.WriteLine(ch); }

Az indexel opertort nem csak vltozkon, de nyers szvegen is alkalmazhatjuk:


Console.WriteLine("ezegystring"[0]); //e

9.1 Metdusok
A C# szmos hasznos metdust biztost a stringek hatkony kezelshez. Most bemutatunk nhnyat, de emlkezznk arra, hogy a metdusoknak szmos vltozata lehet, itt a leggyakrabban hasznltakat mutajuk be:

48 sszehasonlts:
string s = "other"; string c = "another"; int x = String.Compare(s, c) if(x == 0) { Console.WriteLine("A ket string egyenlo..."); } else if(x > 0 || x < 0) { Console.WriteLine("A ket string nem egyenlo..."); } x = String.CompareOrdinal(s, c); x = s.CompareTo(c);

Mindhrom metdus nullt ad vissza, ha a kt string egyenl s nullnl kisebbet/nagyobbat, ha nem (pontosabban ha lexikografikusan kisebb/nagyobb). Az els vltozat nagysg szerint sorbarendezi a karaktereket s gyhasonltja ssze a kt karaktersorozatot, a msodik az azonos indexen lv karaktereket nzi, mg a harmadik az els hz hasonl, de ez nem statikus hanem pldnymetdus. Keress:
using System; class Program { static public void Main() { string s = "verylonglongstring"; char[] chs = new char[]{ 'y', 'z', '0' }; Console.WriteLine(s.IndexOf('r')); //2 Console.WriteLine(s.IndexOfAny(chs)); //3 Console.WriteLine(s.LastIndexOf('n')); //16 Console.WriteLine(s.LastIndexOfAny(chs)); //3 Console.WriteLine(s.Contains("long")); //true Console.ReadKey(); } }

Az IndexOf() s LastIndexOf() metdusok egy string vagy karakter els illetve utols el fordulsi indext (stringek esetn a kezds helyt) adjk vissza. Ha nincs tallat, akkor a visszaadott rtk -1 lesz. A kt metdus Any re vgz d vltozata egy karaktertmbt fogad paramtereknt. A Contains() metdus igaz rtkkel tr vissza, ha a paramtereknt megadott karakter(sorozat) benne van a stringben.

49

Mdosts:

using System; class Program { static public void Main() { string s = "smallstring"; char[] chs = new char[]{ 's', 'g' }; Console.WriteLine(s.Replace('s', 'l')); //lmallltring Console.WriteLine(s.Trim(chs)); //mallstrin Console.WriteLine(s.Insert(0, "one")); //onesmallstring Console.WriteLine(s.Remove(0, 2)); //allstring Console.WriteLine(s.Substring(0, 3)); //sma string c = s.ToUpper(); Console.WriteLine(s); //SMALLSTRING Console.WriteLine(c.ToLower()); //smallstring Console.ReadKey(); } }

A Replace() metdus az els paramternek megfelel karaktert lecserli a msodik paramterre. A Trim() metdus a string elejn s vgn lv karaktereket vgja le, a Substring() kivg egy karaktersorozatot, paramterei a kezd s vgindexek (van egyparamteres vltozata is, ekkor a csak a kezd indexet adjuk meg s a vgig megy). Az Insert()/Remove() metdusok hozzadnak illetve elvesznek a stringb l. Vgl a ToLower() s ToUpper() metdusok pedig kis- illetve nagybet ss alaktjk az eredeti stringet. Fontos megjegyezni, hogy ezek a metdusok soha nem az eredeti stringen vgzik a mdostsokat, hanem egy j pldnyt hoznak ltre s azt adjk vissza.

9.2 StringBuilder
Amikor mdostunk egy stringet akkor automatikusan egy j pldny jn ltre a memriban, hiszen az eredeti vltozat nem biztos, hogy ugyanakkora mint az j. Ha sokszor van szksgnk erre, akkor hasznljuk inkbb a StringBuilder tpust, ez automatikusan lefoglal egy nagyobb darab memrit s ha ez sem elg, akkor allokl egy nagyobb terletet s tmsolja magt oda. A StringBuilder a System.Text nvtrben tallhat.
using System; using System.Text; class Program { static public void Main() { StringBuilder builder = new StringBuilder(50);

50
for(char ch = 'a';ch <= 'z';++ch) { builder.Append(ch); } Console.WriteLine(builder); Console.ReadKey(); } }

A StringBuilder fenti konstruktora (van tbb is) helyet foglal tven karakter szmra (ltezik alaprtelmezett konstruktora is, ekkor az alaprtelmezett tizenhat karakternek foglal helyet). Az Append() metdussal tudunk karaktereket (vagy egsz stringeket) hozzf zni. A for ciklust rdekesen hasznltuk, most nem numerikus tpuson iterltunk, hanem karaktereken. Azt, hogy ez mirt m kdik a kvetkez fejezetb l tudhatjuk meg.

51

10. Tpuskonverzik
Azt mr tudjuk, hogy az egyes tpusok msknt jelennek meg a memriban. Azonban gyakran kerlnk olyan helyzetbe, hogy egy adott tpusnak gy kellene viselkednie, mint egy msiknak. Ilyen helyzetekben tpuskonverzit (vagy castolst) kell elvgeznnk. Ktflekppen konvertlhatunk: implicit s explicit mdon. Az el bbi esetben nem kell semmit tennnk, a fordt elvgzi helyettnk. Implicit konverzi ltalban hasonl tpusokon mehet vgbe, szinte minden esetben a sz kebb tpusrl a tgabbra:
int x = 10; long y = x; //y == 10, implicit konverzi

Ebben az esetben a long s int mindketten egsz numerikus tpusok, s a long a tgabb, ezrt a konverzi gond nlkl vgbemegy. Egy implicit konverzi minden esetben sikeres s nem jr adatvesztssel. Egy explicit konverzi nem felttlenl fog m kdni s adatveszts is fellphet. Vegyk a kvetkez pldt:
int x = 300; byte y = (byte)x; //explicit konverzi

A byte sz kebb tpus mint az int, ezrt explicit konverzit hajtottunk vgre, ezt a vltoz el tti zrjelbe rt tpussal jelltk. Ha lehetsges a konverzi, akkor vgbemegy, egybknt a fordt figyelmeztetni fog. Vajon mennyi most az y vltoz rtke? A vlasz els re meglep lehet: 44. A magyarzat: a 300 egy kilenc biten felrhat szm (100101100), persze az int kapacitsa ennl nagyobb, de a tbbi rsze nulla lesz. A byte viszont (ahogy a nevben is benne van) egy nyolcbites rtket trolhat, ezrt a 300 nak csak az els 8 bitje fog rtkl addni az y nak, ami pont 44.

10.1 Ellen rztt konverzik


A programfejleszts alatt hasznos lehet tudnunk, hogy minden konverzi gond nlkl vgbement e. Ennek ellen rzsre n. ellen rztt konverzit fogunk hasznlni, amely kivtelt dob (err l hamarosan), ha a forrs nem fr el a clvltozban:
checked { int x = 300; byte y = (byte)x; }

Ez a kifejezs kivtelt fog dobni. Figyeljnk arra, hogy ilyen esetekben csak a blokkon bell deklarlt, statikus s tag vltozkat vizsglhatjuk. El fordul, hogy csak egy-egy konverzit szeretnnk vizsglni, amihez nincs szksg egy egsz blokkra:

52
int x = 300; byte y = checked((byte)x);

Az ellen rzs kikapcsolst is megtehetjk az unchecked hasznlatval:


int x = 300; byte y = unchecked((byte)x);

Az ajnls szerint ellen rztt konverzikat csak a fejleszts ideje alatt hasznljunk, mivel nmi teljestmnyvesztssel jr.

10.2 Is s as
Az is opertort tpusok futsidej lekrdezsre hasznljuk:
int x = 10; if(x is int) //ha x egy int { Console.WriteLine("X tipusa int"); }

Ennek az opertornak a legnagyobb haszna az, hogy le tudjuk krdezni, hogy egy adott osztly megvalst e egy bizonyos interfszt (err l ks bb). Prja az as az ellen rzs mellett egy explicit tpuskonverzit is vgrehajt:
int x = 10; byte y = x as y;

Amennyiben ez a konverzi nem hajthat vgre a clvltozhoz a megfelel rtk rendel dik (rtktpusokhoz 0, referenciatpusokhoz null).

nulla

10.3 Karakterkonverzik
A char tpust implicit mdon tudjuk numerikus tpusra konvertlni, ekkor a karakter Unicode rtkt kapjuk vissza:
using System; class Program { static public void Main() { for(char ch = 'a';ch <= 'z';++ch) { int x = ch; Console.WriteLine(x); } Console.ReadKey(); } }

53

Erre a kimenet a kvetkez lesz: 97 98 99 100 A kis a bet hexadecimlis Unicode szma 0061h, ami a 97 decimlis szmnak felel meg, teht a konverzi a tzes szmrendszerbeli rtket adja vissza.

54

11. Gyakorl feladatok I.


Most nhny gyakorl feladat kvetkezik. Itt nem szerepel teljes megolds, viszont a megoldst segt tippek, tancsok igen. J szrakozst!

11.1 Msodfok egyenlet


Ksztsnk programot, amely bekri az egyenlet hrom egytthatjt s kiszmolja a gykeit(gykt), illetve jelzi, ha nem lehet kiszmolni az eredmnyt (a == 0, a diszkriminns negatv, stb). Tipp: nylvn megvizsgljuk, hogy a diszkriminns egyenl e nullval. Azonban ezt az rtket valamilyen lebeg pontos (pl. double) tpussal rdemes reprezentlni. Ekkor viszont az ellen rzsnl gond addhat. Nzzk a kvetkez esetet:
double y = 0; if(y == 0) {
//itt csinlunk valamit

A meglep az, hogy a felttel soha nem lesz igaz. Hogy mirt? Nos, az egyenl sg csak akkor igaz, ha a kt operandusa bitenknt megegyezik, de mivel az els egy double ez nem valszn (a meggondols egyni feladat, de gondoljunk a tpus nagysgra). Szerencsre van beptett megolds, a Double.Epsilon statikus tulajdonsg rtke pont egy double tpus ltal felvehet legkisebb pozitv szm:
double y = 0; if(y == Double.Epsilon) {
//itt csinlunk valamit

Ez mr biztosan m kdni fog.

11.2 Fibonacci szmok


Ksztsnk programot amely kirja az els vltozt a felhasznltl krjk be. n tagjt a Fibonacci szmoknak. Az n kt

Tipp: a Fibonacci sor tagjainak rtke a megel z kt tag sszege. Az els elemet gy nylvn meg kell majd adnunk, ez a 0 s az 1 legyen.

11.3 Felderts
Ksztsnk programot, amely egy feldert repl gpet szimull. A feladata, hogy megkeresse egy adott terleten a legmagasabb szrazfldi- s a legmlyebben fekv vzalatti pont magassgt/mlysgt. A mrsi adatok egy tmbben legyenek eltrolva, pozitv szmok jelezzk a szrazfldet, negatvak a vzet. Neheztsknt hasznlhatunk ktdimenzis tmbt.

55 Tipp: vletlenszmgenertorral tltsk kvetkez kppen tudunk sorsolni:


Random r = new Random(); r.Next(-100, 100);

fel

tmbt,

negatv

szmot

Ebben az esetben a generlt szm -100 s 100 kz esik majd.

11.4 Maximum/minimumkeress
Ksztsnk programot, amely egy tmb elemei kzl legnagyobbat/legkisebbet. Az elemeket a felhasznltl krjk be. kivlasztja a

56

12. Objektum-orientlt programozs - elmlet


A korai programozsi nyelvek nem az adatokra, hanem a m veletekre helyeztk a hangslyt. Ekkoriban mg f leg matematikai szmtsokat vgeztek a szmtgpekkel. Ahogy aztn a szmtgpek szles krben elterjedtek, megvltoztak az ignyek, az adatok pedig tl kompexekk vltak ahhoz, hogy a procedurlis mdszerrel knyelmesen s hatkonyan kezelni lehessen ket. Az els objektum-orientlt programozsi nyelv a Simula 67 volt. Tervez i Ole-Johan Dahl s Kristen Nygaard hajk viselkedst szimulltk, ekkor jtt az tlet, hogy a klnbz hajtpusok adatait egy egysgknt kezeljk, gy egyszer stve a munkt. Az OOP mr nem a m veleteket helyezi a kzppontra, hanem az egyes adatokat (adatszerkezeteket) s kztk lv kapcsolatot (hierarchit). Ebben a fejezetben az OOP elmleti oldalval foglalkozunk, a cl a paradigma megrtse, gyakorlati pldkkal a kvetkez rszekben tallkozhatunk (szintn a kvetkez rszekben tallhat meg nhny elmleti fogalom amely gyakorlati pldkon keresztl rthet bben megfogalmazhat, ezrt ezek csak ks bb lesznek trgyalva, pl.: polimorfizmus).

12.1 UML
Az OOP tervezs el segtsre hoztk ltre az UML t (Unified Modelling Language). Ez egy ltalnos tervez eszkz, a clja egy minden fejleszt ltal ismert kzs jelrendszer megvalstsa. A kvetkez kben az UML eszkzeit fogjuk felhasznlni az adatok kzti relcik grafikus brzolshoz.

12.2 Osztly
Az OOP vilgban egy osztly olyan adatok s m veletek sszessge, amellyel lerhatjuk egy modell (vagy entits) tulajdonsgait s m kdst. Legyen pldul a modellnk a kutya llatfaj. Egy kutynak vannak tulajdonsgai (pl. letkor, sly, stb.) s van meghatrozott viselkedse (pl. csvlja a farkt, jtszik, stb.). Az UML a kvetkez kppen jell egy osztlyt:

Kutya
Amikor programot runk, akkor az adott osztlybl (osztlyokbl) ltre kell hoznunk egy (vagy tbb) pldnyt, ezt pdnyostsnak nevezzk. Az osztly s pldny kzti klnbsgre j plda a recept (osztly) s a stemny (pldny).

12.3 Adattag s metdus


Egy objektumnak az letciklusa sorn megvltozhat az llapota, tulajdonsgai. Ezt az llapotot valahogy el kell tudnunk trolni illetve biztostani kell a szksges m veleteket a tulajdonsgok megvltoztatshoz (pl. a kutya eszik(ez egy m velet), ekkor megvltozik a jllakottsg tulajdonsga). A tulajdonsgokat trol vltozkat adattagnak (vagy mez nek), a m veleteket metdusnak nevezzk. A m veletek sszessgt felletnek is nevezik. Mdostsuk a diagramunkat:

57

Kutya jollak : int eszik() : void Az adattagokat nv : tpus alakban brzoljuk, a metdusokat pedig nv(paramterlista) : visszatrsi_rtk formban. Ezekkel a fogalmakkal egy ks bbi fejezet foglalkozik.

12.4 Lthatsg
Az egyes tulajdonsgokat, metdusokat nem biztos, hogy j kzszemlre bocstani. Az OOP egyik alapelve, hogy a felhasznl csak annyi adatot kapjon amennyi felttlenl szksges. A kutys pldban az eszik() m velet magba foglalja a rgst, nyelst, emsztst is, de err l nem fontos tudnunk, csak az evs tnye szmt. Ugyangy egy tulajdonsg (adattag) esetben sem j, ha mindenki hozzjuk fr (az elfogadhat, ha a kzvetlen csald hozzfr a szmlmhoz, de idegenekkel nem akarom megosztani). Az s-OOP szablyai hromfle lthatsgot fogalmaznak meg, ez nyelvt l fgg en b vlhet, a C# lthatsgairl a kvetkez rszekben lesz sz. A hromfle lthatsg: Public: mindenki lthatja (UML jells: +). Private: csakis az osztlyon bell elrhet , illetve a leszrmazott osztlyok is lthatjk, de nem mdosthatjk (a szrmaztats/rkl ds hamarosan jn) (UML jells: -). Protected: ugyanaz mint a private, de a leszrmazott osztlyok mdosthatjk is (UML jells: #). A Kutya osztly most gy nz ki: Kutya -jollak : int +eszik() : void

12.5 Egysgbezrs
A hagyomnyos, nem OO programnyelvek (pl. a C) az adatokat s a rajtuk vgezhet m veleteket a program kln rszeknt kezelik. Bevett szoks ezeket elklnteni egy nll forrsfile ba, de ez mg mindig nem elg biztonsgos. A kett kztt nincs sszerendels, ezrt ms programozk gond nlkl trhatjk egyiket vagy msikat, illetve hozzfrnek a struktrkhoz s nem megfelel en hasznljk fel azokat. Az OO paradigma egysgbe zrja az adatokat s a hozzjuk tartoz felletet, ez az n. egysgbezrs (encapsulation vagy information hiding). Ennek egyik nagy el nye, hogy egy adott osztly bels szerkezett gond nlkl megvltoztathatjuk, mindssze arra kell figyelni, hogy a fellet ne vltozzon (pl. egy autt biztosan tudunk kormnyozni, attl fggetlenl, hogy az egy szemlyaut, traktor vagy forma-1 es gp).

58

12.6 rkl ds
Az rkl ds vagy szrmaztats az j osztlyok ltrehozsnak egy mdja. Egy (vagy tbb) mr ltez osztlybl hozunk ltre egy jat, gy, hogy az minden szl jnek tulajdonsgt rkli vagy tfogalmazza azokat. A legegyszer bben egy pldn keresztl rthet meg. Legyen egy llat osztlyunk. Ez egy elgg tg fogalom, ezrt sz kthetjk a krt, mondjuk a Gerinces llatok ra. Ezen bell megklnbztethetnk Eml s t vagy Hll t. Az Eml s osztly egy leszrmazottja lehet a Kutya s gy tovbb. Az rkl dst specializlsnak is nevezik. A specializls sorn az osztlyok kztt n. az-egy (is-a) relci ll fenn. gy amikor azt mondjuk, hogy a Kutya az egy llat akkor arra gondolunk, hogy a Kutya egy specializltabb forma, amelynek megvan a sajt karkaterisztikja, de vgeredmnyben egy llat. Ennek a gondolatmenetnek a gyakorlati felhasznls sorn lesz jelent sge.

A diagram a fenti pldt brzolja. UML -l az rkl dst res nyllal jelljk, amely a specializlt osztly fel l mutat az ltalnosabbra. Az osztlyok kztt fenll kapcsolatok sszesgt hierachinak nevezzk. El fordul, hogy nem fontos szmunkra a bels szerkezet, csak a felletet szeretnnk trkteni, hogy az osztlyunkat fel tudja hasznlni a programunk egy msik rsze (ilyen pldul a mr emltett foreach ciklus). Ilyenkor nem egy igazi osztlyrl, hanem egy interfszr l beszlnk, amelynek nincsenek adattagjai csakis a m veleteket deklarlja. A C# rendelkezik nll interfszekkel, de ez nem minden programnyelvre igaz, ezrt k egy hasonl szerkezetet n. absztrakt osztlyokat hasznlnak. Ezekben el fordulnak adattagok is, de leginkbb a fellet definilsra koncentrlnak. A C# nyelvi szinten tmogat absztrakt osztlyokat is, a kett kztt ugyanis lnyegi klnbsg van. Az interfszek az osztlytl fggetlenek, csakis felletet biztostanak (pldul az IEnumerable s IEnumerator a foreach nek (is) biztostanak felletet, az nem lnyeges, hogy milyen osztlyrl van sz). Az absztrakt osztlyok viszont egy shz ktik az utdokat (erre az esetre plda az llat osztly, amelyben mondjuk megadunk egy absztrakt evs metdust, amit az utdok megvalstanak - egy krokodil nylvn mshogy eszik mint egy hangya, de az evs az llatokhoz kthet valami, ezrt kzs).

59

13. Osztlyok
Osztlyt a class kulcssz segtsgvel deklarlhatunk:
using System; class Dog { } class Program { static public void Main() { } }

Lthat, hogy a f program s a sajt osztlyunk elklnl. Felmerlhet a krds, hogy a fordt, honnan tudja, hogy melyik a f osztly? A helyzet az, hogy a Main egy specilis metdus, ezt a fordt automatikusan felismeri s megjelli, mint a program belpsi pontjt. Igazbl lehetsges tbb Main nev metdust is ltrehozni, ekkor a fordtprogramnak meg kell adni (a /main kapcsol segtsgvel), hogy melyik az igazi belpsi pont. Ett l fggetlenl ezt a megoldst ha csak lehet kerljk el. A fenti pldban a lehet legegyszer bb osztlyt hoztuk ltre. A C++ programozk figyeljenek arra, hogy az osztlydeklarci vgn nincs pontosvessz . Az osztlyunkbl a kvetkez kppen tudunk ltrehozni egy pldnyt:
Dog d = new Dog();

Hasonlan a C++ nyelvhez itt is a new opertorral tudunk j objektumot ltrehozni.

13.1 Konstruktorok
Minden esetben amikor pldnyostunk egy specilis metdus, a konstruktor fut le. Br a fenti osztlyunkban nem definiltunk semmilyen metdust, ett l fggetlenl rendelkezik alaprtelmezett (azaz paramter nlkli) konstruktorral. Ez igaz minden olyan osztlyra, amelynek nincs konstruktora (amennyiben brmilyen konstruktort ltrehoztunk akkor ez a lehet sg megsz nik). Az alaprtelmezett konstruktor semmi mst nem tesz, mint helyet foglal a memriban s ltrehoz egy pldnyt. Az adattagok ha vannak automatikusan a nekik megfelel nullrtkre inicializldnak(pl.: int -> 0, bool -> false, referenciatpusok -> null). A C++ nyelvet ismer k vigyzzanak, mivel itt csak alaprtelmezett konstruktort kapunk automatikusan, rtkad opertort illetve msol konstruktort nem. Ugyanakkor minden osztly a System.Object b l szrmazik (mg akkor is ha erre nem utal semmi), ezrt nhny metdust (pldul a tpus lekrdezshez) a konstruktorhoz hasonlan azonnal hasznlhatunk.

60 Jelen pillanatban az osztlyunkat semmire nem tudjuk hasznlni, ezrt ksztsnk hozz nhny adattagot s egy konstruktort:
using System; class Dog { private string name; private int age; public Dog(string n, int a) { this.name = n; this.age = a; } } class Program { static public void Main() { Dog d = new Dog("Rex", 2); } }

A konstruktor neve meg kell egyezzen az osztly nevvel s semmilyen visszatrsi rtke sem lehet. A mi konstruktorunk kt paramtert vr, a nevet s a kort (metdusokkal s paramtereikkel a kvetkez rsz foglalkozik b vebben). Ezeket a pldnyostsnl muszj megadni, egybknt nem fordul le a program. Egy osztlynak paramterlisttl fgg en brmennyi konstruktora lehet s egy konstruktorbl hvhatunk egy msikat a this el:
class MyClass { public MyClass() : this(10) //A msik konstruktort hvtuk { } public MyClass(int x) { } }

Ilyen esetekben a paramter tpushoz leginkbb illeszked konstruktor kerl majd hvsra. A pldban a konstruktor trzsben rtket adtunk a mez knek this hivatkozssal, amely mindig arra a pldnyra mutat, amelyen meghvtk. Nem ktelez hasznlni, ugyanakkor hasznos lehet, ha sok adattag/metdus van, illetve ha a paramterek neve megegyezik az adattagokval. A fordtprogram automatikusan odakpzeli magnak a fordts sorn, gy mindig tudja mivel dolgozik. Az adattagok private elrs ek (ld. elmleti rsz), azaz most csakis az osztlyon bell hasznlhatjuk ket, pldul a konstruktorban, ami viszont publikus.

61 Nem csak a konstruktorban adhatunk rtket a mez knek, hanem hasznlhatunk n. inicializlkat is:
class Dog { private string name = "Rex"; private int age = 5; public Dog(string n, int a) { this.name = n; this.age = a; } }

Az inicializls mindig a konstruktor el tt fut le, ez egyben azt is jelenti, hogy az fellbrlhatja. Ha a Dog osztlynak ezt a mdostott vltozatt hasznltuk volna fentebb, akkor a pldnyosts sorn fellrnnk az alaprtelmezettnek megadott kort. Az inicializls sorrendje megegyezik a deklarls sorrendjvel (fl lr l lefel halad). A konstruktorok egy specilis vltozata az n. msol- vagy copy konstruktor. Ez paramtereknt egy sajt magval megegyez tpus objektumot kap s annak rtkeivel inicializlja magt. Msol konstruktort ltalban az rtkad opertorral szoktak implementlni, de az opertortlterhels egy msik fejezet tmja gy most egyszer bben oldjuk meg:
class Dog { private string name = "Rex"; private int age = 5; public Dog(string n, int a) { this.name = n; this.age = a; } public Dog(Dog otherDog) { this.name = otherDog.Name; this.age = otherDog.Age; } public int Age { get { return age; } } public string Name { get { return name; } } }

62 n. tulajdonsgokat hasznltunk fel (err l hamarosan). Most mr felhasznlhajuk az j konstruktort:


Dog d = new Dog("Rex", 6); Dog newDog = new Dog(d);

13.2 Adattagok
Az adattagok vagy mez k olyan vltozk, amelyeket egy osztlyon (vagy struktrn) bell deklarltunk. Az eddigi pldinkban is hasznltunk mr adattagokat, ilyenek voltak a Dog osztlyon belli name s age vltozk. Az adattagokon hasznlhatjuk a const tpusmdostt is, ekkor a deklarcinl rtket kell adnunk a mez nek, hasonlan az el z fejezetben emltett inicializlshoz. Ezek a mez k pontosan ugyangy viselkednek mint a hagyomnyos konstansok. Egy konstans mez t nem lehet explicite statikusnak jellni, mivel a fordt egybknt is gy fogka kezelni (ha egy adat minden objektumban vltozatlan, felesleges minden alkalommal kln pldnyt kszteni bel le). A mez kn alkalmazhat a readonly mdost is, ez kt dologban klnbzik a konstansoktl: az rtkads elhalaszthat a konstruktorig s az rtkl adott kifejezs rtknek nem kell ismertnek lennie fordtsi id ben.

13.3 Lthatsgi mdostk


A C# nyelv tfle mdostt ismer: public: az osztlyon/struktrn kv l s bell teljes mrtkben hozzfrhet . private: csakis a tartalmaz osztlyon bell lthat, a leszrmazottak sem lthatjk, osztlyok/struktrk esetben az alaprtelmezs. protected: csakis a tartalmaz osztlyon s leszrmazottain bell lthat. internal: csakis a tartalmaz (s a bart) assembly(ke) n bell lthat. protected internal: a protected s internal keverke.

13.4 Parcilis osztlyok


C# nyelven ltrehozhatunk n. parcilis osztlyokat (partial class). Egy parcilis osztly definicija tbb rszb l (tipikusan tbb forrsfile bl) is llhat.
//file1.cs

partial class PClass { public PClass() {


/*...*/

} }
//file2.cs

partial class PClass { public void DoSomeThing(){ /*...*/ } }

63

Az osztly sszes darabjnl ktelez kitenni a partial kulcsszt. A C# a parcilis osztlyokat f knt olyan esetekben hasznlja, amikor az osztly egy rszt a fordt generlja (pl. a grafikus fellet alkalmazsoknl a kezdeti belltsokat az InitializeComponent() metdus vgzi, ezt teljes egszben a fordt kszti el).
A kvetkez tma megrtst el segtheti a kvetkez Metdusok cm rsz elolvassa

A C# 3.0 mr engedlyezi parcilis metdusok hasznlatt is, ekkor a metdus deklarcija s defincija sztoszlik:
//file1.cs

partial class PMClass {


//deklarci

partial public void PartialMethod(string param); }


//file2.cs

partial class PMClass {


//definci

partial public void PartialMethod(string param) {


/*...*/

} }

A partial kulcsszt ilyenkor is ki kell tenni minden el fordulsnl. Csakis parcilis osztly tartalmazhat parcilis metdust.

13.5 Begyazott osztlyok


Egy osztly tartalmazhat metdusokat, adattagokat s ms osztlyokat is. Ezeket a bels osztlyokat begyazott (nested) osztlynak nevezzk. Egy begyazott osztly hozzfr az t tartalmaz osztly minden tagjhoz (belertve a private elrs tagokat s ms begyazott osztlyokat is). Egy ilyen osztlyt ltalban elrejtnk, de ha mgis publikus elrs nek deklarljuk, akkor a kls osztlyon kereszt l rhetjk el.
//a begyazott osztly nem lthat

class Outer { private class Inner { } }


//de most mr igen

class Outer {

64
public class Inner { } }
//plnyosts:

Outer.Inner innerClass = new Outer.Inner();

13.6 Objektum inicializlk


A C# 3.0 objektumok pldnyostsnak egy rdekesebb formjt is tartalmazza:
class Person { private string name; public Person() { } public string Name { get { return name; } set { this.name = value; } } } Person p = new Person { Name = "Istvan" };

Ilyen esetekben vagy egy nylvnos tagra, vagy egy tulajdonsgra hivatkozunk (ez utbbit hasznltuk). Termszetesen, ha ltezik paramteres konstruktor, akkor is hasznlhatjuk ezt a belltsi mdot, a kvetkez kppen:
Osztaly valtozo = new Osztaly(/*parameterek*/) { };

13.7 Destruktorok
A destruktorok a konstruktorokhoz hasonl specilis metdusok amelyek az er forrsok felszabadtsrt felelnek. A C# objektumai ltal elfoglalt memrit a szemtgyjt (Garbage Collector) szabadtja fel, abban az esetben ha az objektumra nincs rvnyes referencia:
MyClass mc = new MyClass(); //mc egy MyClass objektumra mutat mc = null; //az objektumra mr nem mutat semmi, felszabadthat

A szemtgyjt m kdse nem determinisztikus, azaz el re nem tudjuk megmondani, hogy mikor fut le, ugyanakkor kzzel is meghvhat, de ez nem ajnlott:
MyClass mc = new MyClass(); mc = null; GC.Collect(); GC.WaitForPendingFinalizers();

65 A GC a hatkonysg szempontjbl a legjobb id t vlasztja ki arra, hogy elvgezze a gy jtst (de legks bb akkor m kdsbe lp amikor elfogy a memria). A GC miel tt megsemmsiti az objektumokat meghvja a hozzjuk tartoz destruktort, msnven Finalizert. Vegyk a kvetkez kdot:
class DestructableClass { public DestructableClass() { } ~DestructableClass() { } }

A destruktor neve tilde jellel (~) kezd dik, neve megegyezik az osztlyval s nem nem lehet semmilyen mdostja vagy paramtere. A fenti kd valjban a kvetkez formban ltezik:
protected override void Finalize() { try { } finally { base.Finalize(); } }

Minden tpus rkli a System.Object t destruktorral fellrunk. A Finalize() el szr destruktorban ltalunk megszabott mdon), metdust (ez legalbb a System.Object nem r:

l a Finalize() metdust, amelyet a felszabadtja az osztly er forrsait (a aztn meghvja az sosztly Finalize() lesz) s gy tovbb amg a lnc vgre

class Base { ~Base() { Console.WriteLine("Base destruktor.."); } } class Derived { ~Derived() { Console.WriteLine("Derived destruktor..."); } }

Ezutn:

66
class Program { static public void Main() { Derived d = new Derived(); Console.ReadKey(); } }

Amikor elindtjuk a programot nem trtnik semmi, ahogy az vrhat is volt. Amikor viszont lenyomunk egy gombot a Console.ReadKey() nek elindul a GC s a kimenet a kvetkez lesz:
Derived destruktor Base destruktor

Els knt a leszrmazott, majd az sobjektum semmisl meg. A destruktorokra vonatkozik nhny szably, ezek a kvetkez ek: Egy osztlynak csak egy destruktora lehet A destruktor nem rklhet A destruktort nem lehet direkt hvni, ez mindig automatikusan trtnik Destruktora csakis osztlynak lehet, struktrnak nem

67

14. Metdusok
Bizonyra szeretnnk, ha a kutynk nem csak ltezne a semmiben, hanem csinlna is valamit. Ksztsnk nhny metdust (ugye senki nem felejtette el, a metdusokal m velteket tudunk vgezni az objektumon):
class Dog { private string name; private int age; public Dog(string n, int a) { name = n; age = a; } public void Sleep(int time) { Console.WriteLine("A kutya {0} orat alszik..."); } public void Eat() { Consol.WriteLine("A kutya most eszik..."); } }

Most mr tud enni s aludni, a kt legfontosabb dolgot. Nzzk az alvst:


public void Sleep(int time) { Console.WriteLine("A kutya {0} orat alszik...", time); }

El szr megadjuk a lthatsgot, ez publikus lesz, hogy meghvhassuk. Ezutn a metdus visszatrsi rtknek a tpusa jn. Mire j a visszatrsi rtk? Az objektumainkon nem csak m veleteket vgznk, de szeretnnk lekrdezni az llapotukat is s felhasznlni ezeket az rtkeket. Egy msik lehet sg, amivel mr tallkoztunk az int.Parse() metdus kapcsn, azok a kisegt fggvnyek amelyek nem kapcsoldnak kzvetlenl az objektum letciklushoz, de hasznosak s logikailag az osztly hatkrbe tartoznak. Visszatrsi rtkkel rendelkez metdust hasznlhatunk minden olyan helyen, ahol a program valamilyen tpust vr (rtkads, logikai kifejezsek, metdus paramterei, etc..). Kanyarodjunk vissza az eredeti szlhoz! Az alvs metdusunk nem tr vissza semmivel, ezt a void kulcsszval jelezzk, ez az n. ltalnos tpus (a ks bbiekben mg lesz rla sz). Ezutn a metdus neve s paramterlistja kvetkezik. A fggvnyeket az objektum neve utn rt pont opertorral hvhatjuk meg (ugyanez rvnyes a publikus adattagokra, tulajdonsgokra, stb. is):

68

Dog d = new Dog("Rex", 2); d.Eat(); d.Sleep(3);

Erre a kimenet:
A kutya most eszik... A kutya 3 orat alszik...

14.1 Paramterek
Az objektummal val kommunikci rdekben kpesnek kell lennnk kv lr l megadni adatokat, vagyis paramtereket. A paramterek szmt s tpusait a fggvny deklarcijban, vessz vel elvlasztva adjuk meg. Egy metdusnak gyakorlatilag brmennyi paramtere lehet. A metdus nevt s paramterlistjt alrsnak, szignatrnak vagy prototpusnak nevezzk. A paramterek a metduson bell loklis vltozkknt viselkednek, s a paramter nevvel hivatkozunk rjuk. A C# nyelvben paramterek tadhatunk rtk s cm szerint is. El bbi esetben egy teljesen j pldny jn ltre az adott osztlybl, amelynek rtkei megegyeznek az eredetivel. A msik esetben egy az objektumra mutat referencia addik t valjban, teht az eredeti objektummal dolgozunk. Az rtk- s referenciatpusok klnbz en viselkednek az tads szempontjbl. Az rtktpusok alaprtelmezetten rtk szerint addnak t, mg a referenciatpusoknl a cm szerinti tads az el re meghatrozott viselkeds. Utbbi esetben van azonban egy kivtel, mgpedig az, hogy mg a referenciatpus rtkeit megvltoztathatjuk (s ez az eredeti objektumra is hat) addig magt a referencit mr nem. Ha ezt mgis megtesszk, az a program futsra nem hat, de a vltozs csakis a metduson bell lesz szlelhet .
using System; class Program { static public void AllocArray(int[] array, int size) { array = new int[size]; Console.WriteLine(array.Length); //100 } static public void Main() { int[] a = new int[10]; AllocArray(a, 100); Console.WriteLine(a.Length); //10 } }

69 Ha mgis mdostani akarjuk a referenciatpus referencijt, akkor kln jeleznnk kell azt, hogy cm szerint akarjuk tadni. Ktflekppen adhatunk t paramtert cm szerint. Az els esetben az tadott objektum inicializlva kell legyen. A cm szerinti tadst a forrskdban is jellni kell, mind a metdus prototpusnl, mind a hvs helyn:
using System; class Dog { private int age; private string name; public Dog(string s, int a) { name = s; age = a; }
/* mr elksztett metdusok */

public void PlayOtherDog(ref Dog other) { Console.WriteLine("Ket kutya jatszik..."); } }

class Program { static public void Main() { Dog d1 = new Dog("Rex", 2); Dog d2 = new Dog("Rinti", 3); d1.PlayOtherDog(ref d2); Console.ReadKey(); } }

A ref kulcsszval jelljk meg azokat a paramtereket, amelyeknek a cmt akarjuk tadni. Ezt a hvs helyn is meg kell tennnk. A cm szerinti tads msik formjban nem inicializlt paramtert is tadhatunk, de ekkor felttel, hogy a metduson bell lltsuk be. A hasznlata megegyezik a ref el, azaz a szignatrban s a hvsnl is jelezni kell a szndkunkat. A hasznland kulcssz az out (Nomen est omen A nv ktelez):
using System; class Dog { private int age;

70
private string name; public Dog(string s, int a) { name = s; age = a; }
/* mr elksztett metdusok */

public void PlayOtherDog(ref Dog other) { Console.WriteLine("Ket kutya jatszik..."); } public void InitDog(out Dog idog) { idog = new Dog("Lassie", 4); } } class Program { static public void Main() { Dog d1 = new Dog("Rex", 2); Dog d2 = new Dog("Rinti", 3); d1.PlayOtherDog(ref d2); Dog d3; d2.InitDog(out d3); Console.ReadKey(); } }

Ebben a pldban nem lett volna muszj cm szerint tadnunk a paramtereket, hiszen az objektumok rtkeit nem mdostottuk. Amikor nem tudjuk, hogy pontosan hny paramtert akarunk tadni, akkor hasznlhatunk paramtertmbket:
static void Func(params int[] paramarray) {
/*Itt csinalunk valamit*/

} static public void Main() { int[] array = new int[] { 1, 2, 3, 4 }; Func(array); }

71

A paramtertmbt a params kulcsszval jelezzk, ezutn a metdus belsejben pontosan gy viselkedik, mint egy normlis tmb. A cm s rtk szerinti tadsrl a kvetkez oldalakon olvashatunk tbbet: http://msdn.microsoft.com/en-us/library/9t0za5es.aspx http://msdn.microsoft.com/en-us/library/s6938f28.aspx

14.2 Parancssori paramterek


A Main nek ltezik paramteres vltozata is:
using System; class Program { static public void Main(String[] args) { } }

Ekkor a Main paramtere egy tmb, ami a parancssori paramtereket trolja el. Pl. gy futtatjuk a programot: program.exe elso masodik harmadik Ha ki szeretnnk iratni a paramtereket, akkor gy mdostjuk a programot:

using System; class Program { static public void Main(String[] args) { foreach(string s in args) { Console.WriteLine(s); } } }

Erre a kimenet a kvetkez : elso masodik harmadik

14.3 Kiterjesztett metdusok

72

A C# 3.0 lehet sget ad arra, hogy egy mr ltez tpushoz j funkcikat adjunk, anlkl, hogy azt kzvetlenl mdostannk. Egy kiterjesztett metdus (extension method) minden esetben egy statikus osztly statikus metdusa kell legyen. Egsztsk ki a string tpust egy metdussal, ami kirja a kperny re az adott karaktersorozatot:
static public class StringPrinter { static public void Print(this string s) { Console.WriteLine(s); } }

A this mdost utn a paramter tpusa kvetkezik, amely meghatrozza a kiterjesztett osztly tpust. Ezutn a metdust gy hasznljuk:
string s = "abcd"; s.Print();
//vagy hagyomnyos statikus metdusknt:

StringPrinter.Print(s);

Nemcsak osztlyt, de interfszt is b vthetnk. Ha kt kiterjesztett metdus ugyanazzal a szignatrval rendelkezik, akkor a hagyomnyos statikus ton kell hvnunk ket. Ha nem gy tesznk akkor a specilisabb paramter metdus fog meghvdni. Egy kiterjesztett metdust nem defiinlhatunk begyazott osztlyban. A kiterjesztett metdusokrl a kvetkez oldalon olvashatunk tbbet: http://msdn.microsoft.com/en-us/library/bb383977.aspx

14.4 Gyakorl feladatok


1. Ksztsnk metdusokat a kutya osztlyhoz, amelyekkel elvgezhetjk a szoksos teend ket: pl.: frdets, stltats, stb. 2. Ksztsnk osztlyt, amely egy tmbt tartalmaz. Az osztly konstruktorban foglaljuk le a tmbt (a mrett a konstruktor paramtere tartalmazza). rjunk hozz egy metdust, amely kirja a tmbt a konzolra. Pl.:
MyArray ma = new MyArray(10); //10 elem tmb ma.Print(); //kirjuk

3. Ksztsnk egy kiterjesztett metdust, amely kirja egy egsz szmokbl (int) ll tmb elemeit (a szksges statikus osztlyokrl a kvetkez fejezet szl).

73

15. Statikus tagok


A hagyomnyos adattagok s metdusok objektumszinten lteznek, azaz minden pldny sajt pldnnyal rendelkezik. Gyakran van azonban szksgnk arra, hogy minden pldny egy kzs tagot hasznljon, pl. ha szeretnnk szmolni, hogy hny objektumot hoztunk ltre. Az sszes statikus tagbl sszesen egy darab ltezik. A statikus tagok jelent sge a C# tisztn objektum orientltsgban rejlik, ugyanis nem definilhatunk globlis mindenki szmra egyformn elrhet tagokat. Ezt vltjk ki a statikus adattagok s metdusok. gy megklnbztetnk pldny (instance) s statikus tagokat.

15.1 Statikus adattagok


Statikus adattagot (s metdust ld. kvetkez fejezet) a static kulcssz segtsgvel hozhatunk ltre:
public Animals { static public int animalCounter = 0; public Animals() { ++animalCounter; } }

A pldban a statikus adattag rtkt minden alkalommal megnveljk eggyel, amikor meghvjuk a konstruktort. Vagyis a pldnyok szmt troljuk el benne. A statikus tagokhoz az osztly nevn (s nem egy pldnyn) keresztl frnk hozz:
Console.WriteLine("A peldanyok szama: {0}", Animals.animalCounter);

A statikus tagok azel tt inicializldnak, hogy el szr hozzfrnnk, illetve miel tt a statikus konstruktor ha van lefut.

15.2 Statikus metdusok


A statikus metdusokbl, akrcsak az adattagokbl osztlyszinten egy darab ltezik. Statikus metdus ltrehozsa a kvetkez kppen trtnik:
public class Math { static public void Pi() {
/*...*/

} }

Ezt gy tudjuk meghvni:


Console.WriteLine("Pi erteke: {0}", Math.Pi());

74 Brmely statikus taghoz az osztlyon s nem egy pldnyon kereszt l frnk hozz. A pldnyon val hvs fordtsi hibt jelent. Statikus metdust ltalban akkor hasznlunk, ha nem egy pldny llapotnak a megvltoztatsa a cl, hanem egy osztlyhoz kapcsold m velet elvgzse. A statikus metdusok nem frhetnek hozz a pldnytagokhoz (legalbbis direkt mdon nem).

15.3 Statikus tulajdonsgok


Ennek a fejezetnek a megrtshez ajnlott elolvasni a Tulajdonsgok cm rszt

A statikus tulajdonsgok a C# egy viszonylag ritkn hasznlt lehet sge. ltalban osztlyokhoz kapcsold konstans rtkek lekrdezsre hasznljuk:
public class Math { static public double Pi { get { return 3.14; } } }

15.4 Statikus konstruktor


A statikus konstruktor a statikus tagok belltsrt felel. A statikus konstruktor azutn fut le, hogy a program elindult, de azel tt, hogy egy pldny keletkezik. A statikus konstruktornak nem lehet lthatsgot adni, illetve nincsenek paramterei sem. Mivel ez is egy statikus metdus nem frhet hozz a pldnytagokhoz. Plda statikus konstruktorra:
public class Person { public static string name; static Person() { name = "Anonymus"; } }

15.5 Statikus osztlyok


Egy osztlyt statikusnak jellhetnk, ha csak s kizrlag statikus tagjai vannak. Egy statikus osztlybl nem hozhat ltre pldny, nem lehet pldnykonstruktora (de statikus igen) s mindig lezrt (ld. rkl ds). A fordt minden esetben ellen rzi ezeknek a feltteleknek a teljeslst.
static class Math { static private double pi = 3.141516;

75
static public double Pi() { return pi; } static public double Cos(double x) { /*...*/ }
/*Egyb tagok*/

15.6 Gyakorl feladatok


1. Ksztsk el a Math statikus osztlyt! Az osztly tartalmazzon metdusokat a ngy alapm veletre (illetve tetsz leges matematikai m veletre). Az sszeads s szorzs metdusok a paramtereiket a params tmbn (ld. metdusok) kapjk meg, vagyis brmennyi taggal elvgezhet ek legyenek a m veletek. 2. Az el z feladat osztlyt felhasznlva ksztsnk egy egyszer szmolgpet! Azoknl a metdusoknl, amelyek meghatrozatlan szm paramtert vrnak ciklussal olvassuk be a tagokat!

76

16. Tulajdonsgok
A tulajdonsgokat (vagy property ket) a mez k kzvetlen mdostsra hasznljuk, anlkl, hogy megsrtennk az egysgbezrs elvt. A tulajdonsgok kv lr l nzve pontosan ugyanolyanok, mint a hagyomnyos vltozk:
using System; class Person { private string name; public Person(string name) { this.name = name; }
//Ez a tulajdonsg

public string Name { get { return this.name; } set { this.name = value; } } } class Program { static public void Main() { Person p = new Person("Istvan"); Console.WriteLine(p.Name); } }

A tulajdonsggal lekrdeztk a szemly nevt. Hagyomny - de nem ktelez hogy a tulajdonsg neve az adattag nevnek nagybet vel kezd d vltozata.
public string Name { get { return this.name; } set { this.name = value; } }

Els knt megadtuk a lthatsgot, aztn a visszatrsi rtket s a nevet. A tulajdonsg trzse kt rszre az n. getter re s setter re oszlik. El bbivel lekrdezzk az adott rtket, pp gy mint egy hagyomnyos metdus esetben tennnk. A setter mr rdekesebb egy kicsit. Minden esetben amikor az adott tulajdonsgnak rtket adunk egy lthatatlan paramter jn ltre, aminek a neve value, ez fogja tartalmazni a tulajdonsg j rtkt. A settert gy hasznlhatjuk:
class Program { static public void Main()

77
{ Person p = new Person("Istvan"); p.Name = "Dezso"; Console.WriteLine(p.Name); //Dezso } }

Lthat, hogy pontosan gy m kdik mint egy vltoz. Egyik esetben sem vagyunk rknyszertve, hogy azonnal visszadjuk/beolvassuk az adttag rtkt, tetszs szerint vgezhetnk m veleteket is rajtuk:
public string Name { get { return ("Mr." + this.name); } set { this.name = value; } }

Arra is van lehet sg, hogy csak az egyiket hasznljuk, ekkor csak-rhat/csakolvashat tulajdonsgokrl beszlnk (br el bbi hasznlata elg ritka). A getter/setter lthatsgt kln megadhatjuk, radsul ezeknek nem kell egyeznik sem:
public string Name { get { return ("Mr." + this.name); } private set { this.name = value; } }

Ebben az esetben a getter megtartja az alaprtelmezett publikus lthatsgt, mg a setter private elrs lesz, kiv lr l nem lehet meghvni. A C# 3.0 rendelkezik egy nagyon rdekes jtssal az n. automatikus tulajdonsgokkal. Nem kell ltrehoznunk sem az adattagot, sem a teljes tulajdonsgot, a fordt mindkett t legenerlja neknk:
class Person { public string Name { get; set; } }

A fordt automatikusan ltrehoz egy private elrs name nev adattagot s elkszti hozz a gettert/settert is. Van azonban egy problma, mghozz az, hogy a fordts pillanatban ez a vltoz mg nem ltezik. Viszont van setter, amit hasznlhatunk s mr ltezik:
class Person { private string Name { get; set; } public Person(string n) {

78
name = n; //Ez nem m kdik Name = n; //Ez viszont igen } }

79

17. Indexel k
Az indexel k hasonlak a tulajdonsgokhoz, azzal a klnbsggel, hogy nem nvvel, hanem egy indexxel frnk hozz az adott informcihoz. ltalban olyan esetekben hasznljk, amikor az osztly/struktra tartalmaz egy tmbt vagy valamilyen gy jtemnyt (vagy olyan objektumot, amely maga is megvalst egy indexel t). Egy indexel t gy implementlhatunk:
class Names { private ArrayList nameList; public Names() { nameList = new ArrayList(); nameList.Add("Istvan"); nameList.Add("Judit"); nameList.Add("Bela"); nameList.Add("Eszter"); } public string this [int idx] { get { if(idx < nameList.Count) { return nameList[idx].ToString(); } else return null; } } }

Ez gyakorlatilag egy nvtelen tulajdonsg, a this mutat mutat az aktulis objektumra, amin az indexel t definiltuk. Nemcsak egy indexet adhatunk meg, hanem tetszs szerintit, illetve az index tpusa sem kttt. Pl.:
public int this [int idx1, int idx2] {
/*...*/

80

18. Gyakorl feladatok II.


18.1 Tmb tpus
Valstsunk meg egy tmb tpust. Az osztly tartalmazzon metdusokat/tulajdonsgokat/indexel ket a szoksos tmbm veletek (kirs, rendezs, stb) elvgzsre. Az indexel t gy ksztsk el, hogy ellen rizze, hogy ltez indexekre hivatkoznuk. Ha nem akkor rjon hibazenetet a kperny re.

81

19. rkl ds
rkl dssel egy mr ltez tpust terjeszthetnk ki vagy b vthetjk tetsz leges szolgltatssal. A C# csakis egyszeres rkl dst engedlyez, ugyanakkor megengedi tbb interfsz impementlst (interfszekr l hamarosan). Ksztsk el az elmleti rsz pldjt (llat-Kutya-Krokodil) C# nyelven. Az egyszer sg kedvrt hagyjuk ki az llat s Kutya kzti specilisabb osztlyokat:
class Animal { public Animal() { } } class Dog : Animal { public Dog() { } } class Crocodile : Animal { public Crocodile() { } }

A Kutya s Krokodil osztlyok egyarnt megvalstjk az szegnyes) funkcionalitst. B vtsk ki az sosztlyt:


class Animal { public Animal() { } public void Eat() { Console.WriteLine("Az allat eszik..."); } }

sosztly (egyel re

Ezt az j metdust az tdosztlyok is rklni fogjk, hvjuk is meg valamelyik sosztlyon:


Dog d = new Dog(); d.Eat();

Az eredmny az lesz, hogy kirjuk a kperny re a megadott zenetet. Honnan tudja vajon a fordt, hogy egy sosztlybeli metdust kell meghvnia? A referenciatpusok (minden osztly az) specilis mdon jelennek meg a memriban, rendelkeznek tbbek kzt egy n. metdustblval, ami mutatja, hogy az egyes metdushvsoknl melyik metdust kell meghvni. Persze ezt is meg kell hatrozni valahogy, ez nagy vonalakban gy trtnik, hogy a fordt a fordts pillanatban megkapja a metdus nevt s elindul visszafel az osztlyhierarchia mentn. A fenti pldban a hv osztly nem rendelkezik Eat() nev metdussal s nem is definilja t annak a viselkedst (err l hamarosan), ezrt az eggyel feljebbi st kell

82 megnznnk. Ez egszen a lehet legjabb metdusdefinciig megy s amikor megtallja a megfelel implementcit bejegyzi azt a metdustblba.

19.1 Konstruktor
Egy leszrmazott konstruktorban meghvhatjuk az sosztly konstruktort:
class BaseClass { protected int data; public Base(int _data) { this.data = _data; } } class DerivedClass : BaseClass { private int data2; public DerivedClass(int d2) : base(d2) { data2 = data; } }

Ezt a base fggvnnyel tehetjk meg s, ahogy a pldban is ltszik a paramtereket is tadhatjuk.

19.2 Polimorfizmus
Korbban mr beszltnk arrl, hogy az s s leszrmazottak kzt az-egy relci ll fent. Ez a gyakorlatban azt jelenti, hogy minden olyan helyen, ahol egy stpust hasznlunk ott hasznlhatunk leszrmazottat is (pl. egy llatkertben llatok vannak, de az llatok helyre (nylvn) behelyettesthetek egy specilis fajt). Pldul gond nlkl rhatom a kvetkez t:
Animal a = new Dog();

A new opertor meghvsa utn a gy fog viselkedni mint a Dog osztly egy pldnya, hasznlhatja annak metdusait, adattagjait. Arra azonban figyeljnk, hogy ez visszafel nem m kdik, a fordt hibt jelezne. Abban az esetben ugyanis, ha a fordt engedn a visszafel konverzit az n. leszeletel ds (slicing) effektus lpne fel, azaz az adott objektum elveszten a specilisabb osztlyra jellemz karakterisztikjt. A C++ nyelvben sokszor jelent gondot ez a problma, mivel ott egy pointeren keresztl megtehet a lebutts. Szerencsre a C# nyelvben ezt megoldottk, gy nem kell aggdnunk miatta.

83

19.3 Virtulis metdusok


A virtulis (vagy polimorfikus) metdusok olyan sosztlyok olyan metdusai amelyek viselkedst a leszrmazottak tdefinilhatjk. Virtulis metdust a virtual kulcssz segtsgvel deklarlhatunk:
class Animal { public Animal() { } public virtual void Eat() { Console.WriteLine("Az allat eszik..."); } } class Dog { Public Dog() { } public override void Eat() { Console.WriteLine("A kutya eszik..."); } }

A leszrmazott osztlyban az override kulcsszval mondjuk meg a fordtnak, hogy szndkosan hoztunk ltre az sosztlyval azonos nev metdust s a leszrmazott osztlyon ezt kvnjuk hasznlni mostantl. Egy override al jellt metdus automatikusan virtulis is lesz, gy az leszrmazottai is tdefinilhatjk a m kdst. Prbljuk ki az j metdust:
Dog d = new Dog(); d.Eat();

A kimenet:
A kutya eszik

Mi trtnik vajon a kvetkez esetben:


Animal[] aniArray = new Animal[2]; aniArray [0] = new Animal(); aniArray [1] = new Dog(); aniArray [0].Eat(); aniArray [1].Eat();

Amit a fordt lt, az az, hogy ksztettnk egy Animal tpus elemekb l ll tmbt s, hogy az elemein meghvtuk az Eat() metdust. Csakhogy az Eat() egy virtulis

84 metdus, radsul van leszrmazottbeli implementcija is, amely tdefinlja az eredeti viselkedst, s ezt explicit jelltk is az override kulcsszval. gy a fordt el tudja dnteni a futsidej tpust, s ezltal temezi a metdushvsokat. Ez az n. ks i kts (late binding). A kimenet gy mr nem lehet ktsges:
Az llat eszik... A kutya eszik...

Az utdosztly metdusnak szignatrja, visszatrsi rtke s lthatsga meg kell egyezzen azzal amit t akarunk definilni. Mr beszltnk arrl, hogyan pl fel a metdustbla, a fordt megkeresi a legkorbbi implementcit, s most mr azt is tudjuk, hogy az els ilyen implementci egy virtulis metdus lesz, azaz a keress legks bb az els virtulis vltozatnl megll. Tegyk fel, hogy nem ismerjk az sosztly fellett s a hagyomnyos mdon deklarljuk az Eat() metdust (ugye nem tudjuk, hogy mr ltezik). Ekkor a program ugyan lefordul, de a fordt figyelmezet minket, hogy eltakarjuk az rkltt metdust. s valban, ha meghvnnk akkor az j metdus futna le. Ezt a jelensget rnykolsnak (shadow) nevezik. Termszetesen mi azt szeretnnk, hogy a fordts hiba nlkl menne vgbe, gy tjkoztatnunk kell a fordtt, hogy szndkosan takarjuk el az eredeti implementcit. Ezt a new kulcsszval tehetjk meg:
public class Animal { public Animal() { } public virtual void Eat() { Console.WriteLine("Az allat eszik..."); } }

public class Dog : Animal { public Dog() { } public new void Eat() { Console.WriteLine("A kutya eszik..."); } }

Ezutn a Dog utdjai mr nem ltjk az eredeti Eat() metdust. Viszont kszthetnk bel le virtulis metdust, amelyet az utdjai mr kedvkre hasznlhatnak. Azaz, a new mdostval elltott metdus j sort kezd, amikor a fordt felpti a metdustblt, vagyis a new virtual kulcsszavakkal elltott metdus lesz az j metdussorozat gykere.

85 A kvetkez fejezet lezrt osztlyaihoz hasonlan egy metdust is deklarlhatunk lezrtknt, ekkor a leszrmazottak mr nem definilhatjk t a m kdst:
class Animal { public Animal() { } public virtual void Eat() { Console.WriteLine("Egy allat eszik..."); } } class Dog : Animal { public Dog() { } public sealed override void Eat() { Console.WriteLine("Egy kutya eszik..."); } } class Dobermann : Dog { public Dobermann() { } public override void Eat() //Ez nem fog lefordulni {
//valamit csinlunk

} }

Nem jellhetnk virtulisnak statikus, absztrakt s override al jellt tagokat (az utols kett egybknt virtulis is lesz, de ezt nem kell kln jellni).

19.4 Lezrt osztlyok


Egy osztlyt lezrhatunk, azaz megtilthatjuk, hogy j osztlyt szrmaztassunk bel le:
//Ez mr egy vgleges osztly

public sealed class Dobermann : Dog { }


//Ez nem fog lefordulni

public class MyDobermann : Dobermann { }

A struktrk (rluk ks bb) alaprtelmezetten lezrtak.

86

19.5 Absztrakt osztlyok


Egy absztrakt osztlyt nem lehet pldnyostani. A ltrehozsnak clja az, hogy kzs felletet biztostsunk a leszrmazottainak:
abstract class Animal { abstract public void Eat(); } class Dog : Animal { Animal() { } public override void Eat() { Console.WriteLine("A kutya eszik..."); } }

Lthat, hogy mind az osztly, mind a metdus absztraktknt lett deklarlva, ugyanakkor a metdus nem virtulis s nincs defincija. Egy absztrakt osztly csak a fordts kzben absztrakt, a lefordtott kdban teljesen normlis osztlyknt szerepel, virtulis metdusokkal. A fordt feladata az, hogy betartassa a r vonatkoz szablyokat. Ezek a szablyok a kvetkez ek: absztrakt osztlyt nem lehet pldnyostani abszrakt metdusnak nem lehet defincija a leszrmazottaknak definilnia kell az rkltt absztrakt metdusokat.

Absztrakt osztly tartalmazhat nem absztrakt metdusokat is, ezek pont gy viselkednek, mint a hagyomnyos nem-virtulis fggvnyek. Az rkltt absztrakt metdusokat az override kulcssz segtsgvel tudjuk definilni (hiszen virtulisak, mg ha nem is ltszik). Amennyiben egy osztlynak van legalbb egy absztrakt metdusa az osztlyt is absztraktknt kell jellni. Annak ellenre, hogy egy absztrakt osztlyt nem pldnyosthatunk mg lehet konstruktora, mgpedig azrt, hogy bellthassuk vele az adattagokat:
abstract class Animal { protected int age; public Animal(int _age) { this.age = _age; }
//Egyb metdusok...

} class Dog : Animal { public Dog(int _age) : base(_age) { }


//Egyb metdusok

87

Vajon, hogyan m kdik a kvetkez pldban a polimorfizmus elve? :


Animal[] animArray = new Animal[2]; animArray[0] = new Dog(2); animArray[1] = new Crocodile(100);

Ennek a kdnak hiba nlkl kell fordulnia, hiszen tnylegesen egyszer sem pldnyostottuk az absztrakt sosztlyt. A fordt csak azt fogja megvizsglni, hogy mi van a new opertor jobb oldaln, az alaposztly nem rdekli. Termszetesen a kvetkez esetben nem fordulna le:
animArray[0] = new Animal(2);

Az absztrakt osztlyok rendelkeznek nhny gyenge ponttal. Ha egy leszrmazottjbl szrmaztatok, akkor az j osztlynak nem kell megvalstania az absztrakt metdusokat, viszont hasznlhatja azokat:
public abstract class Animal { public Animal() { } public abstract void Eat(); } public class Dog : Animal { public Dog() { } public override void Eat() { Console.WriteLine("A kutya eszik..."); } } public class Dobermann : Dog { public Dobermann() { } } Dobermann d = new Dobermann(); d.Eat(); //Ez m kdik

88

20. Interfszek
Az interfszek hasonlak az absztrakt osztlyokhoz, abban az rtelemben, hogy meghatrozzk egy soztly viselkedst, fellett. A nagy klnbsg a kett kzt az, hogy mg el bbi eleve meghatroz egy osztlyhierarchit, egy interfsz nem kthet kzvetlenl egy osztlyhoz, mindssze el r egy mintt, amit meg kell valstania az osztlynak. Egy msik el nye az interfszek hasznlatnak, hogy mg egy osztlynak csak egy se lehet, addig brmennyi intefszt megvalsthat. Ezen fell interfszt hasznlhatunk struktrk esetben is. Interfszt a kvetkez kppen deklarlunk:
public interface IAnimal { void Eat(); }

Az interfsz nevt ltalban nagy I bet vel kezdjk. Lthat, hogy nincs definci, csak deklarci. A megvalst osztly dolga lesz majd megvalstani a tagjait. Egy interfsz a kvetkez ket tartalmazhatja: metdusok, tulajdonsgok, indexel k s esemnyek. A tagoknak nincs kln lthatsguk, ehelyett minden tagra az interfsz elrhet sge vonatkozik. A kvetkez forrskd megmutatja, hogyan valsthatjuk meg a fenti interfszt:
public interface IAnimal { void Eat(); }

public class Dog : IAnimal { public Dog() { } public void Eat() { Console.WriteLine("A kutya eszik..."); } }

Fontos, hogy amennyiben egy msik osztlybl is szrmaztatunk, akkor a felsorolsnl az sosztly nevt kell el revenni, utna jnnek az interfszek:
public interface IFace1 { } public interface IFace2 { } class Base { } class Derived : Base, IFace1, IFace2 {
/*...*/

89 Egy interfszt szrmaztathatunk ms interfszekb l:


public interface IAnimal { void Eat(); } public interface IDog : IAnimal { void Vau(); } public class Dog : IAnimal, IDog { public Dog() { } public void Eat() { Console.WriteLine("A kutya eszik..."); } public void Vau() { Console.WriteLine("Vau-vau..."); } }

Egy adott interfszt megvalst objektumot implicit tkonvertlhatjuk az interfsz tpusra:


public IEnumerable {
/*...*/

} class EnumClass : IEnumerable {


/*...*/

} IEnumerable ie = new EnumClass(); //Ez m kdik

Az is s as opertorokkal megtudhatjuk, hogy egy adott osztly megvalst e egy interfszt:


public interface IMyIf { } class MyIClass : IMyIf { } MyIClass mic = new MyIClass();

90
if(mic is IMyIf) { Console.WriteLine("Az osztaly megvalositja az interfeszt..."); } IMyIf test = mic as IMyIf; if(test != null) { Console.WriteLine("Az osztaly megvalositja az interfeszt..."); }

Az is hasznlata egyrtelm , az as pedig null t ad vissza, ha a konverzi sikertelen volt.

20.1 Explicit interfszimplementci


Ha tbb interfszt is implementlunk, az nvtkzshez is vezethet. Ennek kikszblsre explicit mdon megadhatjuk a megvalstani kvnt funkcit:
public interface IOne { void Method(); } public interface ITwo { void Method(); } public class MyClass : IOne, ITwo { IOne.Method() { } ITwo.Method() { } }

A kt Method() szignatrja megegyezik, gy valahogy meg kell ket klnbztetnnk. Ekkor azonban a fggvnyhvsnl is problmba tkznk, ezrt konverzit kell vgrehajtanunk:
MyClass mc = new MyClass(); ((IOne)mc).Method(); ((ITwo)mc).Method();

Egy msik lehet sg:


MyClass mc = new MyClass(); IOne iface = (IOne)mc; iface.Method();

Ekkor explicit konverzit hajtunk vgre s a megfelel metdust.

interfszen hvjuk meg a

91

20.2 Virtulis tagok


Egy interfsz tagjai alaprtelmezs szerint lezrtak, de a megvalstsnl jellhetjk ket virtulisnak. Ezutn az osztly leszrmazottjai tetszs szerint mdosthatjk a defincit, a mr ismert override kulcsszval:
public interface IAnimal { void Eat(); }

public class Dog : IAnimal { public Dog() { } public virtual void Eat() { Console.WriteLine("A kutya eszik..."); } } public class Dobermann : Dog { public Dobermann() { } public override void Eat() { Console.WriteLine("A dobermann eszik..."); } }

Egy leszrmazott jraimplementlhatja az adott interfszt, amennyiben nemcsak az snl, de az utdnl is jelljk a megvalstst:
public class Dobermann : Dog, IAnimal { public Dobermann() { } public new void Eat() { Console.WriteLine("A dobermann sokat eszik..."); } }

Ez esetben nylvn hasznlnuk kell a new kulcsszt annak jellsre, hogy eltakarjuk az s megvalstst.

92

20.3 Gyakorlati plda 1.


Korbban mr tallkoztunk a foreach ciklussal, s mr tudjuk, hogy csak olyan osztlyokon kpes vgigiterlni, amelyek megvalstjk az IEnumerator s IEnumerable interfszeket. Mindkett a System.Collections nvtrben tallhat. Els knt nzzk az IEnumerable interfszt:
public interface IEnumerable { IEnumerator GetEnumerator(); }

Ez a foreach nek fogja szolgltatni a megfelel felletet, ugyanis a ciklus meghvja a metdust, s annak vissza kell adnia az osztlyt IEnumerator knt (ld. implicit konverzi). Ezrt kell megvalstani egyttal az IEnumerator interfszt is, ami gy nz ki:
public interface IEnumerator { bool MoveNext(); void Reset(); object Current { get; } }

A MoveNext() a kvetkez elemre mozgatja a mutatt, ha tudja, de ha a vgre rt akkor false rtkkel tr vissza. A Reset() alaprtelmezsre lltja a mutatt, azaz -1 re. Vgl a Current (read-only) tulajdonsg az aktulis poziciban lv elemet adja vissza. Ennek object tpussal kell visszatrnie, hiszen minden tpusra m kdnie kell (ltezik generikus vltozata is, de err l ks bb). Hasznljuk az Animal osztlyunk egy kiss mdostott vltozatt:
public class Animal { private string name; public Animal(string _name) { this.name = _name; } public Name { get { return this.name; } } }

Most ksztsnk egy osztlyt, amelyen megvalstjuk a kt interfszt, s ami tartalmaz egy Animal objektumokbl ll listt:

93
public class AnimalContainer : IEnumerable, IEnumerator { private ArrayList container = new ArrayList(); private int currPosition = -1; public AnimalContainer() { container.Add(new Animal("Rex")); container.Add(new Animal("Rin-Tin-Tin")); container.Add(new Animal("Cheetah")); } }

Ez persze mg nem az egsz osztly, felvettnk egy ArrayList et, amiben eltroljuk az objektumokat, illetve deklarltunk egy egsz szmot, ami az aktulis pozict trolja el s kezd rtk l -1 et adtunk (ld. Reset()). A konstruktorban lthat, hogy rgtn a new al hoztunk ltre j objektumokat, nem pedig kln tettk ezt. Ksztsk el az IEnumerator ltal ignyelt metdusokat:
public bool MoveNext() { return (++position < container.Count) } public object Current { get { return container[currPosition]; } } public void Reset() { currPosition = -1; }

Vgl az IEnumerable interfszt valstjuk meg:


public IEnumerator GetEnumerator() { return (IEnumerator)this; }

Ezutn hasznlhatjuk is az osztlyt:


AnimalContainer ac = new AnimalContainer(); foreach(Animal a in ac) { Console.WriteLine(a.Name); }

94

20.4 Gyakorlati plda 2.


A msodik gyakorlati pldnkban az IComparable interfszt fogjuk megvalstani, amelyre gyakran van szksgnk. Ez az interfsz ltalban olyan adatszerkezeteknl kvetelmny amelyek az elemeiken megvalstanak valamilyen rendezst. A generikus List tpusmak is van rendez metdusa, amely ezzel a metdusal dolgozik. Az IComparable egyetlen metdussal a CompareTo() val rendelkezik, amely egy object tpust kap paramterl:
class ComparableClass : IComparable { int value; public ComparableClass(int val) { this.value = val; } public int Value { get { return value; } } public int CompareTo(object o) { if(o is ComparableClass) { ComparableClass c = (ComparableClass)o; return value.CompareTo(c.Value); } else throw(new Exception("Nem megfelelo objektum...")); } }

Az osztlyban a beptett tpusok CompareTo() metdust hasznltuk, hiszen k mind megvalstjk ezt az interfszt. Ez a metdus -1 et ad vissza ha a hv fl kisebb, 0 t, ha egyenl s 1 et ha nagyobb. A hasznlata:
List<ComparableClass> list = new List<ComparableClass>(); Random r = new Random(); for(int i = 0;i < 10;++i) { list.Add(new ComparableClass(r.Next(1000))); } foreach(ComparableClass c in list) { Console.WriteLine(c.Value); }

Console.WriteLine("A rendezett lista:"); list.Sort();

95

foreach(ComparableClass c in list) { Console.WriteLine(c.Value); }

Hasonl feladatot lt el, de jval rugalmasabb az IComparer interfsz. A List rendezsnl megadhatunk egy sszehasonlt osztlyt is, amely megvalstja az IComparer interfszt. Most is csak egy metdust kell elksztennk, ez a Compare() amely kt object tpust vr paramtereknt:
class ComparableClassComparer : IComparer { public int Compare(object x, object y) { if(x is ComparableClass && y is ComparableClass) { ComparableClass _x = (ComparableClass)x; ComparableClass _y = (ComparableClass)y; return _x.CompareTo(_y); } else throw(new Exception("Nem megfelelo parameter..."); } }

Ezutn a kvetkez kppen rendezhetjk a listt:


list.Sort(new ComparableClassComparer());

Az IComparer el nye, hogy nem kt dik szorosan az osztlyhoz (akr anlkl is megrhatjuk, hogy ismernnk a bels szerkezett), gy tbbfle megvalsts is lehetsges.

96

21. Opertor tlterhels


Nylvn szeretnnk, hogy az ltalunk ksztett tpusok hasonl funkcionalitssal rendelkezzenek mint a beptett tpusok (int, string, stb). Vegyk pl. azt a pldt, amikor egy mtrix tpust valstunk meg. J lenne, ha az sszeads, kivons, szorzs, stb. m veleteket gy tudnnk vgrehajtani, mint egy egsz szm esetben, nem pedig metdushvsokkal. Szerencsre a C# ezt is lehet v teszi szmunkra, ugyanis engedi az opertortlterhelst, vagyis egy adott opertort tetszs szerinti funkcival ruhzhatunk fel az osztlyunkra vonatkoztatva.
Matrix m1 = new Matrix(); Matrix m2 = new Matrix();
//ehelyett

Matrix m3 = m1.Add(m2);
//rhatjuk ezt

Matrix m4 = m1 + m2;

A tlterhelhet opertorok listja: +(unris) -% >> >= -(unris) + & == <= ! | != ~ * ^ > ++ / << <

A C# nyelvben a paramterek statikus metdusok, paramtereik az operandusok, visszatrsi rtkk pedig az eredmny. Egy egyszer plda:
class MyInt { int value; public MyInt(int _value) { this.value = _value; } public int Value { get { return value; } } public static MyInt operator+(MyInt lhs, MyInt rhs) { return new MyInt(lhs.Value + rhs.Value); } }

97 A + opertort m kdst fogalmaztuk t. A paramterek (operandusok) nevei konvenci szerint lhs (left-hand-side) s rhs (right-hand-side), utalva a jobb s baloldali operandusra. Teht ha a kvetkez t rom:
MyInt m1 = new MyInt(10); MyInt m2 = new MyInt(11); MyInt sum = m1 + m2;

Mivel definiltunk az osztlyunkon egy sajt opertort gy a fordt tudni fogja, hogy azt hasznlja s talaktja az utols sort a kvetkez re:
MyOnt sum = MyInt.operator+(m1, m2);

A kvetkez kben megnznk nhny opertort, vgl pedig megvalstjuk a mtrix osztlyt s opertorait.

21.1 Egyenl sg opertorok


A C# nyelv megfogalmaz nhny szablyt az opertortlterhelssel kapcsolatban. Ezek egyike az, hogy ha tlterheljk az egyenl sg opertort (==) akkor definilnunk kell a nem-egyenl (!=) opertort is:
class MyEqual { int value; public MyEqual(int _value) { this.value = _value; } public int Value { get { return value; } } static public bool operator==(MyEqual lhs, MyEqual rhs) { return (lhs.Value == rhs.Value); } static public bool operator!=(MyEqual lhs, MyEqual rhs) { return !(lhs == rhs); } }

A nem-egyenl opertor esetben a sajt egyenl sg opertort hasznltuk fel (a megvalsts elve nem felttlenl vilgos, els knt megvizsgljuk, hogy a kt elem egyenl e, de mi a nem-egyenl sgre vagyunk kvncsiak, ezrt tagadjuk az eredmnyt, ami pontosan ezt a vlaszt adja meg).

98 Ezekhez az opertorokhoz tartozik az object tpustl rklt virtulis Equals() metdus is, ami a CLS kompatibilitst hvatott meg rizni, err l ks bb mg lesz sz. A fenti esetben ezt a metdust is illik megvalstani. Az Equals azonban egy kicsit klnbzik, egyetlen object tpus paramtert vr, ezrt meg kell majd gy zdnnk arrl, hogy valban a sajt objektumunkkal van e dolgunk:
public override bool Equals(object o) { if(!(o is MyEqual)) { return false; } return this == (MyEqual)o; }

Az is opertorral ellen rizzk a futsidej tpust. Mivel ez egy pldny tag ezrt a this t hasznljuk az objektum jellsre, amin meghvtuk a metdust. Az Equals() mellett illik megvalstani a szintn az object t l rkltt GetHashCode() metdust is, gy az osztly hasznlhat lesz gy jtemnyekkel s a HashTable tpussal is. A legegyszer bb implementci visszad egy szmot az adattag(ok)bl szmolva (pl.: hatvnyozs, biteltols, stb):
class MyHash { int value; public MyHash(int _value) { this.value = _value; } public override int GetHashCode() { return value; } }

21.2 Relcis opertorok


Hasonlan a logikai opertorokhoz a relcis opertorokat is csak prban lehet elkszteni, vagyis (<, >) s (<=, >=). Ebben az esetben az IComparable s IComparable<T> interfszek megvalstsa is szksges lehet a klnbz gy jtemnyekkel val egyttm kds rdekben. Ezekr l az el z , interfszekkel foglalkoz fejezet nyjt b vebb informcit.

21.3 Konverzis opertorok


A C# a sz kebbr l tgabbra konverzikat implicit mdon (azaz klnsebb jells nlkl), mg a tgabbrl sz kebbre kovertlst explicite (ezt jellnnk kell) vgzi. Termszetesen szeretnnk, ha a sajt tpusunk ilyesmire is kpes legyen, s bizony

99 erre van opertor, amit tlterhelhetnk. Ezeknl az opertoroknl az implicit illetve explicit kulcsszavakkal fogjuk jellni a konverzi tpust:
class MyConversion { int value; public MyConversion(int _value) { this.intValue = _value; } public int Value { get { return value; } } public static implicit operator MyConversion(int rhs) { return new MyConversion(rhs); } public static explicit operator MyConversion(long rhs) { return new MyConversion((int)rhs); } }

Ezt most gy hasznlhatjuk:


int x = 10; long y = 12; MyConversion mc1 = x; //implicit konverzi MyConversion mc2 = (MyConversion)y; //explicit konverzi

Fontos, hogy a konverzis opertorok mindig statikusak.

21.4 Kompatibilits ms nyelvekkel


Mivel nem minden nyelv teszi lehet v az opertortlterhelst ezrt a CLS javasolja, hogy ksztsk el a hagyomnyos vltozatot is:
class Operators { private int value; public Operators(int _value) { this.value = _value; } public int Value { get { return value; } }

100
static public Operators operator+(Operators lhs, Operators rhs) { return new Operators(lhs.Value + rhs.Value); } static public Operators Add(Operators lhs, Operators rhs) { return new Operators(lhs.Value + rhs.Value); } }

21.5 Gyakorlati plda


Most pedig elksztjk a mtrix tpust:
class Matrix { int[,] matrix; public Matrix(int n, int m) { matrix = new int[n, m]; } public int N { get { return matrix.GetLength(0); } } public int M { get { return matrix.GetLength(1); } } public int this[int idxn, int idxm] { get { return matrix[idxn, idxm]; } set { matrix[idxn, idxm] = value; } } static public Matrix operator+(Matrix lhs, Matrix rhs) { if(lhs.N != rhs.N || lhs.M != rhs.M) return null; Matrix result = new Matrix(lhs.N, lhs.M); for(int i = 0;i < lhs.N;++i) { for(int j = 0;j < lhs.M;++j) { result[i, j] = lhs[i, j] + rhs[i, j]; } } return result; } }

101

Mtrixokat gy adunk ssze, hogy az azonos indexeken lv rtkeket szeadjuk: 123 145 1+1 2+4 3+5 456 + 532 = (stb) 789 211 Az sszeads m veletet csakis azonos nagysg dimenzik mellet lehet elvgezni (3x3 as mtrixhoz nem lehet hozzadni egy 4x4 eset). Ezt ellen riztk is az opertor megvalstsnl. Most csak az sszeads m veletet valstottuk meg, a tbbi a kedves olvasra vr. Plusz feladatknt indexellen rzst is lehet vgezni az indexel nl.

102

22. Struktrk
A struktrk hasonlak az osztlyokhoz, viszont van nhny klnbsg: Egy osztly referenciatpus mg egy struktra rtktpus Struktrbl nem lehet szrmaztatni ill. egy struktra nem szrmazhat ms struktrbl (kivtel a System.Object). Egy struktrnak nem lehet paramter nlkli (default) konstruktora Nem hasznlhatnak finalizer t.

Minden struktra kzvetlenl a System.ValueType tpusbl szrmazik, amely pedig a System.Object b l. Struktrt ltalban akkor hasznlunk, ha adatok egyszer sszesgvel kell dolgoznunk, de nincs szksgnk egy osztly minden szolgltatsra. Struktrk hasznlatnak van mg egy el nye, ugyanis pldnyostsnl nem kell helyet foglalni a halomban, gy sok objektum esetben hatkonyabb a hasznlata. Minden struktra alaprtelmezetten rendelkezik paramternlkli konstruktorral, amelyet nem rejthetnk el. Ez a konstruktor a struktra minden mez jt nullrtkkel tlti fel:
struct MyStruct { public int x; } MyStruct ms = new MyStruct(); // x == 0 Console.WriteLine(ms.x);

Nem ktelez hasznlni a new t, ekkor automatikusan az alaprtelmezett konstruktor hvdik meg.
MyStruct x; //ez is m kdik

Kszthetnk sajt konstruktort, de ekkor minden mez gondoskodnunk kell. Egy struktra mez it nem inicializlhatjuk:
struct MyStruct { int x = 10; //ez hiba int y; public MyStruct(int _x, int _y) { x = _x; //hiba, y nem kap rtket } }

rtkadsrl

Egy struktra tartalmazhat referenciatpust, ekkor a verembe (az rtktpusok memriahelyre) egy referencia kerl amely a referenciatpusra hivatkozik a halomban. Amennyire lehet kerljk ezt a megoldst, struktrk esetn csakis rtktpusokkal dolgozzunk.

103

23. Kivtelkezels
Nylvn vannak olyan esetek, amikor az alkalmazsunk nem gy fog m kdni, ahogy elkpzeltk. Az ilyen abnormlis m kds kezelsre talltk ki a kivtelkezelst. Amikor az alkalmazsunk rossz llapotba kerl, akkor egy n. kivtelt fog dobni, ilyennel mr tallkoztunk a tmbknl, amikor tlindexeltnk:
using System; class Program { static public void Main() { int[] x = new int[2]; x[2] = 10; Console.ReadKey(); } }

Itt az utols rvnyes index az 1 lenne, gy kivtelt kapunk, mgpedig egy System.IndexOutOfRangeException t. Ezutn a program lell. Termszetesen mi azt szeretnnk, hogy valahogy kijavthassuk ezt a hibt, ezrt el fogjuk kapni a kivtelt. Ehhez a m velethez hrom dologra van szksgnk: kijellni azt a programrszt ami dobhat kivtelt, elkapni azt s vgl kezeljk a hibt:
using System; class Program { static public void Main() { int[] x = new int[2]; try { x[2] = 10; } catch(System.IndexOutOfRangeException e) { Console.WriteLine("A hiba: {0}", e.Message); } Console.ReadKey(); } }

A try blokk jelli ki a lehetsges hibaforrst, a catch pedig elkapja a megfelel kivtelt (arra figyeljnk, hogy ezek is blokkok, azaz a blokkon bell deklarlt vltozk a blokkon kv l nem lthatak). A fenti programra a kvetkez lesz a kimenet:
A hiba: Index was outside the bounds of the array.

Kivtelt a throw utastssal dobhatunk:

104
using System; class Program { static public void Main() { try { throw(new System.Exception("Exception...")); } catch(System.Exception e) { Console.WriteLine("A hiba: {0}", e.Message); } } }

A C++ -tl eltr en itt pldnyostanunk kell a kivtelt. A catch nek nem ktelez megadni a kivtel tpust, ekkor minden kivtelt elkap:
try { throw(new System.Exception("Exception...")); } catch { Console.WriteLine("Kivtel trtnt"); }

23.1 Kivtel hierarchia


Amikor kivtel dobdik, akkor a vezrlst az els alkalmas catch blokk veszi t. Az sszes kivtel ugyanis a System.Exception osztlybl szrmazik, gy ha ezt adjuk meg a catch nl, akkor az sszes lehetsges kivtelt el fogjuk kapni vele:
catch(System.Exception e) { Console.WriteLine("A hiba: {0}", e.Message); }

Egyszerre tbb catch is llhat egyms utn, de ha van olyan amelyik az s kivtelt kapja el, akkor a program csak akkor fog lefordulni, ha az az utols helyen ll, hiszen az mindent elkapna.
catch(System.IndexOutOfRangeException e) { Console.WriteLine("A hiba: {0}", e.Message); } catch(System.Exception e) { Console.WriteLine("A hiba: {0}", e.Message); }

105

23.2 Sajt kivtel ksztse


Mi magunk is csinlhatunk kivtelt, a System.Exception bl szrmaztatva:
public class MyException : System.Exception { public MyException() { } public MyException(string message) : base(message) { } public MyException(string message, Exception inner) : base(message, inner) { } }

Az utols konstruktorrl hamarosan lesz sz. Most gy hasznlhatjuk:


try { throw(new MyException("Sajat kivetel..."); } catch(MyException e) { Console.WriteLine("A hiba: {0}", e.Message); }

23.3 Kivtelek tovbbadsa


Egy kivtelt az elkapsa utn ismt eldobhatunk. Ez hasznos olyan esetekben, amikor feljegyzst akarunk kszteni illetve, ha egy specifikusabb kivtelkezel nek akarjuk tadni a kivtelt:
try { } catch(System.ArgumentException e) { throw; //tovbbadjuk throw(new System.ArgumentNullException()); //vagy egy jat dobunk }

Termszetesen a fenti pldban csak az egyik throw szerepelhetne leglisan, ebben a formban nem fog lefordulni.

23.4 Finally blokk


A kivtelkezels egy problmja, hogy a kivtel keletkezse utn az ppen vgrehajtott programrsz futsa megszakad, gy el fordulhat, hogy nem szabadulnak fel id ben az er forrsok (megnyitott file, hlzati kapcsolat, stb), illetve objektumok olyan formban maradnak meg a memriban, amely hibt okozhat.

106 Megoldst a finally blokk hasznlata jelent, amely fggetlenl attl, hogy trtnt e kivtel mindig vgrehajtdik:
int x = 10; try { Console.WriteLine("X erteke a kivetel elott: {0}", x); throw(new Exception()); } catch(Exception) { Console.WriteLine("Kivetel keletkezett..."); } finally { x = 11; } Console.WriteLine("X erteke a kivetel utan: {0}", x);

A kimenet a kvetkez lesz:


X erteke a kivetel elott: 10 Kivetel keletkezett... X erteke a kivetel utan: 11

Valdi er forrsok kezelsekor knyelmesebb a using blokk hasznlata:


using(StreamReader reader = File.OpenText("text.txt")) { }

Ez pontosan megegyezik ezzel:


StreamReader reader = File.OpenText("text.txt"); try { } finally { if(reader != null) { ((IDisposable)reader).Dispose(); } }

Filekezelssel egy ks bbi fejezet foglalkozik majd.

107

24. Generikusok
Az objektum orientlt programozs egyik alapkve a kdjrafelhasznls, vagyis, hogy egy adott kdrszletet elg ltalnosra rjunk meg ahhoz, hogy minl tbbszr felhasznlhassuk. Ennek megvalstsra kt eszkz ll rendelkezsnkre, az egyik az rkl ds, a msik pedig jelen fejezet trgya a generikus tpusok, metdusok.

24.1 Generikus metdusok


Vegyk a kvetkez metdust:
static public void Swap(ref int x, ref int y) { int tmp = x; x = y; y = tmp; }

Ha szeretnnk, hogy ez a metdus ms tpusokkal is m kdjn, akkor bizony sokat kell gpelnnk. Kivve, ha runk egy generikus metdust:
static { T x y } public void Swap<T>(ref T x, ref T y) tmp = x; = y; = tmp;

A T bet fogja jelkpezni az aktulis tpust (lehet ms nevet is adni neki, eredetileg a Template szbl jtt), ezt generikus paramternek hvjk. Generikus paramtere csakis osztlynak vagy metdusnak lehet s ebb l tbbet is hasznlhatnak. Ezutn a metdust a hagyomnyos ton hasznlhatjuk, a fordt felismeri, hogy melyik tpust hasznljuk (ezt megadhatjuk mi magunk is explicite):
int x = 10; int y = 8; Swap(ref x, ref y);
//vagy

Swap<int>(ref x, ref y);

A C# generikusai hasonltanak a C++ sablonjaira, de annl kevsb hatkonyabbak, cserbe sokkal biztonsgosabb a hasznlatuk. Kt fontos klnbsg van a kett kzt, mg a C++ fordtsi id ben kszti el a specializlt metdusokat/osztlyokat, addig a C# ezt a m veletet futsi id ben vgzi el. A msik eltrs az els b l kvetkezik, mivel a C++ fordtskor ki tudja sz rni azokat az eseteket, amelyek hibsak, pl. sszeadunk kt tpust a sablonmetdusban, amelyeken nincs rtelmezve sszeads. A C# ezzel szemben knytelen az ilyen problmkat megel zni, a fordt csakis olyan m veletek elvgzst fogja engedlyezni, amelyek mindenkppen m kdni fognak. A kvetkez plda nem fog lefordulni:

108
static public T Sum<T>(T x, T y) { return x + y; }

Fordtskor csak azt tudja ellen rizni, hogy ltez tpust adtunk e meg, gy nem tudhatja a fordt, hogy sikeres lesz e a vgrehajts, ezrt a fenti kd az sszeads miatt (nem felttlenl valstja meg minden tpus) rossz.

24.2 Generikus osztlyok


Kpzeljk el, hogy azt a feladatot kaptunk, hogy ksztsnk egy verem tpust, amely brmely tpusra alkalmazhat. Azt is kpzeljk el, hogy mg nem hallottunk generikusokrl. gy a legkzenfekv bb megolds egy object tpus tmb lesz:
class Stack { object[] t; int pointer; readonly int size; public Stack(int capacity) { t = new object[capacity]; size = capacity; pointer = 0; } public void Push(object _in) { if(pointer >= size) { throw(new StackOverflowException("Tele van...")); } t[pointer] = _in; ++pointer; } public object Pop() { --pointer; if(pointer >= 0) { return t[pointer]; } pointer = 0; throw(new InvalidOperationException("Ures...")); } }

Ezt most a kvetkez kppen hasznlhatjuk:

109
Stack s = new Stack(10); for(int i = 0;i < 10;++i) { s.Push(i); } for(int i = 0;i < 10;++i) { Console.WriteLine(Console.WriteLine(int.Parse(s.Pop())); }

M kdni m kdik, de se nem hatkony se nem knyelmes. A hatkonysg az rtk/referenciatpusok miatt cskken jelent sen (ld. boxing/unboxing), a knyelem pedig amiatt, hogy mindig figyelni kell pp milyen tpussal dolgozunk, nehogy olyan kasztolssal ljnk ami kivtelt dob. Ezeket a problmkat knnyen kikszblhetjk, ha genrikus osztlyt ksztnk:
class Stack<T> { T[] t; int pointer; readonly int size; public Stack(int capacity) { t = new T[capacity]; size = capacity; pointer = 0; } public void Push(T _in) { if(pointer >= size) { throw(new StackOverflowException("Tele van...")); } t[pointer] = _in; ++pointer; } public object Pop() { --pointer; if(pointer >= 0) { return t[pointer]; } pointer = 0; throw(new InvalidOperationException("Ures...")); } }

Ezutn akrmelyik tpuson knnyen hasznlhatjuk:

110

Stack<int> s = new Stack<int>(10); Stack<string> c = new Stack<string>(10); for(int i = 0;i < 10;++i) { s.Push(i); c.Push(i.ToString()); } for(int i = 0;i < 10;++i) { Console.WriteLine("{0}, {1}", s.Pop(), c.Pop()); }

A generikus tpusok nem kovarinsak, azaz egy generikus pldnyt nem kasztolhatunk olyan tpusra, amelyre normlis esetben igen:
class Base { } class Derived : Base { } Stack<Derived> s = new Stack<Derived>(10); Stack<Base> b = s; //ez nem m kdik

24.3 Generikus megszortsok


Alaprtelmezetten egy generikus paramter brmely tpust jelkpezheti. A deklarcinl azonban kikthetnk megszortsokat a paramterre. A megszortsokat a where kulcsszval vezetjk be:
where where where where where where T T T T T T : : : : : : alaposztly interfsz osztly struktra new() //alaprtelmezett konstruktor U

Egy plda:
public interface IFace { } public class BaseClass { } class NewClass<T> where T : BaseClass, IFace, new() {}

111 A NewClass osztly csak akkor pldnyosthat, ha a megadott tpusa a BaseClass osztlybl szrmazik, megvalstja az IFace interfszt s rendelkezik alaprtelmezett konstruktorral:
class GoodClass : BaseClass, IFace { public GoodClass() { } } NewClass<GoodClass> nc = new NewClass<GoodClass>();

Az osztly s struktra megszorts osztlynak/struktrnak kell lennie.

azt

jelenti,

hogy

paramternek

24.4 rkl ds
Generikus osztlybl szrmaztathatunk is, ekkor vagy az sosztly egy specializlt vltozatbl szrmaztatunk, vagy a nyers generikus osztlybl:
class Base<T> { } class Derived<T> : Base<T> { }
//vagy

class IntDerived : Base<int> { }

24.5 Statikus tagok


Generikus tpusok esetben minden tpushoz kln statikus tag tartozik:
class MyClass<T> { public static int data; } MyClass<int>.data = 10; MyClass<string>.data = 20; Console.WriteLine(MyClass<int>.data); //10 Console.WriteLine(MyClass<string>.data); //20

112

24.6 Generikus gy jtemnyek


A C# 2.0 bevezetett nhny hasznos generikus adatszerkezetet, tbbek kzt listt s vermet. Ezeket a tpusokat a tmbkhz hasonlan hasznlhatjuk. A kvetkez kben megviszglunk ezek kzl nhnyat. Ezek a szerkezetek a System.Collections.Generic nvtrben tallhatak:
List<int> list = new List<int>(); for(int i = 0;i < 10;++i) { list.Add(i); } foreach(int i in list) { Console.WriteLine(i); }

Az Add() metdus a lista vghez adja hozz a pramterknt megadott elemet, hasonlan az ArrayList hez (tulajdonkppen a List<> az ArrayList generikus vltozata). Hasznlhatjuk rajta az indexel opertort is. A SortedList kulcs rtk prokat trol el s a kulcs alapjn rendezi is ket:
SortedList<string, int> slist = new SortedList<string, int>(); slist.Add("elso", 1); slist.Add("masodik", 2); slist.Add("harmadik", 3);

A lista elemei tulajdonkppen nem a megadott rtkek, hanem a kulcs rtk prokat reprezentl KeyValuePair<> objektumok. A lista elemeinek elrshez is hasznlhatjuk ezeket:
foreach(KeyValuePair<string, int> kv in slist) { Console.WriteLine("Kulcs: {0}, Ertek: {1}", kv.Key, kv.Value); }

A lista kulcsai csakis olyan tpusok lehetnek, amelyek megvalstjk az IComparable interfszt, hiszen ez alapjn trtnik a rendezs. Ha ez nem igaz, akkor mi magunk is definilhatunk ilyet, rszletekrt ld. az Interfszek fejezetet. A SortedList rendezetlen prja a Dictionary<>:
Dictionary<string, int> dict = new Dictionary<string, int>(); dict.Add("elso", 1); dict.Add("masodik", 2); foreach(KeyValuePair<string, int> kv in dict) { Console.WriteLine("Key: {0}, Value: {1}", kv.Key, kv.Value); }

113

24.7 Generikus interfszek


A letbb hagyomnyos interfsznek ltezik generikus vltozata is. Pldul az IEnumerable s IEnumerator is ilyen:
class MyClass<T> : IEnumerable<T>, IEnumerator<T> { }

Ekkor a megvalsts teljesen ugyangy m kdik mint a hagyomnyos esetben, csak pp hasznlnunk kell a generikus paramter(eke)t. A generikus adatszerkezetek a generikus ICollection, IList s IDictionary interfszeken alapulnak, gy ezeket megvalstva akr mi magunk is ltrehozhatunk ilyet.

114

25. Delegate -ek


A delegate ek olyan tpusok, amelyek egy metdusra hivatkoznak. Egy delegate deklarcijnl megadjuk, hogy milyen szignatrval rendelkez metdusokra mutathat:
delegate int MyDelegate(int x);

Ez a delegate olyan metdusra mutathat amelynek visszatrsi rtke int tpus s egyetlen int paramtere van:
static public int GoodMethod(int x) { return (x * x); }

A hasznlata:
MyDelegate delgt = GoodMethod; int result = delgt(10); //result == 100

A delegate pldnyostst a hagyomnyos ton is megtehetjk:


MyDelegate delgt = new MyDelegate(GoodMethod);

A delegate ek legnagyobb haszna, hogy nem kell el re megadott metdusokat hasznlnunk, ehelyett dinamikusan adhatjuk meg az elvgzend m veletet:
class ArrayTransform { public delegate int Transformer(int _input); int[] array; public ArrayTransform(int length) { array = new int[length]; Random r = new Random(); for(int i = 0;i < length;++i) { array[i] = r.Next(1000); } } public void Transform(Transformer t) { for(int i = 0;i < array.Length;++i) { array[i] = t(array[i]); } }

115
public void Print() { foreach(int i in array) { Console.Write("{0}, ", i); } Console.WriteLine(); } }

A Transform() metdus egy delegate et kap paramterl, ami elvgzi a vltoztatsokat a tmbn. Pl.:
class Program { static public int Square(int _in) { return (_in * _in); } static public void Main() { ArrayTransform at = new ArrayTransform(10); at.Print(); at.Transform(Square); at.Print(); Console.ReadKey(); } }

Kt delegate nem egyenl , mg akkor sem ha a szignatrjuk megegyezik:


delegate void DelOne(); delegate void DelTwo(); void Method() { } DelOne dlo = Method; DelTwo dlt = dlo; //hiba

25.1 Tbbszrs delegate -ek


Egy delegate nem csak egy metdusra hivatkozhat:
delegate void MyDelegate(); void MethodOne() { Console.Write("Method One"); }

116
void MethodTwo() { Console.Write("Method Two"); } MyDelegate d = MethodOne; d += MethodTwo; d();

Most d hvsakor mindkt metdus meghvdik. Egy delegate b l el is vehetnk metdust:


d -= MethodOne();

25.2 Paramter s visszatrsi rtk


Egy delegate hasznlhat generikus paramtereket is:
delegate T GenericDelegate<T>(T param); int Square(int _in) { return (_in * _in); } GenericDelegate<int> gd = Square;

Egy delegate nek tadott metdus paramterei lehetnek olyan tpusok, amelyek az eredeti paramternl ltalnosabbak:
class MyClass { } delegate void MyDelegate(MyClass mc); void Method(object i) { } MyDelegate md = Method();

Ez az n. kontravarins (contravariant) viselkeds. Ennek a fordtottja igaz a visszatrsi rtkre, azaz az tadott metdus visszatrsi rtke lehet specifikusabb az eredetinl:

class Base { } class Derived { } delegate Base MyDelegate(); Derived Method() { } MyDelegate md = Method();

117 Ezt kovarins (covariant) viselkedsnek nevezzk.

25.3 Nvtelen metdusok


Egy delegate nek nem ktelez ltez metdust megadnunk, akr explicit mdon is megadhatjuk azt:
delegate int MyDelegate(int x); MyDelegate md = delegate(int x) { return (x * x); };

118

26. Esemnyek
Egy osztly esemnyeket hasznlhat, hogy rtestsen ms osztlyokat, hogy valami trtnt (felhasznli aktivits, stb). Gyakran tallkozunk majd esemnyekkel a grafikus fellet alkalmazsokkal foglalkoz fejezetekben. Egy esemny egy olyan metdust (vagy metdusokat) fog meghvni, amelyekre egy delegate hivatkozik. Ezeket a metdusokat esemnykezel knek nevezzk. Az esemnyekhez rendelt delegate eknek konvenci szerint (ett l eltrhetnk, de a Framework esemnyei mind ilyenek) kt paramtere van, az els az az objektum, amely kivltotta az esemnyt, a msodik pedig az esemnyhez kapcsold informcik. A msodik paramter csakis olyan tpus lehet, amely az EventArgs osztlybl szrmazik. A kvetkez pldban egy listhoz kapcsolunk esemnyt, amely akkor hajtdik vgre, amikor hozzadunk a listhoz egy elemet:
using System; using System.Collections.Generic; public delegate void ChangedEventHandler(object sender, EventArgs e); class MyListWithEvent { private List<int> list = null;

public event ChangedEventHandler Changed; public MyListWithEvent() { list = new List<int>(); } public void Add(int value) { list.Add(value); OnChanged(EventArgs.Empty); } protected virtual void OnChanged(EventArgs e) { if(Changed != null) { Changed(this, e); } } }

class ListListener { private MyListWithEvent list = null; public ListListener(MyListWithEvent l)

119
{ list = l; list.Changed += new ChangedEventHandler(ListChanged); } public void Add(int value) { list.Add(value); } private void ListChanged(object sender, EventArgs e) { Console.WriteLine("A lista megvltozott..."); } } class Program { static public void Main() { MyListWithEvent list = new MyListWithEvent(); ListListener llist = new ListListener(list); llist.Add(10); Console.ReadKey(); } }

Az esemnyek segtsgvel megvalsthatjuk (s a .Net meg is valstja) az Observer (Figyel ) tervezsi mintt, ennek lnyege nagy vonalakban, hogy n. kliensek felratkozhatnak a kiszolglra, illetve annak egyes esemnyeire. Amikor a kiszolgln trtnik valami, amely megvltoztatja az llapott, akkor rtesti a klienseit, akik az rkez informcikat felhasznljk a sajt adataik mdostsra. Tervezsi mintkrl a kvetkez oldalon olvashatunk tbbet: http://www.dofactory.com/Patterns/Patterns.aspx#list

26.1 Gyakorl feladatok


1. Ksztsk el a h mr osztlyt! Tartalmazzon egy esemnyt, amely egy bizonyos h mrsklet alatt vagy fltt riaszt (ezt pl. egy ciklussal tesztelhetjk, vletlenszmokat megadva az aktulis h mrskletnek). 2. Ksztsnk t zsdeszimultort! Hozzunk ltre kliens osztlyokat is, amelyek a t zsdt jelkpez osztly egy adott esemnyre irattkozhatnak fel (vagyis bizonyos rszvnyek rfolyamt figyelik).

120

27. Lambda kifejezsek


A C# 3.0 bevezeti a lambda kifejezseket. Egy lambda kifejezs gyakorlatilag megfelel egy nvtelen metdusnak. Minden lambda kifejezs tartalmazza az n. lambda opertort (=>), ennek jelentse nagyjbl annyi, hogy legyen. Az opertor bal oldaln a bemen vltozk, jobb oldaln a kifejezs ll. Mivel nvtelen metdus ezrt egy lambda kifejezs llhat egy delegate rtkadsban:
delegate int MyDelegate(int _in); MyDelegate d = x => (x * x); Console.WriteLine(d(2)); //4

Vagyis x legyen x*x, azaz a bemen paramter ngyzett adjuk vissza. Termszetesen nem csak egy bemen paramtert hasznlhatunk:
delegate bool MyDelegate(int x, int y); MyDelegate d = (x, y) => x > y; Console.WriteLine(d(10, 5)); //True

Ebben a pldban megvizsgltuk, hogy az els paramter nagyobb e mint a msodik. A lambda kifejezsek els sorban a LINQ miatt kerltek a nyelvbe, err l egy ks bbi fejezetben olvashatunk. A kvetkez kben a lambda kifejezsek felhasznlsi terleteit nzzk meg.

27.1 Generikus kifejezsek


Generikus kifejezseknek (tulajdonkppen ezek generikus delegate ek) is megadhatunk lambda kifejezseket amelyek nem ignylik egy el z leg definilt delegate jelenltt, ezzel nll lambda kifejezseket hozva ltre (ugyanakkor a generikus kifejezsek kaphatnak nvtelen metdusokat is). Ktfle generikus kifejezs ltezik a Func amely adhat visszatrsi rtket s az Action, amely nem (void). Els knt a Func ot vizsgljuk meg:
Func<int, int> square = x => x * x; square(3); //9

A generikus paramterek kztt utols helyen mindig a visszatrsi rtk ll, el tte pedig a bemen paramterek kapnak helyet. Itt is hasznlhatunk tbb (maximum ngy bemen ) paramtert is:
Func<int, int, bool> BiggerOrLesser = (x, y) => x > y; Console.WriteLine(BiggerOrLesser(10, 5)); //True

121 Amikor Func ot hasznlunk minden esetben kell visszatrsi rtknek lennie. Ltezik olyan vltozata is, amely csak a visszatrsi rtk tpust kapja meg generikus paramterknt:
Func<bool> RetTrue = () => true;

A Func prja az Action amely maximum ngy bemen paramtert kaphat, s nem lehet visszatrsi rtke:
Action<int> IntPrint = x => Console.WriteLine(x);

27.2 Kifejezsfk
Generikus kifejezsek segtsgvel felpthetnk kifejezsfkat, amelyek olyan formban troljk a kifejezsben szerepl adatokat s m veleteket, hogy futsi id ben a CLR ki tudja azt rtkelni. Egy kifejezsfa egy generikus kifejezst kap generikus paramterknt:
using System; using System.Linq.Expressions; class Program { static public void Main() { Expression<Func<int, int, bool>> BiggerOrLesserExpression = (x, y) => x > y; Console.WriteLine(BiggerOrLesserExpression.Compile().Invoke(10, 5)); Console.ReadKey(); } }

A programban el szr IL kdra kell fordtani (Compile()), csak azutn hvhatjuk meg. Kifejezsfkrl mg olvashatunk a LINQ r l szl fejezetekben.

27.3 Lambda kifejezsek vltozinak hatkre


Egy lambda kifejezsben hivatkozhatunk annak a metdusnak a paramtereire s loklis vltozira amelyben definiltuk. A kls vltozk akkor rtkel dnek ki amikor a delegate tnylegesen meghvdik, nem pedig a deklarlskor, vagyis az adott vltoz legutols rtkadsa szmt majd. A felhasznlt vltozkat inicializlni kell miel tt hasznlnnk egy lambda kifejezsben. A lambda kifejezs fenntart magnak egy msolatot a loklis vltozbl/paramterb l, mg akkor is, ha az id kzben kifut a sajt hatkrb l:
using System; delegate int MyDelegate(int x); class LambdaTest {

122
public MyDelegate d; public void Test() { int localVar = 11; d = x => { return (localVar * x); }; localVar = 100; //ez lesz a delegate -ben } } class Program { static public void Main() { LambdaTest lt = new LambdaTest(); lt.Test();
//localVar mr kifutott a hatkrb l, de a lambdnak mg megvan Console.WriteLine(lt.d(10)); //1000

Console.ReadKey(); } }

A loklis vltozk s paramterek mdosthatak egy lambda kifejezsben. A lambda kifejezsben ltrehozott vltozk ugyangy viselkednek, mint a hagyomnyos loklis vltozk, a delegate minden hvsakor j pldny jn ltre bel lk.

27.4 Esemnykezel k
Rvid esemnykezel k megrshoz kivlan alkalmasak a lambda kifejezsek. Vegyk pldul azt az esetet, amikor egy gomb Click esemnyt kivnjuk kezelni (err l a Windows Forms rl szl fejezetekben olvashat az olvas):
EventHandler<EventArgs> ClickHandler = null; ClickHandler = (sender, e) => {
//itt csinlunk valamit

}; button1.Click += ClickHandler;

Vegyk szre, hogy a kifejezs a tpus megadsa nlkl is tudja kezelni a pramtereket. Ebben az esetben egyetlen dologra kell figyelni, mgpedig arra, hogy az esemnykezel nem az alkalmazs f szlban fog lefutni.

123

28. Attribtumok
Mr tallkoztunk nyelvbe ptett mdostokkal, mint amilyen a static vagy a virtual. Ezek ltalban be vannak betonozva az adott nyelvbe, mi magunk nem kszthetnk jakat kivve, ha a .NET Framework el dolgozunk. Az alaprtelmezett telepts is rengeteg el re definilt attribtumot szolgltat, ezek a megfelel fejezetekben bemutatsra kerlnek majd. Egy attribtum a fordts sorn bepl a Metadata informcikba, amelyeket a futtat krnyezet (a CLR) felhasznl majd az objektumok krelsa sorn. Egy tesztels sorn ltalnosan hasznlt attribtum a Conditional, amely egy el rdt (ld. kvetkez fejezet) ltal definilt szimblumhoz kti programrszek vgrehajtst. A Conditional a System.Diagnostics nvtrben rejt zik:
#define DEBUG_ON using System; using System.Diagnostics; class DebugClass { [Conditional("DEBUG_ON")] static public void DebugMessage(string message) { Console.WriteLine("Debug message: {0}", message); } } class Program { static public void Main() { DebugClass.DebugMessage("Main started..."); Console.ReadKey(); } }

Egy attribtumot mindig szgletes zrjelek kzt adunk meg. Ha nem lenne definilva az adott szimblum a metdus nem futna le. Konvenci szerint minden attribtum neve a nvb l s az utna rt attribute szbl ll, gy a Conditional eredeti neve is ConditionalAttribute, de ezt elhagyhatjuk, mivel a fordt feloldja majd. Ahogy az mr elhangzott, mi magunk is kszthetnk attribtumokat:
[System.AttributeUsage(System.AttributeTargets.Class)] public class VersionAttribute : System.Attribute { private double version; public VersionAttribute() : this(1.0) { }

124
public VersionAttribute(double _version) { } public double Version { get { return version; } set { version = value; } } }

Az attribtumosztly maga is kapott egy attribtumot, amellyel azt jelezzk, hogy hol hasznlhajuk azt, ebben az esetben csakis referenciatpusokon. Ezutn (pldul) a kvetkez kppen hasznlhatjuk:
[VersionAttribute(Version = 1.0)] class MyVersionedClass { }

Az attribtumok rtkeihez pedig gy frnk hozz:


System.Attribute[] attr = System.Attribute.GetCustomAttributes(typeof(MyVersionedClass)); foreach(System.Attribute a in attr) { if(a is VersionAttribute) { Console.WriteLine("The version of the class: {0}", a.Version); } }

125

29. Az el fordt
Az el fordt a tnyleges fordts el tt vgzi el a dolgt, pl. a fordt szmra rtelmezhet v alaktja a forrskdot. A C s C++ nyelvekhez hasonlan a C# is tmogatja az el fordtnak sznt utastsokat, ezt a lehet sget leggyakrabban feltteles vgrehajtsra hasznljuk:
#define DEBUG_ON #if DEBUG_ON Console.WriteLine("Debug symbol exist..."); #endif

Az els sorban utastjuk a fordtt, hogy az n. szimblumtblzatba trolja el a DEBUG_ON szimblumot, ezutn pedig ellen rizzk, hogy definilva van e ez a szimblum. Ha igen, akkor az adott kdrszlet lefordul, egybknt nem. sszetett felttelt is kszthetnk:
//ha mindkt szimblum definilva van

#if SYMBOL_1 && SYMBOL_2 #endif

Szimblum eltvoltsra az undef utastst hasznljuk:


#define SYMBOL #if SYMBOL Console.WriteLine("SYMBOL is defined..."); #endif #undef SYMBOL #define OTHER
//ha nincs definilva

#if !SYMBOL Console.WriteLine("SYMBOL is not defined..."); #elif OTHER //vagy ha egy msik szimblumot definiltunk Console.WriteLine("OTHER is defined..."); #else Console.WriteLine("No symbol defined..."); #endif

Az elif az else-if rvidtse, az else pedig mr ismer s kell legyen. A Visual Studio ban hasznlhat egy kakukktojs, ami nem igazn el fordt utasts, ez pedig a region-endregion, amellyel egy kdrszletet jellnk ki, amit egy osztlydefincihoz hasonlan kinyithatunk-becsukhatunk:
#region LOTOFCODE #endregion

126

30. Unsafe kd
A C# lehet v teszi a memria direkt elrst mutatkon keresztl, igaz ritkn fordul el olyan problma amikor ezt ki kell hasznlnunk (pl.: amikor egytt kell m kdnnk az opercis rendszerrel, vagy meg kell valstanunk egy bonyolult s id ignyes algoritmust). Ahhoz, hogy hasznlhassunk mutatkat (pointereket) az adott metdust, osztlyt, adattagot vagy blokkot az unsafe kulcsszval kell jellnnk:
class UnSafeClass { public unsafe void UnSafeMethod() { } public void MethodWithUnSafeBlock() { unsafe { } } }

rjuk meg a Hello Pointer! programot nevhez h en mutatk hasznlatval (A C/C++ programozk szmra ismer s lesz):
using System; class Program { static public void Main() { unsafe { string message = "Hello Pointer!"; string* messagePointer = &message; Console.WriteLine(messagePointer); Console.ReadKey(); } } }

Els knt ltrehoztunk egy vltozt az zenettel, ezutn pedig deklarltunk egy string objektumra mutat mutatt (minden tpushoz ltezik a hozz tartoz mutattpus) s rtkl adtuk neki az eredeti objektumunk memriabeli cmt, ezt a cme opertorral (&) s a * opertorral vgeztk el, utbbi az adott memriacm rtkt adja visza.

127 A programot prancssorbl az /unsafe kapcsolval fordthatjuk le, Visual Studio esetn jobb klikk a projecten, Properties s ott lltsuk be.
csc /unsafe test.cs

A megfelel explicit konverzival a memriacmet is lekrhetjk:


Console.WriteLine((int)messagePointer);

Unsafe kd esetn az osztlytagokat is msknt rjk el:


class Test { public string message = "TestClass"; } unsafe { Test t = new Test(); Test* tp = &t; Console.WriteLine(tp->message); }

30.1 Fix objektumok


Normlis esetben a szemtgy jt a memria tredezettsgmentestse rdekben mozgatja az objektumokat a memriban. Egy pointer azonban mindig egy fix helyre mutat, hiszen a mutatott objektumnak a cmt krtk le, ez pedig nem fog frisslni, ez pedig nhny esetben gondot okozhat (pl. amikor hoszabb ideig van a memriban a mutatott adat, pl. valamilyen er forrs). Ha szeretnnk, hogy az objektumok a helykn maradjanak a fixed kulcsszt kell hasznlnunk:
unsafe { fixed(Test t = new Test()) { Test* tp = &t; Console.WriteLine(tp->message); } }

128

31. Tbbszl alkalmazsok


Egy Windows alap opercis rendszerben minden *.exe indtsakor egy n. process jn ltre, amely teljes mrtkben elklnl az sszes tbbit l. Egy process t az azonostja (PID Process ID) alapjn klnbztetnk meg a tbbit l. Minden egyes process rendelkezik egy n. f szllal (primary vagy main thread), amely a belpsi pontja a programnak (ld. Main). Azokat az alkalmazsokat, amelyek csak a f szllal rendelkeznek thread-safe alkalmazsoknak nevezzk, mivel csak egy szl fr hozz az sszes er forrshoz. Ugyanakkor ezek az alkalmazsok hajlamosak elaludni, ha egy komplexebb feladatot hajtanak vgre, hiszen a f szl ekkor nem tud figyelni a felhasznl interakcijra. Az ilyen helyzetek elkerlsre a Windows (s a .NET) lehet v teszi msodlagos szlak (n. worker thread) hozzadst a f szlhoz. Az egyes szlak (a process ekhez hasonlan) nllan m kdnek a folyamaton bell s versenyeznek az er forrsok hasznlatrt (concurrent access). J plda lehet a szlkezels bemutatsra egy szvegszerkeszt hasznlata: amg kinyomtatunk egy dokumentumot (egy mellkszllal) az alkalmazs f szla tovbbra is figyeli a felhasznltl rkez utastsokat. A tbbszl programozs legnagyobb kihvsa a szlak s feladataik megszervezse, az er forrsok elosztsa. Fontos megrtennk, hogy valjban a tbszlsg a szmtgp ltal nyjtott illzi, hiszen a processzor egyszerre csak egy feladatot tud vgrehajtani (br terjednek a tbbmagos rendszerek, de gondoljunk bele, hogy amikor hozz sem nylunk a szmtgphez is tven szz szl fut), gy el kell osztania az egyes feladatok kzt a processzorid t (ezt a a szlak prioritsa alapjn teszi) ez az n. id osztsos (time slicing) rendszer. Amikor egy szl ideje lejr a futsi adatait eltrolja az n. Thread Local Storage ben (ebb l minden szlnak van egy) s tadja a helyet egy msik szlnak, amely ha szksges betlti a sajt adatait a TLS -b l s elvgzi a feladatt. A .NET szmos osztlyt s metdust bocsjt rendelkezsnkre, amelyekkel az egyes process eket felgyelhetjk, ezek a System.Diagnostics nvtrben vannak. rjunk egy programot amely kirja az sszes fut folyamatot s azonostjukat:
using System; using System.Diagnostics; class Program { static public void Main() {
//lekrjk az sszes fut folyamatot

Process[] processes = Process.GetProcesses("."); foreach(Process p in processes) { Console.WriteLine("PID: {0}, Name: {1}", p.Id, p.ProcessName); } Console.ReadKey(); } }

129

Amennyiben tudjuk a process azonostjt, akkor hasznlhatjuk a Process.GetProcessById(azonost) metdust is. A kvetkez programunk az sszes fut process minden szlt s azoknak adatait fogja kilistzni:
using System; using System.Diagnostics; class Program { static public void Main() { Process[] processes = Process.GetProcesses("."); foreach(Process p in processes) { Console.WriteLine("The process {0}'s threads:", p.ProcessName); ProcessThreadCollection ptc = p.Threads; foreach(ProcessThread pt in ptc) { Console.WriteLine ( "This thread ({0}), start at {1} and its current state is {2}", pt.Id, pt.StartTime, pt.ThreadState ); } } Console.ReadKey(); } }

Elg valszn , hogy a program futsakor kivtelt kapunk, hiszen a szlak listjba olyan szl is bekerlhet amely a kirskor mr befejezte futst (ez szinte mindig az Idle process (a rendszer resjrati) esetben fordul el , a meggondols hzi feladat, akrcsak a program kivtelbiztoss ttele). A fenti osztlyok segtsgvel remekl bele lehet ltni a rendszer lelkbe, az MSDN en megtalljuk a fenti osztlyok tovbbi metdusait, tulajdonsgait amelyek az utazshoz szksgesek. A kvetkez programunk a process ek rnytst szemllteti, indtsuk el az Internet Explorert, vrjunk t msodpercet s lltsuk le:
using System; using System.Diagnostics; using System.Threading;

class Program {

130
static public void Main() { Process ie = Process.Start("IExplore.exe"); Thread.Sleep(5000); //A f szl pihentetse, ezredmsodpercben ie.Kill(); } }

Egyttal felhasznltuk az els igazi szlkezelshez tartoz metdusunkat is, a Thread.Sleep() et (ld. a kommentet), a Thread osztly a System.Threading nvtrben tallhat.

31.1 Application Domain -ek


Egy .NET program nem direkt mdon process knt fut, hanem be van gyazva egy n. application domain be a processen bell (egy process tbb AD t is tartalmazhat egymstl teljesen elszeparlva). Ezzel a megoldssal egyrszt el segtik a platformfggetlensget, hiszen gy csak az Application Domaint kell portolni egy msik platformra, a benne fut folymatoknak nem kell ismernik az opercis rendszert, msrszt biztostja a programok stabilitst ugyanis ha egy alkalmazs sszeomlik egy AD ben, a tbbi mg tkletesen m kdik majd. Amikor elindtunk egy .NET programot els knt az alaprtelmelmezett AD (default application domain) jn ltre, ezutn ha szksges a CLR tovbbi AD ket hoz ltre. A kvetkez program kirja az aktulis AD nevt:
using System; class Program { static public void Main() { AppDomain currAD = AppDomain.CurrentDomain; Console.WriteLine(currAD.FriendlyName); Console.ReadKey(); } }

A kperny n megjelenik az alkalmazs neve, hiszen egyel re nincs is tbb. Hozzunk ltre egy msodik AppDomaint:

az alaprtelmezett AD s

using System; class Program { static public void Main() { AppDomain secondAD = AppDomain.CreateDomain("SecondAD");

131
Console.WriteLine(secondAD.FriendlyName); AppDomain.UnLoad(secondAD); //megszntetjk Console.ReadKey(); } }

31.2 Szlak
Elrkeztnk a fejezet eredeti trgyhoz, mr eleget tudunk ahhoz, hogy megrtsk a tbbszl alkalmazsok elvt. Els programunkban lekrjk az adott programrsz szlnak az azonostjt:
using System; using System.Threading; class Program { static public void Main() { Console.WriteLine("Hello from thread {0}", Thread.CurrentThread.ManagedThreadId); Console.ReadKey(); } }

A kimenet minden esetben a kvetkez lesz:


Hello from thread 1

Azaz az els , a f szlban vagyunk. A program utastsainak vgrehajtsa szerint megklnbztetnk szinkron- s aszinkron m kdst. A fenti program szinkron mdon m kdik, az utastsait egyms utn hatja vgre, ha esetleg egy hosszas algoritmusba tkzik akkor csak akkor lp a kvetkez utastsra ha azt befejezte, Az aszinkron vgrehajts ennek pp az ellentte az egyes feladatokat el tudjuk kldeni egy msik szlba a f szl pedig fut tovbb, amg a mellkszl(ak) vissza nem trnek.

31.3 Aszinkron delegate -ek


A kvetkez kben delegate ek segtsgvel fogunk aszinkron programot rni, gy tisztban kell lennnk a delegate ek mibenltvel ezrt ha valami nem vilgos inkbb olvassunk vissza. Minden egyes delegate rendelkezik azzal a kpessggel, hogy aszinkron mdon hvjuk meg, ez a BeginInvoke() s EndInvoke() metdusokkal valsul meg. Vegyk a kvetkez delegate et:
delegate int MyDelegate(int x);

132

Ez valjban gy nz ki:
public sealed class MyDelegate : System.MulticastDelegate {
//...metdusok...

public IAsyncResult BeginInvoke(int x, AsyncCallback cb, object state); public int EndInvoke(IAsyncResult result); }

Egyel re ne foglalkozzunk az ismeretlen dolgokkal, nzzk meg azt amit ismernk. A BeginInvoke kal fogjuk meghvni a delegate et, ennek els paramtere megegyezik a delegate paramtervel (vagy paramtereivel). Az EndInvoke fogja majd az eredmnyt szolgltatni, ennek visszatrsi rtke megegyezik a delegate val. Az IasyncResult amit a BeginInvoke visszatrt segt elrni az eredmnyt s az EndInvoke is ezt kapja majd paramterl. A BeginInvoke msik kt paramtervel most nem foglalkozunk, ksztsnk egy egyszer delegate et s hvjuk meg aszinkron:
using System; using System.Threading; class Program { public delegate int MyDelegate(int x); static int Square(int x) { Console.WriteLine("Hello from thread {0}", Thread.CurrentThread.ManagedThreadId); return (x * x); } static public void Main() { MyDelegate d = Square; Console.WriteLine("Hello from thread {0}", Thread.CurrentThread.ManagedThreadId); IAsyncResult iar = d.BeginInvoke(12, null, null); Console.WriteLine("BlaBla..."); int result = d.EndInvoke(iar); Console.WriteLine(result); Console.ReadKey(); } }

133 A kimenet a kvetkez lesz:


Hello from thread 1 BlaBla... Hello from thread 3 144

Lthat, hogy egy j szl jtt ltre. Amit fontos megrtennk, hogy a BeginInvoke azonnal megkezdi a feladata vgrehajtst, de az eredmnyhez csak az EndInvoke hvsakor jutunk hozz, teht kls szemll knt gy ltjuk, hogy csak akkor fut le a metdus. A httrben fut szl zenete is ltszlag csak az eredmny kirtkelsnl jelenik meg, az igazsg azonban az, hogy a Main zenete el bb rt a processzorhoz, ezt hamarosan ltni fogjuk. Tbbszl program rsnl ssze kell tudnunk hangolni a szlak munkavgzst, pl. ha az egyik szlban kiszmolt eredmnyre van szksge egy msik, ks bb indult szlnak. Az ilyen eseteket szinronizlsnak nevezzk. Szinkronizljuk az eredeti programunkat, vagyis vrjuk meg amg a delegate befejezi a futst (termszetesen a szinkronizls ennl jval bonyolultabb, err l a kvetkez fejezetekben olvashatunk):
IAsyncResult iar = d.BeginInvoke(12, null, null); while(!iar.IsCompleted) { Console.WriteLine("BlaBla..."); Thread.Sleep(1000); } int result = d.EndInvoke(iar); Console.WriteLine(result);

Ezt a feladatot az IAsyncResult interfsz IsCompleted tulajdonsgval oldottuk meg. A kimenet:


Hello from thread 1 BlaBla... Hello from thread 3 BlaBla... BlaBla... BlaBla... BlaBla... 144

Itt mr tisztn ltszik, hogy az aszinkron metdus futsa azonnal elkezd dtt, igaz a Main itt is megel zte. Valljuk be elg macers mindig meghvogatni az EndInvoke ot, felmerlhet a krds, hogy nem lehetne valahogy automatizlni az egszet. Nos, pp ezt a gondot oldja meg a BeginInvoke harmadik AsyncCallback tpus paramtere. Ez egy delegate amely egy olyan metdusra mutathat, amelynek visszatrsi rtke void,

134 valamint egy darab IAsyncResult tpus paramterrel rendelkezik. Ez a metdus azonnal le fog futni, ha a mellkszl elvgezte a feladatt:
using System; using System.Threading; using System.Runtime.Remoting.Messaging;

class Program { public delegate int MyDelegate(int x); static int Square(int x) { Console.WriteLine("Hello from thread {0}", Thread.CurrentThread.ManagedThreadId); return (x * x); } static void AsyncMethodComplete(IAsyncResult iar) { Console.WriteLine("Async thread complete..."); AsyncResult result = (AsyncResult)iar; MyDelegate d = (MyDelegate)result.AsyncDelegate; Console.WriteLine("Result: {0}", d.EndInvoke(iar)); } static public void Main() { MyDelegate d = Square; Console.WriteLine("Hello from thread {0}", Thread.CurrentThread.ManagedThreadId); IAsyncResult iar = d.BeginInvoke(12, new AsyncCallback(AsyncMethodComplete), null); Console.WriteLine("BlaBla..."); Console.ReadKey(); } }

A kimenet a kvetkez lesz:


Hello from thread 1 BlaBla... Hello from thread 3 Async thread complete... Result: 144

Egyetlen dolog van htra mgpedig a BeginInvoke utols paramternek megismerse. Ez egy object tpus vltoz, azaz brmilyen objektumot tadhatunk. Ezt a paramtert hasznljuk, ha valamilyen plusz informcit akarunk tovbbtani:

135

IAsyncResult iar = d.BeginInvoke(12, new AsyncCallback(AsyncMethodComplete), "This is a message");


//s

static void AsyncMethodComplete(IAsyncResult iar) { Console.WriteLine("Async thread complete..."); AsyncResult result = (AsyncResult)iar; MyDelegate d = (MyDelegate)result.AsyncDelegate; Console.WriteLine("State: {0}", iar.AsyncState); Console.WriteLine("Result: {0}", d.EndInvoke(iar)); }

31.4 Szlak ltrehozsa


Ahhoz, hogy msodlagos szlakat hozzunk ltre nem felttlenl kell delegate eket hasznlnunk, mi magunk is elkszthetjk. Vegyk a kvetkez programot:
using System; using System.Threading; class TestClass { public void PrintThreadInfo() { Console.WriteLine("I'm in thread: {0}", Thread.CurrentThread.Name); } } class Program { static public void Main() { Thread first = Thread.CurrentThread; tmp.Name = "FirstThread"; TestClass tc = new TestClass(); tc.PrintThreadInfo(); Console.ReadKey(); } }

Els knt elneveztk az els dleges szlat, hogy ks bb azonostani tudjuk, melyikmelyik. Most ksztsnk egy msik objektumot, de ezttal kldjk el a httrbe:
Thread first = Thread.CurrentThread; tmp.Name = "FirstThread"; TestClass tc = new TestClass(); tc.PrintThreadInfo();

136
TestClass ct = new TestClass(); Thread backgroundThread = new Thread(new ThreadStart(ct.PrintThreadInfo)); backgroundThread.Name = "SecondThread"; backgroundThread.Start();

A kimenet pedig ez lesz:


I'm in thread: FirstThread I'm in thread: SecondThread

Ez eddig szp s j, de mi van akkor, ha a meghvott metdusnak paramterei is vannak? Ilyenkor a ThreadStart parametrizlt vltozatt hasznlhatjuk, ami igen eredeti mdon a ParameterizedThreadStart nvre hallgat. A ThreadStart hoz hasonlan ez is egy delegate, szintn void visszatrsi tpusa lesz, a paramtere pedig object tpus lehet:
//az j metdus

public void ParameterizedThreadInfo(object parameter) { if(parameter is string) { Console.WriteLine("Parameter: {0}", (string)parameter); } }

Most mr hvhatjuk:
Thread backgroundThread = new Thread(newParameterizedThreadStart(ct.ParameterizedThreadInfo)); backgroundThread.Name = "SecondThread";
//itt adjuk meg a paramtert

backgroundThread.Start("This is a parameter");

31.5 Foreground s background szlak


A .NET kt klnbz szltpust klnbztet meg: amelyek el trben s amelyek a httrben futnak. A kett kzti klnbsg a kvetkez : a CLR addig nem lltja le az alkalmazst, amg egy el trbeli szl is dolgozik, ugyanez a httrbeli szlakra nem vonatkozik. Logikus (lenne) a felttelezs, hogy az els dleges s msodlagos szlak fogalma megegyezik jelen fejezetnk trgyaival. Az igazsg viszont az, hogy ez az llts nem igaz, ugyanis alaprtelmezs szerint minden szl (a ltrehozs mdjtl s idejt l fggetlenl) el trben fut. Termszetesen van arra is lehet sg, hogy a httrbe kldjk ket:
Thread backgroundThread = new Thread(new ThreadStart(SomeUndeclaredMethod)); backGroundThread.IsBackground = true; backgroundThread.Start();

31.6 Szinkronizci

137

A szlak szinkronizcijnak egy primitvebb formjt mr lttuk a delegate ek esetben, most egy kicsit komolyabban kzeltnk a tmhoz. Ngyflekppen szinkronizlhatjuk a szlainkat, ezek kzl az els a blokkols. Ennek mr ismerjk egy mdjt, ez a Thread.Sleep metdus:
using System; using System.Threading; class Program { static public void Main() { Console.WriteLine("Start..."); Thread.Sleep(5000); Console.WriteLine("Stop..."); Console.ReadKey(); } }

Ez a statikus metdus a paramtereknt ezredmsodpercben megadott ideig vrakoztatja azt a szlat, amelyben meghvtk. Amikor egy szlat leblokkolunk az azonnal elereszti a processzort s addig inaktv marad, amg a blokkols felttelnek a krnyezet eleget nem tesz, vagy a folyamat valamilyen mdon megszakad. Egy viszonylag ritkn hasznlt metdusa a Thread osztlynak a SpinWait. Ez hasonlan m kdik mint a Sleep, de benne marad a processzorban, csak ppen nem csinl semmit, folyamatosan res utastsokat hajt vgre. Ltszlag nem sok rtelme van egy ilyen metdus hasznlatnak, de olyankor hasznos, amikor csak nagyon rvid (nhny ezredmsodperc) id t kell vrni, mondjuk egy er forrsra, ekkor nagy kltsg lenne cserlgetni a szlakat, mint azt a Sleep teszi. A Join metdus addig vrakoztatja a hv szlat, amg az a szl amin meghvtk nem vgezte el a feladatt:
using System; using System.Threading; class Program { static public void Main() { Thread t = new Thread( delegate(){ Thread.Sleep(3000); }); t.Start(); t.Join(); Console.WriteLine("The End"); Console.ReadKey(); } }

A Join nak megadhatunk egy timeout paramtert (ezredmsodpercben), amely id lejrta utn ha a szl nem vgzett feladatval hamis rtkkel tr vissza. A kvetkez plda ezt mutatja meg:

138
using System; using System.Threading; class Program { static public void Main() { Thread t = new Thread( delegate() { Thread.Sleep(3000); Console.WriteLine("Hello from Thread"); }); t.Start(); if(t.Join(1000) == false) { Console.WriteLine("Timed out..."); t.Abort(); //megszaktjuk a szl futst } Console.ReadKey(); } }

A pldban a szl zenete mr nem fog kperny re kerlni, mg el tte megszaktjuk. A kvetkez szinkronizcis mdszer a lezrs (locking). Ez azt jelenti, hogy er forrsokhoz, illetve a program bizonyos rszeihez egyszerre csak egy szlnak engednk hozzfrst. Ha egy szl hozz akar frni az adott dologhoz, amelyet egy msik szl mr hasznl, akkor automatikusan blokkoldik s vrlistra kerl, ahonnan rkezsi sorrendben lehet hozzjutni az er forrshoz (ha az el z szl mr vgzett). Nzzk a kvetkez pldt:
static int x, y; static public Divide() { if(x != 0) Console.WriteLine(y / x); x = 0; }

Tegyk fel, hogy megrkezik egy szl, eljut odig, hogy kirja az eredmnyt s pp ekkor rkezik egy msik szl is. Megvizsglja a felttelt, rendben tallja s tovbblp. Ebben a pillanatban azonban az els knt rkezett szl lenullzza a vltozt s amikor a msodik szl osztani akar, akkor kap egy szp kis kivtelt. A m veletet lezrhatjuk a kvetkez mdon:
static int x, y; static object locker = new object(); static public Divide()

139
{ lock(locker) { if(x != 0) Console.WriteLine(y / x); x = 0; } }

A lock kijell egy blokkot, amelyhez egyszerre csak egy szl fr hozz. Ahhoz azonban, hogy ezt megtehessk ki jellnnk egy n. tokent, amelyet lezrhat. A tokennek minden esetben referenciatpusnak kell lennie. Termszetesen nem csak statikus metdusokbl ll a vilg, egy normlis metdust is lezrhatunk, de ilyen esetekben az egsz objektumra kell vonatkoztatni a zrolst, hogy ne vltozzon meg az llapota egy msik szl keze nyomn:
class LockedClass { public void LockedMethod() { lock(this) {
/*BlaBla*/

} } }

A lock hoz hasonlan m kdik a Mutex is, a legnagyobb klnbsg az a kett kzt, hogy utbbi processz szinten zrol, azaz a szmtgpen fut sszes folyamat ell elzrja a hasznlat lehet sgt. Az er forrs/metdus/stb. hasznlata el tt meg kell hvnunk a WaitOne metdust, a hasznlat utn pedig el kell engednnk az er forrst a ReleaseMutex metdussal (ha ezt nem tesszk meg a kdbl, akkor az alkalmazs futsnak vgn a CLR automatikusan megteszi helyettnk). A kvetkez pldban ltrehozunk tbb szlat s versenyeztetjk ket egy metdus hasznlatrt:
using System; using System.Threading; class Program { private static Mutex m = new Mutex(); static void ResourceMethod() { m.WaitOne(); Console.WriteLine("Thread {0} use this resource...", Thread.CurrentThread.Name); Thread.Sleep(1000); Console.WriteLine("Thread {0} release this resource", Thread.CurrentThread.Name); m.ReleaseMutex(); }

140
static void ThreadProc() { for(int i = 0;i < 10;++i) { ResourceMethod(); } } static public void Main() { Thread[] threads = new Thread[10]; for(int i = 0;i <threads.Length;++i) { threads[i] = new Thread(new ThreadStart(ThreadProc)); threads[i].Name = i.ToString(); threads[i].Start(); } Console.ReadKey(); } }

A Semaphore hasonlt a lock ra s a Mutex re, azzal a klnbsggel, hogy megadhatunk egy szmot, amely meghatrozza, hogy egy er forrshoz maximum hny szl frhet hozz egy id ben. A kvetkez program az el z tirata, egy id ben maximum hrom szl frhet hozz a metdushoz:
using System; using System.Threading; class Program { private static Semaphore s = new Semaphore(3, 3); static void ResourceMethod() { s.WaitOne(); Console.WriteLine("Thread {0} use this resource...", Thread.CurrentThread.Name); Thread.Sleep(500); Console.WriteLine("Thread {0} release this resource", Thread.CurrentThread.Name); s.Release(); } static void ThreadProc() { for(int i = 0;i < 10;++i) { ResourceMethod(); }

141
} static public void Main() { Thread[] threads = new Thread[10]; for(int i = 0;i <threads.Length;++i) { threads[i] = new Thread(new ThreadStart(ThreadProc)); threads[i].Name = i.ToString(); threads[i].Start(); } Console.ReadKey(); } }

142

32. Reflection
A programozstechnikban a reflection fogalmt olyan programozsi technikra alkalmazzuk, ahol a program (futs kzben) kpes megvltoztatni sajt struktrjt s viselkedst. Az erre a paradigmra p l programozst reflektv programozsnak nevezzk. Ebben a fejezetben csak egy rvid pldt tallhat a kedves olvas, a Reflection tmakre risi s a mindennapi programozsi feladatok vgzse kzben viszonylag ritkn szorulunk a hasznlatra (ugyanakkor bizonyos esetekben nagy hatkonysggal jrhat a hasznlata). Vegyk a kvetkez pldt:
using System; class Test { } class Program { static public void Main() { Test t = new Test(); Type tp = t.GetType(); Console.WriteLine(tp); Console.ReadKey(); } }

Futsid ben lekrtk az objektum tpust ( a GetType metdust minden osztly rkli az object t l), persze a reflektv programozs ennl tbbr l szl, lpjnk el re egyet:
using System; using System.Reflection; class Test { public void Method() { Console.WriteLine("Hello Reflection!"); } } class Program { static public void Main() { Type tp = Assembly.GetCallingAssembly().GetType("Test"); tp.InvokeMember("Method", BindingFlags.InvokeMethod, null, Activator.CreateInstance(tp), null); Console.ReadKey(); } }

143 Ebben az esetben az objektum ltrehozsa s a metdusa meghvsa teljes mrtkben futsi id ben trtnik. Megjegyzend , hogy fordtsi id ben semmilyen ellen rzs sem trtnik a pldnyostand osztlyra nzve, gy ha elgpeltnk valamit az bizony kivtelt fog dobni futskor (System.ArgumentNullException). Az InvokeMember els paramtere annak a konstruktornak/metdusnak/tulajdonsgnak/ a neve amelyet meghvunk. A msodik paramterrel jelezzk, hogy mit s hogyan fogunk hvni (a fenti pldban egy metdust). Kvetkez a sorban a binder paramter, ezzel bellthatjuk, hogy az rkltt/tlterhelt tagokat hogyan hvjuk meg. Ezutn maga a tpus amin a hvst elvgezzk, vgl a hvs paramterei (ha vannak)).

144

33. llomnykezels
Ebben a fejezetben megtanulunk file okat rni/olvasni s a knyvtrstruktra llapotnak lekrdezst illetve mdostst.

33.1 Olvass/rs file bl/file ba


A .NET szmos osztlyt biztost szmunkra, amelyekkel file okat udunk kezelni, ebben a fejezetben a leggyakrabban hasznltakkal ismerkednk meg. Kezdjk egy file megnyitsval s tartalmnak a kperny re rsval. Legyen pl. a szveges file tartalma a kvetkez :
alma korte dio csakany konyv penz

A program pedig:
using System; using System.IO; class Program { static public void Main() { FileStream fs = new FileStream("Text.txt", FileMode.Open); StreamReader rs = new StreamReader(fs); string s = rs.ReadLine(); while(s != null) { Console.WriteLine(s); s = rs.ReadLine(); } rs.Close(); fs.Close(); Console.ReadKey(); } }

Az IO osztlyok a System.IO nvtrben vannak. A C# n. stream eket, adatfolyamokat hasznl az IO m veletek vgzshez. Az els sorban megnyitottunk egy ilyen folyamot s azt is megmondtuk, hogy mit akarunk csinlni vele. A FileStream konstruktornak els paramtere a file neve. Ha nem adunk meg teljes elrsi tat, akkor automatikusan a sajt knyvtrban fogja keresni a program. Ha kls knyvtrbl szeretnnk megnyitni az llomnyt, akkor azt a kvetkez kppen tehetjk meg:

145

FileStream fs = FileMode.Open);

new

FileStream("C:\\Egymasikkonyvtar\\Masikkonyvtar\\text.txt",

Azrt hasznlunk dupla backslash t (\), mert az egy n. escape karakter, magban nem lehetne hasznlni (persze ez nem azt jelenti, hogy minden ilyen karaktert kett zve kellene rni, minden ilyen esetben a backslash t kell hasznlnunk). Egy msik lehet sg, hogy az at jelet (@) hasznljuk az elrsi t el tt, ekkor nincs szksg dupla karakterekre, mivel minden karaktert normlis karakterknt fog rtelmezni:
FileStream fs = new FileStream(@"C:\Egymasikkonyvtar\Masikkonyvtar\text.txt", FileMode.Open);

A FileMode enum nak a kvetkez rtkei lehetnek: Create CreateNew Open OpenOrCreate Append Truncate Ltrehoz egy j file t, ha mr ltezik a tartalmt kitrli Ugyanaz mint az el z , de ha mr ltezik a file, akkor kivtelt dob. Megnyit egy file t, ha nem ltezik kivtelt dob. Ugyanaz mint az el z , de ha nem ltezik akkor ltrehozza a file t. Megnyit egy file t s automatikusan a vgre pozcionl. Ha nem ltezik ltrehozza. Megnyit egy ltez file t s trli a tartalmt. Ebben a mdban a file tartalmt nem lehet olvasni (egybknt kivtelt dob).

A FileStream konstruktornak tovbbi kt paramtere is lehet amelyek rdekesek szmunkra (tulajdonkppen 15 fle konstruktora van), mindkett enum tpus. Az els a FileAccess, amellyel belltjuk, hogy pontosan mit akarunk csinlni az llomnnyal: Read Write ReadWrite A fenti pldt gy is rhattuk volna:
FileStream fs = new FileStream("text.txt", FileMode.Open, FileAccess.Read);

Olvassra nyitja meg. rsra nyitja meg. Olvassra s rsra nyitja meg

Vgl a FileShare rel azt lltjuk be, ahogy ms folyamatok frnek hozz a file hoz:

146 None Read Write ReadWrite Delete Inheritable Ms folyamat nem frhet hozz a file hoz, amg azt be nem zrjuk. Ms folyamat olvashatja a file t. Ms folyamat rhatja a file t. Ms folyamat rhatja s olvashatja is a file t. Ms folyamat trlhet a file bl (de nem magt a file t). A gyermek processzek is hozzfrhetnek a file hoz.

Most rjunk is a file ba. Erre a feladatra a StreamReader helyett a StreamWriter osztlyt fogjuk hasznlni:
using System; using System.IO; class Program { static public void Main() { FileStream fs = new FileStream("Text.txt", FileMode.Open, FileAccess.Write, FileShare.None); StreamWriter sw = new StreamWriter(fs); Random r = new Random(); for(int i = 0;i < 10;++i) { sw.Write(r.Next().ToString()); sw.Write("\n"); } sw.Close(); fs.Close(); Console.WriteLine("Operation success!"); Console.ReadKey(); } }

Hzi feladat kvetkezik: mdostsuk a programot, hogy ne dobja el az eredeti tartalmt a file nak, s az rs utn azonnal rjuk ki a kperny re az j tartalmt. Binris file ok kezelshez a BinaryReader/BinaryWriter osztlyokat hasznlhatjuk:
using System; using System.IO; class Program { static public void Main() { BinaryWriter bw = new BinaryWriter(File.Create("file.bin"));

147

for(int i = 0;i < 100;++i) { bw.Write(i); } bw.Close(); BinaryReader br = new BinaryReader(File.Open("file.bin", FileMode.Open)); while(br.PeekChar() != -1) { Console.WriteLine(br.ReadInt32()); } br.Close(); Console.ReadKey(); } }

Ksztettnk egy binris file t, s kirtuk bel a szmokat egyt l szzig. Ezutn megnyitottuk a mr ltez file t s elkezdjk kiovasni a tartalmt. A PeekChar() metdus a soron kvetkez karaktert (byte ot) adja vissza, illetve -1 et, ha elrtk a file vgt. A folyambeli aktulis pozcit nem vltoztatja meg. A cikluson bell van a trkks rsz. A ReadValami metdus a megfelel tpus adatot adja vissza, de vigyzni kell vele, mert ha nem megfelel mret a beolvasand adat, akkor hibzni fog. A fenti pldban, ha a ReadString metdust hasznltuk volna akkor kivtel (EndOfStreamException) keletkezik, mivel a kett nem ugyanakkor mennyisg adatot olvas be. Az integer eket beolvas metdus nylvn m kdni fog, hiszen tudjuk, hogy szmokat rtunk ki.

33.2 Knyvtrstruktra kezelse


A file o kkezelse mellett a .NET a knyvtrstruktra kezelst is szleskr en tmogatja. A System.IO nvtr ebb l a szempontbl kt rszre oszlik: informciss opercis eszkzkre. El bbiek (ahogyan a nevk is sugallja) informcit szolgltatnak, mg az utbbiak (tbbsgkben statikus metdusok) bizonyos m veleteket (j knyvtr ltrehozsa, trlse, stb.) vgeznek a filerendszeren. Els pldnkban rjuk ki mondjuk a C meghajt gykernek knyvtrait:
using System; using System.IO; class Program { static public void Main() { foreach(string s in Directory.GetDirectories("C:\\")) { Console.WriteLine(s); } Console.ReadKey();

148
} }

Termszetesen nem csak a knyvtrakra, de a file okra is kvncsiak lehetnk. A programunk mdostott vltozata nmi plusz informcival egytt ezeket is kirja neknk:
using System; using System.IO; class Program { static public void PrintFileSystemInfo(FileSystemInfo fsi) { if((fsi.Attributes & FileAttributes.Directory) != 0) { DirectoryInfo di = fsi as DirectoryInfo; Console.WriteLine("Directory {0}, created at {1}", di.FullName, di.CreationTime); } else { FileInfo fi = fsi as FileInfo; Console.WriteLine("File {0} created at {1}", fi.FullName, fi.CreationTime); } } static public void Main() { foreach(string s in Directory.GetDirectories("C:\\")) { PrintFileSystemInfo(new DirectoryInfo(s)); } foreach(string s in Directory.GetFiles("C:\\")) { PrintFileSystemInfo(new FileInfo(s)); } Console.ReadKey(); } }

Els knt a mappkon, majd a file okon megynk vgig. Ugyanazzal a metdussal rjuk ki az informcikat kihasznlva azt, hogy DirectoryInfo s a FileInfo is egy kzs st l a FileSystemInfo bl szrmazik (mindkett konstruktora a vizsglt alany elrsi tjt vrja paramterknt), gy a metdusban csak meg kell vizsglni, hogy ppen melyikkel van dolgunk s tkonvertlni a megfelel tpusra. A vizsglatnl egy bitenknti s m veletet hajtottunk vgre, hogy ez mirt s hogyan m kdik, annak meggondolsa az olvas feladata. Eddig csak informcikat knyvtrstruktrt: krtnk le, most megtanuljuk mdostani is a

149
using System; using System.IO; class Program { static public void Main() { string dirPath = "C:\\test"; string filePath = dirPath + "\\file.txt";
//ha nem ltezik a knyvtr

if(Directory.Exists(dirPath) == false) {
//akkor megcsinljuk

Directory.CreateDirectory(dirPath); } FileInfo fi = new FileInfo(filePath);


//ha nem ltezik a file

if(fi.Exists == false) {
//akkor elksztjk s runk bele

StreamWriter sw = fi.CreateText(); sw.WriteLine("Dio"); sw.WriteLine("Alma"); sw.Close(); } Console.ReadKey(); } }

A FileInfo CreateText metdusa egy StreamWriter objektumot ad vissza, amellyel rhatunk egy szveges file ba.

33.3 In memory stream ek


A .NET a MemoryStream osztlyt biztostja szmunkra, amellyel memriabeli adatfolyamokat rhatunk/olvashatunk. Mire is jk ezek a folyamok? Gyakran van szksgnk arra, hogy sszegy jtsnk nagy mennyisg adatot, amelyeket majd a folyamat vgn ki akarunk rni a merevlemezre. Nylvn egy tmb vagy egy lista nem nyjtja a megfelel szolgltatsokat, el bbi rugalmatlan, utbbi memriaignyes, ezrt a legegyszer bb, ha kzvetlenl a memriban troljuk el az adatokat. A MemoryStream osztly jelent sen megknnyti a dolgunkat, mivel lehet sget ad kzvetlenl file ba rni a tartalmt. A kvetkez program erre mutat pldt:
using System; using System.IO; class Program { static public void Main() {

150
MemoryStream mstream = new MemoryStream(); StreamWriter sw = new StreamWriter(mstream); for(int i = 0;i < 1000;++i) { sw.WriteLine(i); } sw.Flush(); FileStream fs = File.Create("inmem.txt"); mstream.WriteTo(fs); sw.Close(); fs.Close(); mstream.Close(); Console.ReadKey(); } }

A MemoryStream re rlltottunk egy StreamWriter t. Miutn minden adatot a memriba rtunk a Flush() metdussal, amely egyttal ki rti a StreamWriter t is. Ezutn ltrehoztunk egy file -t s a MemoryStream WriteTo metdusval kirtuk bel azadatokat.

151

34. Grafikus fellet alkalmazsok fejlesztse


Az eddigi fejezetekben konzolalkalmazsokat ksztettnk, gy arra tudtunk koncentrlni amire kell, vagy is a nyelv elemeinek megrtsre. Azonban a C# (s a .NET) igazi erejt olyan alkalmazsok ksztsvel sajtthatjuk el amelyek sokkal jobban kihasznljk a platform kpessgeit. A jegyzet htralv rszben megismerkednk a Windows Forms s a Windows Presentation Foundation alapelemeivel, megtanulunk adatokat s adatbzisokat kezelni a .NET 2.0 s a 3.5 (LINQ, Entity Framework) eszkzeivel. Els knt a Windows Forms ot vesszk grcs al (lvn inkbb kap helyet a fels oktatsban mint fiatalabb trsa), utna pedig a WPF kvetkezik. A kvetkez kt alfejezet egy-egy egyszer grafikus fellet alkalmazs ksztst mutatja be a kt technolgia segtsgvel, a kvetkez fejezetekben ez a prhuzamossg mr nem fog helyet kapni.

34.1 Windows Forms - Bevezet


A Windows Forms a .NET els grafikus interfsz API (Application Programming Interface) jnak neve. A WF az eredeti Windows API metdusati hvja meg menedzselt krnyezetben. Ksztsk ht el els ablakos programunkat. Most mg parancssorbl fogunk fordtani s a Visual Studio beptett tervez jt sem fogjuk hasznlni, ezrt ez az egyszer program is bonyolultnak fog t nni els re, de a ksbbiekben mr jobb lesz a helyzet. A feladat a kvetkez : az ablakban (hvjuk mostantl formnak) legyen egy gomb s rkattintva jelenjen meg egy MessageBox valamilyen zenettel:
using System; using System.Windows.Forms; class FirstWindow : Form { private Button button; public FirstWindow() { this.Text = "FirstWindow" this.Size = new System.Drawing.Size(200, 200); this.button = new Button(); this.button.Text = "Click Me!"; this.button.Size = new System.Drawing.Size(100, 30); this.button.Location = new System.Drawing.Point(50, 100); this.button.Click += new EventHandler(Button_Click); this.Controls.Add(button); } protected void Button_Click(object sender, EventArgs e) { MessageBox.Show("Hello WinForms!"); } public static void Main()

152
{ Application.Run(new FirstWindow()); } }

A programot a kvetkez kppen fordthatjuk le:


csc /target:winexe file.cs

A kapcsol nlkl is lefordul, de a futskor egy konzolablak is megjelenik. Az eredmny ez lesz:

Ha pedig rkattintunk a gombra:

Jjjn a magyarzat: a FirstWindow lesz az ablakunk osztlya, ezt a System.Windows.Forms.Form osztlybl szrmaztattuk. Az ablakon lesz egy gomb (System.Windows.Forms.Button), t elneveztk button nak. A form konstruktorban belltjuk az ablak cmsort s mrett, ezutn pldnyostjuk a gombot, belltjuk a szvegt (Text), a mrett (Size), az ablakon belli helyzett (Location), majd a kattints (Click) esemnyhez hozzrendeltk az esemnykezel t, amely majd a MessageBox ot fogja megjelenteni a kperny n. Vgl hozzadtuk a gombot a formhoz ( a this szcska vgig az osztlyra azaz a formra mutat). Az esemnykezel metdusok visszatrsi tpusa mindig void s kt paramterk van, az els az esemnyt kivlt objektum (most a gomb), ez mindig object tpus, a msodik pedig egy az esemny informciit trol vltoz. Ez utbbinak tbb specializlt vltozata van (pl.: kln a billenty zet s egr ltal kivltott esemnyekhez), de ha nincs klnsebb szksgnk ezekre az adatokra nyugodtan hasznljuk az sosztlyt (EventArgs).

153 A sender paramter segtsgvel felhasznlhatjuk a kld objektum adatait, mondjuk rjuk ki a kld nevt is. Ehhez el szr nevet kell adnunk a gombnak, rjuk a konstruktorba:
this.button.Name = "FirstButton";

Az esemnykezel ben tpuskonverzit kell vgrehajtanunk, hogy elrjk a gomb tulajdonsgait:


protected void Button_Click(object sender, EventArgs e) { Button mp = (Button)sender; MessageBox.Show("Hello WinForms! Sender: " + mp.Name); }

Most ez lesz a kimenet:

A Main fggvny lesz a program belpsi pontja, itt jelentjk meg a formot, mgpedig az Application osztly Run metdusval amely paramtereknt egy pldnyt kap a form bl. Ez a metdus elindt egy programot az aktulis programszlban s irnytja a form s WinAPI kzti kommunikcit.

34.2 Windows Presentation Foundation - Bevezet


A WPF (kdneve Avalon) a .NET 3.0 verzijban megjelent grafikus alrendszer. A WinForms tl eltr en a WPF a WinAPI helyett kzvetlenl a DirectX xel kommunikl az n. Media Integration Layer en (MIL) keresztl, amely egy natv knyvtr. A WPF s a MIL kzti menedzselt rteget (amelyet a .NET kpes hasznlni) PresentationCore nak hvjk, ez egyrszt hdat kpez a .NET s a MIL kzt, msrszt definilja a WPF m kdst. Az i re a pontot a PresentationFramework teszi fel, ami az animcikat, adatktseket, stb. definilja. A WPF a grafikus fellet, az animcik, az adatktsek (stb) ltrehozshoz bevezeti az n. XAML (eXtensive Application Markup Language) nyelvet, amely az XML en alapul. Az XAML segtsgvel deklaratv ton (vagyis hagyomnyos programozs nlkl) llthatjuk be az egyes komponenesek kinzett, viselkedst. Egyttal azt is lehet v teszi, hogy specifikus programozi kszsg nlkl lehessen definilni a felletet, vagyis egy designer is elksztheti azt. Ugyanakkor nem az XAML az egyetlen tja a WPF programozsnak, hiszen forrskdbl is gond nlkl ltrehozhatunk vezrl ket a mr ismert ton (s persze az XAML is hagyomnyos CLR kdra fordul le). Azt sem rt tudni, hogy az XAML t br a WPF vezeti be, mgsem kt dik hozz szorosan, gyakorlatilag brmely krnyezet tveheti azt.

154 Ksztsk el els WPF programunkat, ehhez mr szksg van a Visual Studio ra (lehet leg 2008 legyen, akr Express is). File men, New -> Project, a Windows flnl vlasszuk ki a WPF Application sablont, adjunk nevet a projectnek, elrsi tat s OK. A kvetkez t kell ltnunk:

Lthatjuk az ablakot (illetve mg nem, mivel nincs rajta semmi), alatta pedig ott a hozz tartoz XAML kd. A feladat ugyanaz lesz, mint amit a Winforms nl csimltunk, teht el szr szksgnk van egy gombra. Most mg mindent kzzel runk be, navigljunk a kt Grid tag kz s ksztsk el. Az IntelliSense segtsgvel knny dolgunk lesz:
<Window x:Class="WPfDemo.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Grid> <Button x:Name="firstWPFButton" Width="100" Height="30" Content="Click Me!" Click="firstWPFButton_Click" /> </Grid> </Window>

A Click esemnyhez tartoz kezel t krsre ltrehozza neknk. A gombnak a tervez nzetben is meg kell jelennie:

155

Most vltsunk t a Windows1.xaml.cs file ra, itt mr benne kell legyen az esemnykezel (ha krtk, hogy ksztse el):
private void firstWPFButton_Click(object sender, RoutedEventArgs e) { }

A MessageBox megjelentse pontosan gy zajlik, mint amit mr lttunk, ennek a rsznek a megrsa az olvas feladata. Futtatni az F5 gomb megnyomsval tudjuk a ksz programot.

156

35. Windows Forms


Az el z rszben mr elksztettk els WF alkalmazsunkat, ebben a fejezetben pedig mlyebbre ssuk magunkat a tmban. Ebben a rszben megtanulunk bnni a Visual Studio val, megismerkednk a leggyakrabban hasznlt vezrl kkel, ksztnk sajt vezrl t, vgl pedig egy sszetett alkalmazst fogunk elkszteni. A Windows Forms fejlesztshez a leggyakrabban a System.Windows.Forms nvteret fogjuk hasznlni. A legtbb osztly amit felhasznlunk majd, a nvtr Controls osztlybl szrmazik, gy rengeteg olyan tulajdonsg/esemny/stb lehet, amelyet kzsen hasznlnak. A kvetkez kben mr szksg lesz a Visual Studio ra (2008 vagy 2005 ( ebben nem hasznlhat a C# 3.0), Expressek is). File men, New -> Project, itt a Visual C# flnl vlasszuk ki a Windows Forms Application sablont:

Adjunk nevet a projectnek, elrsi utat, majd kattintsunk az OK gombra (a VS 2008 ezutn biztonsgi okokbl felajnlja alaprtelmezs szerint, hogy csak megnzni nyissuk meg a projektet, ekkor vlasszuk a Load Project Normally lehet sget). Ezutn a designer fog megjelenni, rajta az ablakkal:

157

A jobb fls sarokban a Solution Explorer tallhat, itt vannak a project file jai. Alatta pedig egy sokkal rdekesebb dolog: a Properties ablak. Itt az adott vezrl tulajdonsgai vannak (ezek a valsgban is tulajdonsgok). Az ablak fels sorban a vezrl neve (Form1) s tpusa (System.Windows.Forms.Form) van. Nzzk a legfontosabb tulajdonsgokat, ezek a legtbb vezrl esetben hasznlhatak: Name: ez lesz a vezrl neve, a programban ezzel az azonostval hivatkozunk majd r. Enabled: azt jelli, hogy a vezrl aktv e. Location: a vezrl helyezete az t tartalmaz vezrl ben, a bal fls sarok koordinti: (0;0). Opacity: tltszsg, minl kisebb az rtk annl tltszbb a vezrl . Size: a vezrl mrete. Text: a vezrl felirata.

Egy Form esetben a Location rtkei csak akkor rvnyesek, ha a StartPosition tulajdonsgot Manual ra lltjuk. Most nzznk egy kicsit a dolgok mlyre. Kattintsunk a jobb egrgombbal a designerre s vlasszuk ki a View Code ot. Ekkor elnk trul az n. code-behind file. Ez nlam a kvetkez kppen nz ki (VS 2008):
using using using using using System; System.Collections.Generic; System.ComponentModel; System.Data; System.Drawing;

158
using System.Linq; using System.Text; using System.Windows.Forms; namespace JegyzetTest { public partial class Form1 : Form { public Form1() { InitializeComponent(); } } }

Kt dolgot kell szrevennnk: az els , hogy az osztly a Form osztlybl szrmazik, a msodik pedig az, hogy ez egy parcilis osztly, hogy ez mirt van, azrt nzzk meg a konstruktort. Ez egy InitializeComponent nev metdust hv meg, a defincijt azonban itt nem ltjuk. Ezt a metdust a Visual Studio automatikusan generlja, a feladata pedig az, hogy belltsa a vezrtl k kezdeti tulajdonsgait. Nzzk meg, ehhez jobb klikk a metdushvson s vlasszuk ki a Go To Definitiont. Ha megnzzk a forrskdot akkor lthat, hogy ez a parcilis osztlyunk msik fele, az InitializeComponent valahogy gy kell kinzzen:
private void InitializeComponent() { this.SuspendLayout();
// // Form1 //

this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(292, 266); this.Name = "Form1"; this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; this.Text = "Form1"; this.ResumeLayout(false); }

Amit fontos megjegyeznnk, az az, hogy ezt a metdust ne nagyon piszkljuk (minden ami itt belltsra kerl bellthat a Properties ablakban), ezt a Visual Studio fordtskor minden esetben jragenerlja, az esetleges ltalunk eszkzlt mdostsok elvesznek. Most mr kszen llunk, hogy elksztsk a msodik WF programunkat. Trjnk vissza a tervez nzethez. A jobb oldalon tallhat a Toolbox, amely a felhasznlhatz vezrl ket tartalmazza (alaprtelmezs szerint ott kell lennie, ha nincs, akkor a View menb l vlasszuk ki). Nyissuk ki az All Windows Forms felirat flecskt (ha mg nincs nyitva), ezutn hzzunk a formra egy TextBox ot s egy Buttont. El bbit nevezzk el (a Properties ablakban) MessageTextBox nak, utbbit pedig OkButton nak. A gomb felirata legyen: OK. Vgl a form feliratt is lltsuk

159 t, mondjuk WinFormsTest re. Most valahogy gy kell kinzzen (a formot tmreteztem):

A feladat a kvetkez : rkattintunk a gombra, ekkor valamilyen zenet jelenjen meg a Textbox ban. Ehhez a feldathoz a gomb mr ismert Click esemnyt fogjuk hasznlni. Az ehhez tartoz esemnykezel t hromflekppen tudjuk ltrehozni, els s legegyszer bb mdszer, hogy megrjuk, ezutn tervez nzetben vlasszuk ki a gombot s a Properties ablakban kattintsunk a kis villmjelre, keressk meg a Click esemnyt s rjuk be a kezel nevt. A msodik mdszer, hogy szintn a villmjelnl dupln kattintunk az esemyn nevre, ekkor a VS lrehozza a metdust. A legutls lehet sgnk pedig az, hogy tervez nzetben dupln kattintunk egy vezrl n, ekkor az adott vezrl alaprtelmezett esemnyhez (ez a gomb esetben a Click) ltrejn a kezel . Akrhogy is, a kezel metdusba rjuk a kvetkez t:
private void OkButton_Click(object sender, EventArgs e) { MessageTextBox.Text = "Ez egy zenet"; }

A Text tulajdonsg egy string et vr (vagy ad vissza, ltezik setter is), amely a TextBox szvegt fogja belltani. A program futtatshoz nyomjuk le az F5 gombot, ezutn gy fog m kdni:

160

36. Windows Forms Vezrl k


Vezrl k alatt azokat az elemeket rtjk, amelyekb l egy grafikus alkalmazs felpl. Minden egyes vezrl nek vannak egyedi tulajdonsgai, esemnyei, amelyek alkalmass teszik ket egy adott feladatra. A kvetkez kben megnznk nhny vezrl t. Nhny vezrl nem kap nll fejezetet, mivel csak egy msik vezrl vel egytt jtszik szerepet.

36.1 Form
Ez az osztly fogja kpezni a felhasznli fellet alapjt. A Form osztly a ContainerClass osztlybl szrmazik, gy tartalmazhat ms vezrl ket is. Egy Form lehet modlis ablak is, azaz olyan prbeszdablak amely megkveteli a felhasznltl, hogy hasznlja, miel tt visszatr az eredeti ablakhoz (ilyen pl. a MessageBox is). Ezt a hatst a ShowDialog metdussal tudjuk elrni. Helyezznk egy formra egy gombot, s a Click esemnyben jelentsnk meg egy modlis ablakot:
private void button1_Click(object sender, EventArgs e) { Form modalForm = new Form(); modalForm.ShowDialog(); }

Alaprtelmezetten egy form cmsorban megjelennek a tlcra helyezshez, illetve a teljes mretre nagytsnak a gombjai. Ezeket a MinimizeBox illetve a MaximizeBox tulajdonsgokkal tudjuk tetszs szerint megjelenteni/elrejteni. Az ablak mretezsnek letiltst a FormBorderStyle tulajdonsg valamelyik Fixed el taggal rendeljez rtkvel tudjuk jelezni. A Form alaprtelmezett esemnye a Load, amely akkor fut le miel tt a Form el szr megjelenik. Miel tt s miutn a Form s gyermekei megjelennek szmos esemny fut le. Ezeket sszefoglal nven a Form letciklusnak nevezzk. Ennek az llomsai a kvetkez ek: HandleCreated: egy kezel jn ltre a formhoz, ez gyakorlatilag egy hivatkozs Load: a form megjelense el tt fut le, ez az a hely ahol a legclszer bb a form ltal hasznlt er forrsok lefoglalsa, illetve az adattagok kezdeti belltsa. Shown esemny: az els alkalommal, amikor a form megjelenik fut le. Activated esemny: a form s vezrl i megjelennek s a formrakerl a fkusz (vagyis van az el trben. Closing: akkor fut le, amikor bezrjuk az ablakot. Closed: miutn bezrtuk az ablakot fut le. Deactivated: az ablakon nincs rajta a fkusz. HandleDestroyes: az ablakhoz tartoz kezel megsemmisl.

161 Ksztsnk egy programot ezeknek a folyamatoknak a szemlltetsre! A Formunk egyik adattagja legyen egy Form, a f -Formra pedig hzzunk egy gombot.
public partial class Form1 : Form { Form form = null; public Form1() { InitializeComponent(); } }

A Click esemnyhez pedig rjuk ezt:


private void button1_Click(object sender, EventArgs e) { this.form = new Form(); form.HandleCreated += new EventHandler(form_HandleCreated); form.Load += new EventHandler(form_Load); form.Activated += new EventHandler(form_Activated); form.Shown += new EventHandler(form_Shown); form.Show(); }

Az egyes esemnyeket mdostsuk gy, hogy lssuk mi trtnik (pl. egy MessageBox ban egy zenet, vagy a f formon egy ListBox, stb). Az esemnykezel k hozzadsnl a += opertor utn nyomjunk dupla TAB ot, ekkor automaikusan kiegszti a sort s ltrehozza a kezel t is. Egy Form hoz ktflekppen adhatunk hozz vezrl t (tulajdonkppen a kett egy s ugyanaz), vagy a tervez t hasznljuk s egyszer en rhzzuk, vagy pedig futs kzben dinamikusan. Utbbihoz tudni kell, hogy minden ContainerClass leszrmazott rendelkezik egy Controls nev tulajdonsggal, amely egy ControlCollection tpus objektumot ad vissza. A ControlCollection megvalstja az ICollection s IEnumerable interfszeket is, vagyis tmbknt kezelhet , valamint foreach ciklussal is hasznlhat. Ehhez tudjuk majd hozzadni a vezrl ket:
Button b = new Button(); b.Size = new Size(100, 30); b.Location = new Point(10, 10); b.Text = "Gomb"; this.Controls.Add(b);

Itt a this egy formra vonatkozik. A Controls tulajdonsg hasznlhat foreach ciklussal.

36.2 Button

162 A gombot mr ismerjk, els dlegesen arra hasznljuk, hogy elindtsunk valamilyen folyamatot (pl. adatok elkldse, beolvassa, stb.). Alaprtelmezett esemnye a kattints.

36.3 Label
Szveget jelentnk meg vele, amelyet a felhasznl nem mdosthat. A leggyakrabban irnymutatsra hasznljuk. rtket a Text tulajdonsgn keresztl adhatunk neki, a pldnkban ezt a form konstrukorban tesszk meg:
public partial class Form1 : Form { public Form1() { InitializeComponent(); label1.Text = "Ez egy Label..."; } }

s az eredmny:

A Label egy specializlt vltozata a LinkLabel, amely hasonlan m kdik mint egy weboldalon tallhat hivatkozs. Ennek alaprtelmezett esemnye a LinkClicked, ekkor kell tirnytanunk a felhasznlt a megfelel oldalra. A LinkLabel szvegt szintn a Text tulajdonsggal tudjuk belltani:
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { System.Diagnostics.Process.Start("http://www.microsoft.com"); }

A Form pedig gy nz ki:

36.4 TextBox
Szintn egy szveges vezrl , de ez mr kpes fogadni a felhasznltl rkez adatokat. Ksztsnk egy programot, amely felhasznlja az eddig megismert hrom

163 vezrl t. Krjk be a felhasznl kereszt- s vezetknevt kln TextBox ban, majd egy harmadikba rjuk ki a teljes nevet, amikor lenyomunk egy gombot. Hzzunk a formra hrom TextBox -ot, hrom Label t s egy Button t. A TextBox ok nevei legyenek pl. LastNameText, FirstNameText s FullNameText, a gombot pedig nevezzk OKButton nak. Most valahogy gy kellene kinznie a formnak:

Kezeljk a gomb kattints esemnyt:


private void OKButton_Click(object sender, EventArgs e) { if (LastNameText.Text == "" || FirstNameText.Text == "") { MessageBox.Show("Krem tltse ki az sszes mez t!"); } else { FullNameText.Text = LastNameText.Text + " " + FirstNameText.Text; } }

Egy kis hibakezelst is belevittnk a dologba. Most mr m kdnie kell a programnak, mghozz gy:

164 A TextBox MultiLine tulajdonsgt igazra lltva tbb sorba is rhatunk. Egy msik rdekes tulajdonsg a PasswordChar, amelynek megadva a kvnt karaktert ezutn minden bert karakter helyett a megadott karaktert jelenti meg, azaz gy viselkedik, mint egy jelszmez : -> A TextBox alaprtelmezett esemnye a TextChanged, ez akkor vltdik ki, amikor a TextBox tartalma megvltozik. Ksztsnk egy programot ennek szemlltetsre! Legyen a formunkon kt TextBox, ha az els ben runk valamit, az jelenjen meg a msikban. Legyen a kt TextBox neve FirstTextBox s SecondTextBox. Generljunk a FirstTextBox hoz egy TextChanged esemnykezel t:
private void FirstTextBox_TextChanged(object sender, EventArgs e) { SecondTextBox.Text = FirstTextBox.Text; }

36.5 ListBox
A ListBox egy olyan vezrl , amelyben adatokat trolunk, jellemz en egy lista stlusban. Egy ListBox ot ktflekppen tlthetnk fel adatokkal, kezdjk az egyszer bbel. A Properties ablakban keressk meg az Items tulajdonsgot, kattintsunk a pontocskkra s rjuk be az adatokat, egy sorba egyet:

Ha futtatjuk a programot, akkor az eredmny a kvetkez lesz:

165

Ennek a mdszernek a htrnya az, hogy el re tudnunk kell, hogy milyen adatokat akarunk. A dinamikus feltltshez bevezetjk az adatktsek (databinding) fogalmt. Ez tulajdonkppen annyit tesz, hogy nhny (adatokat trol) vezrl nek megadhatunk adatforrsokat (egy tmb, egy lekrdezs eredmnye, stb), amelyekb l feltlti magt. rjuk t a fenti programot, hogy adatktst hasznljon. A ListBoxot a Form Load esemnyben fogjuk feltlteni, teht el szr generljuk le azt, majd rjuk:
private void Form1_Load(object sender, EventArgs e) { string[] t = new string[] { "Istvn", "Judit", "Bla", "Viktria", "Dniel" }; listBox1.DataSource = t; }

Mi van azonban akkor, ha nem stringeket, hanem egy sajt osztly pldnyait akarom megjelenteni:
class MyString { public string data; public MyString(string s) { data = s; } public string Data { get { return data; } } }

Ha egy ilyen elemekb l ll tmbt ktnk a ListBox ra, akkor vajon mi trtnik?
private void Form1_Load(object sender, EventArgs e)

166
{ MyString[] t = new MyString[] { new MyString("Istvn"), new MyString("Judit"), new MyString("Bla"), new MyString("Viktria"), new MyString("Dniel") }; listBox1.DataSource = t; }

Ht ez:

A ListBox a rkttt elemeinek a ToString() metdus ltal nyert adatt jelenti meg, mi viszont nem terheltk tl ezt a metdust, ezrt az eredeti vltozat hvdik meg, ami pont a fent lthat csfsgot adja vissza. A megolds teht az, hogy elksztjk a metdust a sajt osztlyunkhoz is:
class MyString { public string data; public MyString(string s) { data = s; } public override string ToString() { return data; } }

Most mr kifogstalanul kell m kdnie. A ListBox alaprtelmezett esemnye a SelectedIndexChanged, amelyet az vlt ki, ha megvltozik a kivlasztott elem. Mdostsuk a programot, hogy a kivlasztott elemre kattintva jelenjen meg egy MessageBox a megfelel szveggel:

167

private void listBox1_SelectedIndexChanged(object sender, EventArgs e) { ListBox lb = (ListBox)sender; MessageBox.Show("A kivlasztott elem: " + lb.SelectedItem.ToString()); }

A kld objektumon explicit tpuskonverzit hajtottunk vgre, hogy hozzfrjnk az eredeti adatokhoz. Fontos tudni, hogy ilyenkor nem jn ltre j objektum, csak egy mr ltez re mutat referencia. A ListBox elemeihez az Items tulajdonsgon keresztl is hozzfrhetnk, ez egy gy jtemnyt ad vissza, vagyis hasznlhatjuk az indexel opertor, illetve az Add metdust. Az Items gy jtemny megcalstja az IEnumerable s IEnumerator interfszeket gy foreach ciklust is hasznlhatunk. Hzi feladat kvetkezik: hzzunk a formra egy msik ListBox -ot s a form Load esemnykezel jben tltsk fel ezt a vezrl t is a msik lista elemeivel. A hasznland eszkzk: Items tulajdonsg, foreach ciklus. A ListBox SelectionMode tulajdonsgt megvltoztatva tbb elemet is kijellhetnk egyszerre. A SelectionMode egy enum tpus, rtkei lehetnek None (nem lehet kivlasztani egy elemet sem), MultiSimple (a Ctrl billenty nyomva tartsval lehet tbb elemet kivlasztani) illetve MultiExtended (az egr nyomva tartsa mellett lehet kijellni az elemeket). A Sorted tulajdonsg belltsval a ListBox elemei ABC sorrendbe rendez dnek. A ListBox BeginUpdate s EndUpdate metdusai segtsgvel anlkl tlthetjk fel a lsitt futsid ben, hogy minden egyes alkalommal jrarajzoln azt:
listBox1.BeginUpdate(); for(int i = 0;i < 100;++i) { listBox1.Items.Add(i.ToString()); } listBox1.EndUpdate();

Ez a metdus a hasonl vezrl knl (CheckedListBox, ComboBox, ) is hasznlhat.

36.6 CheckBox
A CheckBox vezrl a vlaszts lehet sgt knlja fel. Bizonyra mindenki tallkozott mr olyan belptet rendszerrel, amely felajnlja, hogy legkzelebb felismer minket s azonnal belptet. Nos, azt a vlasztst egy CheckBox xal implementltk (termszetesen ilyen ms krnyezetekben is van). A CheckBox alaprtelmezett esemnye a CheckedChanged, ezt a bejells/visszavons vltja ki. Ksztsnk egy egyszer programot, amellyel egy lmpt szimullunk. A felhasznli fellet nagyon egyszer lesz:

168

Az esemnykezel pedig a kvetkez :


private void checkBox1_CheckedChanged(object sender, EventArgs e) { if (checkBox1.Checked) { label1.Text = "A lmpa fel van kapcsolva"; } else label1.Text = "A lmpa le van kapcsolva"; }

36.7 CheckedListBox
Ez a vezrl a ListBox s a CheckBox hzassgbl szletett, az elemeit egy egy CheckBox xal lehet kijellni. Ennl a vezrl nl nem hasznlhatunk adatktst, egyszerre tbb elemet az Items tulajdonsgnak AddRange metdusval tudunk tadni neki (illetve egy elemet az Add metdussal):
private void Form1_Load(object sender, EventArgs e) { string[] t = new string[] { "Istvn", "Judit", "Bla", "Viktria", "Dniel" }; checkedListBox1.Items.AddRange(t); }

Alaprtelmezetten dupla kattintssal tudunk megjellni egy elemet, ezt a CheckOnClick tulajdonsg True rtkre lltsval szimpla kattintsra vltoztathatjuk. A kivlasztott elemekhez a CheckedItems tulajdonsgon kereszt l frhetnk hozz, ez egy gy jtemnyt ad vissza, amelyen hasznlhatjuk pl. a foreach ciklus.

169 Ksztsnk egy programot, amely tartalmaz egy CheckedListBox ot, egy gombot s egy ListBox ot. A gomb megnyomsakor a kivlasztott elemek jelenjenek meg a ListBox ban. A gomb Click esemnyben fogjuk megoldani:
private void button1_Click(object sender, EventArgs e) { foreach (object s in checkedListBox1.CheckedItems) { listBox1.Items.Add(s); } }

Hasonlan a ListBox hoz, a CheckedListBox alaprtelmezett esemnye is a SelectedIndexChanged.

36.8 RadioButton
A RadioButton hasonl funkcionalitst knl, mint a CheckBox, a klnbsg annyi, hogy minden RadioButton egy adott troln bell (pl. egy form) egy csoportot alkot, kzlk pedig egy id ben csak egyet lehet kivlasztani. Vegyk el a jl bevlt lmpakapcsol programunkat s alaktsuk t, hogy hasznlja ezt a vezrl t. A RadioButton alaprtelmezett esemnye a CheckedChanged, ezrt erre fogunk rlni. A kt RadioButton hasznlja ugyanazt az esemnyt (mindkett kijell, utna Properties ablak villmjel s ott lltsuk be):
private void radioButton2_CheckedChanged(object sender, EventArgs e) { RadioButton rb = (RadioButton)sender; if (rb.Name == "radioButton1") { label1.Text = "A lmpa fel van kapcsolva"; } else label1.Text = "A lmpa le van kapcsolva"; }

A form Load esemnyben is lltsuk be a Label kezdeti szvegt. A RadioButton nak tervezsi id ben megadhatjuk, hogy be legyen e jellve, ehhez a Checked tulajdonsgot kell megvltoztatni (itt is rvnyes az egy id ben egy lehet bejellve szably). A lmpakapcsolgat program:

170

A megjelenst tllthatjuk gombszer v az Appearance tulajdonsggal:

Nylvn egy formon bell el fordulhatnak klnll RadioButton csoportok, ekkor, hogy ne zavarjk egymst el kell klnteni ket valahogy. Erre a clra kt vezrl t hasznlhatunk, a Panel t s a GroupBox ot. A kett kzt a klnbsgek a kvetkez k: a Panel scrollozhat, nincs felirata sem lland kerete, a GroupBox pedig nem scrollozhat van felirata s kerete is. Alaktsuk t a progrmunkat, hogy kt kln lmpa legyen rajta, az egyik csoport egy Panel ban legyen a msik egy GroupBox ban. A ngy RadioButton ugyanazt az esemnykezel t fogja hasznlni, gy meg kell tudnunk klnbztetni, hogy melyik csapat kldte a jelzst. Nincs nehz dolgunk, mivel minden vezrl rendelkezik egy Parent nev tulajdonsggal, amely az t tartalmaz vezrl t adja vissza. Egy egyszer tpuskonverzival fogjuk eldnteni a krdst:
private void radioButton2_CheckedChanged(object sender, EventArgs e) { RadioButton rb = (RadioButton)sender; Panel p = rb.Parent as Panel; if (p == null) { if (rb.Name == "radioButton3") { label2.Text = "A lmpa fel van kapcsolva"; } else label2.Text = "A lmpa le van kapcsolva"; } else { if (rb.Name == "radioButton1") { label1.Text = "A lmpa fel van kapcsolva"; } else label1.Text = "A lmpa le van kapcsolva"; } }

171

Kihasznltuk azt, hogy az as opertor nullrtket ad vissza, ha a konverzi sikertelen volt, gy biztosan tudjuk, hogy melyik csapattl rkezett a jelzs. Ugyanakkor meg kell jegyeznnk, hogy a szveg mdostsa nem igazn elegns, ezrt az olvas feladata lesz, hogy egy szebb megoldst talljon. A program most mr szpen fut:

A Panel nek alaprtelmezetten nincs krvonala, de ezt bellthatjuk a BorderStyle tulajdonsggal.

36.9 ComboBox
A ComboBox a TextBox s a ListBox keresztezsb l jtt ltre, a bert szveg alapjn kivlaszthatunk egy elemet egy el re megadott listbl. A ComboBox xal hasznlhatunk adatktst is. Ksztsnk egy programot, amely egy ComboBox bl s egy gombbl ll, a gombot megnyomva megjelenik egy MessageBox, amely a kivlasztott elemet jelenti meg.

A ComboBox ot a mr ismert mdon tltttk fel a form Load esemnyben. A FindString s FindExactString metdusokkal a ComboBox elemei kztt kereshetnk. El bbi a legels olyan elem indext adja vissza, amely a paramtereknt megadott stringgel kezd dik, utbbi pedig pontos egyezst keres. Ksztsnk egy alkalmazst ennek szemlltetsre! Hzzunk a fomra egy TextBoxot, ez fogja tartalmazni a keresend szveget, illetve kt RadioButton t, amelyek a hasznland metdusokat jellik. A gomb funkcionalitsa most megvltozik, a keresst fogja elindtani:

172

A Click esemny kdja pedig ez lesz:


private void button1_Click(object sender, EventArgs e) { comboBox1.SelectedIndex = radioButton1.Checked == true ? comboBox1.FindString(textBox1.Text) : comboBox1.FindStringExact(textBox1.Text); }

Ezek a metdusok a tbbi hasonl vezrl vel (ListBox, stb) is hasznlhatak.

36.10 TreeView
A TreeView vezrl adatok hierarchikus megjelentsre szolgl (pl. egy filerendszer elemei). A fa egyes elemei TreeNode tpusuak.

A vezrl t a form Load esemnyben tltttk fel adatokkal:

173

private void Form1_Load(object sender, EventArgs e) { treeView1.BeginUpdate(); for (int i = 0; i < 10; ++i) { treeView1.Nodes.Add("Parent" + i.ToString()); for (int j = 0; j < 3; ++j) { treeView1.Nodes[i].Nodes.Add(j.ToString()); } } treeView1.EndUpdate(); }

A TreeView alaprtelmezett esemnye az AfterSelect, ez akkor fut le, ha valamelyik Node ot kivlasztjuk. Az esemnykezel a kvetkez kppen nz ki:
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e) { }

A TreeViewEventArgs a kivlaszts mdjrl ad tjkoztatst. Ksztsnk egy programot, amely ezekr l az esemnyekr l ad informcit. Hzzunk a formra egy ListBoxot, ebben fogjuk az esemnyeket megjelenteni. Ezutn az esemnykezel kdja:
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e) { switch (e.Action) { case TreeViewAction.ByKeyboard: listBox1.Items.Add("Az esemny kivltja a billenyt zet"); break; case TreeViewAction.ByMouse: listBox1.Items.Add("Az esemny kivltja az egr"); break; default: break; } }

A program pedig:

174

Ez az esemnykezel azonban csak a Node kivlasztst figyeli, de mi esetleg szeretnnk a kinyit/becsuk esemnyre is feliratkozni, ezek az AfterExpand s AfterCollapse esemnyek:
private void treeView1_AfterCollapse(object sender, TreeViewEventArgs e) { listBox1.Items.Add("Node (" + e.Node.Text + ") bezrult"); } private void treeView1_AfterExpand(object sender, TreeViewEventArgs e) { listBox1.Items.Add("Node (" + e.Node.Text + ") kinyilt"); }

A CheckBoxes tulajdonsg bejellsvel minden egyes Node hoz egy CheckBox is megjelenik. A bejellst az AfterCheck esemnnyel tudjuk kezelni:
private void treeView1_AfterCheck(object sender, TreeViewEventArgs e) { listBox1.Items.Add("Node (" + e.Node.Text + ") bejellve"); }

175

Egy Node szl jre a Parent tulajdonsgval tudunk hivatkozni. A TreeView Nodes tulajdonsga egy TreeNodeCollection tpus gy jtemnyt ad vissza, amely megvalstja az IEnumerable s IEnumerator interfszeket, azaz vgigiterlhat egy foreach ciklussal. Hzi feladat 1.: gombnyomsra listzzuk ki (egy msik ListBox ban, TextBox ban, akrhol) a bejellt Node ok nevt s szl jt (a gykrelemek Parent tulajdonsga null t ad vissza, mivel nekik nincs szl jk). A bejellst a Node Checked tulajdonsgval ellen rizhetjk. Hzi feladat 2.: egy Node bejellse nem jr egytt a gyermekei bejellsvel. Ksztsnk programot, amely ezt megoldja (tipp: a TreeView on meghvott Nodes[x] tulajdonsg az x edik gykrelemet fogja visszaadni, ennek pedig van Nodes tulajdonsga, tipp2: akr az egsz lerendezhet kt egymsba gyazott foreach ciklussal (kulcssz: TreeNodeCollection)).

36.11 DomainUpDown
A DomainUpDown egy stringrtk megjelentsre szolgl, amelyet egy listbl vlaszthatunk ki.
private void Form1_Load(object sender, EventArgs e) { domainUpDown1.Items.Add("Els "); domainUpDown1.Items.Add("Msodik"); domainUpDown1.Items.Add("Harmadik"); }

A Text tulajdonsgot nem rt kitiszttani, vagy tetszs szerint belltani, ellenkez esetben indtsnl a domainUpDown1 felirat jelenik meg.

A Wrap tulajdonsg igaz rtkre lltsval ha elrtk a lista vgt akkor a kvetkez elemnl automatikusan a lista elejre ugrik.

176

36.12 NumericUpDown
Hasonl mint az el z vezrl , de ezttal szmokat jelentnk meg. A Minimum s Maximum tulajdonsgokkal a legkisebb s legnagyobb lehetsges rtket adjuk meg, mg az Increment tulajdonsg a lptk mrett adja meg.

Alaprtelmezett esemnye a ValueChanged, amely akkor indul el, amikor megvltozik a mutatott rtk:
private void numericUpDown1_ValueChanged(object sender, EventArgs e) { MessageBox.Show(((NumericUpDown)sender).Value.ToString()); }

Ebben az esetben a tpuskonverzihoz nem hoztunk ltre j azonostt, a megfelel zrjelezssel is elrhet ek a vezrl adatai.

36.13 ProgressBar
A ProgressBar vezrl t ltalban egy folyamat llapotnak jellsre hasznljuk:

A Minimum s Maximum tulajdonsgai a felvehet rtkek intervallumt jellik ki, ezek alaprtelmezetten 0 s 100. A Style tulajdonsggal a vezrl kinzete (blokkos, egy nvekv tglalap illetve egyetlen mozg blokk) llthat be. A fenti program elszmol szzig s ezt a folyamatot egy ProgressBar on jelzi. A gomb Click esemnyben indtjuk el a folyamatot:
private void button1_Click(object sender, EventArgs e) { for (int i = 0; i < 100; ++i) { progressBar1.PerformStep(); System.Threading.Thread.Sleep(100); } MessageBox.Show("100%"); }

177 Minden lpsnl pihentetjk kicsit a f szlat, hogy ltvnyosabb legyen az eredmny s a folyamat vgt egy MessageBox xal jelezzk. A ProgressBar lptetst a PerformStep metdussal vgeztk el, amely a Step tulajdonsg rtke szerint lpteti az aktulis rtket (a Step alaprtelmezetten 1 re van lltva). Az rtket kzzel is llthatjuk a Value tulajdonsggal:
progressBar1.Value += 10;

Termszetesen ekkor is el re/vissza lp a vezrl .

36.14 TrackBar
Ezzel a vezrl vel a felhasznl ki tud vlasztani egy rtket egy intervallumbl. A Minimum s Maximum tulajdonsgokkal az als s fels hatrt lehet lltani, a TickFrequency a vezrl beosztst mdostja, a Large- illetve SmallChange pedig azt az rtket, amikor az egrrel vagy a billenty zettek mdostjuk az aktulis rtket:

A programban a TrackBar alaprtelmezett esemnyre ltnk r, ez a Scroll, amely akkor aktivldik, amikor megvltozik a vezrl rtke (vagyis elmozdul a mutat):
private void trackBar1_Scroll(object sender, EventArgs e) { label2.Text = ((TrackBar)sender).Value.ToString(); }

Az Orientation tulajdonsggal bellthatjuk, hogy a vezrl vzszintesen (Horizontal) vagy fgg legesen (Vertical) jelenjen meg.

36.15 PictureBox
Ez a vezrl ahogyan azt a neve is mutatja kpek megjelentsre illetve grafikus feladatokra val. Az Image tulajdonsgt kivlasztva egy ablak jelenik meg s kt lehet sget knl neknk. Ha a kpet egy resource llomny tartalmazza, akkor a kp belefordul a futtathat llomnyba (vagy osztlyknyvtrba), csakis a programon bell rhetjk el. Alaprtelmezetten nincs kp hozzrendelve a programhoz, ezt az Import gombra kattintva tehetjk meg. A fordts utn a Solution Explorerben megjeleni egy Resources.resx nev tag, ez fogja tartalmazni az er forrsokat (most a kpet). Ennek a file nak is vannak belltsi, vlasszuk ki s nyissuk meg a Properties ablakot. Most be tudjuk lltani, hogy hogyan kezelje az er forrsokat (begyazva, tmsolva a sajt knyvtrba, stb). A msik lehet sg, hogy a Local Resource t vlasztjuk, ekkor a kp csak simn belefordul a kimenetbe.

178 Termszetesen arra is van lehet sg, hogy a forrskdbl lltsuk be a kpet, ekkor a kp elrsi tjt kell megadnunk (ha a futtathat llomnnyal egy helyen van, akkor csak a nevt):
private void Form1_Load(object sender, EventArgs e) { Image img = new Bitmap("Naplemente.jpg"); pictureBox1.Image = img; }

Az Image egy absztrakt osztly, a Bitmap pedig egy leszrmazottja. Utbbi szmos konstruktorral (12 vel) rendelkezik, a fent bemutatott a legegyszer bb. A PictureBox Image tulajdonsga pedig egy Image leszrmazottat vr.

36.16 RichTextBox
A RichTextBox szveg bevitelre s manipullsra alkalmas, de jval rugalmasabb s tbb szolgltatst nyjt, mint a TextBox. Emellett kapacitsban is tlmutat rajta. Szveget rendelhetnk hozz a Text tulajdonsgn kereszt l, egyszer szveges filebl vagy RTF (Rich Text File) formtum szvegb l:

private void Form1_Load(object sender, EventArgs e) { richTextBox1.Text = "Hello"; }

Egy msik mdja a szveggel val feltltsnek az AppendText metdus:

179
private void Form1_Load(object sender, EventArgs e) { richTextBox1.AppendText("Ez a hozzf ztt szveg"); }

Ekkor a paramterknt tadott karaktersorozat a vezrl tartalmnak vghez f z dik hozz (gyakorlatilag a Text += szoveg rvidtett formja). RTF vagy szveges llomnyt olvashatunk be a LoadFile metdusval:
private void Form1_Load(object sender, EventArgs e) { richTextBox1.LoadFile("test.rtf"); }

A LoadFile prja a SaveFile, amellyel RTF be menthetjk a vezrl tartalmt (a pldban egy gombhoz rendeltk a mentst):
private void button1_Click(object sender, EventArgs e) { richTextBox1.SaveFile("savedfile.rtf"); }

aktulis

A RichTextBox rengeteg metdust llt rendelkezsnkre a szveg formzshoz. Ehhez el szr ki kell jellni a szveget, ezt vagy kzzel tesszk meg, vagy a Find metdussal. Hzzunk a formra hrom CheckBox ot, ezekkel fogjuk belltani, hogy flkvr, d lt vagy alhzott legyen a szveg. A hrom vezrl hasznlja ugyanazt az esemnyt:
private void checkBox3_CheckedChanged(object sender, EventArgs e) { FontStyle style = FontStyle.Regular; switch(((CheckBox)sender).Name) { case "checkBox1": style = FontStyle.Bold; break; case "checkBox2": style = FontStyle.Italic; break; case "checkBox3": style = FontStyle.Underline;

180
break; default: break; }; richTextBox1.SelectionFont = new Font(richTextBox1.Font, style); }

Hasonlan kell kezelni a SelectionColor, SelectedIndent, stb. tulajdonsgokat is. A Find metdussal megkereshetnk s kijellhetnk egy adott karaktersorozatot. Hzzunk egy gombot s egy TextBox ot a formra, a gomb Click esemnyhez pedig rjuk a kvetkez t:
private void button1_Click(object sender, EventArgs e) { int idx = richTextBox1.Find(textBox1.Text); richTextBox1.Focus(); }

Vissza kell adnunk a fkuszt a vezrl nek, klnben nem ltszana az eredmny:

Kijellni a Select metdussal is tudunk, mindssze meg kell adnunk a kezd indexet s a hosszt:
richTextBox1.Select(0, 10);

Ekkor a szveg elejt l kezdve (nulladik index) tz karakter hosszan jelljk ki a vezrl tartalmt. Ha a vezrl tartalmaz egy internetes hivatkozst, akkor azt automatikusan felismeri s megjelli:

181

Ahhoz azonban, hogy a link m kdjn is kezelnnk kell a RichTextBox LinkClicked esemnyt:
private void richTextBox1_LinkClicked(object sender, LinkClickedEventArgs e) { System.Diagnostics.Process.Start(e.LinkText); }

A LinkClickedEventArgs a hivatkozsrl tulajdonsga a cmet adja vissza.

tartalmaz

informcikat,

LinkText

Hasonlan a TextBox hoz a RichTextBox alaprtelmezett esemnye is a TextChanged.

36.17 DateTimePicker
Ez a vezrl dtumot: lehet v teszi a felhasznl szmra, hogy kivlasszon egy adott

Szmos tulajdonsg l lrendelkezsnkre, amelyekkel megvltoztathatjuk a vezrl megjelentst, pl. a CalendarForeColor, CalendarFont, stb A MinDate s MaxDate tulajdonsgaival bellthatjuk, hogy milyen intervallumban vlaszthat a felhasznl. Alaprtelmezett esemnye a ValueChanged, amely a kivlasztott dtum megvltozsakor aktivizldik. rjunk egy programot, amely egy MessageBox ban megjelenti a kivlasztott dtumot:
private void dateTimePicker1_ValueChanged(object sender, EventArgs e)

182
{ MessageBox.Show(dateTimePicker1.Value.ToShortDateString()); }

A Value tulajdonsg egy DateTime tpus rtket ad vissza. A fenti pldban a ToShortDateString metdust hasznltuk, ez csak a napra lebontott dtumot adja vissza (vagyis v-hnap-nap), amennyiben msodpercre pontos rtket akarunk hasznljuk a sima ToString metdust. A DateTimePicker tulajdonkppen kt vezrl b l ll, egy TextBox leszrmazottbl illetve a dtum kivlasztshoz egy MonthCalendar bl, ez utbbi nllan is felhasznlhat:

A FirstDayOfWeek tulajodnsgval a hetek kezd napjt adhatjuk meg, ez egyes orszgokban eltr lehet, alaprtelmezetten az opercis rendszer nyelve alapjn kap kezd rtket. Kijellhetnk napokat, amelyek a tbbit l kiemelkednek, ezt a BodledDates, AnnuallyBoldedDates illetve MonthlyBoldedDates tulajdonsgokkal rhetjk el, mindegyikk egy DateTime tpus tmbt vr:
private void Form1_Load(object sender, EventArgs e) { monthCalendar1.BoldedDates = new DateTime[] { new DateTime(2008, 9, 2) }; monthCalendar1.AnnuallyBoldedDates = new DateTime[] { new DateTime(2008, 9, 6), new DateTime(2008, 9, 1),

183
new DateTime(2008, 9, 23) }; }

Az eredmny pedig ez lesz:

Kt fontos esemnye van, az els a DateChanged, amely akkor aktivizldik, amikor kivlasztunk az egrrel egy dtumot:
private void monthCalendar1_DateChanged(object sender, DateRangeEventArgs e) { MessageBox.Show(e.End.ToShortDateString()); }

A DataRangeEventArgs End illetve Start tulajdonsgai a felhasznl ltal utolsknt s el szr kivlasztott dtumokat adjk vissza. Termszetesen ebben az esetben csak egy id pontot tudunk visszaadni, a msodik paramtert igazbl a msik DateSelected esemnyben tudjuk felhasznlni (s ez a vezrl alaprtelmezett tulajdonsga is). Az egrrel kijellhetnk tbb dtumot s ekkor mr fel tudjuk hasznlni az End s Start tulajdonsgokat:
private void monthCalendar1_DateSelected(object sender, DateRangeEventArgs e) { MessageBox.Show(e.Start.ToShortDateString() + " " + e.End.ToShortDateString()); }

36.18 MenuStrip
Ezzel a vezrl vel a legtbb programban megszokott mensort tudunk ltrehozni:

184

Ktflekppen tudjuk feltlteni a menpontokat, a legegyszer bb a Visual Studio tervez nzett hasznlni, ekkor csak szimpln be kell rni a szveget. A msik lehet sg, hogy kdbl hozzuk ltre, ez a kvetkez kppen nz ki:
private void Form1_Load(object sender, EventArgs e) { for (int i = 0; i < 3; ++i) { ToolStripMenuItem item = new ToolStripMenuItem("Menu" + i.ToString()); item.DropDownItems.Add("Menu" + i.ToString() + ".1"); menuStrip1.Items.Add(item); } }

Lthat, hogy a MenuStrip minden egyes menpontja egy ToolStripItem tpus objektum. A MenuStrip Items s a ToolStripMenuItem DropDownItems tulajdonsga is egy ToolStripItemCollection tpus listt ad vissza, amelynek tagjai mind ToolStripMenuItem tpusuak. A fenti kd ktfle mdjt mutatja be a menhz val hozzadsnak: vagy a hagyomnyos ton ltrehozzuk az objektumot s hozzadjuk, vagy helyben az Add metdus paramtereknt megadott string rtkkel. Utbbi esetben automatikusan meghvdik a ToolStripMenuItem konstruktora. A men megvan, most kellene valami funkcionalitst is hozzadnunk, ezt kt mdon tudjuk megoldani. Kezelhetjk a MenuStrip alaprtelmezett esemnyt, az ItemClick edet, amely rtelemszer en akkor sl el, amikor rkattintunk valamelyik menpontra. Azt is tudnunk kell ekkor, hogy melyik menpont volt a b ns. Nzzk meg az esemnyt:
private void menuStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e) { }

A ToolStripEventArgs ClickedItem tulajdonsga visszaadja azt a ToolStripMenuItem objektumot, amelyre kattintottuk. Viszont van egy problma, mghozz az, hogy ez a MenuStrip esemnye, nem pedig a gyermek objektumok. A legfels bb szint elemekre kivltdik, de az almenkre nem. Ilyenkor a ToolStripMenuItem ItemClicked esemnyt (ez bizony ugyanaz, vajon mirt?) fogjuk hasznlni. Miel tt tovbbmegynk gondolkodjunk egy kicsit: tegyk fel, hogy a MenuStrip esemnyt

185 hasznljuk, s el kell dnteni, hogy az egyes menpontok kivlasztsakor mit tegynk. rhatunk mondjuk valami ilyesmit:
private void menuStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e) { switch (e.ClickedItem.Name) { case "elso":
//itt csinlunk valamit

break; case "masodik":


//itt is csinlunk valamit

break;
/*...*/

default: break; }; }

Ez a megolds nem valami szp. tlthatatlan s nehezen mdosthat. Olyan esetekben azonban j, amikor a men csak egyszint . Bonyolultabb helyzetben viszont sokkal egyszer bb az egyes menpontok ItemClicked esemnyt hasznlni, ehhez az adott menponton kattintsunk dupln:
private void menu2ToolStripMenuItem_Click(object sender, EventArgs e) { }

Itt mr nem jr a specilis esemnyargumentum, mivel ilyen esetekben nem rdekel minket, hogy ki kldte a parancsot, csak az utasts a lnyeg. Amennyiben a menpontot kdbl hoztuk ltre, akkor rgtn meg is adhatjuk neki az esemnykezel jt:
ToolStripMenuItem item = new ToolStripMenuItem("Menu", null, new EventHandler(Click_EventHandler));

A konstruktor msodik paramtere a menponthoz rendelt kp lenne, de ezt most nem adtuk meg. Mdostsuk az esemnykezel t, hogy egy MessageBox ban jelentse meg a menpont nevt:
private void menu11ToolStripMenuItem_Click(object sender, EventArgs e) { MessageBox.Show(((ToolStripMenuItem)sender).Text); }

A menpontok CheckOnlick tulajdonsgnak igazra lltsval bellthatjuk, hogy kattintsra vltoztassa az llapott (ez olyankor jn jl, amikor egy ktlls tulajdonsgot akarunk implementlni):

186

A Checked tulajdonsg visszadja, hogy a menpont be van e jellve, vagy sem:


private void menu11ToolStripMenuItem_Click(object sender, EventArgs e) { MessageBox.Show(((ToolStripMenuItem)sender).Checked.ToString()); }

36.19 ltalnos prbeszdablakok


A .NET ben lehet sgnk van el re megrt dialgusablakok hasznlatra, mint pl. amivel megnyithatunk egy szveges file t. Ezeket az el re legyrtott elemeket a Visual Studio tervez nzetben a ToolBox ablak Dialogs fle alatt talljuk, sszesen hat darab van (a PrintDialog a Printing fl alatt lakik).

Els knt a legegyszer bbet a ColorDialog ot fogjuk megvizsglni. Egy ilyen dialgusablakot ktflekppen hasznlhatunk fel. Vagy a tervez nzetben rhzzuk a formra (ekkor alul megjelenik egy kln svban), vagy a kdbl hozzuk ltre, ha szksg van r. Az el bbi esetben egy (alaprtelmezetten) private elrs ColorDialog tpus adattag jn ltre, azaz a form teljes lettartama alatt ugyanazt az ablakot hasznljuk. Ez olyankor jn jl, ha gyakran van szksgnk az adott ablakra. Az ellenkez esetben, amikor csak ritkn hasznljuk a dialgust elegnsabb, ha loklis vltozknt hozzuk ltre. Jjjn egy gyakorlati plda: hzzunk a formra egy gombot s egy PictureBox ot, illetve egy ColorDialog ot. A gomb Click esemnyt fogjuk hasznlni:
private void button1_Click(object sender, EventArgs e) { if (colorDialog1.ShowDialog() == DialogResult.OK) {

187
pictureBox1.BackColor = colorDialog1.Color; } }

A ShowDialog metdus egy DialogResult tpus felsorolt rtket ad vissza, attl fgg en, hogy a felhasznl mit vlasztott (az IntelliSense mutatja a tbbi lehet sget is, ksrletezznk). A fenti pldban, ha az eredmny OK, akkor tudjuk, hogy a felhasznl vlasztott valamilyen sznt, ezt pedig lekrdezhetjk a Color tulajdonsggal.

Ha az ablakot tisztn kdbl akarjuk ltrehozni, akkor a Click esemny gy nz ki:


private void button1_Click(object sender, EventArgs e) { ColorDialog colorDialog1 = new ColorDialog(); if (colorDialog1.ShowDialog() == DialogResult.OK) { pictureBox1.BackColor = colorDialog1.Color; } }

188 Ekkor viszont minden egyes alkalommal j objektum jn ltre, mivel a loklis vltozk hatkre a metdus vgn lejr. Kvetkez a sorban az OpenFileDialog, ez mint azt a neve is mutatja arra val, hogy file okat nyissunk meg. Hzzunk egy formra egy RichTextBox ot illetve egy gombot, ez utbbit arra hasznljuk, hogy megnyissunk egy szveges llomnyt, amit majd a RichTextBox ban megjelentnk. Az OpenFileDialog nak rengeteg rdekes tulajdonsga van, most azonban csak a Filter t vizsgljuk. Ezzel megadhatjuk, hogy milyen tpus file okat akarunk megnyitni, pl.: Text files|*.txt. A Filter tulajdonsg kdbl is megadhat:
openFileDialog1.Filter = "Text files|*.txt|";

Akr tbbfle sz r t is megadhatunk:


openFileDialog1.Filter = "Text files|*.txt|All Files|*.*";

A programunkban gomb Click esemnye:


private void button1_Click(object sender, EventArgs e) { if (openFileDialog1.ShowDialog() == DialogResult.OK) { richTextBox1.LoadFile(openFileDialog1.FileName, RichTextBoxStreamType.PlainText); } }

Ebben az esetben a LoadFile metdusban meg kell adnunk, hogy milyen tpus file t akarunk a RichTextBox ba beolvasni, mivel az alaprtelmezetten RTF formtumakat olvas. A dialgus FileName tulajdonsga a file elrsi tjt adja vissza.

189

Az OpenFile metdus megnyit egy stream et csakis olvassra a kivlasztott file ra:
private void button1_Click(object sender, EventArgs e) { openFileDialog1.Filter = "Text files|*.txt|All Files|*.*"; if (openFileDialog1.ShowDialog() == DialogResult.OK) { richTextBox1.LoadFile(openFileDialog1.OpenFile(), RichTextBoxStreamType.PlainText); } }

Itt kihasznltuk a RichTextBox azon tulajdonsgt, hogy stream b l is kpes olvasni.

190 Termszetesen a Filter tulajdonsgot elegnsabb s sszer bb a konstruktorban vagy a form Load esemnyben belltani, a fenti megolds az rthet sg miatt szletett. Az OpenFileDialog prja a SaveFileDialog, amely file ok elmentsre szolgl. Az el z pldt fogjuk hasznlni, de most a gomb lenyomsakor elmentjk a RichTextBox tartalmt:
private void button1_Click(object sender, EventArgs e) { if (saveFileDialog1.ShowDialog() == DialogResult.OK) { richTextBox1.SaveFile(saveFileDialog1.OpenFile(), RichTextBoxStreamType.PlainText); } }

Az OpenFile metdus egy rhat/olvashat stream et nyit meg, amelybe a RichTextBox SaveFile metdusval rjuk ki az adatokat. Kvetkez a sorban a FontDialog, amely bet tpus belltsra hasznland. A plda ugyanaz lesz itt is, de gombnyomsra el jn az ablak s megvltoztatjuk a RichTextBox bet tpust:
private void button1_Click(object sender, EventArgs e) { if (fontDialog1.ShowDialog() == DialogResult.OK) { richTextBox1.Font = fontDialog1.Font; } }

A PrintDialog gal kinyomtathatunk egy dokumentumot, mg mindig a pldnknl maradva a RichTextBox tartalmt. Ehhez szksgnk lesz egy PrintDocument tpus objektumra, amely a nyomtatand adatokrl tartalmaz megfelel formtum informcikat. Ezt az objektumot akr a ToolBox bl is a formra hzhatjuk, ez lesz a legegyszer bb. Miel tt tovbbmegynk rdemes a tesztelshez beszerezni valamilyen PDF nyomtatt, pl. az ingyenes PDF995 t. A tnyleges nyomtatst a PrintDocument fogja vgezni, a PrintDialog a belltsok miatt kell. Most is gombnyomsra fogjuk elindtani a folyamatot:
private void button1_Click(object sender, EventArgs e) { if (printDialog1.ShowDialog() == DialogResult.OK) { printDocument1.Print(); } }

191 Amikor meghvjuk a Print metdust kivltdik a PrintDocument PrintPage esemnye, ahol a tnyleges nyomtatst fogjuk megcsinlni:
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e) { Font font = new Font("Arial", 10); float maxLine = e.MarginBounds.Height / font.GetHeight(e.Graphics); int i = 0; while (i < maxLine && i < richTextBox1.Lines.Length) { float y = i * font.GetHeight(e.Graphics) + e.MarginBounds.Top; e.Graphics.DrawString(richTextBox1.Lines[i], font, Brushes.Black, e.MarginBounds.Left, y, new StringFormat()); ++i; } e.HasMorePages = i < richTextBox1.Lines.Length; }

Az esemny paramtere a nyomtatsrl tartalmaz informcikat, pl marg, de ezt fogjuk hasznlni a tnyleges nyomtatshoz is. Els knt ltrehoztunk egy Font tpus objektumot, ez a bet tpust fogja jelenteni. Ezutn kiszmoljuk, hogy a belltott bet tpus s mret mellett hny sor fr el egy oldalra, vagyis elosztjuk a lap magassgt a bet k magassgval. Most egy while ciklust indtunk, amely vagy addig megy amg telerja alapot, vagy az adatforrs vgig. Itt kihasznltuk, hogy a RichTextBox Lines tulajdonsga az egyes sorokat adja vissza egy stringtmb formjban. A ciklusban kiszmoljuk, hogy hol kezd dik az adott sor (vagyis az y koordintt): megszorozzuk a bet k magassgt az aktulis sor szmval, ezt az rtket pedig hozzadjuk a lap tetejnek rtkhez. A kvetkez lpsben az esemny paramtert hasznljuk, annak is a DrawString metdust, ennek els paramtere a nyomtatand sor, a msodik a bet tpus, a harmadik a bet k szne, a negyedik s tdik a kezds x illetve y koordintja, vgl pedig a formtumstring, amelyet resen hagytunk. Az esemny legvgn belltjuk a HasMorePages logikai (bool tpus) tulajdonsgot, vagyis azt, hogy van e mg nyomtatand szveg. Ha van, akkor az esemny automatikusan jrakezd dik (pontosabban folytatdik). Az el re megrt prbeszdablakok kzl az utols a FolderBrowserDialog, amelynek segtsgvel kivlaszthatunk a knyvtrstruktrbl egy mappt. Ksztsn kegy programot, amely gombnyomsra megjelent egy FolderBrowserDialog ot, s miutn a felhasznl kivlasztott egy mappt egy TextBox ban jelentsk meg a mappa elrsi tjt. A gomb Click esemnye kvetkez lesz:
private void button1_Click(object sender, EventArgs e) { if (folderBrowserDialog1.ShowDialog() == DialogResult.OK) { textBox1.Text = folderBrowserDialog1.SelectedPath; } }

192

36.20 TabControl
Ezzel a vezrl vel tbb prhuzamosan ltez rszekb l egyszerre csak egy ltszik: rszre oszthatjuk a formot, amely

Az egyes lapok TabPage tpusak, amelyek lekrhet ek a TabControl TabPages tulajdonsgval, amely egy TabPageCollection tpus listval tr vissza (ez felhasznlhat egy foreach ciklusban, a lista egyes elemei nylvn TabPage tpusak). A TabControl nak nincs alaprtelmezett esemnye, ehelyett az adott lap Click esemnyt kezeljk, ha dupln kattintunk a vezrl re (ezt minden lapnl kln kell megtennnk).

36.21 ContextMenuStrip
Biztosan mindenki ismeri a jobb egrgombra el ugr helyi ment, ez a vezrl pontosan err l szl. Brmelyik vezrl hz hozzkthetjk, ha belltjuk a ContextMenu tulajdonsgt. A ContextMenuStrip tulajdonkppen a mr ismert MenuStrip mobilizlt vltozata, minden amit ott tanultunk itt is hasznosthat.

36.22 Gyakorl feladatok


1. Ksztsnk programot, amely a Misszionrius - kannibl problmt modellezi. Egy foly egyik partjn 3 kannibl s 3 misszionrius ll. A cl az, hogy egy csnakban mindenkit tjuttasunk a tlpartra. A csnak ktszemlyes s semelyik parton nem lehet tbb kannibl, mint misszionrius, egybknt a kanniblok megeszik ket. A kt partot jelezheti mondjuk kt ListBox. 2. Ksztsnk egyszer szvegszerkeszt t a RichTextBox s a menvezrl k segtsgvel. Lehessen menteni, betlteni s formzni is.

193

37.Windows Forms Komponensek


A komponensek olyan osztlyok, amelyek kzvetlenl nem ltszanak a formon, de specilisan arra terveztk ket, hogy Windows Forms krnyezetben m kdjenek a legjobban. Ebben a fejezetben nhny fontosabb komponenssel ismerkednk meg.

37.1 Timer
A Timer egy olyan komponens, amely lehet v teszi, hogy bizonyos id kznknt vgrehajtsunk egy utastst. Ksztsnk egy programot, amely automatikusan mozgat egy vezrl t (mondjuk egy gombot) egy formon. A ToolBox bl hzzunk egy Timer t a formra (a Component fl alatt tallhat), s egy tetsz leges vezrl t is (a pldban gomb lesz). A Timer tulajdonsgai kzt talljuk az Interval t, amely az esemnynk gyakorisgt fogja jellni ezredmsodpercben (teht ha az Interval 1000, akkor msodpercenknt fog aktivldni az esemny). Az aktivlt esemny pedig a Timer alaprtelmezett esemnye a Tick lesz. A programunkban a Tick kdja ez lesz:
private void timer1_Tick(object sender, EventArgs e) { if (button1.Location.X + button1.Width + 10 < this.Width) { button1.Location = new Point(button1.Location.X + 10, button1.Location.Y); } else { timer1.Stop(); } }

A gombot addig mozgatjuk balra, amg elri a form szlt, ha pedig odart akkor meglltjuk a Timer t. Valahol azonban el is kell indtanunk, erre pedig a legjobb hely a gomb Click esemnye:
private void button1_Click(object sender, EventArgs e) { timer1.Start(); }

A Timer nincs felksztve tbbszl vgrehajtsra, ha erre vgyunk, akkor hasznljuk a System.Timers nvtr Timer osztlyt.

37.2 ErrorProvider
Ezzel a komponenssel egy hibazenetet kthetnk egyes vezrl khz, ez alaprtelmezetten egy ikonnal jelenik meg: -

194

A vezrl hz ktst az ErrorProvider SetError metdusval tudjuk megtenni, els paramtere a vezrl amihez ktnk, a msodik pedig a hibazenet. A fenti program egy - nagyon egyszer belptet rendszer. Hzzuk r a formra a megfelel vezrl ket s egy ErrorProvider t is (ez alul fog megjelenni). A felhasznlneveket s jelszavakat egy Dictionary gy jtemnyben fogjuk eltrolni:
private Dictionary<string, string> users = new Dictionary<string, string>(); private void Form1_Load(object sender, EventArgs e) { users.Add("Istvan", "istvan"); users.Add("Judit", "judit"); users.Add("Ahmed", "ahmed"); }

Gombnyomsra fogjuk ellen rizni a megadott adatok helyessgt:


private void button1_Click(object sender, EventArgs e) { if (users.ContainsKey(textBox1.Text)) { if (users[textBox1.Text] == textBox2.Text) { MessageBox.Show("Helyes felhasznlnv s jelsz!"); } else { errorProvider1.SetError(this.textBox2, "Rossz jelsz!"); } } else { errorProvider1.SetError(this.textBox1, "Nem ltez felhasznl!"); } }

37.3 BackGroundWorker
Ezzel a komponenssel egy bizonyos feladatot egy httrben fut szlban vgezhetnk el. Ez olyankor hasznos, ha a feladat id ignyes (pl. egy file letltse) s nem engedhetjk meg, hogy addig a form ne tudja fogadni a felhasznl utastsait. Az utasts vgrehatshoz ltre kell hoznunk a komponens DoWork esemnynek a kezel jt. Ez az esemny a BackGroundWorker RunWorkerAsync metdusnak meghvsakor vltdik ki (ennek tlterhelt vltozata paramtert is kaphat). A folyamat llapott lekrdezhetjk a ProgressChanged esemny

195 kezelsvel, illetve a feladat teljestst a RunWorkerCompleted esemny kezeli le. Ahhoz, hogy hasznlhassuk a ProgressChanged esemnyt a komponens WorkerReportsProgress tulajdonsgt igaz rtkre kell lltanunk. A BackGroundWorker nem tmogatja a szlak kztti kommunikcit, gy a felhasznli fellet nem mdosthat a DoWork esemnyb l, ellenkez esetben InvalidOperationException tpus kivtelt kapunk. Ugyangy nem lehetsges Application Domain-ek kztti kommunikcit sem vgrehajtani. A pldaprogramban egy file letltst fogjuk szimullni. Hzzunk a formra egy ProgressBar t, egy gombot s egy Label t. A ksz program gy nz ki:

A letltst gombnyomsra indtjuk:


private void button1_Click(object sender, EventArgs e) { backgroundWorker1.RunWorkerAsync(); }

A DoWork esemnykezel ez lesz:


private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { for (int i = 0; i < 100; ++i) { System.Threading.Thread.Sleep(200); backgroundWorker1.ReportProgress(i + 1); } }

A ReportProgress metdus fogja kivltani a ProgressChanged esemnyt. Ez a metdus paramtereknt a folyamat llapott kapja szzalkos rtkben (a ciklusvltoz rtkhez azrt adtunk egyet, hogy a vals rtket mutassa, hiszen nullrl indul). Ennek a metdusnak van egy tlterhelt vltozata is, amely msodik paramtereknt egy object tpus vltozt vr, amely tetsz leges adatot tartalmazhat. Ezt a ProgressChangedEventArgs UserState tulajdonsgval krdezhetjk le. Vgl a ProgressChanged s a RunWorkerCompleted esemny:

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { ++progressBar1.Value; label1.Text = "A folyamat llapota: " + e.ProgressPercentage.ToString() + "%";

196
} private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { MessageBox.Show("A letlts ksz!"); }

37.4 Process
Helyi s tvoli folyamatok vezrlsre ad lehet sget ez a komponens. A tbbszlsgrl szl fejezetben mr talkoztunk vele.

37.5 ImageList
Ennek a komponesnek a segtsgvel tbb kpet tudunk kezelni, hasonlan mint egy gy jtemnnyel. A komponensnek el re megadhatjuk az elemeit, ekkor azok belefordulnak a futtathat llomnyba. A komponensben bellthatjuk, a kpek mreteit s sznmlysgt is. Egy ImageList et pl. felhasznlhatunk egy TreeView vezrl hz, megadva az egyes Node okhoz rendelt ikont. Hzzunk a formra egy ImageList komponenenst s egy TreeView vezrl t. Az ImageList tetejn megjelenik egy kis nyilacska, erre kattintva bellthatjuk a kpek formtumt s itt adhatjuk hozz ket:

A TreeView ImageList tulajdonsgnl llthatjuk be a hasznland komponenst. Adjunk nhny elemet a vezrl hz, ezutn az eredmny:

Termszetesen nem csak egy kpet rendelhetnk az sszes Node hoz, hanem az ImageIndex tulajdonsgon keresztl egyesvel is bellthatjuk azt:
treeView1.Nodes[0].ImageIndex = 0;

197

37.6 Gyakorl feladatok


1. Ksztsnk reflexjtkot, a Timer komponens segtsgvel! A formon egy vezrl (mondjuk egy PictureBox, de teljesen mindegy) adott id kznknt vltoztatja a helyt, a jtkos feladata pedig, hogy rkattintson. A felhasznl llthassa be, hogy milyen gyorsan mozogjon a vezrl , de ne engedjk, hogy ez tl lass legyen. 2. Ksztsnk tekn sversenyt! Az llatokat szimbolizl vezrl k egyenes vonalban mozogjanak s a Timer minden Tick jnl dntsk el, hogy mi trtnik: ha a tekn s pp fradt (ezt is mrjk), akkor az adott krb l kimarad, ha nem, akkor sorsoljunk neki egy vletlenszmot. Ez a szm fogja jelezni, hogy hny mez t lphet el re. Neheztskppen a jtk elejn lehessen fogadni a gy ztesre, illetve beilleszthet ek egyb esemnyek is a jtkmenetbe (pl. csapda). 3. Ksztsnk irodaszimultort! A jtkos a fnk, aki elkldi az alkalmazottait klnbz feladatok elvgzsre. Az alkalmazottak BackgroundWorker ek legyenek, s jelezzk, ha elvgeztk a dolgukat. Neheztskppen akr egy egyszer gazdasgi jtkot is csinlhatunk.

198

38. Windows Forms - j vezrl k ltrehozsa


Ha sajt vezrl ltrehozsn trjk a fejnket kt t ll el ttnk. Vagy egy mr ltez t b vtnk ki szrmaztatssal, vagy teljesen jat hozunk ltre, egy n. UserControl t. Egy harmadik lehet sg, hogy kzvetlenl a Control osztlybl szrmaztatunk, amely minden ms vezrl se. Ezt a metdust ez a jegyzet nem rszletezi. Felmerlhet a krds, hogy melyik mdszert mikor hasznljuk? Ha van olyan vezrl , amely rendelkezik a szksges kpessgek nagy rszvel, akkor a legjobb lesz, kib vtjk azt a szksges metdusokkal, tulajdonsgokkal. Ha viszont olyan vezrl t szeretnnk, ami tbb ms vezrl tulajdonsgait olvasztja magba, akkor UserControl t kell ltrehoznunk.

38.1 Szrmaztats
Ebben az esetben rendkvl egyszer dolgunk lesz, ugyanis egy szimpla szrmaztatst kell vgrehajtanunk. A (nagyon egyszer ) pldaprogramban a ListBox vezrl t fogjuk kib vteni oly mdon, hogy lekrdezhessk a leghosszabb elemt. A Solution Explorer ben kattintsunk jobb egrgombbal a Solution ra, teht ne a projectekre, hanem a legfels bejegyzsre. Vlasszuk az Add menpontot, azon bell pedig az New Project et. A Windows flnl vlasszuk ki a Class Library sablont. Amikor ltrejtt az osztly kattintsunk jobb gombbal az jonnan ltrejtt projectre s vlasszuk az Add Reference menpontot, mivel hozz kell adnunk a megfelel knyvtrakat. A megjelen ablak .NET felirat fln keressk meg a System.Windows.Forms assemblyt s adjuk hozz. Most mr minden adott a fejlesztshez. Az osztly most gy nz ki (hozzadtam a szksges nvtereket s elkezdtem rni az osztlyt is):
using using using using using using using System; System.Collections.Generic; System.ComponentModel; System.Data; System.Drawing; System.Text; System.Windows.Forms;

namespace ExtendedListBox { public class ExtendedListBox : ListBox { public ExtendedListBox() : base() { } } }

Fontos, hogy a konstruktorban ne felejtsk el meghvni az sosztly konstruktort. A beptett vezrl k csakis paramternlkli konstruktorral rendelkeznek, ezrt az j vezrl t is ennek szellemben fogjuk megrni.

199 Ksztsk el a metdust:


public string MaxLengthItem() { string max = ""; foreach (string s in this.Items) { if (s.Length > max.Length) { max = s; } } return max; }

Ez a metdus persze csakis az els leghosszabb elemet adja vissza, ha tbb ugyanolyan hossz is van a ListBox ban, akkor azt nem veszi figyelembe. A vezrl mris hasznlhat llapotban van, de egyel re csakis futsi id ben adhatjuk hozz a formhoz. Termszetesen lehet sg van arra, hogy ezt tervezsi id ben tegyk meg, ehhez els knt fordtsuk le az j vezrl projectjt, a Build menponttal, ekkor ltrejn a vezrl t tartalmaz assembly. Ezutn navigljunk el a form tervez nzethez s kattintsunk jobb egrgombbal a projectre. Vlasszuk az Add Reference menpontot, s kattintsunk a megjelen ablakban a Browser flre. Menjnk el az j vezrl knyvtrba, azon bell pedig a Bin mappba. Itt a fordts tpustl fgg en a Debug vagy Release mappban talljuk a megfelel dll kiterjeszts file t. Adjuk hozz a projecthez. Ezutn j esllyel a ToolBox ban megjelenik a vezrl , ha mgsem, akkor jobb klikk a ToolBox on Add Tab menpont, nevezzk el valahogy, majd szintn jobb klikk az j flre s Choose Items. A .NET Framework Components fl alatt keressk meg a vezrl nket s pipljuk ki (berva a nevt gyorsabban megtalljuk). Ezutn ugyangy hasznlhat a vezrl , mint az sszes tbbi, prbljuk is ki:
private void Form1_Load(object sender, EventArgs e) { Random r = new Random(); for (int i = 0; i < 10; ++i) { extendedListBox1.Items.Add(r.Next().ToString()); } }

Hzzunk egy gombot a formra, ennek a Click esemnyben keressk majd meg a leghosszabb elemet:
private void button1_Click(object sender, EventArgs e) { MessageBox.Show(extendedListBox1.MaxLengthItem()); }

Az eredmny:

200

38.2 UserControl -ok


A UserControl plda egy belptet rendszer lesz. Adjunk egy j projectet a Solution hz, de ezttal vlasszuk a Windows Forms Control Library sablont. Amikor ltrejtt a project, akkor egy res alapot kell ltnunk, erre hzhatjuk r a vezrl ket:

A vezrl valahogy gy nzzen ki:

Ezenkv l legyen egy ErrorProvider is, ami jelzi majd a hibkat. A vezrl kdjt megnyithatjuk, ha jobb egrgombbal kattintva a View Code menpontot vlasztjuk:
using using using using System; System.Collections.Generic; System.ComponentModel; System.Drawing;

201
using using using using System.Data; System.Linq; System.Text; System.Windows.Forms;

namespace WindowsFormsControlLibrary2 { public partial class UserControl1 : UserControl { public UserControl1() { InitializeComponent(); } } }

Lthat, hogy az j osztly a UserControl bl szrmazik, ezenkv l rendelkezik egy a tervez ltal generlt metdussal, akrcsak a f programunk. A UserControl osztly a ContainerControl leszrmazottja, gy rendelkezik az sszes olyan tulajdonsggal, amely ahhoz szksges, hogy kezelni tudja a rajta (benne) elhelyezett vezrl ket (tbbek kzt a Form osztly is az utdja). A vezrl t gy fogjuk elkszteni, hogy jrafelhasznlhat legyen, ezrt nem getjk bele a felhasznlk adatait, hanem egy tulajdonsgon keresztl adjuk meg azokat. A ksz osztly gy nz majd ki:
using using using using using using using using System; System.Collections.Generic; System.ComponentModel; System.Drawing; System.Data; System.Linq; System.Text; System.Windows.Forms;

namespace WindowsFormsControlLibrary2 { public partial class UserControl1 : UserControl { public UserControl1() { InitializeComponent(); } } }public partial class LoginControl : UserControl { private Dictionary<string, string> loginData = null; public LoginControl() { InitializeComponent(); } public Dictionary<string, string> LoginData { set { loginData = value; }

202
} private void button1_Click(object sender, EventArgs e) { if (loginData.ContainsKey(textBox1.Text)) { if (loginData[textBox1.Text] == textBox2.Text) { MessageBox.Show("Sikeres belps!"); } else errorProvider1.SetError(textBox2, "Rossz jelsz!"); } else errorProvider1.SetError(textBox1, "Nem ltezik a felhasznl!"); } }

A LoginData lesz az a tulajdonsg, amelyen keresztl megadjuk a nv jelsz prokat. Hzi feladat: mdostsuk a programot, hogy tetsz leges metdust hvhasssunk sikeres belps esetn. Kulcssz: delegate. A tervezsi id ben val tmogatst pontosan ugyangy rhetjk el, mint a szrmaztatott vezrl k esetben. Prbljuk ki:
private void Form1_Load(object sender, EventArgs e) { Dictionary<string, string> logData = new Dictionary<string, string>(); logData.Add("Istvan", "istvan"); logData.Add("Judit", "judit"); logData.Add("Balazs", "balazs"); loginControl1.LoginData = logData; }

s az eredmny:

203

39. Rajzols: GDI+


A GDI (Graphics Device Interface) a Microsoft Windows grafikus alrendszere, amelynek szolgltatsait hasznlja fel az opercis rendszer a felhasznli fellet megjelentshez. Hasznlathoz nem szksges ismerni a hardvert, a GDI API automatikusan kitallja, hogy hogyan kell vgrehajtania az utastsokat. A GDI+ a GDI Windows XP ben megjelent utdja, ez mr fejlettebb grafikus kpessgekkel rendelkezik. A .NET Framework menedzselt felletet nyjt a GDI+ hasznlathoz, a System.Drawing nvtr osztlyain keresztl (korbban, amikor a PrintDialog gal foglalkoztunk mr tallkoztunk ezekkel az osztlyokkal). Els programunkban rajzoljunk ki egy krt egy formra. Ehhez szksgnk lesz egy Graphics tpus objektumra (n. Device Context, ez tartalmazza a megfelel informcikat az objektumrl), amely kezeli az adott vezrl grafikus megjelenst (ilyet brmelyik vezrl t l krhetnk). Ezt az objektumot a vezrl n meghvott CreateGraphics metdussal kaphatjuk meg:
private void button1_Click(object sender, EventArgs e) { Graphics g = this.CreateGraphics(); }

A rajzolst gombnyomsra indtjuk, a form Load esemnyben nem lehet ezt megtenni (ha mgis fggetlenl akarunk rajzolni, akkor vlasszuk a Paint esemnyt vagy a konstruktort). A krt a DrawEllipse metdussal fogjuk megjelenteni:
private void button1_Click(object sender, EventArgs e) { Graphics g = this.CreateGraphics(); g.DrawEllipse(new Pen(Brushes.Red), 0, 0, 30, 30); }

Az els paramterrel a sznt lltjuk be, a msodik s harmadik pedig az alakzat kezd koordintjt, ezek nem ngyzetes alakzat esetn a skidom kr rajzolt legsz kebb ngyzet bal fels sarkt jellik (az brn a pirossal jelzett pont):

Az utols kt paramterrel a szlessget s magassgot lltjuk be. A program eredmnye:

204

A Graphics objektumot biztos felszabadtsa rdekben rdemes using gal egytt hasznlni:
private void button1_Click(object sender, EventArgs e) { using (Graphics g = this.CreateGraphics()) { g.DrawEllipse(new Pen(Brushes.Red), 0, 0, 30, 30); } }

A DrawEllipse (s ms metdusok is) egy vltozata csak kt paramtert vr, ahol a msodik egy Rectangle objektum, amely a fentebb mr emltett ngyzetet jelkpezi. A krt kirajzol sort gy is rhattuk volna:
g.DrawEllipse(new Pen(Brushes.Red), new Rectangle(0, 0, 30, 30));

Egy alakzatot ki is sznezhetnk a megfelel krnkkel:

Fill* metdussal. Tegyk ezt meg a

private void button1_Click(object sender, EventArgs e) { using (Graphics g = this.CreateGraphics()) { g.DrawEllipse(new Pen(Brushes.Red), new Rectangle(0, 0, 30, 30)); g.FillEllipse(Brushes.Red, new Rectangle(0, 0, 30, 30)); } }

205 A Fill* metdusok a kitltend terletet vrjk msodik paramterl.

39.1 Kpek kezelse


Kpek beolvassra hasznlhatjuk a Bitmap osztlyt (amely a System.Drawing nvtrben tallhat). Hzzunk a fomra egy PictureBox ot a form Load esemnybe pedig rjuk:
private void Form1_Load(object sender, EventArgs e) { Bitmap bm = new Bitmap("Naplemente.jpg"); pictureBox1.Image = bm; }

A Bitmap elfogad stream et is. A GDI+ segtsgvel kzvetlenl a fomra (vagy brmely vezrl re) is kihelyezhetnk kpeket, hasznlhatjuk ehhez pl. az ImageList komponenst:
private void button1_Click(object sender, EventArgs e) { using (Graphics g = this.CreateGraphics()) { imageList1.Draw(g, new Point(0, 0), 0); } }

A Draw metdus els paramtere a megfelel Graphics objektum, msodik helyen ll a kezd koordinta vgl pedig a kirajzoland kp indexe. Egy msik fggetlenebb mdja a kzvetlen rajzolsnak az Image osztly hasznlata:
private void button1_Click(object sender, EventArgs e) { using (Graphics g = this.CreateGraphics()) { Image img = Image.FromFile("Naplemente.jpg"); g.DrawImageUnscaled(img, new Point(0, 0)); } }

Ez a metdus a kpet eredeti nagysgban jelenti meg, amennyiben ezt mdostani akarjuk hasznljuk a DrawImage metdust.

206

40. Drag and Drop


A feladat a kvetkez : adott kt ListBox, ksztsnk programot, amely lehet v teszi, hogy tetszs szerint hzzunk elemeket egyikb l a msikba. Az els dolog amit meg kell tennnk (persze a ListBox ok formra helyezse utn), hogy a vezrl k AllowDrop tulajdonsgt igaz rtkre lltjuk, ezzel engedlyezve a fejezet cmben is szerepl metdust. Adjunk hozz nhny elemet az els ListBox hoz:
private void Form1_Load(object sender, EventArgs e) { listBox1.Items.Add("Istvan"); listBox1.Items.Add("Judit"); listBox1.Items.Add("Balazs"); listBox1.Items.Add("Viktoria"); }

A Drag and Drop m veletet akkor indtjuk el, amikor az egrgombot nyomva tartjuk egy elem felett, vagyis a MouseDown esemnyben (mindkt vezrl hasznlhatja ugyanazt az esemnyt).
private void listBox2_MouseDown(object sender, MouseEventArgs e) { this.DoDragDrop(((ListBox)sender).SelectedItem.ToString(), DragDropEffects.Move); }

Az mr lthat, hogy a form fogja levezrelni a vonszolst. A DoDragDrop metdus els paramtere az az rtk lesz, amit mozgatni akarunk, vagyis a kivlasztott elem, a msodik paramter pedig a milyensget fogja meghatrozni. Ha most elindtjuk a programot, akkorr megragadva egy elemet az egrmutat megvltozik. Tegyk fel, hogy jelezni akarjuk ha az ppen hzsban lv elemmel egy vezrl fl rnk. Ezt az adott vezrl DragEnter esemnyben tehetjk meg (ez szintn egy olyan esemnykezel lesz, amin a kt vezrl osztozhat). Mondjuk szinezzk t a htteret. Azt se felejtsk el, hogy vissza kell lltani az eredeti sznt, ezt a DragLeave esemnyben fogjuk megcsinlni. A kt metdus:
private void listBox2_DragEnter(object sender, DragEventArgs e) { ((ListBox)sender).BackColor = Color.Red; } private void listBox2_DragLeave(object sender, EventArgs e) { ((ListBox)sender).BackColor = Color.White; }

A DragOver esemny fogja eldnteni, hogy engedlyezzk e, hogy ledobhassuk az elemet:


private void listBox2_DragOver(object sender, DragEventArgs e) {

207
e.Effect = DragDropEffects.Move; }

Most mr az egrmutat is megvltozik a ListBox ok fltt. Egyetlen dolog van mg htra, mghozz a dolog lnyege, az informci elengedse. Amikor ezt megtesszk, akkor a DragDrop esemny lp m kdsbe:
private void listBox2_DragDrop(object sender, DragEventArgs e) { ((ListBox)sender).Items.Add(e.Data.GetData(DataFormats.Text).ToString()); ((ListBox)sender).BackColor = Color.White; }

A httrsznt vissza kell lltani, mert ebben az esetben a DragLeave esemny mr nem kvetkezik be s piros maradna.

208

41. Windows Presentation Foundation


A WPF teljesen j alapokra helyezi a grafikus megjelentst, mg eddig a Windows API fggvnyeit hvtuk meg s a GDI segtsgvel szoftveresen rajzoltuk ki az alkalmazsok kezel fellett, most kzvetlenl a DirectX fogja hardverb l megtenni ezt. Ez a technolgia egyttal fggetlent minket (elvileg) az opercis rendszert l is. Ez a vlts az elmlt tizegynhny v egyik legnagyobb jelent sg fejlesztse, mivel teljesen j platformot kapunk. Eddig amikor Visual Basic, MFC stb. platformokon fejlesztettnk, ugyanazt tettk, csak ppen mindig fejlettebb krnyzetben. Ennek aztn meg is volt ra, hiszen a rendszer alapvet hinyossgait, gyengesgeit egyik sem tudta kikszblni. A WPF megjelensvel minden megvltozott, hiszen a DirectX egyetlen cllal jtt vilgra, mgpedig villmgyors vizulis megjelentsre. Termszetesen a WPF sem vltja meg a vilgot, pldul a felhasznl interaktivitst mg mindig a Windows API kezeli, de a megjelentst teljes egszben a WPF veszi t a GDI t l. Egy fontos krds felmerlhet, mgpedig az, hogy mi van azokkal a szmtgpekkel, amelyek nem rendelkeznek er s videkrtyval? Nos, erre is van megolds, a WPF kpes szoftveresen is megjelenteni az alkalmazst, termszetesen ez kicsit lassabb lesz. A WPF arra trekszik, hogy a lehet legtbb munkt bzza a clhardverre, de ezt nem er lteti. Ennek megfelel en a videkrtya kpessgei alapjn hromfle besorolsa lehet egy rendszernek: - Rendering Tier 0.: nincs hardveres gyorsts, DirectX 7.0 vagy alatta. - Rendering Tier 1.: alap (rszleges) hardveres gyorsts, DirectX 7.0 vagy felette, de 9.0 alatt. - Rendering Tier 2.: teljes kr hardveres gyorsts, a krtya mindent tud, amit a WPF elvr (Pixel Shader, Vertex Shader, stb.), DirectX 9.0 vagy fltte. Szemltetsknt lssuk a Rendering Tier 2. hardverignyt: DirectX 9.0 vagy fltte, a videkrtya legalbb 120 MB memrival rendelkezik, emellett tmogatja a Pixels Vertex Shader 2.0 t illetve legalbb ngy textrz egysggel br. Tovbbi informcikkal az MSDN megfelel oldala szolgl: - http://msdn.microsoft.com/en-us/library/ms742196.aspx Nzzk meg ,mit knl a megjelents mellett a WPF: Animcik Adatktsek Audio- s vide vezrls Stlusok s sablonok Stb.

209 Mg kt a WPF hez kt d technolgirl is meg kell emlkeznnk, ezek az XBAP s a Silverlight. Els rnzsre mindkett ugyanazt teszi: bngsz b l rhetnk el egy felhasznli felletet. A megvalsts azonban alapjaiban klnbzik: az XBAP a WPF teljes funkcinalitst nyjtja, hiszen ugyanazt a .NET Framework t hasznlja (ugyanakkor a nagytestvrhez kpest vannak korltai, mivel egy n. sandbox ban fut ks bb). Ezzel szemben a Silverlight a Flash ellenlbasaknt egy miniat r .NET FW teleptst ignyli (~5MB), gy a lehet sgek nmileg korltozottabbak, cserben platformfggetlen (Macintosh s Windows rendszerek al Silverlight nven rkezik, mg Linux alatt a Novell szolgltatja az alternatvt, amelyet Moonlight nak hvnak).

41.1 A WPF architectra


Legfels bb szinten termszetesen a C#/Visual Basic.NET/stb. nyelven megrt menedzselt kd tanyzik. Amikor egy ilyen porgramot futtatunk, akkor a krsek a WPF API hoz futnak be, amelyet a szmtgpen a PresentationFramework.dll, PresentationCore.dll s a WindowsBase.dll szerelvnyek jelkpeznek. Ezek teljes egszben menedzselt kdon alapulnak. k fogjk tovbbtani a krseket a Media Integration Layer nek (MIL), amely az sszekt kapcsot jelkpezi a .NET s a DirectX kztt. Ez a milcore.dll s WindowsCodecs.dll fileokban l (a milcore t hasznlja a Windows Vista is a grafikus fellet megjelentsre). A MIL mr egy natv felletet biztost s kzvetlenl hvja a DirectX szolgltatsait. Vgl a WPF architechtra rszt kpezi a Windows API is, amelyet korbban mr emltettnk.

41.2 A WPF osztlyhierarchia


A WPF meglehet sen tlburjnzott osztlyhierarchival jtt a vilgra, viszont ennek ismerete elengedhetetlen a ksbbi fejlesztshez, ezrt lljon itt egy ismertet :

Termszetesen mint minden mese ez is az Object osztllyal kezd dik, alatta eggyel pedig egy absztrakt osztly a DispatcherObject tartzkodik. Ez az osztly a

210 System.Threading nvtrben tallhat s fogja kontrollllni a szlak kzti kommunikcit. Mirt van erre szksg? A WPF az n. Single-Thread Affinity (STA) modellel dolgozik, ami azt jelenti, hogy a felhasznli felletet egyetlen f szl irnytja (s ezrt a vezrl ket is csak a sajt szlukbl lehet mdostani). Igen m, de valahogyan a tbbi szlbl rkez zeneteket is fogadni kell pl. a billenty zet vagy az egr megmozdulsait. s itt jn a kpbe a DispatcherObject, amelynek szolgltatsait meghvhatjuk ms szlbl is. Kvetkez a sorban a DependencyObject, amely az n. Dependency Property k lehet sgt adja hozz a csomaghoz (err l hamarosan b vebben). A DependencyObject a System.Windows nvtrben van. A System.Windows.Media nvtrbeli Visual absztrakt osztly felel s a tnyleges megjelentsrt s a kpezi a kapcsolatot a MIL lel is. A Visual bl szrmaz System.Windows.UIElement tbbek kztt az esemnyekrt felel s, de intzi a vezrl k elrendezst s a felhasznl interaktivitsnak kezelst. is. Az UIElement leszrmazottja a FrameworkElement, amely tnylegesen megvalstja azokat a szolgltatsokat, amiket a WPF hasznl (pl.: az UIElement felel s az elrendezsrt, de a FrameworkElement implementlja az ehhez szksges tulajdonsgokat, pl.: Margin). A Shape s a Panel abszrakt osztlyok, el bbi a primtv alakzatok (kr, ngyszg, stb.) se, utbbi pedig azon vezrl k atyja, amelyek kpesek gyermek vezrl ket kezelni (pl. StackPanel). Most jn az rdekes rsz, a Control osztly. A Windows Forms esetben megszokhattuk, hogy minden ami egy formon lehet, az vezrl . A WPF vilgban ez nem gy van, minden elemet element nek neveznk, nhny ezek kzl pedig vezrl (pl. minden vezrl , amely fkuszba kerlhet illetve azok, amelyeket a felhasznl is hasznlhat ennek alapjn amg a Windows Forms ban a Label vezrl volt, itt mr nem lenne az). Ennek az osztlynak is van kt leszrmazottja, ezek kzl a ContentControl az ami jdonsg lesz: ez az osztly azokat a vezrl ket jelkpezi, amelyeknek van tartalma (szveg, kp, stb.). Az ItemsControl azoknak a vezrl knek az se, amelyek maguk is tbb elemet tartalmaznak, pl. a ListBox.

41.3 XAML
Az XAML egy XML b l elszrmazott lernyelv, amely segtsgvel definilni tudjuk a WPF elemeinek elrendezst (illetve mg ms dolgokat is, de rluk a megfelel id ben). Az XAML hasonl ahhoz, amit a Windows Forms esetben mr lttunk, teht a felhasznli felletet elvlasztjuk a tnyleges forrskdtl. Van azonban egy nagy klnbsg, mgpedig az, hogy ezttal valban klnvlasztva l a kett , ellenben a WF megoldstl, ahol minden C#/VB:NET/stb. forrskdbl llt, maximum azt nem kzzel rtuk, hanem a fejleszt eszkz generlta..

211 XAML t tbbflekppen is ltrehozhatunk, az els mdszer, amit a jegyzet is hasznlni fog az az, hogy egyszer en kzzel begpelnk mindent (a Visual Studio nyjt IntelliSense tmogatst ehhez). A msik kett a Visual Studio tervez nzete (de csak az SP1) illetve a Microsoft Expression Blend nev program, ezek grafikus felletet nyjtanak a tervezshez.

41.3.1 Fordts
Mi trtnik vajon az XAML lel, amikor lefordtunk egy WPF alkalmazst? Az XAML t gy terveztk, hogy jl olvashat s knnyen megrthet legyen, ennek megfelel en azonban egy kicsit b beszd . Hogy a programok futst felgyorstsk valahogyan meg kell kurttani ezt, ezrt az XAML a fordts utn BAML l (Binary Application Markup Language) alakul, ami a forrs binris reprezentcija. A BAML tokenizlt, ami nagy vonalakban azt jelenti, hogy a hossz utastsokat lervidti, gy jval gyorsabban tud betlt dni a file mint a nyers XAML.

41.3.2 Logikai- s Vizulis fa


Vegyk az albbi egyszer XAML kdot:
<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Grid> <ListBox> <ListBoxItem Content="item1" /> <ListBoxItem Content="item2" /> </ListBox> </Grid> </Window>

A formon ltrehozunk egy ListBox ot, amelyhez hozzadunk kt elemet. A logikai fa a kvetkez lesz:

A magyarzat el tt lssuk a vizulis fa egy rszlett is:

212

Hasonltsuk ssze a kett t: az els esetben az alkalmazsnak azon elemeit lttuk, amelyek tnylegesen a szemnk el tt vannak, vagyis a logikai felptst. A vizulis fa esetben mr bejnnek a rszletek is, azaz az egyes elemek kerete, szne, stb (termszetesen a fenti bra messze nem teljes). Fontos megjegyezni, hogy a kt fa meglte nem ignyli az XAML t, akkor is lteznek, ha mindent forrskdbl hozunk ltre.

41.3.3 Felpts
Nzzk meg mg egyszer az el z kdot:
<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Grid> <ListBox> <ListBoxItem Content="item1" /> <ListBoxItem Content="item2" /> </ListBox> </Grid> </Window>

Minden XAML dokumentum tetejn egy olyan elem foglal helyet, ami alkalmas erre, egy n. top-level-element. A WPF tbb ilyet is definil: Window Page Application

Minden XAML dokumentum ahogyan az XML dokumentumok is - egyetlen gykrelemet engedlyez, teht a top-level-element lezrsa (</Window>) utn mr nem llhat semmi. Lpjnk eggyel tovbb. Az x nvtr prefixxel a project szmra biztostunk egy sablont, a fenti esetben a code-behind osztlyt adtuk meg. Ez egy nagyon rdekes dolog: a code-behind osztlyt a fordt fogja generlni, de hla a parcilis osztlyok intzmnynek mi magunk is hozzadhatjunk forrskdot, err l hamarosan. Ezzel a prefixxel tbb ms helyen is fogunk tallkozni. Alaprtelmezs szerint az elemek nem rendelkeznek kln azonostval (ahogy azt a Windows Forms esetben megszokhattuk). Ha szeretnnk ket megjellni, akkor az x:Name attribtumot kell hasznlnunk:

213

<ListBox x:Name="ListBoxWithName"> <ListBoxItem Content="item1" /> <ListBoxItem Content="item2" /> </ListBox>

Mostantl kezdve a hagyomnyos forrskdbl is ezen a nven hivatkozhatunk a ListBox ra. Ezenkv l nhny osztly amely rendelkezik a RunTimeNameProperty attribtummal (pldul a FrameworkElement s gy az sszes leszrmazotta) birtokol egy Name nev tulajdonsgot, amely segtsgvel futsid ben is bellthatjuk az elem nevt:
ListBoxWithName.Name = "NewName";

Ekkor XAML b l is hasznlhatjuk ezt a tulajdonsgot, teht ez is egy helyes megolds:


<ListBox Name=" ListBoxWithName " > <ListBoxItem Content="item1" /> <ListBoxItem Content="item2" /> </ListBox>

Egy vezrl tulajdonsgait bellthatjuk inline s explicit mdon is. Lssunk mindkt esetre egy pldt:
<TextBlock Text="Inline definci" /> <TextBlock> <TextBlock.Text>

Ez pedig az explicit definci


</TextBlock.Text> </TextBlock>

Trjnk vissza a fejlchez. Az xmlns s xmlns:x attribtumok els rnzsre egy weboldalra mutatnak, de valjban nem. Ezek nvterek, az els a WPF alaprtelmezett nvterre mutat, a msodik pedig az XAML kezelshez szksges dolgok nvterre. Mirt van ez gy? Els dlegesen azrt, mert egy hagyomnyos XML dokumentum is gy pl fel, ennek oka pedig az, hogy az egyes gyrtk gond nlkl hasznlhassk a sajt elnevezsket (a schemas.microsoft.com a Microsoft tulajdonban ll, s elg valszn tlen, hogy ms is hasznln). A fordt ezekb l a cmekb l tudni fogja, hogy hol keresse a szmra szksges osztlyokat. A gykrelem rendelkezik mg nhny attribtummal is, ezeknek a cljt nem nehz kitallni. Ami fontos, az az, hogy egy XAML dokumentumban minden element egy ltez WPF osztlyra mutat, s minden attribtum az adott osztly egy tulajdonsgra. Kvetkezik egy Grid objektum, amely kivlan alkalmas a vezrl k elrendezsre (ezt majd ltni fogjuk). Ezenkv l ez egy Panel leszrmazott, gy lehetnek gyermekei.

214 Rendben, most nzzk meg a code-behind kdot. Alaprtelmezs szerint a Visual Studio ezt generlja:
namespace JegyzetWPF {
/// <summary> /// Interaction logic for Window1.xaml /// </summary>

public partial class Window1 : Window { public Window1() { InitializeComponent(); } } }

Ez elgg hasonlt arra, amit mr lttunk akkor, amikor Windows Forms szal dolgoztunk. Az InitializeComponent metdus azonban ms funkcionalitssal br. Jobb egrgombbal klikkeljnk rajta s vlasszuk a Go To Definition lehet sget. Ami el szr felt nhet, az az, hogy a vezrl ket nem itt adja hozz, mint ahogy azt a WF tette. Ahhoz, hogy megtudjuk hogyan jn ltre az alkalmazs vizulis fellete nzzk a kvetkez sorokat:
System.Uri resourceLocater = new System.Uri("/JegyzetWPF;component/window1.xaml", System.UriKind.Relative); #line 1 "..\..\Window1.xaml" System.Windows.Application.LoadComponent(this, resourceLocater);

Ezek az utastsok betltik majd kibontjk az er forrsknt begyazott BAML t, ebb l pedig mr fel tudja a WPF pteni a felhasznli felletet. Ltrejn minden egyes vezrl objektuma s hozzcsatolja az esemnykezel ket is.

215

42. WPF esemnyek s tulajdonsgok


Ksztsnk egy egyszer programot: egy TextBox s egy Button vezrl vel. A gomb megnyomsakor helyezznk a TextBox ba valamilyen szveget. Az XAML a kvetkez lesz:
<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <Grid> <TextBox x:Name="MessageTextBox" Width="100" Height="20" VerticalAlignment="Top" /> <Button x:Name="MessageButton" Width="100" Height="30" Content="Click Me!" VerticalAlignment="Bottom" Click="MessageButton_Click" /> </Grid> </Window>

A VerticalAlignment tulajdonsg a vertiklis (fgg leges) helyzetet lltja be, vagyis a TextBox fell lesz, a gomb pedig alul (ltezik HorizontalAlignment is a vzszintes belltsokhoz). A ksbbiekben megismerkednk hatkonyabb rendezsekkel is, most ez is megteszi. A gombhoz mr hozz is rendeltnk egy esemnykezel t (ezt a Visual Studio krsre ltrehozza), mgpedig a Click esemnyhez. Nzzk is meg:
private void MessageButton_Click(object sender, RoutedEventArgs e) { MessageTextBox.Text = "Hello WPF!"; }

Gyakorlatilag nem klnbzik attl, amit a Windows Forms esetben mr lttunk, egy klnbsg azonban mgis van. A msodik paramter tpusa RoutedEventArgs lesz. Termszetesen ez is egy EventArgs leszrmazott, de utal valamire aminek sokkal nagyobb a jelent sge: eddig megszoktuk azt, hogy az zenetet az az objektum kldi, amelyikkel trtnik valami. A WPF megvltoztatja ezt a helyzetet az n. routed events bevezetsvel, ami lehet v teszi, hogy egy objektum egy esemnyt - ha az kzvetlenl nincs kezelve felutaztassuk (vagy le) a vizulis fn, amg valaki le nem kezeli. Nzzk meg a kvetkez pldt. Az XAML:
<Window x:Class="JegyzetTestWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <Grid>

216
<Label Name="label1" MouseDown="label1_MouseDown"> <TextBlock x:Name="innerText" Text="Hello" /> </Label> </Grid> </Window>

Miel tt rtrnnk az esemnykezelsre vegyk szre, hogy a Label felptst tetszs szerint alakthatjuk, olyan mdon, ahogyan azt a Windows Forms esetben csak usercontrol ok segtsgvel rhettk el. Gyakorlatilag brmilyen vezrl t begyazhatunk ezen a mdon, egyetlen felttel van, mgpedig az, hogy az a vezrl amibe begyazunk ContentControl leszrmazott legyen. Most nzzk az esemnyeket: az UIElement osztlytl minden leszrmazottja rkl egy nagy csom esemnyt az egr s a billenty zet kezelshez, ilyen a MouseDown is, amely az egrrel val klikkelst kvnja megszemlyesteni. Nzzk az esemnykezel t:
private void label1_MouseDown(object sender, MouseButtonEventArgs e) { MessageBox.Show("Event source: " + ((FrameworkElement)e.Source).Name); }

Ez is nagyon egyszer , azt tudjuk, hogy minden vezrl egyben FrameworkElement is s azt is, hogy annak pedig lekrdezhetjk a nevt. A MouseButtonEventArgs egy RoutedEventArgs leszrmazott. A Source tulajdonsg a logikai fa szerinti forrst adja vissza, mg az OriginalSource a vizulis ft veszi alapul. Amikor rkattintunk a Label re, akkor felugrik egy MessageBox:

Igaz, hogy a Label esemnyt kezeltk (volna) le, mgis a TextBlock volt a forrs. Nzzk meg egy kicsit kzelebbr l a routed event eket. Amikor regisztrlunk egy ilyen esemnyre, az vlaszt egyet a hrom lehetsges stratgia kzl: Direct: ez a legltalnosabb, az esemny csakis a forrsobjektumban vltdik ki s csakis kezelheti. Bubbling: az esemny el szr a forrsobjektumban vltdik ki, majd elkezd felfel vndorolni a vizulis fban, amg tall egy kezel t (vagy a gykrhez nem r). Tunneling: az esemny a gykrelemben kezd s lefel vndorol, amg el nem ri a forrsobjektumot (vagy tall egy kezel t).

Az esemny milyensgt a megfelel RoutedEventArgs leszrmazottl krdezhetjk meg, a RoutedEvent tulajdonsg RoutedStrategy tulajdonsgn kereszt l, amely egy felsorolt tpus rtk (rtelemszer en, ha az esemnykezel msodik paramtere RoutedEventArgs tpus, akkor kzvetlenl hvhatjuk a RoutedStrategy t).

217 A pldban a TextBlock MouseDown esemnye a Bubbling stratgit hasznlja, azaz elkezd felfel vndorolni a vizulis fban, amg tall egy bartsgos esemnykezel t (ez volt a Label ). Most nzznk egy rdekesebb pldt:
<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300" ButtonBase.Click="Window_Click"> <Grid> <TextBox Name="messageTextBox" Width="100" Height="20" VerticalAlignment="Top" /> <Button Name="firstButton" Width="100" Height="30" Content="First" HorizontalAlignment="Left"/> <Button Name="secondButton" Width="100" Height="30" Content="Second" HorizontalAlignment="Right"/> </Grid> </Window>

A top-level-element Window minden olyan Click esemnyt el fog kapni, amelyet egy ButtonBase leszrmazott (Button, CheckBox, RadioButton) kld. Nzzk az esemnykezel t:
private void Window_Click(object sender, RoutedEventArgs e) { messageTextBox.Text = ((FrameworkElement)e.Source).Name; }

Mit l olyan rdekes ez a plda? Gondolkodjunk: a Window nem ismerheti a Button t, mgis el tudja kapni a Click esemnyt. Ez az n. attached events (~ csatolt esemnyek) intzmnye miatt van, ami lehet v teszi egy vezrl nek, hogy olyan esemnyt kapjon el, amivel maga egybknt nem rendelkezik. Attached event et kdbl is hozzadhatunk egy vezrl hz:

218
window1.AddHandler(Button.ClickEvent, new RoutedEventHandler(Window_Click));

Egy msik fontos dolog a routed event ek mellett a dependency property k jelenlte. Ezek specilis tulajdonsgok, amelyek hagyomnyos tulajdonsgokba vannak becsomagolva, gy olyan kd is kezelheti ezeket, amelyek nem ismerik a WPF et. A dependency property k (~fgg sgi tulajdonsg) valdi jelensge az, hogy a tulajdonsg rtke fgghet ms tulajdonsgoktl illetve a krnyezet sajtossgaitl (pl. az opercis rendszer). A hagyomnyos tulajdonsgokat ltalban privt elrs adattagok lekrdezsre s rtkadsra hasznljuk, vagyis amit betettnk a gpbe azt kapjuk vissza. A DP ezt a helyzetet megvltoztatja, mivel az aktulis rtk tbb tnyez t l fgg. Nzzk a kvetkez pldt:
<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <Grid> <Label FontSize="42">

Hello WPF!
</Label> </Grid> </Window>

Semmi klns, pontosan azt teszi, amit elvrunk, vagyis megjeleni a szveg 42 es bet mrettel:

Csavarjunk egyet a dolgon:


<Window x:Class="JegyzetTestWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300" FontSize="42"> <Grid> <Label>

Hello WPF!
</Label> </Grid> </Window>

Az egyetlen ami vltozott, az az, hogy a bet mretet a Window nak adtuk meg. Ami rdekes, az pedig az, hogy a program pontosan ugyanazt csinlja, mint az el z . Nzzk meg, hogy mi a titok. A FontSize tulajdonsg gy nz ki a TextBlock esetben:

219
[TypeConverter(typeof(FontSizeConverter)), Localizability(LocalizationCategory.None)] public double FontSize { get { return (double) base.GetValue(FontSizeProperty); } set { base.SetValue(FontSizeProperty, value); } }

Koncentrljunk csak a getter/setter prosra. Els knt, mi az a FontSizeProperty? A defincija a kvetkez :


[CommonDependencyProperty] public static readonly DependencyProperty FontSizeProperty;

Amire mg szksgnk van az az, hogy milyen osztlyra hivatkozunk a base zel? Ez pedig a DependencyObject. Rendben, nzzk sorban, hogy mi is trtnik valjban a msodik pldban: ltrejn a Label s be kell lltania a bet mretet. Csakhogy ilyet mi nem adtunk meg, teht amikor lekrdezn a FontSize tulajdonsgot, akkor elvileg nem kapna semmit. Ekkor a tulajdonsg az s DependencyObject t l lekrdezi a tulajdonsg aktulis rtkt, ami vagy egy a fban nla magasabban elhelyezked (s t magba foglal) vezrl megfelel rtke lesz, vagy pedig a WPF alaprtelmezse (vagy pedig, ha van ltalunk belltott rtke, akkor azt, ez az n. loklis rtk (local value)). Ez fordtva is hasonlan m kdik, ezt a kvetkez pldn lthatjuk is:
<Window x:Class="JegyzetTestWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300" FontSize="42"> <Grid> <Label>

Hello WPF!
</Label> <Button x:Name="changeFontSize" Width="100" Height="30" Content="Change FontSize" Click="changeFontSize_Click" VerticalAlignment="Bottom" /> </Grid> </Window>

Semmi mst nem tesznk, minthogy megvltoztatjuk a Window bet mrett:


private void changeFontSize_Click(object sender, RoutedEventArgs e) { window1.FontSize = 10;

220
}

Amikor a gombra kattintunk, egyttal a Label bet mrete is megvltozik. Teht a SetValue meghvsakor az alrendelt objektumok bet mrete is vltozik (amennyiben nem definilt sajtot). Lteznek n. megosztott tulajdonsgok (shared property), amelyek br klnbz osztlyokban definiltak mgis ugyanarra a DP re hivatkoznak. Ilyen pldul a szveget megjelenteni kpes vezrl k FontFamily tulajdonsga is, amelyek tulajdonkppen mind a TextElement osztly DP jt hasznljk. Hasonlan a routed event ekhez a dependency property k is definilnak egy attached property nev kpessget, amely lehet v teszi, hogy egy msik vezrl dependency property jt hasznljuk. Ezzel leggyakrabban az elemek poziconlsnl tallkozunk majd:
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <DockPanel> <Button DockPanel.Dock="Left" Width="100" Height="30" Content="Left" /> <Button DockPanel.Dock="Right" Width="100" Height="30" Content="Right" /> </DockPanel> </Window>

Most a Grid helyett egy DockPanel t hasznltunk, a gombok pozicit pedig attached property vel adtuk meg. A ks bbiekben mg fogunk tallkozni a fejezet mindkt szerepl jvel, tbbek kztt megtanulunk majd sajt WPF vezrl t kszteni s hozz a megfelel DP ket is.

221

43. WPF Vezrl k elrendezse


A WPF a Windows Forms tl eltr en egy mer ben ms mdszert vezet be a vezrl k pozicinlshoz. El bbi a vezrl k koordintjn alapult, mg a WPF egy leginkbb a HTML hez hasonl (de annl jval sszetettebb) megoldst kapott (ugyanakkor koordintkon alapul elrendezsre is van lehet sg, de nem az a standard). A top-level-element (pl. a Window) egyetlen elemet kpes magban hordozni, ami szksgess teszi azt, hogy az az egy elem tudjon gyermek vezrl ket kezelni. Ezeket az elemeket kontnernek (container) nevezzk s kzs jellemz jk, hogy a Panel absztrakt osztlybl szrmaznak (lsd: WPF osztlyhierarchia). Hat darab ilyen osztlyunk van, nzzk meg a jellemz iket: Grid: a leggyakrabban hasznlt, az elemeit sorok s oszlopok szerint osztja be. UniformGrid: minden elem ugyanolyan mret cellkban foglal helyet, viszonylag ritkn hasznlt. StackPanel: vzszintesen vagy fgg legesen helyezkednek el az elemei, ltalban egy msik kontnerben foglal helyet, az alkalmazs kisebb szekciinak kialaktsra hasznljuk. DockPanel: az elemeit az egyes oldalaihoz rendelhetjk. WrapPanel: egyenes vonalban helyezkednek el az elemei (az Orientation tulajdonsgn kereszt l vzszintes vagy fgg leges lehet a vonal). Canvas: koordinta alap elhelyezkedst tesz lehet v

Az egyes kontnerek egymsba gyazhatak, vagyis kszthetnk egy Grid et, ami tartalmaz egy StackPanel t, ami tartalmaz egy Canvas t, s gy tovbb. Az egyes elemek elhelyezkedsre s mretre is van nhny szably, amelyeket a WPF automatikusan alkalmaz (termszetesen ezeket fellbrlhatjuk), ezek a kvetkez ek: Egy elem mrett nem szksges explicit megadni, az minden esetben megfelel nagysg lesz a tartalmhoz (de bellthatunk minimum s maximum mreteket is). A kontner elemek elosztjk a gyermekeik kzt a felhasznlhat teret.

A kvetkez kben megnzzk ezeknek a kontnereknek a hasznlatt.

43.1 StackPanel
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <StackPanel> <Button>Button1</Button>

222
<Button>Button2</Button> <Button>Button3</Button> </StackPanel> </Window>

Az eredmny:

Az elemek fgg legesen kvetik egymst. Az Orientation tulajdonsg megvltoztatsval vzszintes elrendezst kapunk:
<StackPanel Orientation="Horizontal">

Az lthat, hogy a vezrl k minden esetben teljes mrtkben kitltik a sajt helyket. Amennyiben megadjuk a Horizontal/VerticalAlignment tulajdonsgot, az elemek a lehet legkisebb mretet veszik fl (vagyis igazodnak a tartalomhoz):
<StackPanel> <Button HorizontalAlignment="Left">Button1</Button> <Button HorizontalAlignment="Center">Button2</Button> <Button HorizontalAlignment="Right">Button3</Button> </StackPanel>

Az Alignment tulajdonsgokbl az Orient tulajdonsgnak megfelel en mindig csak egy hasznlhat fel. A StackPanel s WrapPanel sajtja, hogy az elemeit mlesztve, trkz nlkl helyezi el. Ezen a problmn segthetnk margk belltsval.:
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

223
Title="Window1" Height="150" Width="300"> <StackPanel Orientation="Vertical"> <Button Margin="5">Button1</Button> <Button Margin="5">Button2</Button> <Button Margin="5">Button3</Button> </StackPanel> </Window>

A WPF a szlessg, magassg, marg, stb. meghatrozsra n. device independent unit okat hasznl (~rendszerfggetlen egysg). Egy DIU egy angol hvelyknek az 1/96 od rsze: ~0,2 mm (egy hvelyk: 2,54 cm). Azrt ennyi, mert az alaprtelmezett Windows DPI (dot-per-inch, az egy hvelyken elhelyezked pixelek szma) ppen 96, vagyis ebben az esetben egy DIU pontosan egy pixelnek felel meg, mivel a WPF a kvetkez kpletet hasznlja egy egysg meghatrozsra: Fizikai mret = DIU x DPI Ahol a DPI az ppen aktulis rendszer DPI nek felel meg. Valjban a DPI szm a megjelent eszkzt l fgg, egy nagyfelbonts s nagymret monitornak sokkal nagyobb a DPI rtke. A fenti pldban a gombok krl 5 egysg mret eredmny: trkzt lltottunk be. Az

A CSS t ismer k ismers nek fogjk tallni a helyzetet, ugyanis a fenti esetben a trkz nagysga mind a ngy oldalra rvnyes. Bellthatjuk ezt persze egyenknt is:
<Button Margin="5, 10, 5, 10" >Button1</Button>

Az oldalak sorrendje balrl jobbra: balra, fnt, jobbra, lent (ez eltr a CSS t l, ahol a sorrend: fnt, jobbra, lent, balra).

43.2 WrapPanel
A WrapPanel hasonlan m kdik mint a StackPanel, csak itt egy sorban/oszlopban tbb elem is lehet:
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

224
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <WrapPanel Margin="5"> <Button>Button1</Button> <Button>Button2</Button> <Button>Button3</Button> </WrapPanel> </Window>

Hasonlan mint a trsnl az Orientation tulajdonsgon kereszt l tudjuk a vzszintes/fgg leges elrendezst lltani.

43.3 DockPanel
Vegyk a kvetkez XAML t:
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <DockPanel> <Button>Button1</Button> <Button>Button2</Button> <Button>Button3</Button> </DockPanel> </Window>

Vagyis egy DockPanel ben elhelyeztnk hrom gombot, de semmi mst nem lltottunk. Ekkor ez trtnik:

Az els kt gomb fgg legesen kitlti a helyet, de vzszintesen a tartalomhoz igazodik a mretk. Nem gy tesz az utols gomb, ami mindkt irnyban elfoglalja a maradk helyet. Ez azrt van, mert az elemei minden esetben ki kell tltsk a lehet legnagyobb helyet, hacsak mst nem adunk meg (teht az els kett gomb mg normlis, de a harmadikhoz rve ltja, hogy van mg hely s automatikusan megnyjtja). Nzznk megy egy msik pldt is:

225

<DockPanel> <Button DockPanel.Dock="Top">Top</Button> <Button DockPanel.Dock="Left">Left</Button> <Button DockPanel.Dock="Right">Right</Button> <Button>Middle</Button> </DockPanel>

Most egy - az el z fejezetben megismert - attached property t hasznltunk, hogy meghatrozzuk, hogy a gombok a DockPanel melyik oldalhoz igazodjanak. Az eredmny ez lesz:

Most mr knnyen meghatrozhatjuk a viselkedst: az elemeket balra, fnt, jobbra, lent sorrendben rajzolja ki, s ha az utols elemhez rve mg van hely, akkor azt maradktalanul kitlti (persze, ha van belltva pl. Margin tulajdonsg, akkor azt figyelembe veszi).

43.4 Canvas
A Canvas lehet v teszi, hogy a rajta koordintarendszer segtsgvel pozicinljuk: elhelyezked vezrl ket egy

<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <Canvas> <Button>Button1</Button> </Canvas> </Window>

Lthat, hogy az alaprtelmezett koordintk szerint az elem helye a bal fels sarok, vagyis a (0;0) pont. Szintn attached property kel tudjuk megadni a pontos helyet:
<Canvas> <Button Canvas.Left="10" Canvas.Top="10">Button1</Button> </Canvas>

226

A pldban a gombot a bal oldaltl s a fels rszt l is tz egysggel toltuk el (a viszonytst mindig az adott vezrl adott oldalhoz szmoljuk):

Ha tbb egymst esetleg eltakar elem van a Canvas on, akkor a ZIndex tulajdonsgt is bellthatjuk. Ez alaprtelmezetten minden elemnek nulla, s a WPF el szr mindig a legalacsonyabb z-index elemet rajzolja ki (teht a magasabb index ek eltakarjk az alacsonyabbakat):
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <Canvas> <Button Canvas.Left="100" Width="170" Height="70">Button1</Button> <Button Panel.ZIndex="1" Width="170" Height="70">Button2</Button> </Canvas> </Window>

A ZIndex et a Panel tl rkli, s csakis azon kereszt l rhetjk el.

43.5 UniformGrid
Az UniformGrid el re definilt oszlopokba s sorokba (cellkba) rendezi a vezrl it (valjban a mret megadsra sincs szksg, automatikusan is elrendezi ket). Minden egyes vezrl ugyanannyi helyet foglal el, pontosabban minden cella mrete egyenl . Ha valamelyik elemnek explicit megadjuk a szlessgt vagy magassgt, akkor az rvnyeslni is fog, de ekkor kt lehet sg van: vagy kisebb mint amekkora egy cella, ekkor a cella kzepn foglal helyet s krlette megfelel mret trkz lesz, vagy pedig nagyobb mint egy cella, ekkor a ZIndex e mdosul s a kilg rsze a tbbi vezrl t alatt lesz. Nzznk ezekre nhny pdt: A normlis eset:
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

227
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <UniformGrid> <Button>Button1</Button> <Button>Button2</Button> <Button>Button3</Button> <Button>Button4</Button> </UniformGrid> </Window>

Explicit mretmegads, de kissebb:


<UniformGrid> <Button Width="100" Height="20">Button1</Button> <Button>Button2</Button> <Button>Button3</Button> <Button>Button4</Button> </UniformGrid>

Explicit megads, de nagyobb mint kellene:


<UniformGrid> <Button Width="170" Height="20">Button1</Button> <Button>Button2</Button> <Button>Button3</Button> <Button>Button4</Button> </UniformGrid>

43.6 Grid
A Grid a WPF leger teljesebb (s leggyakrabban hasznlt) container osztlya. Amit az el z ekkel meg tudunk csinlni, azt vele is reproduklni tudjuk.

228 Hasonlan a UniformGrid hez itt is sorok s oszlopok szerint rendezzk a terletet, de most megadhatjuk azok szmt, mrett illetve egy adott cellba tbb elemet is tehetnk. Egy Grid sorait illetve oszlopait a RowDefinitions s a ColumnDefinitions tulajdonsgain kereszt l tudjuk belltani, amelyek Row/ColumnDefinitionCollection tpus gy jtemnyeket kezelnek. Ezeket legyakrabban XAML b l lltjuk be, a kvetkez mdon:
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="40" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="100" /> <ColumnDefinition Width="150" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> </Grid> </Window>

Kt sort s hrom oszlopot ksztettnk. Az nem meglepets, hogy az egyes oszlopok/sorok defincija Column/RowDefinition tpus. Az oszlopoknl csakis a szlessget, a soroknl csakis a magassgot llthatjuk. Ha ezeknek az rtkeiknek a helyre csillagot runk, az azt jelenti, hogy az adott sor/oszlop ki fogja tlteni a megmarad sszes helyet. Viszont nem ktelez megani ezeket az rtkeket, ekkor a Grid egyenl nagysg cellkra osztja a felletet, a sorok s oszlopok szmnak fggvnyben. Ezenkv l rbzhatjuk a Grid re is a mret belltst, ha a szlessg/magassg rtknek auto t adunk, ekkor pontosan akkora lesz, amekkora kell (vagyis a benne lv vezrl mrethez igazodik). Ez utbbi kt esetben hasznosak lehetnek a MinWidth/MaxWidth, MinHeight/MaxHeight tulajdonsok, amelyekkel a sorok/oszlopok mrethatrait llthatjuk be. Ha most elindtannk a programot, akkor semmit sem ltnk, ami nem meglep , hiszen nem lenne tlsgosan vonz egy olyan alkalmazs, amelynek ngyzetrcsos a fellete. Szerencsre a fejleszts ellen rzsnek cljra lehet sgnk van bekapcsolni, hogy ltszdjon ez a rcs, a Grid ShowGridLines tulajdonsgn keresztl:
<Grid ShowGridLines="True">

Ekkor az eredmny:

229

Helyezznk el most nhny vezrl t:


<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <Grid ShowGridLines="True"> <Grid.RowDefinitions> <RowDefinition Height="40" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="100" /> <ColumnDefinition Width="150" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Button Grid.Column="0" Grid.Row="0">Button1</Button> <Label Grid.Column="1" Grid.Row="1">Hello Grid</Label> <ListBox Grid.Column="2" Grid.Row="1"> <ListBoxItem Content="Grid" /> </ListBox> </Grid> </Window>

Ismt attached property ket hasznltunk. Lthat, hogy mind az oszlopok, mind a sorok szmozsa nulltl kezd dik. Most gy nz ki a programunk:

Fontos, hogy minden a Grid en elhelyezni kvnt vezrl nek be kell lltani a helyt, klnben nem jelenik meg (de elg csak az egyiket oszlop vagy sor - megadni, ekkor a WPF a hinyz rtkhez automatikusan nullt rendel). A Grid.RowSpan s a Grid.ColumnSpan tulajdonsgokkal azt adhatjuk meg, hogy a vezrl hny oszlopot illetve sort foglaljon el:
<Button Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2">Button1</Button>

230

Most a gomb kt oszlopot kapott.

231

44. WPF Vezrl k


Ebben a fejezetben megismerkednk a WPF vezrl ivel. Sokuk ismer s lesz a korbbi WinForms szal foglalkoz fejezetb l, legalbbis cljt s m kdst tekintve (a hasznlatuk, a ltrehozsuk mr nem biztos). Mr korbban megtanultuk, hogy nem minden elem vezrl a WPF vilgban, hanem csak azok, amelyek kpesek felhasznli interaktivitst kezelni. Azt is tudjuk, hogy a vezrl k a System.Windows.Control osztlybl szrmaznak, ami felvrtezi ket nhny alaprtelmezett, mindegyikkre jellemz kpessggel (ilyen pl. az, hogy formzhatjuk a vezrl feliratt, szveges tartalmt). A vezrl k bemutatsnl nhny elem kimarad, mivel vals haszna csak egy ks bbi fejezet tudsanyagval lesz, gy majd a megfelel helyen ismerkednk meg velk.

44.1 Megjelens s szvegformzs


Megjelens szempontjnbl a WPF jval flexibilisebb mint a WinForms. Ebben a fejezetben a vezrl k vizulis megjelenst fogjuk kicsinostani. Kt ltalnos vezrl t fogunk ehhez hasznlni, a Button t s a Label t (illetve a szinezs demonstrlshoz egy egyszer alakzatot a Rectangle t). Minden vezrl magv teszi az el tr s a httr fogalmt. Utbbi a vezrl tnyleges felszne, mg el bbi ltalban a szveg. Windows Forms ban ezeket a Color osztly segtsgvel lltottuk, de most rendelkezsnkre ll valami sokkal jobb, mgpedig a Brush. Ez egy absztrakt osztly, ami el revetti, hogy tbb gyermeke is van, amelyek mindegyike ms-ms lehet sget bztost szmunkra. A lehet legegyszer bb esetben hasznlhatjuk gy a szneket, mint ahogy azt a Color osztly esetben tettk, csak most ugyanazt a funkcit a Brushes osztly tlti majd be. Vezrl k esetben a htteret a BackGround tulajdonsgon kereszt l llthatjuk be, ugyanezt alakzatoknl a Fill tulajdonsg fogaj szolgltatni. A kett ben a kzs, hogy Brush tpus (vagy leszrmazott) objektumot vrnak. Ksztsnk egy ngyzetet:
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="150" Width="150"> <Grid> <Rectangle x:Name="brushRectangle" Width="70" Height="70" VerticalAlignment="Center" Fill="Cyan"/> </Grid> </Window>

232

Ugyanezt megtehetjk kdbl is:


brushRectangle.Fill = Brushes.Cyan;

Az a Brush leszrmazott, amelyet itt hasznltunk SolidColorBrush nvre hallgat. Pldul rhatnnk ezt is a fenti helyett:
SolidColorBrush scb = new SolidColorBrush(); scb.Color = Color.FromRgb(100, 10, 100); brushRectangle.Fill = scb;

Ugyanezt megtehetjk XAML b l is:


<Rectangle x:Name="brushRectangle" Width="70" Height="70" VerticalAlignment="Center"> <Rectangle.Fill> <SolidColorBrush> <SolidColorBrush.Color> <Color A="255" R="100" G="10" B="100" /> </SolidColorBrush.Color> </SolidColorBrush> </Rectangle.Fill> </Rectangle>

Itt csak arra kell figyelni, hogy ARGB rtket kell megadnunk, ahol A az tltszsg mrtkt jelzi (ha 0 akkor lesz teljesen tltsz, a maximlisan felvehet rtke 255). Ha el re definilt szneket akarunk hasznlni, akkor egyszer bb a dolgunk:
<Rectangle x:Name="brushRectangle" Width="70" Height="70" VerticalAlignment="Center"> <Rectangle.Fill> <SolidColorBrush Color="Cyan" /> </Rectangle.Fill> </Rectangle>

A LinearGradientBrush kt (vagy tbb) sznt olvaszt ssze. A sznnel feltlttt terleteknek megadhatunk egy d lszszget (gradiens tengely) amely a sznek haladsi irnyt jelli majd.

233
LinearGradientBrush lgb = new LinearGradientBrush( Color.FromRgb(0, 0, 255), Color.FromRgb(255, 0, 0), 90); brushRectangle.Fill = lgb;

A harmadik paramter a tengely d lsszge:

XAML:
<Rectangle.Fill> <LinearGradientBrush> <GradientStop Color="Blue" Offset="0.0" /> <GradientStop Color="Red" Offset="0.5" /> </LinearGradientBrush> </Rectangle.Fill>

Az Offset tulajdonsg a sznek tmenetnek a vlasztvonala, a bal fels sarkot jelli a 0.0, mg a jobb alst az 1.1. Alaprtelmezetten a gradiens tengely ezt a kt vonalat kti ssze. A tengelyt a StartPoint illetve EndPoint tulajdonsgokkal tudjuk belltani:
<Rectangle.Fill> <LinearGradientBrush StartPoint="0.5, 0" EndPoint="0.5, 1"> <GradientStop Color="Blue" Offset="0.0" /> <GradientStop Color="Red" Offset="0.5" /> </LinearGradientBrush> </Rectangle.Fill>

A RadialGradientBrush hasonl mint az el z , de a szneket kr alakzatban olvasztja ssze. Hasonlan mint a LinearGradientBrush nl, itt is GradientStop objektumokkal tudjuk szablyozni a sznek hatrait:
RadialGradientBrush rgb = new RadialGradientBrush(); rgb.GradientStops.Add( new GradientStop(Color.FromRgb(255, 0, 0), 0.0)); rgb.GradientStops.Add( new GradientStop(Color.FromRgb(0, 0, 255), 0.5)); brushRectangle.Fill = rgb;

234 Most egy kicsit msknt adtuk hozz szneket, kzvetlenl a GradientStops tulajdonsgot hasznlva (eddig a konstruktorban adtuk meg ket). Ugyanez XAML ben:
<Rectangle.Fill> <RadialGradientBrush> <GradientStop Color="Red" Offset="0.0" /> <GradientStop Color="Blue" Offset="0.5" /> </RadialGradientBrush> </Rectangle.Fill>

Az ImageBrush sal kpeket tudunk kezelni:


ImageBrush ib = new ImageBrush(); ib.ImageSource = new BitmapImage(new Uri("test.jpg", UriKind.Relative)); brushRectangle.Fill = ib;

XAML:
<Rectangle.Fill> <ImageBrush ImageSource="test.jpg" /> </Rectangle.Fill>

Ha rosszul adtuk meg a kp nevt XamlParseException t kapunk. A vezrl knek egyszer bben llthatjuk be a sznezst, mivel ezek a Control osztlytl rklik a Background s Foreground tulajdonsgukat. Ezek valamilyen Brush leszrmazottat vrnak:
<Button Background="Blue" Foreground="Red" Width="100" Height="30" Content="Click" />

A kvetkez weboldalon tbb pldt s lerst tallunk:

235

http://msdn.microsoft.com/en-us/library/aa970904.aspx Nemcsak alakzatokat, vagy vezrl ket sznezhetnk, hanem megadhatunk nekik keretet (Border) is, amely szintn kaphat egyedi sznezst:
<Border BorderBrush="Coral" BorderThickness="5"> <Rectangle x:Name="brushRectangle" Width="70" Height="70" VerticalAlignment="Center"> </Rectangle> </Border>

Ez a keret klnnll a Rectangle t l, mivel annak nincs sajt kerete. Vannak vezrl k, amelyek viszont rendelkeznek ilyennel:
<Label BorderBrush="Coral" BorderThickness="5">

Hello Border!
</Label>

A BorderBrush tulajdonsg a keret sznt, mg a BorderThickness a keret vastagsgt jelli. Egy Border objektum csakis egyetlen gyermek vezrl vel rendelkezhet, ezrt ha tbbet akarunk sszefogni akkor (pl.) egy Panel be tegyk ket s azt jelljk meg kerettel. Nem csak szgletes keretet adhatunk, meg, hanem a CornerRadius tulajdonsggal kerekthatjk is:
<Border BorderBrush="Black" BorderThickness="5" CornerRadius="45"> <Button Width="100" Height="30" Content="Click" /> </Border>

236

Egyes vezrl k rendelkeznek sajt kerettel is, a Button pldul ilyen:


<Button BorderBrush="Black" BorderThickness="5" Width="100" Height="30" Content="Click" />

Hasonlan Windows Forms hoz a WPF is a jl ismert tulajdonsgokat knlja a szvegformzsokhoz:


<Label FontFamily="Arial" FontSize="30">

Hello Border!
</Label>

44.2 Vezrl k
Ebben a fejezetben megismerkednk az alapvet vezrl kkel. Funkcijuk szinte semmit nem vltozott a WinForms hoz tapasztaltakhoz kpest, de tulajdonsgaik illetve deklarcijuk igen. A pldkban jobbra a kls megjelenssel foglalkozunk, hiszen a vezrl k kpessgei nagyjbl ugyanazok, mint amit mr ismernk.

44.2.1 TextBlock s Label


Ez a kt vezrl els rnzsre ugyanazt a clt szolglja, de valjban nagy klnbsg van kzttk. S t, a TextBlock kakukktojs ebben a fejezetben, hiszen nem vezrl , de a Label hez val hasonlsga miatt itt a helye. A TextBlock ot

237 arra talltk ki, hogy kis mennyisg szveges (akr formzott) tartalmat jelentsen meg:
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xml:lang="hu-HU" Title="Window1" Height="100" Width="200"> <Grid> <TextBlock> Ez a szveg <Bold>flkvr.</Bold><LineBreak /> Ez pedig <Italic>d lt bet s.</Italic> </TextBlock> </Grid> </Window>

A szvegformzst a Label is tmogatja, de tmogatja a gyorsbillenty ket:

t nem ezrt szeretjk, hanem mert

<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xml:lang="hu-HU" Title="Window1" Height="100" Width="200"> <Grid> <Label Target="{Binding ElementName=tb1}">_TextBox</Label> <TextBox x:Name="tb1" Width="100" Height="20" /> </Grid> </Window>

A Label Target tulajdonsgnak megadhatjuk a hozz kttt vezrl nevt, amelyre a megfelel billenty kombincival a fkusz kerl. Az adatktsekr l (amelyet a Binding osztly vezrel majd) ks bb beszlnk majd. A gyorsbillenty t a kivlasztott karakter el rt alulvonssal tudjuk kijellni, a pldban az ALT+t kombinci azt eredmnyezi, hogy a fkusz a TextBox ra kerl.

238 A Visual Studio ban az IntelliSense nem tmogatja az adatktseket, gy a Binding et sem fogja felajnlani, neknk kell berni. Ezenkv l a Label egy ContentControl leszrmazott is, err l a kvetkez fejezetben olvashatunk tbbet.

44.2.2 CheckBox s RadioButton


Vegyk a kvetkez XAML kdot:
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="150" Width="200"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="30" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Label x:Name="label1" Content="Napszak: " Grid.Row="0"/> <GroupBox Grid.Row="1"> <GroupBox.Header>

Milyen napszak van?


</GroupBox.Header> <StackPanel> <CheckBox x:Name="cb1" Content="Nappal" /> <CheckBox x:Name="cb2" Content="jszaka" /> </StackPanel> </GroupBox> </Grid> </Window>

Amib l ez lesz:

Amikor WinForms szal dolgoztunk, akkor megszokhattuk, hogy egy GroupBox ban elhelyezett CheckBox oknak megadhattunk csoportot, gy mindig csak egyet tudtunk megjelellni. A WPF mshogy m kdik, a CheckBox ot nem lehet

239 csoporthoz rendelni, s van mg egy nagy klnbsg. A CheckBox a ContentControl osztly leszrmazottja, gy knnyedn tformlhatjuk:
<CheckBox> <StackPanel> <Label>Hello, ez egy CheckBox!</Label> <Button>OK</Button> </StackPanel> </CheckBox>

A ContentControl leszrmazottak csakis egy gyermeket tudnak kezelni, gy ha tbbet szeretnnk egy olyan eszkzt kell bevetnnk, amely erre kpes, ez pedig (pl.) a StackPanel lesz ( t mr ismerjk).Ezt hasznltuk a GroupBox esetben is, ami felttelezni engedi s nem tved -, hogy az is egy ContentControl leszrmazott. S t, ez egy mg specializltabb eszkz, egy HeaderedContentControl, vagyis lehet valamilyen fejlce. Kanyarodjunk vissza az eredeti problmhoz, vagyis, hogy hogyan lehetne megoldani, hogy egyszerre csak egy CheckBox ot jellhessnk meg? Ez viszont az olvas feladata lesz, tbbfle megolds is ltezik (egy kulcssz: attached event). A CheckBox nak hrom llapota lehet: checked (amikor bejelljk), unchecked (amikor levesszk rla a jellst) s indeterminated (amikor nincs bejellve, de nem nyltunk mg hozz, vagyis ilyen az llapota pl. amikor elindtjuk az alkalmazst). Az llapotoknak megfelel en mindegyikhez ltezik esemnyvezrl . rjuk t a programot s hasznljuk ki inkbb a RadioButton csoportosthatsgt. Ezt ktflekppen tehetjk meg, vagy a GroupName tulajdonsgon kereszt l, vagy pedig a csoportostand RadioButton okat egy sz l al rendeljk (pl. GroupBox):
<GroupBox Grid.Row="1"> <GroupBox.Header>

Milyen napszak van?


</GroupBox.Header> <StackPanel> <RadioButton x:Name="rb1" Content="Nappal" /> <RadioButton x:Name="rb2" Content="jszaka" /> </StackPanel> </GroupBox>

Hasonlan a CheckBox hoz, a RadioButton is hromlls. Mr csak egy dolog van htra, mgpedig az, hogy a napszaknak megfelel en megvltoztassuk a Label feliratt. Ezt egy attached event segtsgvel fogjuk megtenni, amit a StackPanel hez ragasztunk, lvn van a legkzelebb:

240

<StackPanel ToggleButton.Checked="StackPanel_Checked"> <RadioButton x:Name="rb1" Content="Nappal" /> <RadioButton x:Name="rb2" Content="jszaka" /> </StackPanel>

Mind a CheckBox, mind a RadioButton a ToggleButton osztlybl szrmazik, s a checked/unchecked/indeterminate esemnyeket is innen rklik. Az esemnykezel ez lesz:
private void StackPanel_Checked(object sender, RoutedEventArgs e) { label1.Content = "Napszak: " + ((RadioButton)e.OriginalSource).Content; }

Mivel ez egy routed event, ezrt nem konvertlhatjuk kzvetlenl az els paramtert, (hiszen az ebben az esetben az az objektum lesz, ami elkapta az esemnyt, vagyis most a StackPanel), ezrt a msodik paramtert l krjk el az esemny eredeti forrst.

44.2.3 TextBox, PasswordBox s RichTextBox


Ebben a fejezetben a szveges inputot kezel vezrl ket vesszk grcs Els knt ksztsnk egy egyszer belptet alkalmazst:
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="150" Width="220"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="30" /> <RowDefinition Height="30" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="90" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Label Content="Felhasznlnv:" Grid.Row="0" Grid.Column="0" /> <TextBox x:Name="usernamebox" Width="100" Height="20" Grid.Row="0" Grid.Column="1" /> <Label Content="Jelsz:" Grid.Row="1" Grid.Column="0" />

al.

241
<PasswordBox x:Name="passwordbox" Width="100" Height="20" Grid.Row="1" Grid.Column="1" /> <Button x:Name="loginbutton" Content="Belps" Margin="5 0 0 0" Width="80" Height="20" Grid.Row="2" Grid.Column="0" Click="loginbutton_Click" /> </Grid> </Window>

Az eredmny:

Ha elindtjuk az alkalmazst lthat lesz, hogy a PasswordBox ugyanazt csinlja, mint a WF -beli TextBox belltott PasswordChar tulajdonsggal. A gomb esemnykezel jt mr knnyen meg tudja rni az olvas. Kvetkez vizsglati alanyunk a RichTextBox. El szr a hagyomnyos hasznlatval ismerkednk meg:
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="150" Width="220"> <Grid> <RichTextBox x:Name="richtb1" Width="200" Height="100" /> </Grid> </Window>

Amikor elindtjuk az alkalmazst, egy teljesen tlagos RichTextBox jelenik meg, rhatunk bele, akr formzott tartalmat is, stb Van azonban nhny vltozs is. Az egyik legjelent sebb, hogy mostantl nem tudunk kzvetlenl menteni a vezrl b l, ezt neknk kell intznnk. Adjunk a Grid hez egy gombot is, amivel ezt majd megtesszk:

242

<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="220" Width="220"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="150" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <RichTextBox x:Name="richtb1" Width="200" Height="100" Grid.Row="0"/> <Button x:Name="savebutton" Width="100" Height="20" Content="Ments" Grid.Row="1" Click="savebutton_Click" /> </Grid> </Window>

Rendben, most nzzk meg az esemnykezel t:


private void savebutton_Click(object sender, RoutedEventArgs e) { using (FileStream fstream = new FileStream("rtb.rtf", FileMode.Create)) { TextRange range = new TextRange( richtb1.Document.ContentStart, richtb1.Document.ContentEnd); range.Save(fstream, DataFormats.Rtf); } }

A TextRange osztllyal valamilyen szveges objektumbl tudunk szveget kiemelni (ez akr egy szimpla strig is lehet). Ennek az osztlynak a segtsgvel tudunk menteni, mghozz igen sokfle formtumban. Kls file t- ugyangy tudunk beolvasni, kt klnbsg van, a stream nek nem ltrehozni, hanem megnyitni kell a forrst, s a TextRange objektumnak a Load metdust hasznljuk. A RichTextBox Document tulajdonsga egy FlowDocument objektumot ad vissza, ami tulajdonkppen a tnyleges adattrolsrt s megjelentsrt felel a RichTextBox on bell. A ContentStart s ContentEnd tulajdonsgok TextPointer objektumokat adnak vissza, amelyet a TextRange tud hasznlni. Egy RichTextBox minden esetben tartalmaz pontosan egy FlowDocument objektumot, mg akkor is, ha ez a forrskdban nem jelenik meg. Ennek segtsgvel akr el re megadhatunk egy megformlt szveget az XAML kdban:
<RichTextBox x:Name="richtb1" Width="200"

243
Height="100" Grid.Row="0"> <FlowDocument> <Paragraph> <Bold>Hello RichTextBox!</Bold> </Paragraph> </FlowDocument> </RichTextBox>

A FlowDocument en bell Paragraph elemeket hasznltunk, hogy megjelentsnk egy szveget. Mind a Pragraph mind a Bold elemek tartalmazhatnak egyb formzsokra utast parancsot (pl.: karakterkszlet, bet mret). Maga a FlowDocument nem kt dik a RichTextBox hoz, s t ilyen esetben jelent sen korltozottak a kpessgei. A kvetkez oldalon b vebb lerst tall az olvas err l az osztlyrl: http://msdn.microsoft.com/en-us/library/aa970909.aspx A RichTextBox s a TextBox egyarnt kpes helyesrsellen rzsre, ezt a SpellCheck.IsEnabled attached propeprty segtsgvel llthatjuk be. Ugyangy mindkt vezrl rendelkezik helyi menvel, amelyet a jobb egrgombbal val kattintssal hozhatunk el s a kivgs/msols/beilleszts m veleteket tudjuk vele elvgezni. A RichTextBox alaprtelmezetten nem tmogatja a grget svot, ezt neknk kell bekapcsolni a Horizontal/VerticalScrollBarVisibility tulajdonsgokkal, amelyek vzszintes vagy fgg leges grget svot eredmnyeznek.

44.2.4 ProgressBar, ScrollBar s Slider


Ezzel a kt vezrl vel klnsebb gondunk nem lesz, hiszen egyikk sem knl semmilyen extra lehet sget, csak azt amit mr ismernk. Br ez gy nem teljesen igaz, mivel a WPF ms terleteinek lehet sgeivel felvrtezve ezek a vezrl k is rendkvl rdekesek lesznek, de ez egy msik fejezet(ek) tmja lesz, gy most meg kell elgednnk a hagyomnyos mdszerekkel. A ProgressBar ral kezdnk s egyttal megismerkednk a WPF szlkezelsvel is egy kicsit kzelebbr l. A feladat a szoksos lesz, gombnyomsra elkezdjk szp lassan nvelni a ProgressBar rtkt. Ez viszont, egy kis trkkt ignyel. Azt mr tudjuk, hogy a WPF csakis a sajt szlban hajland mdostani a felhasznli felletet s ez a tudsunk most hasznos lesz. Az nylvnval, hogy a gombnyomsra elindtott ciklust le kell lasstanunk, ezt megtehetjk mondjuk a Thread.Sleep metdussal. Amikor ezt hasznljuk, akkor felt nhet az, hogy a ProgressBar meg se mukkan, s a felhasznli fellet sem hasznlhat, egy id utn pedig a vezrl megtelik s ismt aktv lesz az alkalmazs. Ez azrt van, mert az alkalmazs sajt szlt blokkoltuk, annyi ideje azonban nem volt, hogy kt cikluslps kzt frisstse a felletet. Az eredmny az lesz, hogy az alkalmazs nem reagl kls beavatkozsra, s csak a futs vgn telik meg a ProgressBar. A megolds az lesz, hogy egy a f szllal szinkronban m kd szlban csinljuk meg a feladatot. Azrt kell ennek a szlnak az alkalmazs szlval szinkronban m kdnie, mert csak gy tudjuk azt blokkolni.

244 A WPF osztlyhierarchijrl szl fejezetben megtanultuk azt is, hogy a legtbb WPF beli vezrl a DispatcherObject osztly leszrmazottja. Ez az osztly minden egyes alkalmazshoz biztost egy Dispatcher objektumot, amellyel a f szlat tudjuk vezrelni. Ennek az osztlynak az Invoke s BeginInvoke metdusaival, tudunk szinkron illetve aszinkron hvsokat vgezni, akr egy msik szlbl is. Mi most az Invoke ot fogjuk hasznlni. Szksgnk lesz egy delegate re is, mert mindkt metdus azt vr paramtereknt:
public delegate void SleepDelegate(); public static void SleepMethod() { System.Threading.Thread.Sleep(100); } private SleepDelegate sleepdlgt;

Ne felejtsk el hozzadni a metdust, mondjuk a konstruktorban:


sleepdlgt += new SleepDelegate(SleepMethod);

Most mr nagyon egyszer dolgunk lesz, a gomb Click esemnye gy fog kinzni:
private void startbutton_Click(object sender, RoutedEventArgs e) { for (int i = 0; i < 100; ++i) { Dispatcher.Invoke( System.Windows.Threading.DispatcherPriority.Render, sleepdlgt); ++progressbar1.Value; } }

Az Invoke els paramtere a hvs prioritst jelenti, tbbfle priorits ltezik s ebben az esetben tbbet is hasznlhatunk. A Render rtkkel hvott delegate ugyanazzal a prioritssal rendelkezik, mint az alkalmazs fellett kirajzol folyamat, ami neknk most tkletesen megfelel. Ksztnk mg egy Label t is, hogy jelezzk, hogy ppen hny szzalknl jrunk a folyamatnak, ezt a ProgressBar ValueChanged esemnyben fogjuk frissteni:
private void progressbar1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { progresslabel.Content = progressbar1.Value.ToString() + "%"; }

Az esemnykezel msodik paramtere is rdekes szmunkra. A neve rulkod lehet, mivel ez a paramter egy adott tulajdonsg vltozsnak krlmnyeit mutatja. Nzzk meg kzelebbr l. A ProgressBar Value tulajdonsga egy dependency property, a ValueChanged pedig egy routed event. Egy ks bbi fejezetben fogunk tanulni a WPF adatktseir l is, ami pontosabb kpet ad a folyamatrl. Ez a

245 paramtertpus egy generikus osztly, a fenti esetben pldul double rtkekre specializldott. A paramter tulajdonsgai kzt megtalljuk a NewValue s az OldValue tagokat, amelyek rtelemszer en a rgi s az j rtket hordozzk magukban. Az XAML gy nz majd ki:
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="200" Width="300" Background="Black"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="100" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <ProgressBar x:Name="progressbar1" Minimum="0" Maximum="100" Width="150" Height="30" Grid.Row="0" ValueChanged="progressbar1_ValueChanged"/> <Label x:Name="progresslabel" FontSize="20" Foreground="Cyan" Content="0%" Grid.Row="0" /> <Button x:Name="button1" Content="Start" Width="100" Height="30" Grid.Row="1" Click="button1_Click" /> </Grid> </Window>

Az eredmny pedig:

246

Felmerlhet a krds, hogy nem e lehetne ezt egyszer bben megoldani, pldul hasznlhatnnk a korbban mr bemutatott Timer osztlyt is. Ha megnzzk a WPF ToolBox t, akkor ilyet bizony nem fogunk tallni (s ms komponenst sem). S t, igazbl a WinForms -os Timer -t itt nem is igazn ajnlott hasznlni. Semmi vsz, van helyette ms: DispatcherTimer. Ksztsnk egy j adattagot:
private System.Windows.Threading.DispatcherTimer timer;

Ez a timer hasonlan m kdik, mint a mr ismert komponens, a Tick esemnye fogja jelezni, hogy ideje cselekedni:
timer = new System.Windows.Threading.DispatcherTimer(); timer.Interval = new TimeSpan(10000); timer.Tick += new EventHandler(timer_Tick);

Az esemnykezel pedig ez lesz:


void timer_Tick(object sender, EventArgs e) { if (progressbar1.Value < 100) { ++progressbar1.Value; } else timer.Stop(); }

Az Interval tulajdonsg egy TimeSpan tpus objektumot vr, ennek (pl.) nanoszekundumban adhatunk rtket. A DispatcherTimer igazbl azrt j neknk, mert a f szlban fut, gy mdosthatja a felhasznli felletet minden tovbbi nlkl. Kvetkezzen a ScollBar. Ez a vezrl tbb rszb l ll, a kt vgpontjn egy-egy RepeatButton tartzkodik, illetve az aktulis pozcit egy Thumb objektum jelkpezi. Ezek egyttesen egy Track objektumot alkotnak. A mostani plda hasonl lesz az el z hz, a ScrollBar t mozgatva kirjuk az aktulis rtkt egy Label re. Az rtkvltozst ktflekppen is kezelhetjk, a Scroll esemny a vltozs krlmnyeit rja le, mg a ValueChanged csakis a mutatott rtk vltozst tudja kezelni.

Az XAML:

247

<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="150" Width="300"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="75" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <ScrollBar x:Name="scrollbar1" Minimum="0" Maximum="100" Orientation="Horizontal" Grid.Row="0" ValueChanged="scrollbar1_ValueChanged"/> <Label x:Name="valuelabel" Content="Value: 0" Grid.Row="1" /> </Grid> </Window>

Nzzk az esemnykezel t:
private void scrollbar1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { valuelabel.Content = "Value: " + scrollbar1.Value.ToString(); }

Vgl foglalkozzunk a Slider rel is. Ezt a vezrl t gyakorlatilag ugyangy kezeljk, mint a ScrollBar t, az XAML defincija:
<Slider x:Name="slider1" Width="200" Height="30" Minimum="0" Maximum="100" Grid.Row="0" ValueChanged="slider1_ValueChanged" />

Az llapotvltozsokat szintn a ValueChanged esemny segtsgvel tudjuk kezelni. Hamarosan foglalkozunk a WPF adatktseivel, amelyek segtsgvel jval egyszer bben oldhatjuk meg az ebben a fejezetben szerepl feladatokat.

44.2.5 ComboBox s ListBox


Ez a kt vezrl is a hagyomnyos szolgltatsokat nyjtja szmunkra. Kezdjk a ComboBox xal:

248
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="120" Loaded="window1_Loaded"> <Grid> <ComboBox x:Name="combobox1" Width="100" Height="30" /> </Grid> </Window>

Az elemeket (el szr) az ablak betltsekor, a Loaded esemnyben adjuk hozz:


private void window1_Loaded(object sender, RoutedEventArgs e) { string[] names = new string[] { "Istvn", "Bla", "Judit", "Mrta" }; combobox1.ItemsSource = names; }

Az ItemSource tulajdonsgot az ItemsControl sosztlytl rkli a vezrl s olyan adatszerkezetet vr, amely megvalstja az IEnumerable interfszt, ilyen pl. egy hagyomnyos tmb. Elemeket az Items tulajdonsgon kereszt l is megadhatunk, a fentivel tkletesen megegyez eredmnyt kapunk, ha gy csinljuk:
private void window1_Loaded(object sender, RoutedEventArgs e) { combobox1.Items.Add("Istvn"); combobox1.Items.Add("Bla"); combobox1.Items.Add("Judit"); combobox1.Items.Add("Mrta"); }

Ezeken kv l XAML b l is ltrehozhatjuk az elemeit:


<ComboBox x:Name="combobox1" Width="100" Height="30" > <ComboBoxItem Content="Istvn" /> <ComboBoxItem Content="Bla" /> <ComboBoxItem Content="Judit" /> <ComboBoxItem Content="Mrta" /> </ComboBox>

A ComboBoxItem a ListBoxItem b l szrmazik s leszrmazottja a ContentControl oszlynak, gy sszetettebb elemei is lehetnek a vezrl nek (pl. egy gomb):
<ComboBox x:Name="combobox1" Width="100"

249
Height="30" > <ComboBoxItem> <Button>Istvn</Button> </ComboBoxItem> <ComboBoxItem Content="Bla" /> <ComboBoxItem Content="Judit" /> <ComboBoxItem Content="Mrta" /> </ComboBox>

Ugyanakkor nem vagyunk rknyszertve a ComboBoxItem hasznlatra:


<ComboBox x:Name="combobox1" IsEditable="True" TextSearch.TextPath="Name" Width="100" Height="30" > <Button>Istvn</Button> <Button>Bla</Button> <Button>Judit</Button> <Button>Mrta</Button> </ComboBox>

A ComboBox alaprtelmezetten nem jelenti meg az ltalunk bert szveget, de keresni tud az alapjn. Ha ltni is szeretnnk, amit runk, akkor az IsEditable tulajdonsgt kell true rtkre lltanunk. A ComboBoxItem, akr olyan objektumot is tartalmazhat, amely nem szveges tus. Ekkor is rkereshetnk, ha belltottuk a TextSearch.Text attached property t:
<ComboBox x:Name="combobox1" IsEditable="True" Width="100" Height="30" > <ComboBoxItem> <Button TextSearch.Text="Istvn">Istvn</Button> </ComboBoxItem> <ComboBoxItem Content="Bla" /> <ComboBoxItem Content="Judit" /> <ComboBoxItem Content="Mrta" /> </ComboBox>

Hasonl clt szolgl a TextSearch.TextPath tulajdonsg, amivel az elemek egy tulajdonsgnak rtkt tehetjk kereshet v:
<ComboBox x:Name="combobox1" IsEditable="True" TextSearch.TextPath="Name" Width="100" Height="30" > <Button Name="Istvn" >Istvn</Button> <Button Name="Bla">Bla</Button> <Button Name="Judit">Judit</Button> <Button Name="Mrta">Mrta</Button> </ComboBox>

250

A ComboBox szmunkra fontos esemnye a SelectionChanged, amely akkor aktivldik, amikor megvltozik a kivlasztott elem. Nagyon kevs dologban tr el a ListBox a ComboBox tl, a legszembet n bb, hogy ez a vezrl nem tesz lehet v keresst, illetve az elemei tpusa ListBoxItem:
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="120" Width="120"> <Grid> <ListBox x:Name="listbox1" Width="100" Height="80" > <ListBoxItem Content="Istvn" /> <ListBoxItem Content="Bla" /> <ListBoxItem Content="Judit" /> <ListBoxItem Content="Mrta" /> </ListBox> </Grid> </Window>

A SelectionMode tulajdonsgval meghatrozhatjuk, hogy hny elemet vlaszthat ki a felhasznl. Hrom md ltezik Single (ez az alaprtelmezett, egyszerre egy elem), Multiple (tbb elem is kivlaszthat, nem ignyel kln billenyt letst) illetve Extended (a SHIFT billenty nyomvatartsval egymst kvet elemeket vagy a CTRL billenty nyomvatartsval nll elemeket vlaszthatunk ki). A kivlasztst vagy annak visszavonst a ListBox SelectionChanged esemnyvel tudjuk kezelni. Ennek msodik paramtere SelectionChangedEventArgs tpus. AddedItems illetve RemovedItems tulajdonsga az ppen kijellt, vagy kijellsb l eltvoltott elemeket tartalmazza.

44.2.6 TreeView
A TreeView vezrl vel hierarchikus sorrendbe llthatjuk az elemeit. is egy ItemsControl leszrmazott, elemeinek tpusa TreeViewItem. Ez utbbi a HeaderedItemsControl osztlybl szrmazik, vagyis egy adott elemnek lehetnek gyermek elemei (mivel ItemsControl leszrmazott) illetve minden elem rendelkezik a Header tulajdonsggal, amellyel az egyes elemek feliratt llthatjuk be:
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="130" Width="150" Loaded="window1_Loaded"> <Grid> <TreeView x:Name="treeview1" Width="100" Height="100"> <TreeViewItem Header="vszakok">

251
<TreeViewItem Header="Tavasz" /> <TreeViewItem Header="Nyr" /> <TreeViewItem Header=" sz" /> <TreeViewItem Header="Tl" /> </TreeViewItem> </TreeView> </Grid> </Window>

Mivel a TreeViewItem egyben ItemsControl is, az egyes csompontok szvegen kvl mst is tartalmazhatnak:
<TreeViewItem Header="vszakok"> <TreeViewItem Header="Tavasz" > <Button>Tavasz - Gomb</Button> </TreeViewItem> <TreeViewItem Header="Nyr" /> <TreeViewItem Header=" sz" /> <TreeViewItem Header="Tl" /> </TreeViewItem>

A vezrl ItemSource tulajdonsgn kereszt l akr gy jtemnyeket hozzadhatunk, de csakis akkor, ha nem tartalmaz ms elemeket.

is

Az Items tulajdonsg hagyomnyos gy jtemnyknt viselkedik, gy indexelhetjk, hozzadhatunk vagy akr trlhetnk is bel le.

44.2.7 Menu
A vezrl vel egy hagyomnyos ment tudunk ltrehozni. Ez a vezrl ItemsControl leszmazott, elemeinek tpusa MenuItem, amelyek HeaderedItemsControl osztlybl szrmaznak:
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="130" Width="150"> <Grid> <Menu x:Name="menu1"> <MenuItem Header="Menu1"> <MenuItem Header="SubMenu1.1" /> </MenuItem> <MenuItem Header="Menu2" />

is a

252
</Menu> </Grid> </Window>

Egy menpont kivlasztst s gy egy parancs vgrehajtst pl. a Click esemnnyel (amely egybknt a MenuItem osztlyhoz tartozik) tudjuk kezelni (van egy sokkal elegnsabb mdszer is, de az egy msik fejezet tmja lesz):
<Menu x:Name="menu1" MenuItem.Click="menu1_Click"> <MenuItem Header="Menu1"> <MenuItem Header="SubMenu1.1" /> </MenuItem> <MenuItem Header="Menu2" /> </Menu>

Az esemnykezel :
private void menu1_Click(object sender, RoutedEventArgs e) { MessageBox.Show(((MenuItem)e.OriginalSource).Header.ToString()); }

A MenuItem attached event -jeinek segtsgvel tbb esemnyt is tudunk kezelni, mint pl. egy almen megnyitsa.

44.2.8 Expander s TabControl


Az Expander a HeaderedContentContorl osztlybl szrmazik, ezrt tartalmazhat ms elemeket is. Ezt a tulajdonsgt kiegszti azzal, hogy a tartalmazott elemeket kpes elrejteni/megjelenteni:
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="130" Width="150"> <Grid> <Expander x:Name="expander1" Width="130" Height="100" > <Expander.Content> <TextBlock>

Ezt a szveget elrejthetjk...


</TextBlock> </Expander.Content>

253
</Expander> </Grid> </Window>

Az Expander (a ContentControl osztlybl val szrmazsa miatt) csak egyetlen gyermekelemet kpes kezelni, gy ha tbb vezrl t akarunk elhelyezni benne szksgnk lehet egy olyan elemre, amely kpes ket kezelni (pl.: Grid, StackPanel, stb):
<Expander x:Name="expander1" Width="130" Height="100" > <Expander.Content> <StackPanel> <TextBox x:Name="textbox1" Width="100"/> <Button x:Name="button1" Width="100" Height="20" Content="OK" /> </StackPanel> </Expander.Content> </Expander>

A vezrl nyitott vagy csukott llapott az IsExpanded tulajdonsggal krdezhetjk le (illetve llthatjuk be), ennek alaprtelmezett rtke true. A nyits irnyt az ExpandDirection tulajdonsggal llthatjuk. A TabControl vezrl tbb oldalt tartalmaz, amelyek kzl mindig csak egy ltszik teljes valjban. Az egyes oldalak TabItem tulajdonsgak s a HeaderedContentControl osztlybl szrmaznak.
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

254
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="130" Width="150"> <Grid> <TabControl x:Name="tabcontrol1" Width="120" Height="100" > <TabItem Header="TAB1"> <TextBlock Text="TAB1" /> </TabItem> <TabItem Header="TAB2"> <TextBlock Text="TAB2" /> </TabItem> </TabControl> </Grid> </Window>

44.2.9 Image s MediaElement


Az Image vezrl kpek megjelentsre szolgl (gyakorlatilag megfelel a WinForms os PictureBox nak). A megjelentend kpet a Source tulajdonsgn kereszt l tudjuk megadni:
<Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="130" Width="150"> <Grid> <Image x:Name="image1" Width="100" Height="100" Source="image.jpg" /> </Grid> </Window>

A MediaElement lehet v teszi klnbz mdiafile ok lejtszst. Minden olyan tpust tmogat, amelyet a Windows Media Player 10 is. Kezelse rendkv l egyszer (viszonylag knnyen kszthetnk egy egyszer lejtsz programot), rendelkezik az alapvet m veletek (lejtszs, megllts, stb) elvgzshez szksges metdusokkal.
<Window x:Class="JegyzetWPF.Window1" x:Name="window1"

255
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="130" Width="150"> <Grid> <MediaElement x:Name="mediaelement1" Source="file.wmv" /> </Grid> </Window>

Nzzk meg a fontosabb metdusokat, tulajdonsgokat s esemnyeket: Play ez a metdus elindtja mdia lejtszst. Pause a lejtszs meglltsa, ismtelt Play utn az aktulis pozcitl folytatja a lejtszst. Stop a lejtszs meglltsa s az aktulis pozic a file eleje lesz. Volume ez a tulajdonsg a hanger belltsrt felel s. MediaOpened s MediaEnded ezek az esemnyek a mdia megnyitsakor illetve a lejtszs vgn lpnek akciba.

256

45. Er forrsok
ltalban amikor valamilyen er forrsrl beszlnk, akkor egy az alkalmazsba belefordtott binris adatra (pl. egy kp, vagy hang) gondolunk, ezt binary- vagy assembly resource nak nevezzk. A WPF bevezet egy msikfajta er forrst, amelynek neve: object resource. A nv alapjn knny kitallni, hogy ebben az esetben .NET objektumokrl beszlnk. Ebben a fejezetben mindkt tpust megvizsgljuk.

45.1 Assembly resource


Ezzel a tpussal mr tallkoztunk, mg ha nem is tudtunk rla. Az XAML is ilyen er forrs lesz a fordts sorn, amikor BAML l alakul. Egy projecthez nagyon knnyen tudunk er forrst hozzadni, kattintsunk jobb egrgombbal a solution- n, majd vlasszuk az Add men Existing Item lehet sgt. Ezutn a kivlasztott elem megjelenik a project file jai kzt is. Jobb gombbal kattintsunk rajta s vlasszuk a Properties menpontot. Ezutn a Build Action t lltsuk Resource ra. Kszen vagyunk, most lssuk, hogyan hasznlhatjuk fel ket. Ennek kt mdja van, el szr az egyszerbbikkel foglalkozunk: tegyk fel, hogy egy kp file t raktroztunk el, a neve legyen picture.jpg. Helyezznk el az alkalmazsban egy Image vezrl t is:
<Image x:Name="image1" Source="picture.jpg" Width="100" Height="100" />

Lthat, hogy egyszer en csak a file nevvel hivatkoztunk kpre (persze elhelyezhetnk er forrst mappban is, ekkor a teljes elrsi utat oda kell rnunk). Ha ugyanezt forrskdbl akarjuk elrni, akkor a kvetkez t rjuk:
image1.Source = new BitmapImage(new Uri("picture.jpg", UriKind.Relative));

Rugalmasabban tudjuk kezelni az er forrsokat egy StreamResourceInfo objektummal (ez a System.Windows.Resources nvtrben van):
StreamResourceInfo sri = Application.GetResourceStream( new Uri("picture.jpg", UriKind.Relative));

Ezutn kt dolgot tudunk kinyerni ebb l az objektumbl, a ContentType tulajdonsggal ez er forrs tpust (ez most image/jpg) a Stream tualjdonsggal pedig az er forrsra mutat stream et kapunk (ennek tpusa UnmanagedMemoryStream). Az elrsi t nem mindig ilyen egyrtelm , mi van akkor, ha az er forrs egy msik assembly ben van? A kvetkez pldhoz tegyk fel, hogy ennek a msik assembly nek a neve ImageAssembly:
image1.Source = new BitmapImage( new Uri("ImageAssembly;component/picture.jpg", UriKind.Relative));

45.2 Object resource

257

Az object resource koncepcija j szmunkra, de a WPF szmos lehet sge pt r (pl.: stlusok). Emellett ezzel a megoldssal knnyen hasznlhatunk egy adott objetumot tbb helyen (pl. TextBlock objektumokhoz definilunk egy stlust, ekkor ezt brhol felhasznlhatjuk ksbb). Er forrst brmely FrameworkElement vagy FrameworkContentElement leszrmazott tartalmazhat. Minden ilyen osztly rendelkezik a Resources nev tulajdonsggal amely egy ResourceDictionary tpus listt ad vissza, amely megvalstja (tbbek kzt) az IDictionary interfszt, gy hasonlan m kdik, mint a mr ismert Dictionary osztly, ebb l kvetkezik az egyik legfontosabb jellemz je is: vagyis minden tartalmazott er forrsnak egyedi azonostjnak kell lennie. A gyakorlatban a legtbb esetben a top-level-element fogja az alkalmazs (pontosabban az aktulis form) er forrsait tartalmazni. Egy er forrsra ktflekppen statikusan s dinamikusan hivatkozhatunk, elbbi esetben az alkalmazs indtsakor rtkel dik ki a kifejezs, vagyis a WPF kikeresi az sszes rendelkezsre ll er forrslistbl a megfelel tagot. A dinamikus esetben ez akkorra halasztdik, amikor az er forrs tnylegesen hasznlatra kerl, s ezt minden egyes alkalommal megteszi, vagyis reagl arra, ha az adott er forrs tulajdonsgai megvltoztak. Nzznk egy pldt:
<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Grid> <Grid.Resources> <LinearGradientBrush x:Key="buttoncolor"> <GradientStop Color="Black" Offset="0.0" /> <GradientStop Color="Red" Offset="0.7" /> </LinearGradientBrush> </Grid.Resources> <Button Width="100" Height="30" Background="{StaticResource buttoncolor}" VerticalAlignment="Top" /> <Button Width="100" Height="30" Background="{DynamicResource buttoncolor}" VerticalAlignment="Bottom" /> </Grid> </Window>

A kt gomb ugyanazt az er forrst hasznlja, de az els esetben statikusan (StaticResource), mg a msodiknl dinamikusan (DynamicResource) hasznltuk fel. A Visual Studio az er forrsok kezelshez nem nyjt IntelliSense tmogatst, gy azt neknk kell bernunk. Er forrst termsztesen kdbl is hozzkthetnk egy vezrl hz:
private void Window_Loaded(object sender, RoutedEventArgs e) {

258
resbutton.Background = (Brush)this.FindResource("buttoncolor"); }

Persze a megfelel objektumon kell meghvnunk a FindResource metdust, a fenti esetben a this a legfels szint elemre a Window ra mutat, teht az el tte lv pldval nem fog m kdni, helyette rhatjuk ezt (vagy adhatunk nevet a Grid nek):
<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Window.Resources> <LinearGradientBrush x:Key="buttoncolor"> <GradientStop Color="Black" Offset="0.0" /> <GradientStop Color="Red" Offset="0.7" /> </LinearGradientBrush> </Window.Resources> <Grid> <Button Width="100" Height="30" Background="{StaticResource buttoncolor}" VerticalAlignment="Top" /> <Button x:Name="resbutton" Width="100" Height="30" VerticalAlignment="Bottom" /> </Grid> </Window>

A FindResource helyett hasznlhatjuk a Resource tulajdonsg indexel jt is:


resbutton.Background = (Brush)this.Resources["buttoncolor"];

A FindResource biztonsgosabb vltozata a TryFindResource, amely null rtket ad vissza, ha az er forrs nem tallhat. Az App.xaml file ban elhelyezhetnk globlis er forrsokat, amelyek az alkalmazs brmely pontjrl elrhet ek:
<Application x:Class="JegyzetWPF.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="Window1.xaml"> <Application.Resources> <LinearGradientBrush x:Key="buttoncolor"> <GradientStop Color="Black" Offset="0.0" /> <GradientStop Color="Red" Offset="0.7" /> </LinearGradientBrush> </Application.Resources> </Application>

Mg jobban kiterjeszthetjk ezt, ha ResourceDictionary t hasznlunk, ezt egy hagyomnyos XAML file ba helyezhetjk (projecten jobb klikk, Add/New Item s a WPF fl alatt vlasszuk a Resource Dictionary t):

259

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <LinearGradientBrush x:Key="buttoncolor"> <GradientStop Color="Black" Offset="0.0" /> <GradientStop Color="Red" Offset="0.7" /> </LinearGradientBrush> </ResourceDictionary>

Ezt a file -t nevezzk el mondjuk AppRes.xaml nek. Ahhoz, hogy a tartalmt hasznlni tudjuk be kell olvasztanunk az alkalmazsunkba, ezt az App.xaml ben kell megtennnk:
<Application x:Class="JegyzetWPF.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="Window1.xaml"> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="AppRes.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> </Application>

Termszetesen brmennyi forrst felvehetnk, illetve ha magunk akarunk itt er forrsokat felvenni, azt is megtehetjk, csak rjuk a MergedDictionaries al.

260

46. Stlusok
Az el z fejezetben bemutatott er forrsok egyik leggyakrabban hasznlt terlete a stlusok. A koncepci nagyon hasonl a CSS hez, vagyis megadott tulajdonsgokhoz rendelnk rtkeket. Van azonban egy dolog, amely mg er teljesebb teszi a WPF stlusait, mgpedig az, hogy a segtsgkkel brmely dependency property t bellthatjuk de csakis azokat. A stlusok tmakrhez tartoznak a triggerek s a sablonok is, velk is megismerkednk ebben a fejezetben. Kezdjk rgtn egy pldval:
<Window x:Class="JegyzetTestWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <Window.Resources> <Style x:Key="labelfontstyle"> <Setter Property="Control.FontFamily" Value="Arial Black" /> <Setter Property="Control.FontSize" Value="42" /> </Style> </Window.Resources> <Grid> <Label Style="{StaticResource labelfontstyle}" Content="Hello Style!" /> </Grid> </Window>

Az eredmny:

A kd nmagrt beszl. Stlust nem csak XAML b l rendelhetnk egy elemhez:


label1.Style = (Style)this.FindResource("labelfontstyle");

A Style tulajdonsgot minden vezrl a FrameworkElement osztlytl rkli, tpusa pedig - mint az a fenti pldban is ltszik Style lesz. Lthat, hogy a stlusokat az er forrsokhoz hasonlan kezelhetjk (lvn maga is egy er forrs, csak van sajt neve). Ebb l a pldbl az is kiderl, hogy egy elemhez csak egyetlen stlust rendelhetnk hozz, teht tbb stlus sszeollzsa nem fog m kdni legalbbis gy nem. Azt sem rt tudni, hogy egy stlus brmely rtkt fellbrlhatjuk, azzal, hogy explicit mdon megadjuk a vezrl adott tulajdonsgnak az rtkt. Vegyk pldul a fent ltrehozott stlust s rendeljk hozz egy Label vezrl hz:

261
<Label Style="{StaticResource labelfontstyle}" FontSize="10" Content="Hello Style!" />

Ebben az esetben csakis a bet tpust fogja a stlus meghatrozni, mivel belltottuk a bet mretet, amely fellbrlja a stlust. Gyakran el fordul, hogy egy tulajdonsg rtkt nem tudjuk egy egyszer rtkkel megadni, ekkor a kvetkez t tehetjk:
<Style x:Key="backgroundstyle"> <Setter Property="Control.Background"> <Setter.Value> <RadialGradientBrush> <GradientStop Color="Red" Offset="0.0" /> <GradientStop Color="Black" Offset="0.75" /> </RadialGradientBrush> </Setter.Value> </Setter> </Style>

Nem csak tulajdonsgokat, de esemnyeket is hozzrendelhetnk egy vezrl hz az EventSetter objektumokkal:


<Style x:Key="eventstyle"> <EventSetter Event="ButtonBase.Click" Handler="Button_Click" /> </Style>

A BasedOn tulajdonsg belltsval megadhatunk egy msik stlust, amelynek rtkeit az j stlus is hasznlja, gy lehet v tve tbb stlus sszeolvasztst:
<Style x:Key="style1"> <Setter Property="Control.FontFamily" Value="Arial Black" /> <Setter Property="Control.FontSize" Value="42" /> </Style> <Style x:Key="style2" BasedOn="{StaticResource style1}"> <Setter Property="Control.Foreground" Value="Red" /> </Style>

Ezt alkalmazva az eredmny:


<Label Style="{StaticResource style2}" Content="Hello Style!" />

Eddig a pontig az ltalnos Control tulajdonsgokra hivatkoztunk, a TargetType tulajdonsg segtsgvel viszont egy adott vezrl tpusra rvnyesthetjk a stlust:

262
<Style x:Key="typestyle" TargetType="Label" > <Setter Property="FontFamily" Value="Arial Black" /> <Setter Property="FontSize" Value="42" /> </Style>

Egy stlust hozzrendelhetnk egy adott tpushoz, anlkl, hogy azt az elem defincijnl kln jeleznnk. Ebben az esetben a stlust definil elem sszes megfelel tpus gyermekelemre vonatkozni fognak a belltsok:
<Style x:Key="{x:Type Label}" TargetType="Label" > <Setter Property="FontFamily" Value="Arial Black" /> <Setter Property="FontSize" Value="42" /> </Style> <Label Content="Hello Style!" />

A fenti pldban a Label vezrl br ezt explicite nem jeleztk rendelkezni fog a stlus belltsaival (ugyangy az sszes tbbi Label objektum is). Amennyiben megadunk a vezrl nek egy msik stlust, az fellbrlja a globlisat. Azt is megtehetjk, hogy eltvoltunk minden stlust egy vezrl r l:
<Label Style="{x:Null}" Content="Hello Style!" />

46.1 Triggerek
Egy stlus rendelkezhet triggerekkel, amelyek segtsgvel bellthatjuk, hogy hogyan reagljon egy vezrl egy esemny bekvetkeztvel, vagy egy tulajdonsg megvltozsakor. Ahogyan azt a WPF t l megszokhattuk, a hozzrendelt tulajdonsgok csakis dependency property k lehetnek. Trigger t kthetnk kzvetlenl egy vezrl hz is a FrameworkElement.Triggers tulajdonsgon kereszt l, de ekkor csak s kizrlag esemny trigger t hasznlhatunk. tfle trigger ltezik, ezek kzl most hrommal fogunk megismerkedni, a tbbiek az adatktsek tmakrhez tartoznak: Trigger: a legegyszer bb, egy dependency property t figyel s aszerint vltoztatja meg a stlust. MultiTrigger: hasonl az el z hz, de tbb felttelt is megadhatunk, amelyek mindegyiknek teljeslnie kell. EventTrigger: a fent mr emltett esemny trigger, a megadott esemny hatsra lp akciba.

Ebb l a felsorolsbl kimaradt a DataTrigger s a MultiDataTrigger, velk egy ksbbi fejezetben lehet majd tallkozni.

46.1.1 Trigger
Ksztsnk egy egyszer trigger t, ami az IsMouseOver tulajdonsgot figyeli s akkor aktivldik, amikor az rtke igaz lesz:
<Style x:Key="triggerstyle"> <Style.Triggers>

263
<Trigger Property="Control.IsMouseOver" Value="true"> <Setter Property="Control.FontSize" Value="42" /> </Trigger> </Style.Triggers> </Style>

Rendeljk hozz a stlust egy Label hez:


<Label Style="{StaticResource triggerstyle}" Content="Hello Style!" />

Ezutn ha a Label fl visszk az egeret, akkor a bet mret megn . Nem marad viszont gy, a Trigger ltal definilt viselkeds csakis addig tart, amg az aktivlst kivlt tulajdonsg a megfelel rtken ll. Termszetesen egy stlus tbb trigger t s egy trigger tbb Setter t is tartalmazhat.

46.1.2 MultiTrigger
A MultiTrigger segtsgvel sszetettebb feltteleket is felllthatunk. Ezek kztt a felttelek kzt S kapcsolat van, azaz minden felttelnek teljeslnie kell ahhoz, hogy a trigger aktivldjon.
<Style x:Key="multitriggerstyle"> <Style.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="Control.IsEnabled" Value="true" /> <Condition Property="Control.IsFocused" Value="true" /> </MultiTrigger.Conditions> <MultiTrigger.Setters> <Setter Property="Control.Foreground" Value="Red" /> </MultiTrigger.Setters> </MultiTrigger> </Style.Triggers> </Style>

Ebben a pldban az aktv s fkuszban lv legyen ez egy TextBox:

vezrl ket clozzuk meg, mondjuk

<TextBox Style="{StaticResource multitriggerstyle}" Width="100" Height="20" />

Amikor gpelni kezdnk a TextBox ba, akkor a szveg pirosra vlt, de ha lekerl rla a fkusz visszatr a rendszerrtkhez.

46.1.3 EventTrigger
Az EventTrigger esemnyekre reagl, de eltr en trsaitl egy animcit fog elindtani (ez a tmakr egy kvetkez fejezet trgya lesz, most csak egy egyszer plda kvetkezik):
<Style TargetType="{x:Type Button}"> <Style.Triggers> <EventTrigger RoutedEvent="MouseEnter">

264
<EventTrigger.Actions> <BeginStoryboard> <Storyboard> <ColorAnimation Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)" From="White" To="Black" Duration="0:0:5" /> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Style.Triggers> </Style>

A pldban egy egyszer animcit definiltunk, amely akkor kezd dik, amikor az egeret egy gomb fl visszk s egy fehrb l feketbe tart szntmenetet eredmnyez.

265

47. Sablonok
Ez a fejezet a vezrl k sablonjaival foglalkozik, a WPF mg kt msik HierarchicalDataTemplate) rendelkezik, rluk egy msik fejezetben esik sz majd. sablontpussal (DataTemplate s

Sablonok segtsgvel gyakorlatilag brmilyen vltozst eszkzlhetnk egy vezrl kinzetn, gy, hogy az semmit sem vesztsen funkcionalitsbl (s t, akr egy teljesen eltr elemet is kszthetnk). Amikor Windows Forms szal dolgoztunk, egy ilyen akci kivitelezshez User Control t kellet gyrtanunk s rengeteg munkval trnunk a renderelsrt felel s metdusokat. A WPF felptsb l addan vltoztat ezen. Egy korbbi fejezetben mr megismerkedtnk a logikai- s vizulis fk intzmnyvel, most ezt a tudsunkat fogjuk egy kicsit b vteni. Segtsgnkre lesz az XamlPad nev program, amely a Windows SDK val rkezik (ltalban bepl a Start menbe). Indtsuk el s ksztsnk egy egyszer ListBox vezrl t egy ListBoxItem mel. Fll tallunk egy ikont, amely megmutatja a vizulis ft. Kapcsoljuk be s nzzk meg az eredmnyt:

Amit ltunk az nem ms, mint a ListBox vezrl alaprtelmezett sablonja. Tartalmaz pl. egy Grid et egy Rectangle t, stb Most mr knny kitallni, hogy miknt tudunk j sablont kszteni: ezt a ft kell tdefinilni. Egy pldn kereszt l vilgosabb lesz a dolog egy kiss, ezrt ksztsnk egy a megszokottl eltr formj gombot! Sablont az er forrsoknl megszokott mdon a Resources tulajdonsgon bell a ControlTemplate osztllyal tudunk definilni:
<ControlTemplate x:Key="circlebuttontemplate" TargetType="{x:Type Button}"> <Grid Width="50" Height="50"> <Ellipse Fill="Red"> </Ellipse> <ContentPresenter VerticalAlignment="Center"

266
HorizontalAlignment="Center" Content="{TemplateBinding Content}" /> </Grid> </ControlTemplate> <Button Content="Hello" Template="{StaticResource circlebuttontemplate}" />

Az eredmny:

A sablon definicijnak elejn rgtn megmondtuk, hogy ez a sablon gombok szmra kszlt. Ez egyrszt konvenci msrszt elengedhetetlen a helyes m kdshez, mivel a ContentControl leszrmazottak amilyen a Button is enlkl nem jelentenk meg a tartalmukat. Ez utbbihoz szksges a ContentPresenter osztly is amely a sablonunk aljn helyezkedik el, fogja trolni a tartalmazott adatokat. A TemplateBinding segtsgvel ki tudjuk nyerni a sablonozott vezrl tulajdonsgainak rtkt jelen esetben ez a Content lesz. A TemplateBinding m kdsr l az adatktsekr l szl fejezet tbbet is mesl majd. A gombunk egyel re nem csinl tl sokat, legalbbis vizulisan nem. Egybknt pont ugyangy m kdik mint egy mezei gomb, rendelhetnk hozz pl. Click esemnykezel t, amely rendesen le is fut majd, de nem fog ltszani, hogy rkattintottunk. Mi sem knnyebb, mint megoldani ezt a problmt, Trigger eket fogunk hasznlni:
<ControlTemplate x:Key="circlebuttontemplate" TargetType="{x:Type Button}"> <Grid Name="main" Width="50" Height="50"> <Ellipse Name="back" Fill="Red"> </Ellipse> <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" Content="{TemplateBinding Content}" /> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="true"> <Setter TargetName="back" Property="Fill" Value="Black" /> <Setter TargetName="main" Property="TextElement.Foreground" Value="White" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate>

Mostantl ha az egr a gombunk flk kerl, akkor azt ltni is fogjuk. Ugyangy ltrehozhatjuk a tbbi esemny stlust is.

267

48. Commanding
Kpzeljk el a kvetkez helyzetet: egy szvegszerkeszt programot fejlesztnk s ppen a kivgs/msols/beilleszts funkcikat prbljuk megvalstani. A hasonl programok esetben megszokhattuk, hogy ezeket a funcikat tbbflekppen is elrhetjk: menb l, helyi menb l, billenty kombincival, stb Az nylvnval, hogy brmelyiket vlasszuk is ki az ugyanazt a metdust fogja meghvni. Ezen a ponton mr belefutunk abba a problmba, hogy rnunk kell egy raks esemnykezel t, amelyek mind ugyanazt csinljk. s ez mg nem minden, hiszen a felhasznli felletet is mdostanunk kell, hiszen nem mindegyik funkci rhet el mindig (pl. msolni csak akkor lehet ha van kijellt szveg, beilleszteni csak akkor, ha van valami a vglapon, stb), ezrt ennek megfelel en vltoztatni kell a fellet elemeinek llapott is, ami jabb esemnykezel k ksztst jelenti. Ennyi problma pp elg ahhoz, hogy egy amgy egyszer alkalmazs elksztse rmlomm vljon. Szerencsre a WPF erre is ad vlaszt a command model bevezetsvel. Mit is takar ez? Definilhatunk n. parancs objektumokat, amelyeket vezrl khz kthetnk, amelyek llapota a parancs elrhet sgt l fgg en vltozik. Ennek a megoldsnak is vannak hinyossgai, pl. nincs beptett lehet sg a parancsok kvetsre s ezrt vissza sem tudjuk vonni azokat. A command model ngy f szerepl vel br, ezek a kvetkez ek: Maga a parancs (command), ez reprezentlja a valdi kdot a parancs mgtt s szmontartja annak aktulis llapott (aktv/passzv). A kts (command binding) vagyis egy parancsot hozzkthetnk egy vagy tbb vezrl hz. A forrs (command source), a vezrl amely kivltja az adott parancs vgrehajtst. A cl (command target), amin a parancs vgrehajtja a feladatt (pl. a pldnkban ez lehet egy RichTextBox, amibe beillesztnk).

Miel tt rtrnk a gyakorlatiasabb rszre fontos megismernnk egy kicsit mlyebben a commanding m kdst. Minden egyes parancs objektum megvalstja az ICommand interfszt amely gy nz ki:
public interface ICommand { void Execute(object parameter); bool CanExecute(object parameter); event EventHandler CanExecuteChanged; }

Az els metdus a parancs vgrehajtsrt felel, a msodik a parancs vgrehajthatsgt adaj vissza, mg a harmadik tag egy esemny, amely a parancs llapotnak megvltozsakor aktivizldik. Ez persze csak egy leegyszer stett magyarzat, nzzk meg, hogy mi trtnik valjban: az Execute metdus meghvsakor egy esemny vltdik ki, amely jelzi az alkalmazs egy msik rsznek, hogy ideje cselekedni (ez lesz a valdi kd). A

268 CanExecuteChanged pedig jelzi az adott parancshoz kttt sszes vezrl nek, hogy meg kell hvniuk a CanExecute metdust s ennek megfelel en mdostani magukat (pl. a msols menpont ne legyen aktv). Ez a m kds a Commanding egy nagy htrnya is egyben, hiszen ha a felleten trtnik valami akkor az sszes parancsot vgig kell ellen rizni. Ez persze finomthat, de ez mr tlmutat a jegyzeten. Az el bb azt mondtuk, hogy minden parancs megvalstja az ICommand interfszt, de ez nem teljesen igaz. Valjban a WPF egyetlen olyan osztllyal rendelkezik amely direkt mdon megvalstja ezt az interfszt (s ide rtjk a sajt magunk ltal ksztett parancsokat is) ez pedig a RoutedCommand. Ez az osztly csal egy kicsit ugyanis az ICommand metdusait privtknt implementlja s sajt metdusokat kszit, amelyek eltakarjk azokat. Mirt van ez gy? Az osztly nevnek el tagja megadja a vlaszt: azrt, hogy tmogatni tudja a routed event eket. Az ICommand egy platformfggetlen megolds, vagyis nem tud arrl, hogy t most WPF alatt szeretnnk hasznlni, a parancsoknak s az ket kivlt vezrl kek viszont tudniuk kell kommuniklni egymssal. Ezenkv l a RoutedCommand valstja meg a tnyleges parancskezelst, vele rszletesebben meg fogunk ismerkedni, amikor sajt parancsot ksztnk. A legtbb parancs mgsem ebb l az osztlybl szrmazik, hanem az leszrmazottjbl a RoutedUICommand bl. Ez az osztly egy extra Text tulajdonsggal rendelkezik, amely a parancs vizulis reprezentcija lesz, teht ez fog megjelenni pl. egy menben. Ez azrt j, mert ha tbbnyelv megvalstst szeretnnk, akkor csak egy helyen kell a lokalizcirl gondoskodnunk. A WPF jnhny el re definilt paranccsal rendelkezik szm szerint 144 gyel. Ezek a parancsok t csoportba oszthatak: ApplicationCommands: a legltalnosabb parancsok tartoznak ide, pl. a vglaphoz kapcsoldak (msols, kivgs, beilleszts, stb.) is. EditingCommands: a dokumentumok kezelsvel kapcsolatos parancsok, pl.: kijells, mozgs a szvegben, szvegformzs, stb MediaCommands: multimdis tartalmakat irnytanak ezek a parancsok (lejtszs, sznet, stb.). NavigationCommands: a navigcirt felel s parancsok gy jtemnye, velk egy kln fejezetben fogunk foglalkozni. ComponentCommands: ezek a parancsok nagyon hasonlak az EditingComands csoporthoz, azzal a klnbsggel, hogy k a felhasznli felleten fejtik ki a hatsukat.

Eljtt az ideje, hogy a gyakorlatban is felhasznljuk tudsunkat. Az EditingCommands csoportot fogjuk ehhez felhasznlni. Teht, parancsunk mr van, most kell egy forrs, ami kivltja ezt a parancsot. Ez brmelyik vezrl lehet, amely megvalstja az ICommandSource interfszt. Ilyen pl. az sszes ButtonBase leszrmazott, egy ListBox elemei vagy egy menben a menpontok. Ez az interfsz hrom tulajdonsgot definil: Command: ez az alkalmazand parancsra mutat. CommandParameter: az esetleges paramter, amit a paranccsal kldnk.

269 CommandTarget: az az elem, amelyen a parancs kifejti hatst.

Nzzk a kvetkez XAML t:


<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="400" Loaded="Window_Loaded" > <Grid> <Grid.RowDefinitions> <RowDefinition Height="200" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <RichTextBox Name="rtbox1" Width="300" Height="200" Grid.Row="0" /> <Button Name="boldbutton" Width="100" Height="30" Content="Flkvr" Grid.Row="1" Command="EditingCommands.ToggleBold" CommandTarget="{Binding ElementName=rtbox1}"/> </Grid> </Window>

Amikor rkattintunk a gombra akkor a RichTextBox ban a bet tpus flkvrre mdosul. A gomb Command tulajdonsgnl nvterest l adtuk meg a parancs nevt, ez egyrszt az tlthatsgot szolglja, msrszt elkerljk a nvtkzseket (ugyanakkor nem ktelez gy hasznlni). A Visual Studio 2008 XAML szerkeszt je sajnos nem ad IntelliSense kiegsztst ehhez a feladathoz. A parancsok vgrehajtshoz rendelhetnk esemnykezel ket, ami hasznos lehet pl. a szvegszerkeszt programunkban, amikor meghatrozzuk, hogy melyik parancs legyen elrhet . Ehhez n. CommandBinding et fogunk hasznlni, amelyet a kvetkez kppen definilhatunk:
<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="150" Width="150" Loaded="Window_Loaded" > <Window.CommandBindings> <CommandBinding Command="ApplicationCommands.Close" Executed="CommandBinding_Executed" /> </Window.CommandBindings> <Grid> <Button Command="ApplicationCommands.Close"

270
Content="Close" Width="100" Height="30" /> </Grid> </Window>

Ngy esemnyt kezelhetnk: Executed: a parancs vgrehajtsakor aktivizldik, a stratgija bubbling, vagyis a forrstl indul. PreviewExecuted: ugyanaz mint az el z , de a stratgija tunneling. CanExecute: akkor aktivizldik, amikor a hozzrendelt vezrl meghvja a CanExecute metdust, a stratgija bubbling. PreviewCanExecute: ugyanaz mint az el z de a stratgija tunneling.

Az esemnykezel k hozzrendelst csakis egyzer kell megtennnk, ha az adott parancsot hozzrendeltk legalbb egy vezrl hz azutn automatikusan meghvdnak ezek is. Mr beszltnk a RoutedUICommand Text tulajdonsgrl. Ezt felhasznlhatjuk a vezrl inken, gy azzal sem kell trdnnk, hogy rrjuk pl. egy gombra, hogy mit csinl:
<Button Command="ApplicationCommands.Close" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" Width="100" Height="30" />

Els re egy kicsit ijeszt lehet ez a megolds s most nem is foglalkozunk vele majd csak az adatktsekr l szl fejezetben. Nzznk meg egy msik pldt:
<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="250" Width="300" Loaded="Window_Loaded" > <Grid> <Grid.RowDefinitions> <RowDefinition Height="30" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Menu Grid.Row="0"> <MenuItem Command="ApplicationCommands.Cut" /> <MenuItem Command="ApplicationCommands.Paste" /> </Menu> <RichTextBox Width="250" Height="150" Grid.Row="1"/> </Grid> </Window>

271

Kt dolog is felt nhet, az egyik, hogy a menpontoknl nem adtunk meg szveget, a msik, hogy ltszlag nem ktttk r a parancsot a RichTextBox ra. Az els esetben azt hasznltuk ki, hogy bizonyos vezrl k automatikusan tudjk hasznlni a RoutedUICommand Text tulajdonsgt. A msodik eset egy kicsit bonyolultabb: bizonyos parancscsoportok egy adott vezrl csoporthoz kthet ek, pl. a mi esetnkben ezek a szveges vezrl k (RichTextBox s TextBox). Ha mst nem adunk meg, akkor a parancs mindig az ppen fkuszban lv , az csoportja ltal hasznlhat vezrl hz lesz ktve. Van mg egy kikts, mgpedig az, hogy ez csakis a ToolBar s Menu vezrl kre vonatkozik, minden ms esetben explicit meg kell adnunk a ktst. Ha elindtjuk a programot minden m kdni is fog:

Parancsok ksztsvel egy ks bbi fejezetben foglalkozunk.

272

49. Animcik s transzformcik


Mr megtanultuk, hogy a WPF mer en j grafikus rendszerrel rendelkezik, ennek egyik hatalmas el nye, hogy olyan tulajdonsgokkal vrtezhetjk fel az alkalmazsainkat, amelyek megvalstsa eddig embertprbl feladat vagy ppen lehetetlen volt. Ilyen lehet sg a vezrl ink letrekeltse is. A WPF rgtn kt mdszert is felknl, animcik s transzformcik kpben. El bbi a vezrl k tulajdonsgainak (pl.: helyzet, szlessg, stb) manipullsval dolgozik, mg transzformcik alkalmazsa esetn alacsonyabb szintre visszk a dolgot s a koordintarendszerben elfoglalt pozcit mdostjuk. Ebben a rszben csakis 2D s transzformcikat s animcikat nznk meg.

49.1 Transzformcik
Minden transzformcis osztly a System.Windows.Media.Transform osztlybl szrmazik. Ennek az osztlynak hat leszrmazottja van, ebb l ngy el re definilt viselkedst hordoz, egy lehet v teszi tetsz leges transzformci ltrehozst mg az utols tbb transzformcibl llhat. A kvetkez fejezetekben mindegyikkkel megismerkednk. El tte viszont megnznk egy kis elmletet is (br ez nem felttele a megrtsnek): Minden transzformci lerhat egy n. transzformcis mtrixxal: M11, [ M21, OffsetX, M12, 0 M22, 0] OffsetY, 1

A harmadik oszlop mivel a WPF csak lineris transzformcikat hasznl lland lesz, az OffsetX s OffsetY tagok pedig az X illetve Y tengelyen val eltols mrtkt jelzik. A maradk ngy vltoz a szndkunktl fgg en kap szerepet. A transzformcis mtrix kiindulsi helyzete a kvetkez : 100 [010] 001 Teht az egysgmtrix. Ennek gyakorlatilag csak a MatrixTransform esetn lesz szerepe. A transzformcis mtrix segtsgvel kiszmolhatjuk az alakzat j koordintit a kvetkez kpletekkel (X s Y a rgi koordintkat jellik X s Y pedig az jakat): X = X * M11 + Y * M21 + OffsetX Y = X * M12 + Y * M22 + OffsetY Nzznk egy gyakorlati pldt! Legyen a koordintarendszer a kvetkez :

273

A pirossal jellt hromszg cscsainak a koordinti: (1;1), (1;2) s (2;1). A transzformcis mtrix igen egyszer lesz, mindssze az M12 vltozt rtuk t 1 re: 110 [010] 001 Most alkalmazzuk a kpletet s szmtsuk ki a hrom j koordintt: X1 = 1 * 1 + 1 * 0 + 0 = 1 Y1 = 1 * 1 + 1 * 1 + 0 = 2 X2 = 1 * 1 + 2 * 0 + 0 = 1 Y2 = 1 * 1 + 2 * 1 + 0 = 3 X3 = 2 * 1 + 1 * 0 + 0 = 2 Y3 = 2 * 1 + 1 * 1 + 0 = 3 Az j koordintk gy: (1;2), (1;3) s (2;3). Vagyis egy egysggel eltoltuk a hromszget az Y tengelyen pozitv irnyba. Termszetesen ugyanezt megtehettk volna sokkal egyszer bben is az OffsetY vltoz lltsval. Az j koordintk brzolva (zlddel):

49.1.1 MatrixTransform
Az j ismereteinknek megfelel en az els vizsglt alany a MatrixTransform lesz, mivel ez az egyetlen olyan WPF transzformci, amikor szksgnk van a transzformcis mtrixra, a tbbiek ezt a rszletet elrejtik el lnk. Gyakorlatilag az esetek kilencvenkilenc szzalkban nem lesz szksgnk erre a transzformcira. Vegyk a kvetkez pldt:

274

<Polygon Points="100,50 50,75 50,95 100,50" Stroke="Black" StrokeThickness="2"> <Polygon.Fill> <SolidColorBrush Color="Red"/> </Polygon.Fill> <Polygon.RenderTransform> <MatrixTransform Matrix="1 0 0 1 0 0" /> </Polygon.RenderTransform> </Polygon>

Ltrehoztunk egy Polygon objektumot, a Points tulajdonsgnl megadott szmprok a poligon pontjait jelzik, ebben a sorrendben kti ssze ket a WPF. Viszonylag knny kitallni, hogy a fnti plda eredmnye egy hromszg lesz, mgpedig egy ilyen hromszg:

Mr meg is adtuk a transzformcis mtrixot, amely pp alaphelyzetben az egysgmtrixon ll, gy hatsa nincsen. A transzformcik megadsnak ksbb megtanuljuk praktikusabb mdjait is, egyel re gy is j lesz, lehet vele ksrletezni. Pldul legyen a mtrix a kvetkez :
<MatrixTransform Matrix="2 0 0 1 0 0" />

Az eredmny egszen drasztikus, nemcsak eltoltuk az X tengelyen pozitv irnyba, de a ktszeresre is n tt a skidom. A kplet segtsgvel elg knny rjnni, hogy mirt van ez gy:

275

Most ksztsnk egy egyszer programot, amely segtsgvel egyszer en llthatjuk a mtrix rtkeit. A ksz alkalmazs gy nz ki:

Az XAML kd:
<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="500"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="150" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <StackPanel Background="Cornsilk" Height="150" VerticalAlignment="Top" Grid.Column="0" Margin="5"> <WrapPanel> <TextBlock Text="M11: " /> <TextBox Name="m11" Width="30">0</TextBox>

276
</WrapPanel> <WrapPanel> <TextBlock Text="M12: " /> <TextBox Name="m12" Width="30">1</TextBox> </WrapPanel> <WrapPanel> <TextBlock Text="M21: " /> <TextBox Name="m21" Width="30">0</TextBox> </WrapPanel> <WrapPanel> <TextBlock Text="M22: " /> <TextBox Name="m22" Width="30">1</TextBox> </WrapPanel> <WrapPanel> <TextBlock Text="OffsetX: " /> <TextBox Name="offsetx" Width="30">0</TextBox> </WrapPanel> <WrapPanel> <TextBlock Text="OffsetY: " /> <TextBox Name="offsety" Width="30">0</TextBox> </WrapPanel> <Button Name="applybutton" Content="Alkalmaz" Margin="5" Width="50" Click="applybutton_Click" /> </StackPanel> <Canvas Grid.Column="1"> <Polygon Points="100,50 50,75 50,95 100,50" Stroke="Black" StrokeThickness="2"> <Polygon.Fill> <SolidColorBrush Color="Red"/> </Polygon.Fill> <Polygon.RenderTransform> <MatrixTransform x:Name="polytransform" /> </Polygon.RenderTransform> </Polygon> </Canvas> </Grid> </Window>

Vgl a gombhoz tartoz esemnykezel :


private void applybutton_Click(object sender, RoutedEventArgs e) { Matrix m = new Matrix(); m.M11 = double.Parse(m11.Text); m.M12 = double.Parse(m12.Text); m.M21 = double.Parse(m21.Text); m.M22 = double.Parse(m22.Text); m.OffsetX = double.Parse(offsetx.Text); m.OffsetY = double.Parse(offsety.Text);

277
polytransform.Matrix = m; }

Lthat, hogy a WPF adja neknk a megfelel mtrix adatszerkezetet. Az rtkeket megvltoztatva elrhetjk a WPF sszes lehetsges transzformcijt. Termszetesen nem csak az el re megadott poligont hasznlhatjuk, hanem brmely Shape (pl.: Rectangle) illetve FrameworkElement leszrmazottat. Ez utbbira egy plda:

Az XAML nagyon egyszer , csak a poligon defincit cserltk le:


<Button Content="Ez egy gomb" Width="100" Height="100"> <Button.RenderTransform> <MatrixTransform x:Name="polytransform" /> </Button.RenderTransform> </Button>

49.1.2 RotateTransform
Ezzel a transzformcival egy adott pont krl adott szgben el tudjuk forgatni a megclzott objektumot, ami a pldnkban egy Rectangle lesz:

278

Nzzk meg a hozz tartoz XAML t is:


<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="240" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Rectangle Width="100" Height="100" Fill="Red" Grid.Row="0"> <Rectangle.RenderTransform> <RotateTransform Angle="{Binding ElementName=angleslider, Path=Value}" /> </Rectangle.RenderTransform> </Rectangle> <Slider Name="angleslider" Minimum="0" Maximum="360" Grid.Row="1" /> </Grid> </Window>

Most csakis a szget adtuk meg, a forgats kzppontjt teht azt a pontot ami krl forog nem. Ilyenkor automatikusan az objektum bal fels sarkt tekinti kiindulpontnak. Adjuk meg ezeket az rtkeket is! A kzppont koordinti rtelemszer en arra az objektumra rtend ek, amelyen alkalmazzuk a transzformcit, vagyis az objektumon belli koordintra gondolunk. A bal fels sarok koordintja ekkor (0;0) lesz. Mivel most tudjuk a ngyzet mreteit nem nehz kitallni a valdi kzppontot:

279
<Rectangle Width="100" Height="100" Fill="Red" Grid.Row="0"> <Rectangle.RenderTransform> <RotateTransform Angle="{Binding ElementName=angleslider, Path=Value}" CenterX="50" CenterY="50" /> </Rectangle.RenderTransform> </Rectangle>

Az eredmny:

49.1.3 TranslateTransform
Ezzel a transzformcival a transzformcis mtrix OffsetX s OffsetY tagjait tarthatjuk kzben. Ennek megfelel en kt olyan tulajdonsggal rendelkezik, amelyek hatnak az objektumra: X s Y. Nzzk a pldt:

Az XAML:

280
<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="200" Width="180"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="130" /> <RowDefinition Height="40" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="40" /> <ColumnDefinition Width="130" /> </Grid.ColumnDefinitions> <Canvas Background="SlateGray" Grid.Row="0" Grid.Column="1"> <Rectangle Width="30" Height="30" Fill="Red" Canvas.Top="0" Canvas.Left="0"> <Rectangle.RenderTransform> <TranslateTransform X="{Binding ElementName=xslider, Path=Value}" Y="{Binding ElementName=yslider, Path=Value}" /> </Rectangle.RenderTransform> </Rectangle> </Canvas> <Slider Name="xslider" Background="Aqua" Minimum="0" Maximum="100" Height="20" Width="130" Grid.Row="1" Grid.Column="1" /> <Slider Name="yslider" Background="Aqua" IsDirectionReversed="True" Orientation="Vertical" Width="20" Height="130" Minimum="0" Maximum="100" Grid.Row="0" Grid.Column="0" /> </Grid> </Window>

49.1.4 ScaleTransform

281 A ScaleTransform az X illetve Y tengelyeken val nyjtsrt felel, ezeket a ScaleX s ScaleY tulajdonsgain keresztl llthatjuk. A tulajdonsgok rtkei szorzknt funkcinlnak, gy alaprtelmezett rtkk 1 lesz. Ezenkvl megadhatjuk a transzformci kzpponttjt is a CenterX s CenterY tulajdonsgokkal. A plda:

<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="200" Width="180"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="130" /> <RowDefinition Height="40" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="40" /> <ColumnDefinition Width="130" /> </Grid.ColumnDefinitions> <Canvas Background="SlateGray" Grid.Row="0" Grid.Column="1"> <Rectangle Width="30" Height="30" Fill="Red" Canvas.Top="45" Canvas.Left="45"> <Rectangle.RenderTransform> <ScaleTransform ScaleX="{Binding ElementName=xslider, Path=Value}" ScaleY="{Binding ElementName=yslider, Path=Value}" CenterX="15" CenterY="15" /> </Rectangle.RenderTransform> </Rectangle> </Canvas> <Slider Name="xslider" Background="Aqua" Minimum="1" Maximum="10" Height="20" Width="130"

282
Grid.Row="1" Grid.Column="1" /> <Slider Name="yslider" Background="Aqua" IsDirectionReversed="True" Orientation="Vertical" Width="20" Height="130" Minimum="1" Maximum="10" Grid.Row="0" Grid.Column="0" /> </Grid> </Window>

49.1.5 SkewTransform
Ez a transzformci objektumok elhajltsra szolgl:

Lthat, hogy ez egyfajta 3D imitci is lehet. Az XAML:


<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="200" Width="180"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="130" /> <RowDefinition Height="40" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="40" /> <ColumnDefinition Width="130" /> </Grid.ColumnDefinitions> <Canvas Background="SlateGray" Grid.Row="0" Grid.Column="1"> <Rectangle Width="30" Height="30"

283
Fill="Red" Canvas.Top="45" Canvas.Left="45"> <Rectangle.RenderTransform> <SkewTransform AngleX="{Binding ElementName=xslider, Path=Value}" AngleY="{Binding ElementName=yslider, Path=Value}" CenterX="15" CenterY="15" /> </Rectangle.RenderTransform> </Rectangle> </Canvas> <Slider Name="xslider" Background="Aqua" Minimum="0" Maximum="180" Height="20" Width="130" Grid.Row="1" Grid.Column="1" /> <Slider Name="yslider" Background="Aqua" IsDirectionReversed="True" Orientation="Vertical" Width="20" Height="130" Minimum="0" Maximum="180" Grid.Row="0" Grid.Column="0" /> </Grid> </Window>

49.1.6 TransformGroup
Ennek az osztlynak a segtsgvel tbb transzformcit tudunk sszefogni. A kvetkez pldban pl. egyszerre alkalmazzuk a Rotate- s TranslateTransformation t:

284

Az XAML:
<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="260" Width="410"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="200" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Canvas Background="SlateGray" Grid.Row="0"> <Rectangle Width="30" Height="30" Fill="Red" Canvas.Top="70" Canvas.Left="0"> <Rectangle.RenderTransform> <TransformGroup> <RotateTransform CenterX="15" CenterY="15" Angle="{Binding ElementName=xslider, Path=Value}" /> <TranslateTransform X="{Binding ElementName=xslider, Path=Value}" /> </TransformGroup> </Rectangle.RenderTransform> </Rectangle> </Canvas> <Slider Name="xslider" Background="Aqua" Minimum="0" Maximum="360" Height="20" Width="400" Grid.Row="1" /> </Grid> </Window>

49.1.7 Transzformci mint er forrs


Termszetesen egy transzformcit nem kell helyben kifejtennk, megadhatjuk er forrsknt is gy nvelve a kdjrafelhasznlst. Ksztsk el az el z program er forrst hasznl vltozatt! Sok dolgunk igazbl nincsen, mindssze kt helyen kell vltoztatnunk:
<Window.Resources> <TransformGroup x:Key="transformresource"> <RotateTransform CenterX="15" CenterY="15" Angle="{Binding ElementName=xslider, Path=Value}" /> <TranslateTransform X="{Binding ElementName=xslider, Path=Value}" /> </TransformGroup> </Window.Resources>

285
/* */ <Rectangle Width="30" Height="30" Fill="Red" Canvas.Top="70" Canvas.Left="0" RenderTransform="{StaticResource transformresource}" />

Ez a lehet sg egyttal azt is jelenti, hogy akr stlusokhoz is rendelhetnk transzformcit.

49.2 Animcik
A WPF esetben az animcik tulajdonkppen tulajdonsgok manipulcijval jnnek ltre. Hogy mg pontosabbak legynk csakis olyan objektumokat animlhatunk, amelyek megfelelnek hrom felttelnek: Az animland tulajdonsg egy dependency property Az osztly aminek a pldnyn az animcit alkalmazzuk megvalstja az IAnimatable interfszt s a DependencyObject osztlybl szrmazik (ld. els pont) Kell legyen alkalmas animcitpus a tulajdonsghoz (err l mindjrt)

Ezek a felttelek els rnzsre szigornak t nnek, de nem azok. Szinte az sszes vezrl eleget tesz a feltteleknek, szval a legnagyobb akadlyt a kpzeletnk lehet. Emltettk, hogy alkalmas animcikra van szksgnk. Mit is jelenthet ez? Mondjuk inkbb gy: kompatibilis. Egsz egyszer en arrl van sz, hogy az adott tulajdonsg tpust kezelni tud animcis objektumra van szksgnk. Fogjuk majd ltni, hogy a legtbb beptett tpushoz ltezik a megfelel animci, de ha ez nem elg mi magunk is kszthetnk ilyet (s fogunk is a fejezet vgn). A WPF animciit hrom csoportra oszthatjuk s ennek megfelel en az elnevezsi konvencik is klnbz ek lesznek: Linear Interpolation: a legegyszer bb mind kzl, a dependency property rtke egy kezd - s vgpont kztt fokozatosan vltozik. Ilyen pl. ahogyan azt majd ltni is fogjuk a DoubleAnimation. Ezt a tpust from-to-by animcinak is nevezzk. Az ide tartoz animcik neve Animation uttagot kap. Key Frame Animation: mg az el z tpus megadott kezd - s vgrtkkel dolgozott, addig itt ez nincs megktve, azaz a dependency property rtke tetsz legesen megvltoztathat egy adott pillanatban. A Key Frame animcik uttagja: AnimationUsingKeyFrames. Path-Based Animation: ebben az esetben az animland objektumot mozgsra brjuk gy, hogy egy ltalunk meghatrozott tvonalat kvessen. Az uttag: AnimationUsingPath.

Miel tt tovbblpnk a gyakorlati dolgok fel, mg kt osztlyrl kell megemlkeznnk. Az els minden animcik satyja a TimeLine. Minden egyes

286 animcitpus ebb l az osztlybl szrmazik. A szerepe az, hogy a neki belltott id szelet viselkedst felgyelje: meghatrozza a hosszt, hnyszor ismteljen, stb A msik fontos osztly a StoryBoard. egy TimeLine leszrmazott (a ParallelTimeLines, ez kpes tbb TimeLine kezelsre) leszrmazottja. Ez az osztly kzvetlenl kontrolllja a gyermekanimciit. XAML kdban nem is definilhatunk animcit StoryBoard nlkl. Ezenkv l a StoryBoard fogja szolgltatni azokat a tulajdonsgokat, amelyek az animci cljnak kijellsre szolglnak.

49.2.1 From-to-by animcik


Eljtt az ideje, hogy tudsunkat a gyakorlatban is kamatoztassuk. Egy egyszer pldval fogunk kezdeni, ksztnk egy Rectangle objektumot s hozzktnk egy animcit, amely az egrmutat Rectangle fl rsekor szp lassan megvltoztatja az objektum sznt. Bonyolultabb elmondani, mint megcsinlni. Az esemny bekvetkezst termszetesen egy EventTrigger fogja jelezni. Az XAML:
<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="200" Width="200"> <Grid> <Rectangle Width="150" Height="150"> <Rectangle.Fill> <SolidColorBrush x:Name="brush" Color="Red" /> </Rectangle.Fill> <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.MouseEnter"> <BeginStoryboard> <Storyboard> <ColorAnimation Storyboard.TargetName="brush" Storyboard.TargetProperty="Color" To="Black" Duration="0:0:3" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle> </Grid> </Window>

Akrcsak a transzformcik az animcik is tkletesen hasznlhatak stlusokkal vagy sablonokkal (erre mr lttunk is pldt). A kvetkez pldk az egyszer sg kedvrt csakis helyi animcikkal dolgoznak. A ColorAnimation To tulajdonsga a clsznt fogja jellni, mg a Duration az id tartamot amg eljutunk oda. Ez utbbi a klasszikus ra-perc-msodperc formtumot kveti, vagyis a fenti pldban az animci hrom msodperc alatt lesz ksz. A Storyboard.TargetType/Property tulajdonsgok attached property k, vagyis akr a StoryBoard deklarciban megadhattuk volna ket.

287 Azt bizonyra mindenki kitallta, hogy egy olyan animcitpussal ismerkedtnk meg, amely sznekhez kt d tulajdonsgokra specializldott. Ez elgg lesz kti a lehetsges clpontokat, nzznk meg egy ltalnosabb tpust:
<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="250" Width="250"> <Grid> <Rectangle Width="150" Height="150"> <Rectangle.Fill> <SolidColorBrush Color="Red" /> </Rectangle.Fill> <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.MouseEnter"> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Width" To="170" Duration="0:0:1" /> <DoubleAnimation Storyboard.TargetProperty="Height" To="170" Duration="0:0:1" /> </Storyboard> </BeginStoryboard> </EventTrigger> <EventTrigger RoutedEvent="Rectangle.MouseLeave"> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Width" To="150" Duration="0:0:1" /> <DoubleAnimation Storyboard.TargetProperty="Height" To="150" Duration="0:0:1" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle> </Grid> </Window>

Sokmindennel kiegsztettk a kdot, haladjunk sorjban: els knt trtuk az animcit, hogy double rtkeket kezeljen, majd a cljnak megadtuk a Rectangle szlessgt. Ezenkv l hozzadtunk egy ugyanolyan animcit, a magassgra vonatkozan. Magyarul, ha az egrmutat az objektum fl r, az szp lassan megn . Ugyanezt eljtszottuk arra az esetre is, ha az egeret elvesszk. Ez utbbi valjban felesleges volt s csak a plda kedvrt szerepelt. Ltezik ugyanis a FillBehavior nev tulajdonsg, amely azt hatrozza meg, hogy mji trtnjen ha az animci vget rt. Az alaprtelmezett rtke HoldEnd, vagyis semmi nem trtnik, az animci ltal mdostott rtkek gymaradnak. A msik lehetsges rtk a Stop, vagyis az animci vgn visszallnak az eredeti rtkek. Van azonban egy problma is ezzel a megoldssal, mgpedig az, hogy azonnal visszallnak az rtkek, vagyis mgsem

288 volt olyan felelsleges a fenti megolds, mivel gy tudtunk az egr elmozdulsra reaglni. Tulajdonkppen ez az egyetlen mdszer, hogy esemnyekhez kssk az animcik lettartamt. A FillBehavior hoz hasonl szerepet lt el az AutoReverse, de attl kevsb direktebb, a visszalls a tnyleges animci megfordtsa annak minden tulajdonsgval s id tartamval. Az AccelerationRatio s DeccelerationRatio nemlineriss teszik az animcit, azaz az nem egyenletes sebessggel megy vgbe hanem vagy az elejn vagy a vgn gyorsabb. Mindkt tulajdonsg egy 0 s 1 kztti (double tpus) rtket vr, amely a gyorsuls id tartamra vonatkozik szzalkos arnyban. Teht ha az AccelerationRation tulajdonsgnak 0.2 rtket adunk az azt jelenti, hogy az animci az id tartamnak els hsz szzalkban fog gyorstani. Emltst rdemel mg a RepeatBehavior tulajdonsg is, amellyel meghatrozhatjuk, hogy hnyszor ismtl djn az animci. XAML kdban kiss nehzkes ennek a tulajdonsgnak a kezelse, mivel tbbfle viselkedst definilhatunk. A kvetkez deklarcik mindegyike helyes:
//az animci ktszer ismtl dik
<DoubleAnimation RepeatBehavior="2x" />

//az animci 5 msodpercig ismtl dik


<DoubleAnimation RepeatBehavior="0:0:5" />

//folyamatosan ismtl dik


<DoubleAnimation RepeatBehavior="Forever" />

A kt plda alatt mr biztosan krvonalazdik a tny, hogy az egyes tulajdonsgokat a megfelel tpust kezelni tud animcitpusokkal animlhatjuk. A WPF a legtbb beptett tpushoz tartalmaz tmogatst, ugyanakkor vannak olyan tpusok amelyeket szndkosan kihagytak, ilyenrk pl. a logikai s a felsorolt tpusok (elbbi csak kt rtket vehet fl, mg utbbi rtkkszlete nincs el re definilva). Termszetesen adott a lehet sg sajt animcis osztlyok ltrehozsra, hamarosan kszteni is fogunk egyet. Miel tt tovbbmennnk nzznk meg egy msik pldt is:
<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="70" Width="250"> <Canvas> <Rectangle Width="20" Height="20" Canvas.Left="0"> <Rectangle.Fill> <SolidColorBrush Color="Red" /> </Rectangle.Fill> <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.MouseDown"> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Canvas.Left"

289
To="220" Duration="0:0:5" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle> </Canvas> </Window>

Els rnzsre ez egy teljesen jl m kd program. Lefordul, el is indul, de amikor el akarjuk indtani az animcit egy InvalidOperationException t kapunk. Nzzk meg mgegyszer a kdot! Mi az ami ms az eddigiekt l? Ht persze! Egy attached property t hasznltunk, ezt pedig a CLR nem tudta feloldani, hiszen nem tudja rla, hogy nem a Rectangle tulajdonsgai kzt kell keresglnie. Amikor ilyen tulajdonsgot hasznlunk, azt jeleznnk kell a fordtnak is, hogy tudja mit hol kell keresnie. A fenti pldt nagyon egyszer en kijavthatjuk:
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" To="220" Duration="0:0:5" />

Teht a szably: ha attached property t akarunk animlni, akkor a tulajdonsg nevt zrjelek kz kell rni. Egy objektum egyszerre tbb animcit is tud kezelni (itt nem a fenti pldban ltott tbb animci egy StoryBoard objektumban esetre gondolunk, hanem arra amikor tbb forrsunk van (pl. kt stlus)). Van azonban egy kis problma, mivel a msodik animci megrkezsekor az els azonnal lell, ez pedig nem tl szp. A megoldst a BeginStoryBoard HandoffBehavior tulajdonsga jelenti. Ha ennek rtkt Compose ra lltjuk akkor a kt animci egyesl. Ennek egyetlen htult je a memriaignye, ugyanis ekkor egy teljesen j animci jn ltre, ugyanakkor az eredeti animci(k) is a memiban marad(nak), egszen addig amg az animlt elemet a GC el nem takartja, vagy amg egy j animcit alkalmazunk az objektumon. Teljestmnynvels cljbl szablyozhatjuk az animci framerate tulajdonsgt vagyis az egy msodperc alatt megjelentett kiszmolt kpek szmt. A WPF alaprtelmezs szerint 60 frame/sec rtkkel dolgozik, de ha kmlni akarjuk a processzort vagy szeretnnk ha gyengbb szmtgpen is jl teljestsen az alkalmazsunk akkor rdemes ezt az rtket lejjebb venni. Az emberi szem nagyjbl 25-30 frame/sec et lt folytonos mozgsnak.
<Storyboard Timeline.DesiredFrameRate="30">

Egyetlen dolog van amir l mg nem beszltnk ez pedig a By tulajdonsg. Ezt viszonylag ritkn hasznljuk, de rdemes tudni rla. Az els s legfontosabb informci rla az az, hogy nem hasznlhatjuk egyszerre mindhrom tulajdonsgot. Ha a To mellett a By is jelen van, akkor utbbi semmilyen hatssal nem lesz. By csakis From mal vagy nmagban llhat, el bbi esetben az animlt tulajdonsg a From rtkt l a From s a By sszegig tart, mg utbbi esetben az alaprtkt l a By ig.

290

49.2.2 Key Frame animcik


A from/to/by animcik meghatrozott ton linerisan m kdtek, egy kezd ponttal s egy vgponttal. A Key Frame tpus animciknl nincs ilyen megkts. A Key Frame animcik nevnek uttagja UsingKeyFrames. Ezeknek a tpusoknak van egy KeyFrameCollection tulajdonsga, amelyek az egyes mozgsokat rgztik. A kvetkez pldban egy primitv lktets effektet implementlunk:
<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="200" Width="200"> <Grid> <Rectangle Width="40" Height="40"> <Rectangle.Fill> <SolidColorBrush Color="Red" /> </Rectangle.Fill> <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.MouseDown"> <BeginStoryboard> <Storyboard> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Width" Duration="0:0:4"> <LinearDoubleKeyFrame Value="50" KeyTime="0:0:1" /> <LinearDoubleKeyFrame Value="40" KeyTime="0:0:2" /> <LinearDoubleKeyFrame Value="50" KeyTime="0:0:3" /> <LinearDoubleKeyFrame Value="40" KeyTime="0:0:4" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle> </Grid> </Window>

A KeyTime tulajdonsg a kakukktojs, ami nem egyrtelm , ez kijelli azt az id szeletet ahol az adott KeyFrame m kdik (a pldban az els az els msodpercben, a msodik a msodikban s gy tovbb). Ez az animcitpus maga is tbbfel oszlik, aszerint, hogy miknt rik el a szmukra kijellt vgrtket. Az els s leggyakrabban hasznlt ilyen tpus a lineris interpolcit (itt interpolci alatt rtjk azt, ahogyan a tulajdonsgok rtke vltozik) hasznl, vagyis az animlt tulajdonsg rtke konstans md, folyamatosan nvekszik/cskken a neki kiszabott id alatt. Ezt mr lttuk az el z pldban is. Ez a tpus Linear el tagot kap. A msodik az n. diszkrt interpolci mechanizmust hasznlja, vagyis a megszabott vgrtket a az animcinak jr id legvgn egyetlen mozdulattal ri el. Egy plda:
<Window x:Class="JegyzetWPF.Window1"

291
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="200" Width="200"> <Grid> <Rectangle Width="40" Height="40"> <Rectangle.Fill> <SolidColorBrush Color="Red" /> </Rectangle.Fill> <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.MouseDown"> <BeginStoryboard> <Storyboard> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Width" Duration="0:0:8"> <DiscreteDoubleKeyFrame Value="50" KeyTime="0:0:2" /> <DiscreteDoubleKeyFrame Value="40" KeyTime="0:0:4" /> <DiscreteDoubleKeyFrame Value="50" KeyTime="0:0:6" /> <DiscreteDoubleKeyFrame Value="40" KeyTime="0:0:8" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle> </Grid> </Window>

Most a DoubleKeyFrame diszkrt interpolcit hasznl vltozatt vettk el . Az egyszer tpusok legtbbjhez rendelkezsnkre ll mindhrom altpus (a harmadikkal is mindjrt megismerkednk). A diszkrt interpolcit hasznl animcik el tagja rtelemszer en Discrete lesz.

49.2.3 Spline animcik


Vgl a harmadik tpus a Spline interpolcik csaldja. Ez mr bonyolultabb, s a jegyzet nem is fogja rszletesen trgyalni, hiszen a teljes megrtshez er s matematikai ismeretek szksgesek. Az animci a Bezier grbn alapszik, kt kontrollponttal tudjuk szablyozni, hogy az animci az id szelet egyes pillanataban mennyire gyorsoljun/lassuljon. Egy egyszer plda:
<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="200" Width="200"> <Grid> <Rectangle Width="40" Height="40"> <Rectangle.Fill> <SolidColorBrush Color="Red" /> </Rectangle.Fill>

292
<Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.MouseDown"> <BeginStoryboard> <Storyboard> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Width" Duration="0:0:8"> <SplineDoubleKeyFrame Value="50" KeyTime="0:0:2" KeySpline="0.0,1.0 1.0,0.0" /> <SplineDoubleKeyFrame Value="40" KeyTime="0:0:4" KeySpline="0.0,1.0 1.0,0.0" /> <SplineDoubleKeyFrame Value="50" KeyTime="0:0:6" KeySpline="0.0,1.0 1.0,0.0" /> <SplineDoubleKeyFrame Value="40" KeyTime="0:0:8" KeySpline="0.0,1.0 1.0,0.0" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle> </Grid> </Window>

A KeySpline tulajdonsggal jelljk ki a kontrollpontokat, oly mdon, hogy ezt a kpzeetbeli koordintarendszert egy 1x1 egysgnyi nagysg ngyzetnek vesszk.

49.2.4 Animcik s transzformcik


Egy transzformci aktulis llapota egy vagy tbb tulajdonsgnak rtkt l fgg, mrpedig ha tulajdonsg van, akkor azt animlni is lehet. Hozzuk ssze a kt fogalmat s ksztsnk egy forg gombot. Els knt szksgnk van egy mr ltez transzformcira, ez pedig mi ms is lehetne most, mint a RotateTransform. Msodszor egy animcira lesz szksgnk, mghozz egy DoubleAnimation ra. Mr csak egyvalami kell, ssze kell ktnnk a kett t. Ezt gy tehetjk meg, hogy a transzformcit elnevezzk, az animciban pedig erre a nvre tudunk hivatkozni:
<Button Width="100" Height="30" Content="Anitrans button" RenderTransformOrigin="0.5, 0.5"> <Button.RenderTransform> <RotateTransform x:Name="transform" /> </Button.RenderTransform> <Button.Triggers> <EventTrigger RoutedEvent="Button.MouseEnter"> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetName="transform" Storyboard.TargetProperty="Angle" FillBehavior="Stop" To="360" Duration="0:0:5" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Button.Triggers>

293
</Button>

A RenderTransformOrigin tulajdonsgot azrt lltottuk be, hogy a gomb a kzppontja krl forogjon, mg az animciban a FillBehavior tulajdonsgot Stop ra lltottuk, hogy az animci befejeztvel az eredeti rtkek visszalljanak s ismt lehet sgnk legyen elindtani a mozgst.

294

50. Vezrl k ksztse


Az eddigi fejleszt platformok viszonylag nagy hangslyt fektettek sajt vezrl k ksztsre. Gondoljunk csak a WPF el d Windows Forms ra! Ott, ha pl. egy nem tglalap alak gombra volt szksgnk, akkor knytelenek voltunk szrmaztatssal s rengeteg kd rsval ksztennk egy j gombtpust. A WPF nagyban megvltoztatta ezt az ideolgit, hiszen a stlusok, sablonok, stb. megjelensvel borzaszt egyszer en testre szabhat a vezrl k viselkedse s kinzete. Ugyanakkor tovbbra is rendelkezsnkre ll a lehet sg, hogy ksztsnk egy teljesen j vezrl t. Igaz, erre az esetek nagy tbbsgben nem lesz szksgnk, hiszen a WPF ebb l a szempontbl eddig elkpzelhetetlen lehet sgeket nyjt. Az egsz WPF filozfia alapja az, hogy az sszes vezrl n. lookless llapot, vagyis a vezrl k megjelenst s funkcionalitst teljesen sztvlasztottk. pp ezrt ltalnos jtancs, hogy ha csak lehet hagyatkozzunk az j elemekre s csak a legvgs esetben nyljunk a k kori eszkzkhz. A ks bbiekben megismerkednk majd az adatkezelshez sablontpusokkal is, ezzel is b vtve a fegyvertrunkat. szksges

Felmerlhet a krds, hogy akkor mikor van szksgnk sajtkszts vezrl kre?. Nos, mg a megjelense egy vezrl nek gyorsan megvltoztathat, addig a viselkedse, funkcionalitsa mr ms tszta. Gondoljunk pl. arra, hogyegy olyan TextBox ra van szksgnk, amely bizonyos formtum adatot vr, pl. egy telefonszmot. Itt bizony mr szksgnk van arra, hogy j alapokra helyezzk a TextBox viselkedst. Hasonlan el djhez a WPF is alapvet en kt vlasztsi lehet sget ad: szrmaztatunk vagy pedig UserControl t ksztnk. Innent l viszont a jtkszablyok teljesen megvltoznak: a UserControl bl szrmaztats lesz a nem is igazi vezrl tpus, hiszen ezt a mdszert tbbnyire (de nem kizrlagosan) tbb mr ltez vezrl sszefogsra hasznljuk. Windows Forms szal val munka sorn ez egy viszonylag gyakran alkalmazott mdszer volt, a WPF felptse miatt azonban kicsit httrbe szorult, nagy rszben a limitlt lehet sgei miatt (err l a megfelel fejezetben). A kvetkez fejezetekben mindkt ttal megismerkednk majd, megtanuljuk, hogy hogyan hozzunk ltre olyan vezrl ket, amelyek tkletesen illeszkednek a WPF filozfijhoz, illetve, hogyan vrtezzk fel ket a szksges tulajdonsgokkal (dependency property, routed event, stb). Windows Forms alatt ltalban hrom clpontunk volt amikor j vezrl t ksztettnk: vagy valamelyik mr ltez vezrl t terjesztettk ki, vagy kzvetlenl a Control osztlybl szrmaztattunk, vagy pedig a UserControl -bl. A WPF szertegaz csaldfval rendelkezik, gy tbb lehet sgnk is van, attl fgg en, hogy mire van szksgnk. A kvetkez kben megnzzk a f bb sosztlyokat: FrameworkElement: ez a legalacsonyabb szint, akkor fordulunk ehhez a megoldshoz, ha a felletet teljes mrtkben magunk akarjuk kialaktani. Ugyanakkor ez egy egy viszonylag ritkn alkalmazott mdszer, mivel a legtbb esetben jobb egy specilisabb alaprl indulni.

295 Control: ez az osztly mr rendelkezik azzal, amivel a FrameworkElement nem, mgpedig a sablonok tmogatsval. Ezt az osztlyt hasznljuk a leggyakrabban. ContentControl: olyan helyzetekben hasznljuk, amikor arra van szksg, hogy a vezrl maga is tartalmazhasson ms elemeket. UserControl: tulajdonkppen egy ContentControl, amelyet egy Border objektum foglal magba. Kifejezetten arra ksztettk, hogy grafikus felleten tudjuk megtervezni az j vezrl t. Vannak azonban korltai, err l majd a megfelel fejezetben. ItemsControl, Selector: mindkt osztly olyan vezrl k se, amelyek kpesek gyermekobjektumok kezelsre. A kett kztt a f klnbsg, hogy a Selector (amely maga is az ItemsControl leszrmazottja) lehet v teszi elemek kivlasztst. A Selector osztlybl szrmazik tbbek kztt a ComboBox s a ListBox, mg az ItemsControl neves leszrmazottja a TreeView. rdekessg, hogy br az ItemsControl nem definil vizulis megjelenst, attl mg hasznlhatjuk kzvetlenl, ha megadunk neki egy sablont. Panel: olyan vezrl t hozhatunk ltre a hasznlatval, amelyek kpesek gyermekvezrl k kezelsre.

A fentieken kv l meg kell emltsk a lehet legegyszer bb lehet sget is, mgpedig azt, hogy egy mr ltez vezrl t egsztnk ki.

50.2 UserControl
Els knt a gyengbb UserControl osztlybl val szrmaztatssal ismerkednk meg. Korbban ez a mdszer nagyjbl egyenrtk volt a hagyomnyos szrmaztatssal, de a WPF felptse miatt mostanra httrbe szorult. Mi is tulajdonkppen az ok? Eddig jnhny WPF alkalmazst ksztettnk s a felhasznli fellet felptst megszabtuk az XAML kdban. Az alkalmazs dinamikus m kdst a sablonok, stlusok, animjcik jelenltnek ksznhette, gy nem volt r szksg, hogy a meglv vezrl ket klnsebben tvariljuk. Az XAML kd pedig adta a knny kezelhet sget. Amikor egy WPF usercontrol t ksztnk, akkor ez a dinamizmus kicsit felborul, mivel egy usercontrol eleve megszabott kinzettel rendelkezik, amelyet viszonylag nehezen lehet mdostani (de nem lehetetlen, ezt is megnzzk). Ebben a fejezetben els knt elksztnk egy viszonylag egyszer usercontrol t, felruhzzuk a dependency property kkel, routed event ekkel, stb., vgl pedig megnzzk, hogyan tehet a megjelense dinamikuss. A szban forg vezrl egy rlap lesz, amellyel a felhasznl megadhatja az adatait (nv, cm, telefonszm), illetve majd meg is jelenthessk vele azokat. Mr most el kell dntennk, hogy szeretnnk e tmogatni adatktseket, vagyis, hogy az rlapot feltlthessk mondjuk egy adatbzisbl. A vlasz ebben az esetben igen lesz (persze, ha csak adatbekrsre lenne szksg, akkor nemet is mondhatunk), mivel gy megtanuljuk, hogyan kell dependency property t kszteni, mivel a WPF az ilyen tulajdonsgokkal szeret adatktni (err l hamarosan).

296 Tegyk fel, hogy mindssze hrom adatra vagyunk kvncsiak: nv, cm s telefonszm, ez hrom darab dependency property t kvn. Tegyk meg az el kszleteket, a projecten jobb egrgommbal kattintva vlasszuk ki az Add menpontot, azon bell pedig a User Control t. Nevezzk el mondjuk Form nak. Ezutn ltrejn a vezrl , lthat, hogy gyakorlatilag ugyanolyan felleten dolgozhatunk, mint eddig, azzal a klnbsggel, hogy a legfels bb szint elem most a UserControl lesz. Tulajdonkppen ez a tervez nzet a legnagyobb el nye UserControl ok hasznlatnak. Adjunk hozz egy osztlyt is a projecthez FormPart.cs nven. Ezt az tlthatsg miatt tesszk, mivel a Form osztly egy parcilis osztly megvan a lehet sgnk, hogy tovbb bontsuk s most lnk is vele. Az j osztlyunk fogja tartalmazni a dependency property ket. Kiindulsknt nzzen ki valahogy gy:
using using using using using using using using using using using using using System; System.Collections.Generic; System.Linq; System.Text; System.Windows; System.Windows.Controls; System.Windows.Data; System.Windows.Documents; System.Windows.Input; System.Windows.Media; System.Windows.Media.Imaging; System.Windows.Navigation; System.Windows.Shapes;

namespace JegyzetTestWPF { public partial class Form { } }

Ne felejtsk el a nvtereket is hozzadni, nlklk nem hasznlhatjuk a WPF lehet sgeit. Most ksztsk el a hrom DP t. Minden dependency property statikus tag kell legyen, valamint a nevk konvenci szerint Property uttaggal rendelkezik.:
static public DependencyProperty UserNameProperty; static public DependencyProperty AddressProperty; static public DependencyProperty PhoneProperty;

A nv tulajdonsgnl azrt nem csak simn NameProperty lett, mert azt a FrameworkElement mr lefoglalta magnak. Mg korntsem vagyunk kszen, szksgnk van egy statikus konstruktorra ahol regisztrljuk a tulajdonsgokat, megadva nevket, tpusukat valamint az osztlyt amelyhez tartoznak:

297

static Form() { UserNameProperty = DependencyProperty.Register ( "UserName", typeof(string), typeof(Form) ); AddressProperty = DependencyProperty.Register ( "Address", typeof(string), typeof(Form) ); PhoneProperty = DependencyProperty.Register ( "Phone", typeof(string), typeof(Form) ); }

Itt lljunk meg egy kicsit! A fenti esetben a legegyszer bb mdot hasznltuk a regisztrcihoz, nzznk meg nhny bonyolultabb esetet. Tegyk fel, hogy a DP egy egsz szmot vr, amely nem lehet kisebb nullnl:
static public DependencyProperty PositiveIntProperty; PositiveIntProperty = DependencyProperty.Register ( "PositiveInt", typeof(int), typeof(Form), new FrameworkPropertyMetadata(int.MaxValue, null, null), new ValidateValueCallback(IsPositive) ); static public bool IsPositive(object value) { return (int)value >= 0; }

A FrameworkPropertyMetadata osztly segtsgvel a dependency property m kdshez adhatunk meg adatokat. Az els paramter az alaprtelmezett rtke a tulajdonsgnak, a msodik paramter amely egy FrameworkPropertyMetadataOptions enum egy rtkt veszi fel olyan dolgok belltsra szolgl, mint pl. az adatktsek engedlyezse, hogy a renderels melyik fzisban vegye figyelembe a tulajdonsg rtkt a WPF, stb... A harmadik paramter tulajdonkppen egy esemnykezel , amely a tulajdonsg megvltozsakor fut le, hamarosan megnzzk ezt is. Neknk most a Register metdus utols paramtere fontos, amely szintn egy esemnykezel re mutat, ez szolgl a kapott rtk ellen rzsre. A Register metdus rendelkezik mg tovbbi paramterekkel is, hamarosan ket is megnzzk. Egyetlen dologgal tartozom mg, ez pedig az, hogy hogyan lesz mindebb l igazi tulajdonsg. A vlaszt a DependecyObject t l rklt GetValue s SetValue metdusok jelentik, amelyek a dependency property k rtknek lekrdezsre illetve belltsra szolgl.

298

public int PositiveInt { get { return (int)GetValue(PositiveIntProperty); } set { SetValue(PositiveIntProperty, value); } }

rdemes figyelni arra, hogy ebben a burkol tulajdonsgban ne nagyon hasznljunk semmilyen ellen rzst az adatok fel, mivel a SetValue nmagban is gond nlkl hvhat. Teszteljk is le az j tulajdonsgot, ehhez adjuk hozz mondjuk az j vezrl nkhz, majd valahol a kdban pldnyossunk egy Form objektumot, most nem jelentjk meg:
Form f = new Form(); f.PositiveInt = 10; f.PositiveInt = -1;

Fordulni lefordul, de kapunk egy ArgumentException t, mivel a -1 nem megfelel rtk. Trjnk vissza a Register metdushoz, azon bell is a FrameworkPropertyMetadata konstruktorhoz. Ennek utols paramtere szintn egy esemnykezel re mutat, amely a kapott rtk korriglsra szolgl:
PositiveIntProperty = DependencyProperty.Register ( "PositiveInt", typeof(int), typeof(Form), new FrameworkPropertyMetadata( int.MaxValue, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(PositivePropertyChanged), new CoerceValueCallback(CoercePositiveValue)), new ValidateValueCallback(IsPositive) );

Egyttal elksztettk az rtk vltozsakor lefut esemnykezel t is. Van teht hrom metsusunk, amelyek a tulajdonsg rtknek vltozsakor futnak le. J lenne tudni, hogy milyen sorrendben rkeznek: Els knt a ValidateValueCallback fut le. Ami nagyon fontos, az az, hogy itt csakis a kapott rtket ellen rizhetjk, maghoz a fogad objektumhoz nem frnk hozz. Kvetkez a sorban a CoerceValueCallback. Ebben a fzisban finomthatunk a kapott rtken, illetve figyelembe vehetnk ms dependency property ket

299 is, mivel hozzfrnk a kld objektumhoz. A CoerceValueCallBack hasznlatra j plda a Slider osztly: rendelkezik Minimum s Maximum rtkekkel, amelyeket az aktulis rtk nem lphet t. Vgl a PopertyChangedCallback jn. Itt elindthatunk egy esemnyt, amellyel rtestjk a rendszert, hogy valami megvltozott.

Felmerlhet a krds (mghozz jogosan), hogy nem e lehetne ezekkel a metdusokkal kikerlni a felhasznl ltal megadott helytelen adatokat. Lehetne, de nem ajnlott. Egsz egyszer en nem lehet arra pteni, hogy kijavtjuk a felhasznl hibit, az ajnlott mdszer tovbbra is egy rthet hibazenet legyen. Most nzzk a metdusokat:
static private object CoercePositiveValue(DependencyObject d, object value) { return value; }

Ugyan most nem csinltunk semmi klnset, azrt sok lehet sgnk van: ha az rtk valamirt nem megfelel a DependencyObject metdusait hasznlhatjuk: ClearValue: trli a paramterknt megadott dependency property helyi rtkt (ld. korbban). CoerceValue: meghvja a paramterknt megadott DP CoerceValueCallback jhoz rendelt esemnykezel t. InvalidateProperty: jraszmtja az adott DP t. ReadLocalValue: visszadja a megadott DP helyi rtkt.

static public void PositivePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { }

Itt a DependencyPropertyEventArgs fontos neknk. Hrom tulajdonsga van: OldValue: a rgi rtk NewValue: az j rtk Property: a megvltoztatott dependency property

Most mr mindent tudunk, ksztsk el a vezrl nk igazi tulajdonsgait:


public string UserName { get { return (string)GetValue(UserNameProperty); } set { SetValue(UserNameProperty, value); }

300
} public string Address { get { return (string)GetValue(AddressProperty); } set { SetValue(AddressProperty, value); } } public string Phone { get { return (string)GetValue(PhoneProperty); } set { SetValue(PhoneProperty, value); } }

Az egyszer sg kedvrt nem alkalmaztunk semmilyen ellen rzst, ezt az olvas majd megteszi az ismertetett eszkzkkel. Az adatokat felvettk, most el is kellene kldeni ket. A feldolgozs nem a vezrl feladata lesz, az csak trolja az informcikat. Teht, amikor a felhasznl megadta az adatait, szlni kell a rendszernek, hogy dolgozza fel azokat. Ezt legegyszer bben gy tehetjk meg, hogy ksztnk erre a clra egy esemnyt, mgpedig egy routed event et. Az esemnyt termszetesen gombnyomsra kldjk el, de ezt hagyjuk akkorra, amikor elksztettk a felhasznli felletet. Routed event ksztse nagyon hasonlt a dependency property hoz. Legyen az esemnynk neve SubmitEvent:
static public readonly RoutedEvent SubmitEvent;

Ezt is regisztrlnunk kell, szintn a statikus konstruktorban:


SubmitEvent = EventManager.RegisterRoutedEvent ( "Submit", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Form) );

A paramterek sorrendben: az esemny neve, stratgija, az esemnykezel tpusa vgl pedig a tartalmaz osztly. Lehet sget kell adnunk arra, hogy ms osztlyok is feliratkozzanak az esemnyre, innent l a hagyomnyos esemnydefincit hasznljuk:

301
public event RoutedEventHandler Submit { add { base.AddHandler(Form.SubmitEvent, value); } remove { base.RemoveHandler(Form.SubmitEvent, value); } }

Az esemnyt el is kell indtanunk, ezt a WPF vilgban az UIElement osztlytl rklt RaiseEvent metdussal tesszk meg:
protected void RaiseSubmitEvent() { RoutedEventArgs args = new RoutedEventArgs(Form.SubmitEvent); RaiseEvent(args); }

Eljtt az id , hogy megvalstsuk a felhasznli felletet. Borzasztan puritnok lesznk, ez most igazbl nem lnyeges:
<UserControl x:Class="JegyzetWPF.Form" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="110" Width="180" BorderThickness="2" BorderBrush="Blue"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="80" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <UniformGrid Width="150" Height="70" Rows="3" Columns="2" Grid.Row="0"> <TextBlock Text="Nv:" /> <TextBox Name="nametextbox" /> <TextBlock Text="Cm:" /> <TextBox Name="addresstextbox" /> <TextBlock Text="Telefonszm:" /> <TextBox Name="phonetextbox" /> </UniformGrid> <Button Name="submitbutton" Content="OK" Width="70" Height="20" Grid.Row="1" Click="submitbutton_Click" /> </Grid> </UserControl>

A submitbutton Click esemnye fogja elindtani az ltalunk definilt esemnyt:

302
private void submitbutton_Click(object sender, RoutedEventArgs e) { if (nametextbox.Text == "" || addresstextbox.Text == "" || phonetextbox.Text == "") { MessageBox.Show("Tltse ki az adatokat!"); } else { RaiseSubmitEvent(); ClearFields(); } }

Tettnk bele egy kis felhasznlbartsgot is, a ClearFields metdus pedig kitakart:
public void ClearFields() { nametextbox.Clear(); addresstextbox.Clear(); phonetextbox.Clear(); }

Rendben vagyunk, a legfontosabb dolgot hagytuk a vgre, az j vezrl felhasznlst. Trjnk vissza a f ablakunkhoz s nyissuk meg a hozz tartoz XAML t. Ahhoz, hogy hasznlni tudjuk a vezrl t regisztrlnunk kell a tartalmaz nvteret. Ezt a kd tetejn tehetjk meg (vastagbet vel kiemelve):
<Window x:Class="JegyzetWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:uc1="clr-namespace:JegyzetWPF" Title="Window1" Height="400" Width="400" > <Grid> <uc1:Form x:Name="form" Submit="Form_Submit" /> </Grid> </Window>

Meg kell adnunk egy prefixet is, amivel majd a nvtrre hivatkozunk. Szerencsre itt van IntelliSense tmogats, ezrt gyorsan kivlaszthatjuk a neknk kell nvteret. Lthat, hogy megjelenik a Submit esemny is, ehhez tetsz leges esmnkezel t rendelhetnk. A kvetkez napirendi pont a commanding tmogats bevezetse. Itt kt vlasztsunk van, vagy egy mr ltez parancsot hasznlunk fel, vagy pedig jat csinlunk. El szr az egyszer bbet nzzk meg, vagyis azt amikor ltez parancsot hasznlunk. Ez a parancs az ApplicationCommands.Save lesz, elmentjk az rlap tartalmt. Korbban azt mondtuk, hogy nem a form dolga lesz az adatok mentse, most ezen vltoztatunk egy kicsit. A mentsrt felel s kd ezrt mostantl a vezrl rsze lesz, de ezt most nem fogjuk megrni, mivel nem kapcsoldik szorosan a tmhoz (legyen hzi feladat az olvas szmra). Parancsot ktflekppen adhatunk hozz egy vezrl hz: vagy kzvetlenl a CommandBindings listhoz adjuk hozz, ami viszont azzal jr, hogy a

303 vgfelhasznl tetszs szerint emdosthatja azt, vagy pedig az er sebb megoldst vlasztjuk s a RegisterClassCommandBinding metdussal kapcsoljuk hozz az osztlyhoz a parancsot. Kezdjk az egyszer bb esettel! A form konstruktorba helyezzk a kvetkez kdrszletet:
CommandBinding binding = new CommandBinding ( ApplicationCommands.Save, SaveCommand_Execute, SaveCommand_CanExecute ); this.CommandBindings.Add(binding);

A SaveCommand_Execute s a SaveCommand_CanExecute a parancs kt esemnynek kezel i lesznek, elbbibe fogjuk helyezni az adatok mentsrt felel s kdot, mg utbbi azt vizsglja, hogy lehet e mentennk. Most azt a felttelt adtuk meg, hogy minden mez legyen kitltve. A kt metdus az osztly tagja lesznek. gy nznek ki:
private void SaveCommand_Execute(object sender, ExecutedRoutedEventArgs e) {
//mentnk...

} private void SaveCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = nametextbox.Text != "" && addresstextbox.Text != "" && phonetextbox.Text != ""; }

A bizotnsgosabb vltozat nem sokban klnbzik az el z t l. konstruktort fogjuk hasznlni:


CommandManager.RegisterClassCommandBinding( typeof(Form), new CommandBinding( ApplicationCommands.Save, SaveCommand_Execute, SaveCommand_CanExecute));

Most a statikus

Ebben az esetben a kezel metdusok statikusak lesznek s mivel mr nem rszei az osztlynak ezrt a sender paramtert konvertlnunk kell, hogy hozzfrjnk az eredeti pldny adataihoz:
static private void SaveCommand_Execute(object sender, ExecutedRoutedEventArgs e) {
//mentnk...

} static private void SaveCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e) { var form = (Form)sender;

304
e.CanExecute = form.nametextbox.Text != "" && form.addresstextbox.Text != "" && form.phonetextbox.Text != ""; }

Mr csak egyvalami hinyzik a fegyvertrunkbl, ez pedig az j parancsok ltrehozsnak kpessge. Korbban mr volt rla sz, hogy a WPF a RoutedComand vagy a RoutedUICommand osztlyt hasznlja parancsok ltrehozshoz, mi az elbbit fogjuk vlasztani. Adjunk hozz az osztlyhoz egy statikus tagot:
static public RoutedCommand SaveCommand;

A statikus konstruktorban fogjuk belltani:


InputGestureCollection igc = new InputGestureCollection(); igc.Add(new KeyGesture(Key.S, ModifierKeys.Shift)); FormSaveCommand = new RoutedCommand("Save", typeof(Form), igc);

Az

IputGestureCollection

segtsgvel

gyorsbillenty ket

lltottuk

be.

305

51. ADO.NET
Az ADO (ActiveX Data Objects) a COM (Component Object Model a .NET el tti f fejleszt i platform) adatelrsi rtege volt. Hdat kpezett a programozsi nyelv s az adatforrs kztt, vagyis anlkl rhattunk programot, hogy ismertk volna az adatbzist. A .NET Framework sok ms mellett ezt a terletet is jelent sen feljtotta, olyannyira, hogy az ADO.NET s az ADO kzt a nv jelenti a legnagyobb hasonlsgot. Az vek sorn az ADO.NET a Framework szinte minden verzivltsakor b vlt egy kicsit, gy napjainkban mr igen szleskr vlasztk ll rendelkezsnkre. A kvetkez nhny fejezetben nagyjbl id rendi sorrendben fogunk megismerkedni ezekkel az eszkzkkel. Els knt a hagyomnyos mddal kerlnk kzelebbi kapcsolatba, ezek az osztlyok lnyegben mr a .NET els verziiban is megvoltak. Az adatbzist n. Data Provider eken keresztl rhetjk el, a lekrdezett adatokat pedig DataSet objektumokban troljuk, amelyek tulajdonkppen megfeleltek egy in memory relcis adatbzisnak, vagyis az adatok a memriban vannak XML formtumban. Emiatt ezt a metdust Disconnected model nek nevezzk, hiszen nincs szksg folyamatos kapcsolatra az adatbzissal. A kvetkez lpst a LINQ csald jelentette, amely tbb rszre oszlik s ezek nem mindegyike tartozik az ADO.NET al (de mindannyiukkal megismerkednk majd). A LINQ a Language Integrated Query kifejezs rvidtse, amely annyit tesz, hogy kzvetlenl a forrskdbl a nyelvben tnylegesen szerepl eszkzkkel SQL szer lekrdezseket rhatunk. A LINQ jelent sge az, hogy nem csak relcis adatbzisbl krdezhetnk le, hanem brmilyen forrsbl, amelyre implementlva van gy hagyomnyos objektum vagy listk esetben is hasznlhatjuk. Hivatalosan ten alkotjk a LINQ csaldot: LINQ To Objects: nem az ADO.NET rsze. A memriban lv gy jtemnyeket krdezhetnk le vele. LINQ To XML: XML forrsokon hajthatunk vgre lekrdezseket. LINQ To SQL: A Microsoft els ORM (Object Relational Mapping) eszkze. Az adatbzis tblit hagyomnyos objektumknt kezelhetjk. LINQ To DataSets: Mivel a LINQ To SQL csakis Microsoft SQL adatbzissal m kdik ezrt szksg volt valamire, ami a tbbivel is elbr. Miutn az adatok bekerlnek egy DataSet be rhatunk r lekrdezst.

Ebb l a felsorolsbl egyvalaki kimaradt, mghozz a LINQ To Entities. Ez a .NET Framework 3.5 SP1 ben debtlt Entity Framework hz kszlt. Az EF a LINQ To SQL tovbbgondolsa, jval rugalmasabb s sokrt bb annl. Az EF egy msik lehet sget is biztost a lekrdezsekhez, az n. Entity SQL t. Hogy a kett kzt mi a klnbsg majd a megfelel fejezetekben kiderl.

51.1 MS SQL Server 2005/2008 Express


A kvetkez fejezetek megrtshez, illetve a tanultak alkalmazshoz szksgnk lesz valamilyen relcis adatbzisra. A .NET hez legjobban nylvn valamilyen Microsoft termk illik. Szerencsre Redmondban gondoltak azokra is, akik mg tanuljk a mestersget, gy a nagy MS SQL Server ek mellett ott vannak a kistestvrek is, az Express csald. Ezek a szoftverek teljes mrtkben ingyenesek, akr cges keretek kzt is hasznlhatjuk ket. Termszetesen nem nyjtjk az

306 sszes szolgltatst, mint btyjaik, de tanulshoz (s kisebb kereskedelmi programokhoz) tkletesek. A letlt oldalak: - http://www.microsoft.com/downloads/details.aspx?familyid=220549b5-0b074448-8848-dcc397514b41&displaylang=en http://www.microsoft.com/downloads/details.aspx?FamilyID=58ce885d-508b45c8-9fd3-118edd8e6fff&DisplayLang=en

A Visual Studio 2005/2008 nemExpress vltozatai felteleptik az SQL Server Express -t, ha a teleptsnl ezt krjk. Az adatbzisok kezelshez rdemes telepteni a megfelel verzihoz tartoz SQL Server Management Studio Express t is, amely grafikus felletet nyjt lekrdezsek rshoz, adatbzisok kezelshez.

A kpen a Management Studio 2005 Express lthat.

307

52. SQL alapok


Az SQL (Structured Query Language) egy adatbzisokhoz kifejlesztett nyelv. Tulajdonkppen ez csak egy gy jt nv, mivel az sszes RDBMS (Relation Database Management System) sajt dialektust beszl. Ugyanakkor az eltrsek ltalban nem nagyok, gy viszonylag knny vltani. A kvetkez fejezetekben az MS SQL nyelvvel ismerkednk meg, ennek neve Transact SQL vagy TSQL. A kvetkez webhelyen egy kivl TSQL lerst tall a kedves olvas, angol nyelven: http://www.functionx.com/sqlserver/index.htm

SQL lekrdezsek rshoz indtsuk el a Management Studio t, a belpsnl vlasszuk a Windows Authentication mdot, majd kattintsunk a Connect felrat gombra:

Termszetesen a Server name sor vltozhat.

52.1 Adatbzis ltrehozsa


Mivel mg nincs adatbzisunk, ezrt kattintsunk jobb egrgombbal a bal oldalon lthat Object Explorer ben a legfels bb szintre:

Vlasszuk a New Query menpontot, ekkor megjelenik a szerkeszt ablak.

308

A CREATE DATABASE paranccsal fogunk dolgozni:


create database TestDataBase

Az SQL nem case-sensitive, azaz teljesen mindegy, hogy kis- vagy nagybet vel rjuk az utastsokat. A vgrehajtshoz nyomjuk meg az F5 billenty t, ekkor ha nem rontottunk el semmit - megjelenik az adatbzisunk a Databases fl alatt (ha nem, akkor jobb egrgomb rajta s Refresh):

52.2 Tblk ltrehozsa


Eljtt az ideje, hogy feltltsk adatokkal az adatbzisunkat. Egy adatbzisban egy vagy tbb tbla trolja az adatokat. Egy tbla sorokbl (Row) s oszlopokbl (Column) ll. Minden egyes oszlopnak van tpusa, illetve azonostja, amellyel hivatkozhatunk r. Az egyes sorok n. rekordok, amelyek egy-egy bejegyzsei a tblnak. Vegyk a kvetkez egyszer pldt: egy tblban eltroljuk emberek nevt s cmt. Ekkor az oszlopok neve legyen pl. Name s Address, tpusuk pedig valamilyen szveges formtum (err l b vebbet hamarosan). Egy sor a tblban pl. gy nz majd ki: Dr. A. Kula Erdly, Baljs Kastly Fasor. 13/b. Ezt a tblt a kvetkez kppen tudjuk ltrehozni: a Databases fl alatt keressk ki az adatbzisunkat, majd jobb egrgombbal kattintsunk rajta s New Query:
create table Persons (Name varchar(50), Address varchar(100));

A tbla neve utn az oszlopok, s tpusaik kvetkeznek. A varchar nagyjbl megfelel egy string tpusnak, zrjelben a maximlis hossz szerepel. Ezutn, ha kinyitjuk a Databases t, akkor a Tables alatt megtalljuk az j adattblnkat (ha nem ltszik, akkor Refresh). Egy tblnak lehetnek specilis oszlopai. Gyakran kerlnk olyan helyzetbe, amikor egy tbla egy sort egyrtelm en meg akarjuk klnbztetni a tbbit l, pl. legyen egy tbla a dolgozknak, s egy a munkahelyeknek. A tblk felptse legyen olyan, hogy a dolgozk tblja tartalmaz egy Munkahely oszlopot, amely a munkahely nevt tartalmazza. Ezzel a megoldssal egyetlen nagy problma van, mgpedig az, hogy felttelezi, hogy nincs kt azonos nev cg. Ha viszont van, akkor egy munkahelyre vonatkoz lekrdezsnl bajban lesznk. Erre a problmra megolds az n. els dleges kulcs (primary key v. PK). Egy tbla tbb els dleges kulcsot jelkpez oszlopot is tartalmazhat, s ezek rtknek egyedinek kell lennie (rtsd.: nem lehet egy tblban kt azonos rtk , azonos oszlopban lv PK). Els dleges kulcsot a kvetkez kppen hozunk ltre:
create table varchar(100)) Persons (ID int primary key, Name varchar(50), Address

Els dleges kulcsnak legegyszer bb szimpla numerikus rtket adni (ms megoldshoz kulcssz: GUID), ezt tettk a fenti pldban is, az int egy egsz szmot

309 jell. Egy kis knyelmetlensg azonban mg van, ugyanis minden esetben kzzel kell megadnunk a kulcsot, ez pedig nem tl biztonsgos. Szerencsre van megolds, automatizlhatjuk a folyamatot, megadva a kezd rtket s a kulcs nvelsnek mrtkt:
create table Persons (ID int identity(1,10)primary key, Name varchar(50), Address varchar(100))

Most az els PK 1 lesz s tzesvel fog nvekedni, teht a msodikknt beillesztett elem kulcsa 11, a msodiknak 21, stb lesz. Egy oszlopnak azt is megengedhetjk, hogy nullrtket tartalmazzon. Mdostsuk a fenti kifejezst, hogy a Name sor lehessen semmi, az Address pedig nem:
create table Persons (ID int identity(1,10)primary key, Name varchar(50) null, Address varchar(100) not null)

Els dleges kulcs nem tartalmazhat nullrtket, ilyen tblt nem is hozhatunk ltre.

52.3 Adatok beszrsa tblba


Az INSERT parancsot fogjuk hasznlni:
insert into Persons (Name, Address) values ('Dr. A. Kula', 'Erdly, Baljs Kastly Fasor 13/b')

Megadtuk a tbla nevt, majd azokat az oszlopokat, amelyeknek rtket adunk. Most az utoljra ltrehozott tblt hasznltuk, amelyik automatikusan lltja be az els dleges kulcsokat, ezrt azt nem kell megadnunk. Ha ezt nem krtk, akkor a PK t is meg kell adnunk. Azokat az oszlopokat sem kell megadnunk, amelyeknek megengedtk, hogy elfogadjanak nullrtket.

52.4 Oszlop trlse tblbl


Ha el akarunk tvoltani egy oszlopot, akkor azt a kvetkez kppen tehetjk meg:
alter table Persons drop column Name

Els knt meg kell adnunk a mdostani kvnt tblt, majd a trlend oszlop nevt.

52.5 Kivlaszts
Egy lekrdezs legelemibb tagja, amelyet minden egyes lekrdezs tartalmaz, az a select utasts:
select * from Persons

Ezzel az utastssal a Persons tblbl kivlasztottuk az sszes oszlopot. Termszetesen megadhatunk oszlopnevet is:

310

select Name from Persons

A wildcard karaktert (*) les alkalmazsoknl ritkn, de leginkbb soha nem hasznljuk, mivel szinte soha nincs szksg minden oszlopra.

52.6 Sz rs
A where a lekrdezett adatok sz rsre szolgl, megadhatunk vele egy felttelt, s csak azokat a sorokat kapjuk vissza, amelyek ennek megfelelnek:
select Name from Persons where Name='Judit'

Ez a lekrdezs az sszes olyan sort fogja megjelenteni a Persons tblbl, amelyeknek a Name oszlopa megegyezik a megadottal. A where szmos opertort hasznlhat, nhny plda:
/*Azokat a sorokat vlasztuk ki, ahol a kor nem kissebb mint 18*/

select * from Persons where Age !< 18


/*Azokat a sorokat vlasztjuk ki, ahol a kor 18 s 65 kztt van*/

select * from Persons where Age between 18 and 65


/*Azokat vlasztjuk ki akiket nem Istvn -nak hvnak*/

select * from Persons where Name != 'Istvn'


/*Ugyanaz mint elbb*/

select * from Persons where Name <> 'Istvn'

sszetett felttelt is megadhatunk:


/*A kor nagyobb mint 18 s kissebb mint 65*/

select * from Persons where Age > 18 and Age < 65


/*A Jen vagy Jzsef nev eket vlasztjuk ki*/

select * from Persons where Name = 'Jen ' or Name = 'Jzsef'

Azt is ellen rizhetjk vele, hogy egy adott mez nullrtket tartalmaz:
select * from Persons where Age is null

A like opertorral tovbb finoimthatjuk a sz rst:


select * from Persons where Name like re%

Itt a % azt jelenti, hogy brmelyik s brmennyi karakter, teht azokat a neveket keressk, amelyek a re szcskval kezd dnek. Egy msik ilyen sz r a _ karakter, ez a % kal ellenttben csak egyetlen karaktert jelent:
select * from Persons where Name like _stvan

311

Ebben a nmileg kicsavart pldban az sszes olyan szemlyt keressk, akiknek egy valamilyen bet vel kezd dik a nevk s utna a stvan karaktersor ll. A szgletes zrjelekkel tbb karaktert is megadhatunk, amelyek valamelyike illeszkedik:
select * from Persons where Name like [AB]%

Most azokat keresk, akiknek neve A vagy B bet vel kezd dik. A where-t nagyon gyakran fogjuk hasznlni trlsnl, mdostsnl, kivlasztsnl. Egy msik kulcssz amely a kapott eredmny finomtsra szolgl a distinct, amely a tbbszri el fordulst javtja. Kpzeljk el, hogy egy webruhzat m kdtetnk s szeretnnk megtudni, hogy melyik vrosokbl rkeznek megrendelsek. A vsrlk tblja (Customers) tartalmaz is egy vrosra vonatkoz oszlopot (City). Ekkor a lekrdezs gy alakul:
select distinct City from Customers

Az eredeti tbla egy rszlete: Kis Miska Baja Kovcs Jnos Budapest Nagy Ede Budapest s az eredmny:
Baja Budapest

52.7 Rendezs
A lekrdezett adatokat abban a sorrendben kapjuk vissza, ahogy a tblban szerepelnek. Ez persze nem mindig j, ezrt tudnunk kell rendezni a sorokat. Erre a feladatra szolgl az order by.
select Names, Address from Persons order by Address

A lekrdezs elejn megadtuk a megnzni kvnt oszlopokat, eddig semmi szokatlan nem trtnt. A vgn viszont megjelenik az order by, mgtte pedig az az oszlop, amely alapjn majd rendezi a vgeredmnyt. A rendezs abc szerint trtnik, pl. legyenek a cmek a kvetkez k(a nevek most lnyegtelenek): Budapest, Budars, Baja, A rendezs utn az eredmny ez lesz:

312

Baja, Budars, Budapest, Itt az tdik pozicban lesz eltr karakter ( p) s ez alapjn mr tud rendezni.

52.8 Adatok mdostsa


Az UPDATE parancsot fogjuk hasznlni:
update Persons set Name='Bla' where Name='Attila'

El szr megadjuk a tblt, majd azokat a mez ket, amelyeket mdostani akarunk az j rtkekkel egytt. Eztn jn a felttel, amivel kivlasztjuk a kvnt sort.

52.9 Relcik
A relcik megrtshez kpzeljnk el egy egyszer pldt. Vegyk a mr ltez Persons adatbzist s ksztsnk egy j adattblt Houses nvvel. Teht vannak szemlyek s hozzjuk tartoz hzak. Felmerlhet a krds, hogy mirt nem hasznljuk a mr meglv tblt s egsztjk ki a hzakra vonatkoz informcikkal? Az ok nagyon egyszer , ugyanis egy szemly tbb hzzal is rendelkezhet. Ezt a relcit egytbb (onee-to-many) relcinak nevezzk, lteznek ezenkv l egy-egy illetve tbb-tbb relcik is. Rendben, van mg egy problma, mgpedig az, hogy hogyan rendeljk ssze a kt tblt. Erre fogjuk hasznlni az els dleges kulcsokat, vagyis az egyes hzak tartalmaznak egy azonostt, amely az t birtokl szemlyre mutat (a Persons tbla megfelel sornak els dleges kulcsra). Ezt az azonostt, amely egy msik tblra mutat idegen kulcsnak (foreign key) nevezzk. Nzzk meg, hogy nz ki a kt tbla (a plda nagyon egyszer ): Persons: ID Name 1 Kovcs Jnos 2 Nagy Sndor Houses: HID PID Address 11 1 Budapest, 21 2 Kecskemt, A tblk ltehozsnl n. constraint et hasznlunk, amely meghatrozza egy oszlop tulajdonsgait. Tbb tpusa is van, mi most egy foreig key constraint et fogunk bevezetni. A tblkat ltrehoz utastsok a kvetkez ek lesznek:

313

create table Persons ( ID int not null primary key, Name varchar(50) ) create table Houses ( HID int not null primary key, PID int foreign key references Persons(ID), Address ntext )

Az idegen kulcs ltehozsakor meg kell adnunk, hogy melyik tbla melyik oszlopra hivakozunk.

52.10 Join
Tbb tblbl krdezhetnk le a join utastssal. Vegyk az el z fejezet kt tbljt s rjunk egy lekrdezst, amely visszaadja a hzakhoz tartoz szemlyeket:
select Name, Address from Persons inner join Houses on Houses.PID = Persons.ID

314

53. Kapcsolds az adatbzishoz


Miel tt nekiltunk adatokkal dolgozni ltre kell hoznunk egy olyan objektumot, amely segtsgvel parancsokat tudunk kldeni s adatokat visszakapni. Ez egy n. connection object lesz, ami tulajdonkppen egy tolmcs szerept fogja betlteni kztnk s az adatforrs kzt. A kapcsolatot ktflekppen tudjuk elkszteni, vagy a beptett varzslkat hasznljuk, vagy kzzel kapcsoldunk. Mivel el bbihez szksg van plusz ismeretekre, ezrt a kzi mdszert fogjuk el szr tnzni. Els knt ki kell vlasztanunk, hogy milyen adatbzishoz kapcsoldunk. Az egyszer sg kedvrt a jegyzet az MSSQL adatbziskezel t preferlja, de lesz plda msra is. A szksges osztlyok a System.Data illetve a System.Data.SqlClient nvterekben rejt znek. A kapcsolatot az SqlConnection osztllyal fogjuk megteremteni, ehhez szksgnk lesz egy n. connectionstring re, amely a kapcsoldshoz szksges informcikat (szerver neve, adatbzis neve, kapcsolds mdja, stb) tartalmazza. Az SqlConnection a DbConnection absztrakt osztly leszrmazottja. A DbConnection utdja lesz minden ms kapcsoldsrt felel s osztly, gy gyakorlatilag ugyangy dolgozhatunk egy MySQL szerverrel, mint az MSSQL -lel.
using using using using using System; System.Collections.Generic; System.Text; System.Data; System.Data.SqlClient;

namespace JegyzetTestDB { class Program { static void Main(string[] args) { SqlConnection connection = new SqlConnection ( @"Data Source=computer381\SQLEXPRESS;Initial Catalog=testDataBase;Integrated Security=True" ); connection.Open(); Console.WriteLine(connection.State); connection.Close(); Console.ReadKey(); } } }

Ebben a programban a mr elksztett adatbzishoz csatlakoztunk. Az SqlConnection konstruktora a connectionstring et kapja paramterl (be lehet ezt lltani utlag is, a ConnectionString tulajdonsgon keresztl). Egy connectionstring sokkal bonyolultabb is lehet, most a lehet legegyszer bbet alkalmaztuk. Els knt a

315 szerver nevt adtuk meg, ahol az adatbzis megtallhat. Ez ha a szerver a sajt gpnk - lekrdezhet a parancssorba bert hostname paranccsal. Hasznlhatjuk helyette az univerzlisabb (local) t is, erre hamarosan lesz pda. Az SQLEXPRESS az SQL Server Express pldnynak a neve, ez alaprtelmezett teleptsnl mindig ugyanaz lesz. A kvetkez a sorban az adatbzis s nem a tbla neve. Ezutn megnyitjuk a kapcsolatot. Most mr dolgozhatunk az adatbzissal, kezdetnek lekrjk a kapcsolat llapott, ha mindent jl csinltunk, akkor a kperny n meg kell jelennie az Open sznak. Ha viszont elrontottuk (pl. elrtuk az adatbzis nevt), akkor egy SqlException kivtelt fogunk kapni. Vgl bezrjuk a kapcsolatot. Szerencsre az SqlConnection, pontosabban a DbConnection megvalstja az IDisposable interfszt, gy nem muszj mindig kzzel lezrni a kapcsolatot, egyszer en betehetjk az egszet egy using blokkba:
using(SqlConnection connection = new SqlConnection(...)) { connection.Open(); }

Egy connectionstring elg bonyolult is lehet, gy megadsakor knnyen hibzhatunk. A .NET tartalmaz egy DbConnectionStringBuilder nev osztlyt, ami a connectionstring felptst knnyti meg. Ezt az osztlyt az sszes provider specializlja, gy ltezik OracleConnectionStringBuilder, OleDbConnectionStringBuilder, stb Mi most az SqlConnectionStringBuilder t fogjuk hasznlni:
SqlConnectionStringBuilder csBuilder = new SqlConnectionStringBuilder(); csBuilder.DataSource = @"(local)\SQLEXPRESS"; csBuilder.InitialCatalog = "testDataBase"; csBuilder.IntegratedSecurity = true; SqlConnection connection = new SqlConnection(); connection.ConnectionString = csBuilder.ConnectionString; connection.Open(); Console.WriteLine(connection.State); connection.Close();

A connectionstring ek knnyebb kezelshez egy msik lehet sg, hogy eltroljuk a konfigurcis file ban.

53.1 Kapcsolds Access adatbzishoz


A fejezet cmben szerepl cselekvst a Microsoft Jet adatzis vezrl n kereszt l fogjuk megtenni. Valjban a Jet jval tbb szolgltatst rejt magban, mint gondolnnk, de az vek sorn hasznlata egybeforrt az Access szel. Mi sem fogjuk msra hasznlni, hiszen a tbbi adatbziskezel hz van specializlt .NET osztlyknyvtr. Hasznlathoz a System.Data.OleDb nvtrre van szksgnk, ezen bell is az OleDbConnection osztlyra, amely egy jabb DbConnection leszrmazott.

316
using (OleDbConnection connection = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source='test.mdf';Persist Security Info=False")) { connection.Open(); }

Az OleDb nvtr osztlyainak segtsgvel minden olyan adatforrshoz hozzfrnk, amely tmogatja az OLE protokollt (pl. az Access ilyen). Ezt a nvteret leginkbb akkor hasznljuk, amikor az adatforrshoz nincs ms specifikus .NET osztly.

53.2 Kapcsolds Oracle adatbzishoz


A tbbivel ellenttben az ehhez szksges nvtr nem szerepel az alapbelltsok kztt, gy neknk kell hozzadnunk az assembly t, ehhez jobb egrgomb a References fln, majd Add Reference. Az assembly neve System.Data.OracleClient. Oracle adatbzishoz OLE protokoll segtsgvel is csatlakozhatunk. A kapcsolatot nem meglep mdon az OracleConnection osztllyal hozzuk ltre. A fentieken kv l (s t, inkbb helyett) Oracle adatbzisokkal val fejlesztshez rdemes telepteni az Oracle Data Provider for .NET (ODP.NET) knyvtrakat illetve az Oracle Developer Tools for Visual Studio t. A letlts mindkt esetben ingyenes, s a dokumentci is szleskr . A letlt oldalak: http://www.oracle.com/technology/tech/windows/odpnet/index.html http://www.oracle.com/technology/tech/dotnet/tools/index.html

317

54. Kapcsolat nlkli rteg


Az ADO.NET kapcsolat nkli rtegt azok az osztlyok alkotjk, amelyek nllan, kapcsolat nlkl troljk magukban az adatokat. Vagyis lekrdezzk az adatbzisbl a szksges dolgokat s azokat a kliens oldalon eltroljuk. Ezutn mdostsokat vgznk s ha ksz vagyunk, akkor ismt kapcsoldunk a kiszolglhoz s vgrehajtjuk a vltoztatsokat. Ez az egsz folyamat els re bonyolultul hangzik, de a munka nagy rszt nem neknk kell elvgeznnk. A kapcsolat nlkli rteg osztlyai a System.Data nvtrben vannak.

54.1 DataTable
A DataTable osztly lesz a kapcsolat nlkli rteg alapkve. Gyakorlatilag megfelel egy adatbzis egy adattbljnak. Egy DataTable oszlopokbl s sorokbl ll, illetve az ezeknek megfelel DataColumn s DataRow osztlyok pldnyaibl. Hozzunk ltre egy egyszer DataTable t:
DataTable table = new DataTable("PersonTable"); table.Columns.Add("Name", typeof(string)); table.Columns.Add(new DataColumn("Address", typeof(string)));

A tbal konstruktorban megadtuk a tbla nevt (ez nem ktelez ). Ezutn ktfle mdja kvetkezik oszlop hozzadsnak. Vagy helyben intzzk, megadva a nevt s tpust, vagy pedig egy mr ltez DataColumn pldnyt adunk hozz, hasonlan kivitelezve. A DataTable az oszlopait egy DataColumnCollection tpus gy jtemnyben trolja, amely az InternalDataCollection osztlybl szrmazik, amely megvalstja az ICollection s IEnumerable interfszeket, gy gyakorlatilag tmbknt (is) kezelhet , illetve hasznlhat foreach konstrukciban. A DataColumn konstruktorban els helyen az oszlop neve, majd a tpusa ll. Utbbinl egy Type tpust vr s a typeof opertor pont ezt adja vissza. Amit fontos tudnunk, hogy egy DataTable hoz csakis akkor adhatunk hozz adatokat (sorokat), ha tartalmaz legalbb egy oszlopot. Egy oszlopnak szmos ms tulajdonsga is lehet:
DataColumn dc = new DataColumn("TestColumn"); dc.DataType = typeof(string); dc.AllowDBNull = true; //nullrtk engedlyezett dc.MaxLength = 30; //maximlis szveghossz dc.DefaultValue = "semmi"; //a sorokban az oszlop alaprtelmezett rtke dc.Unique = true; //egyedi rtk az sszes sora vonatkoztatva

Egy DataTable- hz els dleges kulcso(ka)t is bellthatunk:


DataTable table = new DataTable("PersonTable"); table.PrimaryKey = new DataColumn[] { new DataColumn("ID", typeof(int)) };

318 A PrimaryKey tulajdonsg egy DataColumn tmbt vr, hiszen egy hagyomnyos SQL adatbzis is rendelkezhet tbb els dleges kulccsal. Egy els dleges kulcshoz (de tulajdonkppen brmelyik oszlophoz) automatikusan is hozzrendelhetnk rtkeket (hasonlan mint az igazi adatbzisnl):
DataTable table = new DataTable("PersonTable"); DataColumn dt = new DataColumn("TestColumn"); dt.DataType = typeof(int); dt.AutoIncrement = true; dt.AutoIncrementSeed = 0; //kezd rtk dt.AutoIncrementStep = 1; //amennyivel nveli table.PrimaryKey = new DataColumn[] { dt };

Miutn ltrehoztuk a tbla smjt feltlthetjk adatokkal. Ezt a DataTable Rows tulajdonsgn keresztl tehetjk meg, amely rtelemszer en DataRow pldnyokat vr illetve ad vissza. Van azonban egy kis problma. Nylvn azrt lltottuk be az els dleges kulcs automatikus nvelst, mert azt nem szeretnnk minden alkalommal kzzel megadni. Nzzk mi okozza a bajt. A hagyomnyos mdszer gy nzne ki:
table.Rows.Add("Reiter Istvan", "Toalmas");

Ekkor a Rows tulajdonsg Add metdusa egy paramtertmbt vr, amelyben a sor adatai vannak. Ebben az esetben azonban ez a sor kivtelt (ArgumentException) okoz, mivel nem adtunk meg els dleges kulcsot. Ez els re meglep lehet hiszen belltottuk az els dleges kulcs tulajdonsgait. A hiba oka az, hogy ilyenkor csakis a megfelel smval rendelkez sorokat adhatunk meg, vagyis olyat ami ismeri a tbla felptst s automatikusan belltja a megfelel rtkeket, vagy pedig megadjuk az sszes oszlopot s gy az els dleges kulcsot is. A DataTable osztlynak van is egy NewRow nev metdusa, ami pont erre val, vagyis egy olyan DataRow pldnyt ad vissza, amely megfelel a tbla smjnak. A teljes plda most ez lesz:
DataTable table = new DataTable("PersonTable"); DataColumn primKey = new DataColumn("ID"); primKey.DataType = typeof(int); primKey.AutoIncrement = true; primKey.AutoIncrementSeed = 0; primKey.AutoIncrementStep = 1; table.Columns.Add(primKey); table.PrimaryKey = new DataColumn[] { primKey }; table.Columns.Add("Name", typeof(string)); table.Columns.Add(new DataColumn("Address", typeof(string)));

319

DataRow drOne = table.NewRow(); drOne["Name"] = "Reiter Istvan"; drOne["Address"] = "Toalmas ..."; table.Rows.Add(drOne); DataRow drTwo = table.NewRow(); drTwo["Name"] = "Dr. A. Kula"; drTwo["Address"] = "Erdely, Baljos Fasor 13/b."; table.Rows.Add(drTwo);

A DataRow indexel ivel az oszlopok nevre hivatkozhatunk. Egy tbla sorait szinte mindig rdemes a NewRow metdussal ltrehozni, gy sokkal kevesebb a hibalehet sg. Termszetesen egy DataTable elemeit elrhetjk a programunkban, a kvetkez pldban az el z plda tbljt ratjuk ki:
foreach (DataRow row in table.Rows) { Console.WriteLine("Name: {0}, Address: {1}", row["Name"], row["Address"]); }

Az biztosan felt nt mr, hogy az oszlopok neveihez nem kapunk semmilyen tmogatst a fejleszt eszkzt l, s t a fordt sem tud mit csinlni. Ez a hagyomnyos tpustalan adattblk legnagyobb htrnya, erre a problmra (is) megoldst jelenthet a tpusos DataSet ek hasznlata. Egy DataTable hez kls adatforrsbl (SQL adatbzis, XML file) is rendelhetnk adatokat, ezeket a mdokat a megfelel fejezet rszletezi. A DataTable LoadDataRow metdusval fell tudunk rni ltez adatokat:
DataTable table = new DataTable("PersonTable"); table.Columns.Add(new DataColumn(("Name"), typeof(string))); table.Columns.Add(new DataColumn(("Age"), typeof(int))); table.Columns.Add(new DataColumn(("Address"), typeof(string))); DataRow dr = table.NewRow(); dr["Name"] = "Reiter Istvan"; dr["Age"] = 22; dr["Address"] = "Toalmas, ..."; table.Rows.Add(dr); table.LoadDataRow(new object[] { "Reiter Istvan", 44, "World" }, LoadOption.OverwriteChanges);

A metdus az els kulcs (vagy az els dleges kulcs) alapjn sztnz az adatbzisban s ha tall egyezst, akkor azt a bejegyzst mdostja, egybknt ltehoz egy j sort. Ha most kiratnnk a tbla adatait, akkor azt ltnk, hogy egyetlen bejegyzst tartalmaz, a mdostott adatokkal.

320 A DataRow nak klnbz llapotai lehetnek, amelyeket a RowState tulajdonsggal krdezhetnk le (vigyzat, ezt a tulajdonsgot csakis olvashatjuk). Egy sort trlhetnk, mdosthatunk, stb de a vltozsok csakis akkor lesznek vglegesek, ha meghvtuk az AcceptChanges metdust. Ezzel a metdussal szintn rendelkezik a DataTable osztly is, amikor meghvjuk, akkor az sszes sorra is meghvdik. Egy sor llapota a kvetkez k lehetnek: - Detached: a DataRow pldny mr elkszlt, de nincs hozzadva egy tblhoz sem. - Added: a sort hozzadtuk egy tblhoz - Unchanged: a legutols AcceptChanges hvs ta nem vltozott a sor rtke - Modified: a sor vltozott az utols AcceptChanges ta - Deleted: a sort trltk a Delete metdussal Nhny plda:
DataTable table = new DataTable("PersonTable"); table.Columns.Add(new DataColumn(("Name"), typeof(string))); table.Columns.Add(new DataColumn(("Age"), typeof(int))); table.Columns.Add(new DataColumn(("Address"), typeof(string))); DataRow dr = table.NewRow(); Console.WriteLine(dr.RowState); //Detached dr["Name"] = "Reiter Istvan"; dr["Age"] = 22; dr["Address"] = "Toalmas, ..."; table.Rows.Add(dr); Console.WriteLine(dr.RowState); //Added dr["Age"] = 56; Console.WriteLine(dr.RowState); //Added dr.AcceptChanges(); Console.WriteLine(dr.RowState); //Unchanged dr["Age"] = 22; Console.WriteLine(dr.RowState); //Modified

Lthat, hogy az Unchanged s a Modified llapotokat csakis az AcceptChanged hvsa ut veheti fel. A BeginEdit s EndEdit metdusokkal tbb mdosts is elvgezhet egy soron. Mdostsuk az el z programot, hogy az utols mdosts gy nzzen ki:
dr.BeginEdit(); dr["Age"] = 22;

321
Console.WriteLine(dr.RowState); //Unchanged dr.EndEdit();

A fenti pldkban a DataRow pldnyokat az eredeti azonostjukkal kezeltk, de ezt megtehettk volna a DataTable Rows tulajdonsgval amely DataRowsCollectionnel tr vissza s ennek van indexel je.

54.1.1 DataGridView
Korbban mr lttunk pldt adatktsekre, amikor a ListBox vezrl vel s trsaival ismerkedtnk. Ezt ebben az esetben is megtehetjk, hiszen a DataTable rendelkezik mindazon tulajdonsgokkal (interfszekkel), amelyek alkalmass teszik erre a feladatra. Adatok megjelentsre van egy specilis vezrl nk, a DataGridView. Ez a vezrl a .NET Framework 2.0 verzijban jelent meg el szr, a rgi (.NET 1.0) DataGrid et levltva. Ksztsnk egy j Windows Forms Application projectet! A ToolBox ban keressk ki a DataGridView ot (a Data fl alatt lesz) s hzzuk a formra. A vezrl hz kapcsold belltsok ablakot bezrhatjuk a vezrl tetejn megjelen ki nyilacskval, most nem lesz r szksg.

A DataGridView AllowUserToAddRows s AllowUserToDeleteRows tulajdonsgainak rtkt lltsuk false ra. Ezutn hozzuk ltre a form Load esemnykezel jt s rjuk bele a kvetkez t:
private void Form1_Load(object sender, EventArgs e) { DataTable table = new DataTable("Persons"); table.Columns.Add(new DataColumn(("Name"), typeof(string))); table.Columns.Add(new DataColumn(("Age"), typeof(int))); table.Columns.Add(new DataColumn(("Address"), typeof(string))); DataRow dr = table.NewRow();

322
dr["Name"] = "Reiter Istvan"; dr["Age"] = 22; dr["Address"] = "Toalmas, ..."; table.Rows.Add(dr); DataRow dr2 = table.NewRow(); dr2["Name"] = "Kovcs Jnos"; dr2["Age"] = 55; dr2["Address"] = "Budapest"; table.Rows.Add(dr2); table.AcceptChanges(); dataGridView1.DataSource = table; }

Az eredmny:

Br a DataGridView alaprtelmezett megjelense kiss csnycska, de rszletesen konfigurlhat, a jegyzet ezt a tmt nem rszletezi, de rdemes elltogatni a kvetkez oldalra: - http://msdn.microsoft.com/en-us/library/ms171598.aspx Egy DataGridView hoz brmely olyan osztly kthet , amely megvalstja az IList, IListSource, IBindingList illetve IBindingListView interfszek valamelyikt. Az tisztn ltszik, hogy egy DataGridView sorokbl s oszlopokbl ll, ezeket DataGridViewRow s DataGridViewColumn tpusokkal rhatjuk le. Az egyes cellk pedig DataGridViewCell tpusak, illetve ennek az osztlynak a leszrmazottai, err l ks bb mg lesz sz. Tbb cella kombincijt a DataGridViewBand osztllyal csoportosthatjuk, gy pl. kivlaszthatjuk egy sor vagy oszlop sszes celljt is:
DataGridViewBand rowBand = dataGridView1.Rows[0]; DataGrdiViewBand columnBand = dataGridView1.Columns[1];

A Rows s Columns tulajdonsgok DataGridViewRowCollection illetve DataGridViewColumnCollection tpus gy jtemnyekkel trnek vissza, sok hasonlval tallkoztunk mr, ezeket is lehet indexelni, felsorolni, stb

323 Egy DataGridView hoz nem csak DataTable objektumokat kthetnk, hanem hagyomnyos listkat is. Hozzunk ltre egy egyszer osztlyt:
public class Person { public Person(string name, int age, string address) { Name = name; Age = age; Address = address; } private string name; public string Name { get { return name; } set { name = value; } } private int age; public int Age { get { return age; } set { age = value; } } private string address; public string Address { get { return address; } set { address = value; } } }

Ennek az osztlynak a pldnyaival fogjuk feltlteni a DataGridView ot. Egyetlen dolognak kell eleget tennnk, el kell kszteni a megfelel tulajdonsgokat, mivel ezeket ktjk majd hozz. A tblzatban az adatok a tulajdonsgok definicijnak sorrendjben jelennek meg fellr l lefel haladva. Most hozzuk ltre a listnkat:
private void Form1_Load(object sender, EventArgs e) { List<Person> personList = new List<Person>(); Person p1 = new Person("Reiter Istvn", 22, "Talms, ..."); Person p2 = new Person("Kovcs Jnos", 66, "Budapest, ..."); Person p3 = new Person("Kis Balzs", 10, "Baja, ..."); personList.Add(p1); personList.Add(p2); personList.Add(p3); table.AcceptChanges(); dataGridView1.DataSource = personList; }

324

Az eredmny:

Az eddigi pldink nem voltak tlzottan rugalmasak, mivel csak egyszeri adatokat jelentettnk meg, hiszen a DataTable illetve a lista loklis objektumok voltak. Termszetesen felmerlhet az igny, hogy mdostsuk az adatainkat a DataGridView bl. A kvetkez programunk erre is alkalmas lesz, ezenkv l j sorok beszrst is meg fogjuk oldani. A DataGridView AllowUserToAddRows tulajdonsgt lltsuk true rtkre. Ezutn hozzuk ltre az adattblt, de ezttal a form osztly tagjaknt. A konstruktorban belltjuk az oszlopokat:
table.Columns.Add(new DataColumn("Name", typeof(string))); table.Columns.Add(new DataColumn("Age", typeof(int))); table.Columns.Add(new DataColumn("Address", typeof(string)));

A form Load esemnyben pedig feltltjk a tblt s hozzktjk a DataGridView hoz:


DataRow dr1 = table.NewRow(); dr1["Name"] = "Kovcs Jnos"; dr1["Age"] = 55; dr1["Address"] = "Budapest, ..."; table.Rows.Add(dr1); DataRow dr2 = table.NewRow(); dr2["Name"] = "Kis Balzs"; dr2["Age"] = 8; dr2["Address"] = "Kecskemt, ..."; table.Rows.Add(dr2); dataGridView1.DataSource = table;

Hzzunk egy gombot is a formra, ezzel fogjuk vglegesteni a vltoztatsokat, azltal, hogy meghvjuk a DataTable AcceptChanges esemnyt. Hrom feladatunk lesz: els knt ellen rizzk, hogy a felhasznl megfelel adatot adjon meg (a kornl ne szerepeljen pl. string), msodszor a mr ltez adatok mdostsa, vgl pedig j sor hozzadsa az adatforrshoz. Haladjunk sorban: brmilyen mdosts nlkl indtsuk el az alkalmazst s valamelyik kor oszlopba rjunk nhny bet t. Ha megprbljuk elhagyni azt a cellt, akkor egy szp nagy MessageBox ot kapunk a kpnkbe. Ez valljuk be nem tl szp megolds, ezrt mdostjuk a programot, hogy egy bartsgosabb

325 hibazenettel rukkoljon el . A DataGridView brmely hiba esetn a DataError esemnyt aktivlja, amely DataGridViewDataErrorEventArgs tpus paramtere hordozza az informcit, amivel mr azonostani tudjuk a hiba forrst. Hozzuk ltre az esemnykezel t, de ne rjunk bele semmit. Ha most elindtjuk a programot s el idznk valamilyen hibt (mondjuk a kor oszlopba nem numerikus karaktert runk), akkor az alkalmazs nem szl semmit, de nem lphetnk tovbb a cellbl, amg j rtket nem adtunk meg. Finomtsunk kicsit a dolgon:
private void dataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e) { if (e.Exception != null) { MessageBox.Show("Krem adjon meg helyes adatokat!"); } }

A paramter Exception tulajdonsga null rtket vesz fel, ha nem volt kivtel, de ha mgis, akkor kirakunk egy MessageBox ot s tjkoztatjuk a felhasznlt a hibjrl. Ha csak bizonyos kivtelek rdekelnek minket, az is megoldhat. A pldnknl maradva kezeljk a FormatException t, amely a helytelen formtum, vagy tpus adatok esetben vltdik ki:
private void dataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e) { if (e.Exception is FormatException) { MessageBox.Show("Krem adjon meg helyes adatokat!"); } }

Ezenkvl az is megoldhat, hogy tovbbadjuk a kivtelt, ha a ThrowException tulajdonsg rtkt igazra lltjuk:
e.ThrowException = true;

A Context tulajdonsggal a hiba krlmnyeir l kapunk informcit, egy felsorolt tpus (DataGridViewDataErrorContexts) formjban. Pldul, ha helytelen adatot adtunk meg egy cellban s megprblunk tovbblpni, akkor a tulajdonsg rtke egyszerre lesz Parsing, Commit s CurrentCellChanged. Ezt a logikai vagy opertorral tudjuk ellen rizni, az rtkek kombinlsval:
private void dataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e) { if (e.Context == (DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.CurrentCellChange)) { MessageBox.Show("Krem adjon meg helyes adatokat!"); } }

326

A hibt okoz cellt is egyrtelm en azonosthatjuk, a ColumnIndex s RowIndex tulajdonsgokkal, amelyek a DataGridView soraira s oszlopaira hivatkoznak:
private void dataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e) { if (e.Context == (DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.CurrentCellChange)) { MessageBox.Show("A " + e.ColumnIndex.ToString() + ", " + e.RowIndex.ToString() + " cella helytelen adatot tartalmaz: " + dataGridView1[e.ColumnIndex, e.RowIndex].Value.ToString()); } }

Lthat, hogy maga a DataGridView is DataGridViewCell tpus objektumot ad vissza.

indexelhet ,

az

indexel je

egy

Mshogy is jelezhetjk a felhasznlknak a hibs adatokat, mgpedig ErrorProvider ekkel. Ezeket a DataGridView mr beptve tartalmazza, csak be kell lltanunk a sor vagy cella ErrorText tulajdonsgt:
private void dataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e) { if (e.Exception != null) { dataGridView1.Rows[e.RowIndex].ErrorText = "Hibs adatok"; } }

A cellk kezelshez van mg nhny fontos esemny. A CellValidating akkor lp letbe, amikor a cella elveszti a fkuszt. Ezzel az esemnnyel megakadlyozhatjuk, hogy nem megfelel rtk kerljn egy cellba, illetve, hogy a rkttt adatforrsban ne mdosuljanak az adatok. Prja a CellValidated, amely kzvetlenl a CellValidating utn jn, ez az esemny utlagos formzsok kivitelezsre hasznos. Amennyiben a megadott adatok megfelel ek, akkor a DataGridView mgtt lv adatforrs is mdosul (ez persze csakis adatkttt krnyezetben igaz).

327 Termszetesen egy DataTable sorainak llapota addig nem vgleges, amg meg nem hvtuk az AcceptChanges metdust, ezt a korbban ltrehozott gombra drtozzuk r:
private void button1_Click(object sender, EventArgs e) { table.AcceptChanges(); }

Sorok hozzadsa rendkv l egyszer en zajlik. Ha az AllowUserToAddRows tulajdonsg igaz rtket kapott, akkor a DataGridView vgn megjelenik egy csillaggal jelzett sor. Ha valamelyik celljra rkerl a fkusz, akkor az j sor automatikusan ltrejn s ez megtrtnik a mgtte lv adatforrsban, jelen esetben a DataTable ben. A sor alaprtelmezett rtkekkel inicializldik (ezeket a DataColumn DefaultValue tulajdonsgval tudjuk belltani), illetve ha az nincs belltva akkor nullrtkkel (ha engedlyezett: AllowDBNull tulajdonsg, alaprtelmezetten igaz rtk ). Az alaprtelmezett sorrtkeket futs kzben is megvltoztathatjuk a DataGridView DefaultValuesNeeded esemnyben, amely akkor fut le, amikor az j sorra kerl a fkusz.
private void dataGridView1_DefaultValuesNeeded(object sender, DataGridViewRowEventArgs e) { e.Row.Cells[0].Value = "<Szemly neve>"; e.Row.Cells[1].Value = 0; e.Row.Cells[2].Value = "<Szemly lakhelye>"; }

Ekkor az adatforrs mg nem mdosul, illetve, ha nem runk be semmit s visszalpnk egy msik sorba, akkor ezek az rtkek elt nnek. Ezt az esemnyt a felhasznl irnytsra hasznlhatjuk. Sorok trlshez engedlyeznnk kell az AllowUserToDeleteRows tulajdonsgot, az EditMode tulajdonsg rtke legye EditOnKeyStrokeOrF2 illetve a SelectionMode legyen FullRowSelect vagy RowHeaderSelect. Ekkor egyszer en kattintsunk valamelyik sorra, hogy kijelljk s nyomjuk le a Del billenty t. Ezenkv l hasznlhatjuk mg a DataGridView Rows tulajdonsgnak RemoveAt metdust is, amely az eltvoltand sor indext vrja paramterknt.

328 Egy DataGridView nem csak hagyomnyos cellkat tartalmazhat, hanem specializltabb vltozatokat is, mint pl. egy kp vagy egy ComboBox. Akr arra is van lehet sg, hogy egy oszlopot a megjelentett rtk tpustl fgg en mdostsunk. A specializlt oszlopok specializlt cellkbl llnak, teht egy DataGridViewTextBoxColumn tpus oszlop cellkra vonatkoz tulajdonsga DataGridViewTextBoxCellCollection tpus gy jtemnyt fog visszaadni, amelynek tagjai rtelemszer en DataGridViewTextBoxCell tpusak lesznek. Egsztsk ki a programunkat, hogy kivlaszthassa a felhasznl a szemlyek nemt. Els knt egsztsk ki ezzel az oszloppal a DataTable t:
table.Columns.Add(new DataColumn(("Sex"), typeof(string)));

Ezzel ms dolgunk nincsen, hiszen alaprtelmezs szerint engedlyezett nullrtket megadni erre az oszlopra, gy a a tblabeli adatainkon nem kell vltoztatni. A DataGridView ennl problmsabb lesz. Az oszlopokat kzzel fogjuk ltrehozni, ehhez lltsuk az AutoGenerateColumn tulajdonsgot false rtkre, gy megakadlyozzuk, hogy a vezrl az adatktskor elksztse a megfelel oszlopokat. Most hozzadjuk a mr ismert adatok oszlopait (az egsz a form Load esemnyben zajlik):
dataGridView1.AutoGenerateColumns = false; DataGridViewTextBoxColumn tbc = new DataGridViewTextBoxColumn(); tbc.DataPropertyName = "Name"; tbc.DisplayIndex = 0; tbc.Name = "Name"; DataGridViewTextBoxColumn tbc1 = new DataGridViewTextBoxColumn(); tbc1.DataPropertyName = "Age"; tbc1.DisplayIndex = 1; tbc1.Name = "Age"; DataGridViewTextBoxColumn tbc2 = new DataGridViewTextBoxColumn(); tbc2.DataPropertyName = "Address"; tbc2.DisplayIndex = 2; tbc2.Name = "Address";

A DataPropertyName tulajdonsg azt a tulajdonsgot jelli, ahonnan adatktskor a megjelentett adatok szrmaznak. A DisplayIndex a megjelents sorrendjt hatrozza meg, mg a Name az oszlop nevt adja meg, s ez fog megjelenni a fejlcben is, ha nem adjuk meg a HeaderText tulajdonsg rtkt. Miel tt tovbbmegynk ksztsnk egy stringtmbt:
string[] sexList = new string[] { "Frfi", "N " };

Ezt fogjuk rktni a ComboBox ra. Ebben az esetben tulajdonkppen kt kts is lesz, hiszen egyszer megadjuk a vezrl rtkkszeltt, msodszor pedig hozzktjk a DataTable oszlophoz is. Most jn a DataGridViewComboBoxColumn:

329

DataGridViewComboBoxColumn cbt = new DataGridViewComboBoxColumn(); cbt.DataPropertyName = "Sex"; cbt.DisplayIndex = 3; cbt.Name = "Sex"; cbt.DataSource = sexList;

Gyakorlatilag kszen vagyunk, semmi mst nem kell mdostanunk. A ksz program gy nz ki:

Mentskor az adattbla is mdosulni fog, err l knnyen meggy zdhetnk, ha kiratjuk az adott sor rtkeit (pl. egy MessageBox xal).

54.2 DataView
A DataView osztllyal egy tblt tbb szemszgb l nzhetjk, illetve rendezhetjk, sz rhetjk (hasonl a funkcionalitsa mint az SQL where s order by parancsainak). Egy DataView jellemz en egy DataTable pldnyra l r. Vegyk a kvetkez pldt:
private void Form1_Load(object sender, EventArgs e) { DataView view = new DataView(table); view.RowFilter = "Name = 'Reiter Istvan'"; dataGridView1.DataSource = view; }

A RowFilter tulajdonsg egy kifejezst vr, amely megadja, hogy milyen felttel alapjn vlogassa ki az oszlopokat. A RowStateFilter pedig a sorok llapotnak megfelel en keres. Lthat, hogy a DataView kthet , gy egy tblt ms ms nz pontbl kthetnk r vezrl kre. A Sort tulajdonsggal rendezhetjk is az adatokat:
private void Form1_Load(object sender, EventArgs e) { DataView view = new DataView(table); view.Sort = "Name";

330

dataGridView1.DataSource = view; }

Ebben a pldban a rendezs a Name oszlop alapjn valsul meg.

54.3 DataSet
Egy DataSet pldny DataTable s DataRelation objektumokbl ll, vagyis tblkbl s a kztk lv relcikbl. Egy DataSet tulajdonkppen megfelel egy adatbzisnak, amely a memriban van. Hozzunk ltre egy DataSet et kt tblval:
DataTable customers = new DataTable("Customers"); DataTable orders = new DataTable("Orders"); DataSet data = new DataSet("ShopDataSet");

A pldaprogramban egy egyszer Ksztsk el az oszlopokat:

gyfl rendels szitucit fogunk modellezni.

DataColumn primKeyCust = new DataColumn("CID", typeof(int)); primKeyCust.AllowDBNull = false; primKeyCust.AutoIncrement = true; primKeyCust.AutoIncrementSeed = 0; primKeyCust.AutoIncrementStep = 1; customers.Columns.Add(primKeyCust); customers.PrimaryKey = new DataColumn[] { primKeyCust }; customers.Columns.Add(new DataColumn("Name", typeof(string))); customers.Columns.Add(new DataColumn("Address", typeof(string))); DataColumn primKeyOrd = new DataColumn("OID", typeof(int)); primKeyOrd.AllowDBNull = false; primKeyOrd.AutoIncrement = true; primKeyOrd.AutoIncrementSeed = 0; primKeyOrd.AutoIncrementStep = 1; orders.Columns.Add(primKeyOrd); orders.PrimaryKey = new DataColumn[] { primKeyOrd }; orders.Columns.Add(new DataColumn("CID", typeof(int))); orders.Columns.Add(new DataColumn("OrderedThing", typeof(string))); DataRow custRow1 = customers.NewRow(); custRow1["Name"] = "Nagy Sndor"; custRow1["Address"] = "Budapest, ..."; DataRow custRow2 = customers.NewRow(); custRow2["Name"] = "Kis Balzs";

331
custRow2["Address"] = "Eger, ..."; customers.Rows.Add(custRow1); customers.Rows.Add(custRow2); DataRow ordRow1 = orders.NewRow(); ordRow1["CID"] = 0; ordRow1["OrderedThing"] = "Elektromos gyomorkapar"; DataRow ordRow2 = orders.NewRow(); ordRow2["CID"] = 1; ordRow2["OrderedThing"] = "Napelemes szemfenktrl "; orders.Rows.Add(ordRow1); orders.Rows.Add(ordRow2); shopData.Tables.Add(customers); shopData.Tables.Add(orders); shopData.AcceptChanges();

Maga a program elg egyrtelm semmi olyan nincs benne, amivel mr ne tallkoztunk volna. A DataSet en meghvott AcceptChanges meghvja az egyes tblk metdust, amelyek majd a tbla soraira is meghvjk. Kssk hozz az egyik tblt egy DataGridView hoz:
dataGridView1.DataSource = shopData.Tables[1];

54.3.1 Relcik tblk kztt


Amikor egy valamilyen adatbzisbl beolvasunk adatokat egy DataSet be, akkor a tblk kzti relcik (amik az adatbzisban lteztek) elvesznek. Ebb l kifolylag kpesnek kell lennnk reproduklni ezeket a kapcsolatokat. Erre a feladatra a DataRelation osztly szolgl:
DataRelation relation = new DataRelation("CustAndOrd", shopData.Tables["Customers"].Columns["CID"], shopData.Tables["Orders"].Columns["CID"]); shopData.Relations.Add(relation);

A DataRelation itt bemutatott konstruktora (van mg t) hrom paramtert vr: a relci nevt, a szl oszlopot, amelyre majd a harmadik paramter a gyermekoszlop mutat (ez gyakorlatilag megfelel egy idegenkulcs relcinak). Mi most az egyes vsrlk rendelseire vagyunk kvncsiak. A relcis objektumot hozz is kell adnunk a DataSet hez. Termszetes az igny, hogy az adatainkat a relcik alapjn brzoljuk, teht pl. meg tudjuk nzni, hogy ki mit rendelt. Erre a feladatra kt megolds is van: az els a rgi DataGrid (a DataGridView el dje) vezrl hasznlata, ennek tulajdonkppen ez az egyetlen el nye az j vezrl vel szemben.

332 Ajunk hozz a formhoz egy DataGrid et (ha nincs ott a ToolBox ban, akkor jobb klikk valamelyik szimpatikus fln s Choose Items, majd keressk ki a vezrl t). A form Load esemnyben fogjuk rktni az adatforrsokat:
private void Form1_Load(object sender, EventArgs e) { dataGrid1.DataSource = shopData; dataGrid1.DataMember = "Customers"; }

A DataMember tulajdonsg azt mondja meg, hogy melyik tblt hasznljuk. Ezutn az eredmny a kvetkez lesz:

A sor elejn lv jelre kattintva megjelennek a hozzrendelt relcik:

Erre kattintva pedig megnzhetjk a msik tbla adatait:

333 Ez volt az egyszer bb megolds. A DataGridView nem kpes relcik megjelentsre, ehelyett egy n. master-detail megoldst fogunk ltrehozni. Kt DataGridView lesz a formon, a master ben naviglva (ez a fenti pldban a vsrlk tblja lesz) lthatjuk a detail ben a hozz tartoz adatokat (a rendelst). Hzzuk r a kt vezrl t a formra s nevezzk el ket masterGrid nek s detailGrid nek. Ezenkv l szksgnk lesz kt BindingSource objektumra is, ez az osztly az adatktsek finomrahangolsban segt minket. Ksztsk el ket is masterBinding s detailBinding nven. Az els BindingSource hz a Customers tblt fogjuk ktni, a msodikhoz pedig a tblk kzti relcit, gy amikor az els griden a kivlasztott sor mdosul, akkor a msikon megjelenik a hozz rendelt adat:
private void Form1_Load(object sender, EventArgs e) { masterBinding.DataSource = shopData; masterBinding.DataMember = "Customers"; detailBinding.DataSource = masterBinding; detailBinding.DataMember = "CustAndOrd"; masterGrid.DataSource = masterBinding; detailGrid.DataSource = detailBinding; }

Az eredmny:

Rszletesebb informcikat tall a master-detail kapcsolatokrl s a BindingSource osztlyrl az olvas a kvetkez oldalakon: http://msdn.microsoft.com/en-us/library/y8c0cxey.aspx http://msdn.microsoft.com/en-us/library/system.windows.forms.bindingsource.aspx

54.3.2 Tpusos DataSet ek


Eddig normlis tpustalan DataSet eket hasznltunk, vagyis fordtsi id ben semmilyen ellen rzs nem trtnik a tpusok ellen rzsre. Trjnk vissza a Persons tblhoz s helyezzk el azt egy DataSet ben, ezutn pedig rjuk a kvetkez t:
personsData.Tables["Persons"].Rows[0]["Age"] = "ezegystring";

334 Ezzel a sorral a program ugyan lefordul, de futs kzben kivtel generldik (ArgumentException), hiszen stringet nem lehet implicit numerikus rtkk alaktani (fordtva viszont m kdne). Persze odafigyelssel meg lehet el zni a bajt, de ha nem akarunk ezzel bajldni, akkor hasznlhatunk tpusos DataSet eket, amelyeket fordtskor ellen rizhetnk. A vlemnyek megoszlanak a tpustalan s tpusos megoldsok kzt, igazbl egyik hasznlatbl sem szrmazik klnsebb el ny vagy htrny. A Solution Explorerben kattintsunk jobb egrgombbal a projectre, majd vlasszuk az Add/New Item menpontokat s az ablakban vlasszuk ki a DataSet et. A pldban a neve Persons.xsd lesz. A Visual Studio ltrehozza a megfelel llomnyokat, ezutn pedig grafikus felleten tervezhetjk meg a DataSet elemeit. A ToolBox bl hozzadhatunk tblkat s relcikat is (meg mst is, de err l a kvetkez fejezet szl majd). Ksztsk is el a Persons DataSet et egy tblval, amelynek legyen a neve PersonsTable. Az egyes oszlopok (amik most fizikailag sorok) tulajdonsgait a Properties ablakban llthatjuk. Hzzunk most a formra egy DataGridView ot, majd hozzuk ltre a DataSet pldnyt:
Persons personsDataSet = new Persons(); Persons.PersonsTableRow row = personsDataSet.PersonsTable.NewPersonsTableRow(); row.Name = "Bla"; row.Age = 20; row.Address = "Budapest"; personsDataSet.PersonsTable.Rows.Add(row); personsDataSet.AcceptChanges(); dataGridView1.DataSource = personsDataSet.Tables[0];

Lthat, hogy a tbla adatait tulajdonsgokon kereszt l adhatjuk meg, gy mr fordtskor kiderlnek a rossz tpus hasznlatbl fakad hibk. Ami mg fontos, az az, hogy a tblk a DataSet pldnyostsakor azonnal ltejnnek, nem kell kzzel elkszteni illetve hozzadni azokat.

335

55. Adatbzis adatainak lekrdezse s mdostsa


Azt mr tudjuk, hogyan kapcsoldjunk egy adatbzis szerverhez s az sem okozhat gondot, hogy a lekrdezett adatokkal dolgozzunk. Ami hinyzik, az az n. connected layer, ami tulajdonkppen az adatok kezelsrt felel s. Egy adatbzison ktflekppen tudunk lekrdezst vgrehajtani, vagy a klienst l kldjk a lekrdezst, vagy pedig a szerveren hvunk meg egy trolt eljrst. Akrhogy is dntnk, mindkt esetben a DbCommand absztrakt osztly egy leszrmazottjt fogjuk hasznlni, ez MSSQL szerver esetn az SqlCommand lesz. Ksztsnk egy Console Application t (az egyszer sg kedvrt) s nyissunk egy kapcsolatot:
using (SqlConnection connection = new SqlConnection()) { connection.ConnectionString = @"Data Source=(local)\SQLEXPRESS;Initial Catalog=Test;Integrated Security=True"; connection.Open(); }

Az adatbzisunkban legyen egy Persons nev tbla, amelynek egy ID s egy Name oszlopa van. rjunk egy lekrdezst, amellyel ezeket le tudjuk krdezni:
SqlCommand cmd = new SqlCommand(); cmd.Connection = connection; cmd.CommandText = "select ID, Name from Persons";

A Connection tulajdonsg az SqlConnection re mutat, a CommandText pedig a lekrdezst fogja tartalmazni. Most kldjk is el a szerver fel a lekrdezst:
SqlDataReader reader = cmd.ExecuteReader(); while (reader.Read()) { Console.WriteLine("ID: {0}, Name: {1}", reader.GetInt32(0), reader.GetString(1)); }

Az SqlDataReader sorokat olvas ki az adatbzisbl, mghozz azokat, amelyeket az ExecuteReader metdus visszaad. Az SqlCommand utastsait hromfle mdon hathatjuk vgre: az ExecuteReader visszaadja az sszes sort, ami a felttelnek megfelel , Az ExecuteScalar csakis az els sort mg az ExecuteNonQuery csakis azoknak az oszlopoknak a szmt, amelyekre a lekrdezs hatssal volt (ahogy a nevben is benne van, ezt nem lekrdezsre, hanem trlsre, mdostsra, stb. hasznljuk). Most meg is kell jelentennk az adatokat, ehhez egy ciklust hasznlunk, ami addig megy, amg van mit olvasni. Az SqlDataReader Get*** metdusai a megfelel tpus adatokat adja vissza a paramtereknt megadott oszlopbl.

336 Most nzzk meg, hogy mit kell tennnk, ha trolt eljrst akarunk hasznlni. Tegyk fel, hogy a szerveren mr van is egy trolt eljrs GetPersonById nven, amely egy paramtert kap, a keresett szemly azonostjt:
SqlCommand cmd = new SqlCommand(); cmd.Connection = connection; cmd.CommandText = "GetPersonById"; cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add("@PersonID", SqlDbType.Int).Value = 1;

55.1 Connected s Disconnected rtegek kzti kapcsolat


Az el z rszben mr megismerkedtnk a kapcsolat nlkli rteggel. Ahhoz, hogy a kett t ssze tudjuk kapcsolni egy olyan osztlyra van szksgnk, amely hdat kpez kztk. Erre a DataAdapter osztlyt fogjuk hasznlni. Ez ahogy azt mr megszokhattuk egy absztrakt osztly, amelynek leszrmazottai az egyes adatbzis szerverek sajtossgaihoz specializldnak. Mi most rtelemszer en az SqlDataAdapert osztly pldnyait fogjuk hasznlni. Az adatbzis legyen ugyanaz, mint az el z fejezetben, de most hozzunk ltre egy Windows Application t, mivel a DataGridView vezrl t fogjuk hasznlni az adatok megjelentsre. El szr egy tpustalan DataSet et hasznlunk, utna a tpusos trsnak hasznlata is bemutatsra kerl.
SqlCommand selectCmd = new SqlCommand(); selectCmd.Connection = connection; selectCmd.CommandText = "select ID, Name from Persons"; SqlDataAdapter adapter = new SqlDataAdapter(); adapter.SelectCommand = selectCmd; adapter.Fill(personsDataSet);

Maga az adapter gyakorlatilag semmir l nem tud semmit, egyetlen dolga van, hogy kzvettsen. A SelectCommand tulajdonsga egy SqlCommand pldnyt vr, ez fog az adatok lekrdezsre szolglni (ugyangy van UpdateCommand, DeleteCommand, stb.). A Fill metdusa meghvja a SelectCommand utastst s feltlti a paramtereknt megadott DataSet et (a httrben tulajdonkppen az el z fejezetben bemutatott DataReader osztlyok m kdnek). A DataSet mellett a tbla nevt is megadhatjuk, ha az mr ltezik, akkor az adatokat hozzf zi, ha mg nem, akkor pedig ltehozza:
adapter.Fill(personsDataSet, "Persons");

Az adatok visszarsa az adatbzisba is nagyon egyszer . Meg kell rnunk a szksges trls, mdosts SQL lekrdezseket s ezeket SqlCommand formjban megadni az adapternek. Ezutn az Update metdussal egyszer en meghvjuk ezeket a lekrdezseket:
adapter.Update(personsDataSet, "Persons");

Most a personsDataSet Persons tbljnak mdostsait rtuk vissza.

337 Tpusos DataSet ek esetben mg egyszer bb lesz a dolgunk, hiszen grafikus felleten varzslk segtsgvel konfigurlhatjuk be az egszet. A projecthez adjunk hozz egy DataSet et, ezt s szoksos jobb klikk a projecten Add/New Item m veletekkel tudjuk megtenni. Ezutn megjelenik a tervez nzet, amely egyel re teljesen res. Most szksgnk lesz a Server Explorer ablakra, ha alaprtelmezetten nem lthat, akkor a View menben megtalljuk (vagy hasznljuk a Ctrl+Alt+S billenty kombincit). Kattintsunk jobb egrgombbal a Data Connections elemen s vlasszuk ki, hogy Add Connection. Ha korbban mr dolgoztunk valamilyen adatbzisal, akkor rgtn az Add Connection ablak jelenik meg, ha a Data Source sorban nem a kvetkez ll: Microsoft SQL Server (SqlClient), akkor kattintsunk a Change gombra, ekkor megjelenik az adatforrst kivlaszt ablak. Szintn ez jelenik meg akkor, ha mg nem dolgoztunk adatbzissal:

Vlasszuk ki a Microsoft SQL Server t, azon bell pedig a .NET Framework Data Provider for SQL Server t. OK utn visszatrnk az el z ablakhoz:

338

A Server Name sorba kell rnunk a szmtgp azonostjt s az SQL Server pldnynak nevt. Elbbit megtudhatjuk, ha a parancssorba (Start/Futtats cmd) berjuk a hostname parancsot. Az SQL Server Express neve alaprtelmezetten SQLEXPRESS lesz, ezt meg kellett adnunk teleptskor (kivve ha a Visual Studio teleptette, akkor alaprtelmezs szerint ment). Knnyebben is hozz tudunk jutni ezekhez az informcikhoz, ha megnyitjuk az SQL Server Management Studio t, ekkor a kapcsolds ablakban megjelennek a szksges informcik:

339

Trjnk vissza a Visual Studio hoz. rjuk be a megszerzett adatokat, majd kattintsunk az alul lv Test Connection gombra. Ha hibazenetet kapunk, akkor ellen rizzk, hogy elindtottuk e az SQL Servert. A Start men programjai kzl keressk ki az SQL Servert, azon bell pedig a Configuration Tools mappt. Itt talljuk az SQL Server Configuration Manager t. Ha nincs elindtott szerver (az ikon piros a pldnyok neve mellett), akkor jobb egrgombbal kattintsunk a szerverpldnyon s vlasszuk ki, hogy Start.

A kpen a Configuration Manager lthat, fll zld ikonnal s fut sttusszal egy m kd SQL Express 2005 lthat, alul naktv 2008 as trsa. Ismt teszteljk a kapcsolatot a Visual Studio ban. Ha m kdik, akkor ki tudjuk vlasztani a hasznlni kvnt adatbzist. Nyomjuk meg az Ok gombot, ekkor az adatbzis megjelenik a Server Explorer ben. Ezutn ha hasznlni akarjuk, akkor csak nyissuk le a flt, automatikusan csatlakozik majd (ha van fut SQL Server). Teht nyissuk le a flet s keressk ki a tblk kzl azokat amelyeket hasznlni akarunk. Hzzuk t ezeket a DataSet tervez jbe. Ezutn ltrejnnek a tblk s automatikusan a hozzjuk tartoz relcik is:

340

Minden egyes tblhoz elkszl egy TableAdapter objektum is. A tblkat tesztelhetjk is, kattintsunk jobb egrgombbal a tervez n s vlasszuk a Preview Data menpontot. Ebben az ablakban megtekinthetjk a tblk tartalmt az adapterek segtsgvel. Egyel re csak annyit tudunk tenni, hogy minden oszlopot lekrdeznk, de ezt b vthetjk is. Vlasszuk ki valamelyik adaptert, kattintsunk rajta jobb egrgombbal s vlasszuk ki a Preview Data menpontot. Ekkor a megjelen ablakban megnzhetjk az adott tbla tartalmt, illetve az adapteren ltrehozott lekrdezsek alapjn mst is csinlhatunk:

341 Egy dataset nem csak egy adatbzisbl tartalmazhat adatokat. A ToolBox bl hzzunk egy TableAdapter t a tervez be, ekkor megjelenik egy varzsl, amelynek megadhatjuk az j tblk helyt, illetve, hogy milyen m veleteket generljon. Ugyangy arra is van lehet sg, hogy adatbzistl fggetlenl DataTable objektumokat adjunk a dataset hez. Az egyes tblkat tesz leges kddal is b vthetjk, mivel a generlt osztlyok mind parcilis osztlyok, vagyis attl fggetlenl kiegszthet ek. Ehhez kattintsunk jobb gombbal valamelyik tblra s vlasszuk, hogy View code. A tblk generlsa utn a tpusos dataset ekkel val munka nagyon hasonl a hagyomnyosokhoz, azzal a klnbsggel, hogy mostantl IntelliSense tmogats s fordtsi idej tpusellen rzs is jr a csomaghoz.

342

56. XML
A .NET Framework er sen pt az XML re, a tpusos DataSet ek, a LINQ To SQL, stb is ezt a formtumot hasznlja az adatsmk felptsre.

56.1 XML file ok kezelse


A szksges osztlyok a System.Xml nvtrben vannak. A kt legalapvet bb ilyen osztly az XmlReader s az XmlWriter. Ezek absztrakt osztlyok, amelyek segtsgvel villmgyorsan hatjhatak vgre a m veletek (persze van nekik szmos specializlt vltozatuk is, ezekr l is lesz sz). Els knt nzzk meg, hogyan tudunk beolvasni egy XML filet. A megnyitshoz az XmlReader egy statikus metdust a Create et fogjuk hasznlni, ez egy stream t l kezdve egy szimpla filenvig mindent elfogad:
XmlReader reader = XmlReader.Create("test.xml");

Fggetlenl attl, hogy az XmlReader egy absztrakt osztly tudjuk pldnyostani. Ennek oka az, hogy ilyen esetekben valjban egy XmlTextReaderImpl tpus objektum jn ltre, amely az XmlReader egy bels , internal elrs leszrmazottja (teht kzvetlenl nem tudnnk pldnyostani). Miutn teht megnyitottuk a filet, vgig tudunk iterlni rajta (a pldban egy RichTextBox vezrl t hasznlunk a megjelentsre):
while (reader.Read()) { switch (reader.NodeType) { case XmlNodeType.Element: richTextBox1.AppendText("<" + reader.Name + ">"); break; case XmlNodeType.EndElement: richTextBox1.AppendText("</" + reader.Name + ">"); break; case XmlNodeType.Text: richTextBox1.AppendText(reader.Value); break; default: break; }; }

Az XmlReader NodeType tulajdonsga egy XmlNodeType felsorols egy tagjt adja vissza, a pldban a legalapvet bb tpusokat vizsgltuk s ezek fggbnyben rtuk ki a file tartalmt. A kvetkez pldban hasznlni fogjuk az XmlWriter osztly egy leszrmazottjt, az XmlTextWriter t, ugyanis a filet kdbl fogjuk ltrehozni:

343
XmlTextWriter writer = new XmlTextWriter("newxml.xml", Encoding.UTF8); writer.Formatting = Formatting.Indented;

A Formatting tulajdonsg, az Xml file -oktl megszokott hierachikus szerkezetet fogja megteremteni (ennek az rtkt is bellthatjuk).
writer.WriteStartDocument(); writer.WriteComment(DateTime.Now.ToString());

Elkezdjk az adatok feltltst, s beszrunk egy kommentet is. A file tartalma most a kvetkez :
<?xml version="1.0" encoding="utf-8"?> <!--2008.10.17. 20:00:59-->

A file szemlyek adatait fogja tartalmazni:


writer.WriteStartElement("PersonsList"); writer.WriteStartElement("Person"); writer.WriteElementString("Name", "Reiter Istvan"); writer.WriteElementString("Age", "22"); writer.WriteEndElement();

A StartElement egy j tagcsoportot kezd a paramtereknt megadott nvvel, a WriteElementString pedig feltlti azt rtkekkel, kulcs rtk prokkal. A file:
<?xml version="1.0" encoding="utf-8"?> <!--2008.10.17. 20:02:05--> <PersonsList> <Person> <Name>Reiter Istvan</Name> <Age>22</Age> </Person> </PersonsList>

Az egyes tagekhez attribtumokat is rendelhetnk:


writer.WriteAttributeString("Note", "List of persons");

Els paramter az attribtum neve, utna pedig a hozz rendelt rtk jn. Ekkor a file gy alakul:
<?xml version="1.0" encoding="utf-8"?> <!--2008.10.17. 20:05:24--> <PersonsList Note="List of persons"> <Person> <Name>Reiter Istvan</Name> <Age>22</Age> </Person>

344
</PersonsList>

Az XmlReader rendelkezik nhny MoveTo el taggal rendelkez metdussal, ezekkel a file egy olyan pontjra tudunk naviglni, amely megfelel egy felttelnek. Nhny plda:
bool l = reader.MoveToAttribute("Note");

Ez a metdus a pramtereknt megadott attribtumra lltja a reader t, s igaz rtkkel tr vissza, ha ltezik az attribtum. Ellenkez esetben a pozci nem vltozik, s a visszatrsi rtk hamis lesz.
reader.MoveToContent();

Ez a metdus a legkzelebbi olyan csompontra ugrik, amely tartalmaz adatot is, ekkor az XmlReader (vagy leszrmazottainak) Value tulajdonsga ezt az rtket fogja tartalmazni. A MoveToElement metdus visszalp arra a csompontra, amely azt az attribtumot tartalmazza, amelyen a reder ll (rtelemszer en kvetkezik, hogy az el z kett metdust gyakran hasznljuk egytt):
XmlReader reader = XmlReader.Create("newxml.xml"); while (reader.Read()) { if (reader.HasAttributes) { for (int i = 0; i < reader.AttributeCount; ++i) { reader.MoveToAttribute(i); richTextBox1.AppendText(reader.Name + " " + reader.Value); } reader.MoveToElement(); } }

A HasAttribute metdussal megtudakoljuk, hogy van e a csomponton attribtum, utna pedig az AttributeCount tulajdonsg segtsgvel (amely azoknak a szmt adja vissza) vgigiterlunk rajtuk. Miutn vgeztnk visszatrnk a kiindulsi pontra (hogy a Read metdus tudja vgezni a dolgt). Emltst rdemelnek mg a MoveToFirstAttribute s a MovetoNextAttribute metdusok, amelyek nevkhz mltan az els illetve a kvetkez attributumra pozcionlnak. Mdostsuk egy kicsit az el z pldt:
for (int i = 0; i < reader.AttributeCount; ++i) {
//reader.MoveToAttribute(i);

reader.MoveToNextAttribute(); richTextBox1.AppendText(reader.Name + " " + reader.Value);

345
}

Vgl, de nem utolssorban a Skip metdus maradt, amely tugorja egy csompont gyermekeit s a kvetkez azonos szinetn lv csompontra ugrik. Most ismerkedjnk meg az XmlReader egy leszrmazottjval az XmlTextReader rel. Hasznlata teljesen ugyanolyan, mint amit az XmlReader esetben mr lttunk. Az objektum ltrehozsa a hagyomnyos ton zajlik (neki is megadhatunk pl. egy filestream et):
XmlTextReader reader = new XmlTextReader("newxml.xml");

A pldban az el z leg ltrehozott file t fogjuk hasznlni. Gyakorlatilag az XmlTextReader tulajdonsgai megegyeznek annak az osztlyval, amelyet az XmlReader pldnyostsnl ltrehoztunk.

56.2 XML DOM


Eddig az XML llomnyokat hagyomnyos file knt kezeltk, ennl azonban van egy nmileg knyelmesebb lehet sgnk is. Az XML DOM (Document Object Model) a file okat a hierarchikus felptsk szerint kezeli. Az osztly, amelyen kereszt l elrjk a DOM ot az az XmlDocument lesz. Egy msik fontos osztly, amelyb l egybknt maga az XmlDocument is szrmazik, az az XmlNode. Egy XML file egyes csompontjait egy-egy XmlNode objektum fogja jelkpezni. Ahogy azt mr megszokhattuk az XmlDocument szmos forrsbl tpllkozhat, pl. stream vagy egyszer filenv. A forrs betltst az XmlDocument Load illetve LoadXml metdusaival vgezzk:
XmlDocument xdoc = new XmlDocument(); xdoc.Load("test.xml");

Egy XmlDocument bejrhat foreach ciklussal a ChildNodes tulajdonsgon kereszt l, amely egy XmlNodeList objektumot ad vissza. Persze nem biztos, hogy lteznek gyermekei, ezt a HasChildNodes tulajdonsggal tudjuk ellen rizni. A kvetkez pldban egy XmlDocument els szinten lv gyermekeit jrjuk be:
foreach (XmlNode node in xdoc.ChildNodes) { richTextBox1.AppendText(node.Name); }

Az XmlDocument a ChildNodes tulajdonsgt az XmlNode tl rkli (ugyangy a HasChildNodes t is), gy az egyes XmlNode okat bejrva a teljes ft megkaphatjuk. Most nzzk meg, hogyan tudjuk manipullni a file t. Hozzunk ltre kdbl egy teljesen j dokumentumot:
XmlDocument xdoc = new XmlDocument();

346
XmlElement element = xdoc.CreateElement("Test"); XmlText text = xdoc.CreateTextNode("Hello XML DOM!"); XmlNode node = xdoc.AppendChild(element); node.AppendChild(text); xdoc.Save("domtest.xml");

Az XmlText s XmlElement osztlyok is az XmlNode leszrmazottai. A Save metdussal pedig el tudjuk menteni a dokumentumot, akr filenevet, akr egy stream et megadva. A fenti kd eredmnye a kvetkez lesz:
<Test>Hello XML DOM!</Test>

A CloneNode metdussal mr ltez cscsokat klnozhatunk:


XmlNode source = xdoc.CreateNode(XmlNodeType.Element, "test", "test"); XmlNode destination = source.CloneNode(false);

A metdus egyetlen paramtert vr, amely jelzi, hogy a cscs gyermekeit is tmsoljuk e. Kt msik metdusrl is megemlkeznk, els a RemoveChild, amely egy ltez cscsot tvolt el a cscsok listjbl, a msik pedig a ReplaceChild, amely fellr egy cscsot. Az XmlDocument esemnyeket is szolgltat, amelyek segtsgvel teljes kr felgyeletet nyerhetnk. Kezeljk le pldul azt az esemnyt, amikor egy j cscsot adunk hozz:
XmlNodeChangedEventHandler handler = null; handler = (sender, e) => { MessageBox.Show(e.Node.Value); }; xdoc.NodeInserting += handler;

56.3 XML szerializci


Mi is az a szrializls? A legegyszer bben egy pldn kereszt l lehet megrteni a fogalmat, kpzeljk el, hogy rtunk egy jtkot, s a jtkosok pontszmt szeretnnk eltrolni. Az elrt pontszm mell jn a jtkos neve s a teljests ideje is. Ez els re semmi klns, hiszen simn kirhatjuk az adatokat egy file ba, majd visszaolvashatjuk onnan. Egy htult mgis van, mgpedig az, hogy ez egy elg bonyolult feladat: figyelni kell a beolvasott adatok tpusra, formtumra, stb Nem lenne egyszer bb, ha a kirt adatokat meg tudnnk feleltetni egy osztlynak? Ez megtehetjk, ha ksztnk egy megfelel osztlyt, amelyet szrializlva kirhatunk XML formtumba s onnan vissza is olvashatjuk (ezt deszrializlsnak hvjk). Termszetesen a szrializls jelent sge ennl sokkal nagyobb s nemcsak XML segtsgvel tehetjk meg. Tulajdonkppen a szrializls annyit tesz, hogy a

347 memriabeli objektumainkat egy olyan szekvencilis formtumba konvertljuk, amelyb l vissza tudjuk alaktani az adatainkat. Mr emltetk, hogy tbb lehet sgnk is van a szerializcira, ezek kzl az XML a legltalnosabb, hiszen ezt ms krnyezetben is felhasznlhatjuk. Kezdjk egy egyszer pldval (hamarosan megvalstjuk a pontszmos pldt is), szrializljunk egy hagyomnyos beptett objektumot:
FileStream fstream = new FileStream("serxml.xml", FileMode.Create); XmlSerializer ser = new XmlSerializer(typeof(DateTime)); ser.Serialize(fstream, DateTime.Now);

Ezutn ltrejn egy XML file, benne a szrializlt objektummal:


<?xml version="1.0"?> <dateTime>2008-10-23T16:19:44.53125+02:00</dateTime>

Az XmlSerializer a System.Xml.Serialization nvtrben tallhat. A deszrializls hasonlkppen m kdik:


FileStream fstream = new FileStream("serxml.xml", FileMode.Open); XmlSerializer ser = new XmlSerializer(typeof(DateTime)); DateTime deser = (DateTime)ser.Deserialize(fstream);

Most mr eleget tudunk ahhoz, hogy ksztsnk egy szrializlhat osztlyt. Egy nagyon fontos dolgot kell megjegyeznnk, az osztlynak csakis a publikus tagjai szrializlhatak a private vagy protected elrs ek automatikusan kimaradnak (emellett az osztlynak magnak is publikus elrsnek kell lennie). Ezeken kv l mg szksg lesz egy alaprtelmezett konstruktorra is (a deszrializlshoz, hiszen ott mg nem tudja, hogy milyen objektumrl van sz).
public class ScoreObject { public ScoreObject() { } private string playername; [XmlElement("PlayerName")] public string PlayerName { get { return playername; } set { playername = value; } } private int score; [XmlElement("Score")] public int Score {

348
get { return score; } set { score = value; } } private DateTime date; [XmlElement("Date")] public DateTime Date { get { return date; } set { date = value; } } }

Az egyes tulajdonsgoknl belltottuk, hogy az miknt jelenjen meg a file ban (sima element helyett lehet pl. attribtum is). Enlkl is lefordulna s m kdne, de gy jobban kezelhet . Rendben, most szrializljuk:
ScoreObject so = new ScoreObject(); so.PlayerName = "Player1"; so.Score = 1000; so.Date = DateTime.Now; FileStream fstream = new FileStream("scores.xml", FileMode.Create); XmlSerializer ser = new XmlSerializer(typeof(ScoreObject)); ser.Serialize(fstream, so);

s az eredmny:
<?xml version="1.0"?> <ScoreObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <PlayerName>Player1</PlayerName> <Score>1000</Score> <Date>2008-10-23T16:34:32.734375+02:00</Date> </ScoreObject>

56.4 XML s a kapcsolat nlkli rteg


Az el z fejezetben szrializcival rtk el, hogy eltroljuk az objektumainkat. Mi van akkor, ha az objektum jval bonyolultabb, esetleg nem is egy objektumrl van sz? Ebben a fejezetben megtanuljuk, hogyan tudunk egy DataTable/DataSet objektumot XML formtumba menteni. Ksztsnk egy egyszer tblt:
DataTable table = new DataTable("Persons"); DataColumn primcol = new DataColumn(); primcol.ColumnName = "ID"; primcol.DataType = typeof(int); primcol.AutoIncrement = true; primcol.AutoIncrementSeed = 0; primcol.AutoIncrementStep = 1;

349
table.Columns.Add(primcol); table.PrimaryKey = new DataColumn[] { primcol }; table.Columns.Add(new DataColumn("Name", typeof(string))); table.Columns.Add(new DataColumn("Address", typeof(string))); DataRow row1 = table.NewRow(); row1["Name"] = "Kovcs Jnos"; row1["Address"] = "Budapest, ..."; table.Rows.Add(row1); DataRow row2 = table.NewRow(); row2["Name"] = "Nagy Ede"; row2["Address"] = "Baja, ..."; table.Rows.Add(row2);

Ezutn a WriteXml metdus segtsgvel kirhatjuk a tbla adatait:


table.WriteXml("persons.xml");

A file:
<?xml version="1.0" standalone="yes"?> <DocumentElement> <Persons> <ID>0</ID> <Name>Kovcs Jnos</Name> <Address>Budapest, ...</Address> </Persons> <Persons> <ID>1</ID> <Name>Nagy Ede</Name> <Address>Baja, ...</Address> </Persons> </DocumentElement>

Ahogy azt ltjuk, ez a megolds csak a tbla adatait rja ki. Ha szeretnnk visszaolvasni a tblt, akkor annak a szerkezett, a smjt is el kell trolnunk:
table.WriteXml("persons.xml", XmlWriteMode.WriteSchema);

Az eredmny sokkal sszetettebb lesz, hiszen az egyes oszlopok felptst is kirjuk:


<?xml version="1.0" standalone="yes"?> <NewDataSet> <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemasmicrosoft-com:xml-msdata"> <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="Persons" msdata:UseCurrentLocale="true">

350
<xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="Persons"> <xs:complexType> <xs:sequence> <xs:element name="ID" msdata:AutoIncrement="true" type="xs:int" /> <xs:element name="Name" type="xs:string" minOccurs="0" /> <xs:element name="Address" type="xs:string" minOccurs="0" /> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> <xs:unique name="Constraint1" msdata:PrimaryKey="true"> <xs:selector xpath=".//Persons" /> <xs:field xpath="ID" /> </xs:unique> </xs:element> </xs:schema> <Persons> <ID>0</ID> <Name>Kovcs Jnos</Name> <Address>Budapest, ...</Address> </Persons> <Persons> <ID>1</ID> <Name>Nagy Ede</Name> <Address>Baja, ...</Address> </Persons> </NewDataSet>

Ezt a file t mr tudjuk hasznlni:


DataTable backupTable = new DataTable(); backupTable.ReadXml("persons.xml");

Amennyiben a tbla adataira nincs szksg, csak a smra, akkor hasznljuk a WriteXmlSchema/ReadXmlSchema metdusokat. DataSet ek esetben gyakorlatilag pontosan ugyangy jrunk el, ahogy azt a tblk esetben mr lttuk. Figyelem! Mg nincs vge: a kvetkez frissts 2009. prilis 12. n vrhat.

You might also like