Forrás: http://www.doksi.

hu

Reiter István

C#
2009, 0.91 verzió

Forrás: http://www.doksi.hu

2

Tartalomjegyzék
1. Bevezet …………………………………………………………………………….......8 1.1. A jegyzet jelölései…………………………………………………………………8 1.2. Jogi feltételek………………………………………………………………………8 2. Microsoft .NET Framework…………………………………………………………...9 2.1. A .NET platform…………………………………………………………………….9 2.1.1. MSIL/CIL……………………………………………………………………...9 2.1.2. Fordítás/futtatás…………………………………………………………….9 2.1.3. BCL. …………………………………………………………………………10 2.2. A C# programozási nyelv……………………………………………………….10 2.3. Alternatív megoldások…………………………………………………………10 2.3.1. SSCLI………………………………………………………………………10 2.3.2. Mono…………………………………………………………………………10 2.3.3. DotGNU……………………………………………………………………..10 3. Fejleszt i környezetek………………………………………………………………..12 3.1. Microsoft Visual Studio…………………………………………………………12 3.2. SharpDevelop…………………………………………………………………….13 3.3. MonoDevelop……………………………………………………………………..14 4. “Hello C#!”……………………………………………………………………………..16 4.1. A C# szintaktikája………………………………………………………………..17 4.1.1. Kulcsszavak……………………………………………………………….17 4.1.2. Megjegyzések……………………………………………………………..18 4.2. Névterek…………………………………………………………………………...18 5. Változók………………………………………………………………………………...20 5.1. Típusok…………………………………………………………………………….20 5.2. Lokális változók………………………………………………………………….21 5.3. Referencia- és értéktípusok……………………………………………………21 5.4. Boxing és Unboxing…………………………………………………………….22 5.5. Konstansok……………………………………………………………………….23 5.6. A felsorolt típus…………………………………………………………………..23 5.7. Null típusok………………………………………………………………………..24 6. Operátorok……………………………………………………………………………..25 6.1. Operátor precedencia…………………………………………………………...25 6.2. Értékadó operátor………………………………………………………………..25 6.3. Matematikai operátorok…………………………………………………………26 6.4. Relációs operátorok……………………………………………………………..26 6.5. Logikai/feltételes operátorok…………………………………………………..27 6.6. Bit operátorok…………………………………………………………………….30 6.7. Rövid forma……………………………………………………………………….32 6.8. Egyéb operátorok………………………………………………………………..33 7. Vezérlési szerkezetek………………………………………………………………...34 7.1. Szekvencia………………………………………………………………………...34 7.2. Elágazás……………………………………………………………………………34 7.3. Ciklus……………………………………………………………………………….36 7.3.1. Yield………………………………………………………………………….39 7.4. Gyakorló feladatok……………………………………………………………….39 8. Tömbök………………………………………………………………………………….41 8.1. Többdimenziós tömbök…………………………………………………………42

Forrás: http://www.doksi.hu

3 8.2. ArrayList…………………………………………………………………………...44 8.3. Gyakorló feladatok……………………………………………………………….46 9. Stringek…………………………………………………………………………………47 9.1. Metódusok…………………………………………………………………………47 9.2. StringBuilder………………………………………………………………………49 10. Típuskonverziók……………………………………………………………………….51 10.1. Ellen rzött konverziók…………………………………………………………51 10.2. Is és as…………………………………………………………………………….52 10.3. Karakterkonverziók……………………………………………………………..52 11. Gyakorló feladatok I………………………………………………………………...54 12. Objektum-orientált programozás – elmélet…………………………………….54 12.1. UML………………………………………………………………………………..56 12.2. Osztály…………………………………………………………………………….56 12.3. Adattag és metódus…………………………………………………………….56 12.4. Láthatóság………………………………………………………………………..57 12.5. Egységbezárás…………………………………………………………………..57 12.6. Örökl dés………………………………………………………………………...58 13. Osztályok……………………………………………………………………………..58 13.1. Konstruktorok……………………………………………………………….......59 13.2. Adattagok…………………………………………………………………………62 13.3. Láthatósági módosítók………………………………………………………...62 13.4. Parciális osztályok……………………………………………………………...62 13.5. Beágyazott osztályok…………………………………………………………..63 13.6. Objektum inicializálók………………………………………………………….64 13.7. Destruktorok……………………………………………………………………..64 14. Metódusok……………………………………………………………………………67 14.1. Paraméterek……………………………………………………………………...68 14.2. Parancssori paraméterek……………………………………………………...71 14.3. Kiterjesztett metódusok…….………………………………………………….71 14.3.1. Gyakorló feladatok……………………………………………………72 15. Statikus tagok………………………………………………………………………….73 15.1. Statikus adattagok………………………………………………………………73 15.2. Statikus metódusok…………………………………………………………….73 15.3. Statikus tulajdonságok………………………………………………………...74 15.4. Statikus konstruktor……………………………………………………………74 15.5. Statikus osztályok………………………………………………………………74 15.6. Gyakorló feladatok……………………………………………………………...75 16. Tulajdonságok………………………………………………………………………76 17. Indexel k……………………………………………………………………………..79 18. Gyakorló feladatok II……………………………………………………………….80 19. Örökl dés…………………………………………………………………………….81 19.1. Konstruktorok…………………………………………………………………...82 19.2. Polimorfizmus…………………………………………………………………...82 19.3. Virtuális metódusok…………………………………………………………….83 19.4. Lezárt osztályok…………………………………………………………………85 19.5. Absztrakt osztályok…………………………………………………………….86 20. Interfészek……………………………………………………………………………88 20.1. Explicit interfészimplementáció……………………………………………...90 20.2. Virtuális tagok……………………………………………………………………91 20.3. Gyakorlati példa 1……………………………………………………………….92

Forrás: http://www.doksi.hu

4 20.4. Gyakorlati példa 2……………………………………………………………….94 21. Operátor túlterhelés………………………………………………………………...96 21.1. Egyenl ség operátorok………………………………………………………..97 21.2. Relációs operátorok…………………………………………………………….98 21.3. Konverziós operátorok…………………………………………………………98 21.4. Kompatibilitás más nyelvekkel……………………………………………….99 21.5. Gyakorlati példa………………………………………………………………..100 22. Struktúrák…………………………………………………………………………...102 23. Kivételkezelés………………………………………………………………………103 23.1. Kivétel hierarchia………………………………………………………………104 23.2. Saját kivétel készítése………………………………………………………...105 23.3. Kivétel továbbadása…………………………………………………………..105 23.4. Finally blokk…………………………………………………………………….105 24. Generikusok………………………………………………………………………..107 24.1. Generikus metódusok………………………………………………………...107 24.2. Generikus osztályok…………………………………………………………..108 24.3. Generikus megszorítások……………………………………………………110 24.4. Örökl dés……………………………………………………………………….111 24.5. Statikus tagok……………………………………………………………….....111 24.6. Generikus gy jtemények…………………………………………………….112 24.7. Generikus interfészek………………………………………………………...113 25. Delegate –ek………………………………………………………………………..114 25.1. Többszörös delegate –ek…………………………………………………….115 25.2. Paraméter és visszatérési érték…………………………………………….116 25.3. Névtelen metódusok…………………………………………………………..117 26. Események………………………………………………………………………….118 26.1. Gyakorló feladatok…………………………………………………………….119 27. Lambda kifejezések……………………………………………………………….120 27.1. Generikus kifejezések………………………………………………………...120 27.2. Kifejezésfák……………………………………………………………………..121 27.3. Lambda kifejezések változóinak hatóköre………………………………..121 27.4. Eseménykezel k..……………………………………………………………..122 28. Attribútumok………………………………………………………………………..123 29. Az el fordító………………………………………………………………………..125 30. Unsafe kód………………………………………………………………………….126 30.1. Fix objektumok………………………………………………………………...127 31. Többszálú alkalmazások………………………………………………………...128 31.1. Application Domain –ek………………………………………………………130 31.2. Szálak……………………………………………………………………………131 31.3. Aszinkron delegate –ek………………………………………………………131 31.4. Szálak létrehozása…………………………………………………………….135 31.5. Foreground és background szálak………………………………………...136 31.6. Szinkronizáció………………………………………………………………….136 32. Reflection……………………………………………………………………………142 33. Állománykezelés…………………………………………………………………..144 33.1. Olvasás/írás file –ból/file –ba………………………………………………..144 33.2. Könyvtárstruktúra kezelése…………………………………………………147 33.3. In – memory streamek………………………………………………………..149 34. Grafikus felület alkalmazások készítése…………………………………….151 34.1. Windows Forms – Bevezet ..……………………………………………….151

Forrás: http://www.doksi.hu

5 34.2. Windows Presentation Foundation - Bevezet .………………………....153 35. Windows Forms…………………………………………………………………...156 36. Windows Forms – Vezérl 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. Általános párbeszédablakok………………………………………………186 36.20. TabControl…………………………………………………………………….192 36.21. ContextMenuStrip……………………………………………………………192 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 vezérl k létrehozása…………………………………....198 38.1. Származtatás…………………………………………………………………...198 38.2. UserControl -ok………………………………………………………………..200 39. Rajzolás: GDI+……………………………………………………………………..203 39.1. Képek kezelése………………………………………………………………...205 40. Drag and Drop……………………………………………………………………...206 41. Windows Presentation Foundation…………………………………………….208 41.1. A WPF Architektúra…………………………………………………………...209 41.2. A WPF osztályhierarchiája…………………………………………………...209 41.3. XAML…………………………………………………………………………….210 41.3.1. Fordítás……………………………………………………………………211 41.3.2. Logikai- és Vizuális fa…………………………………………………..211 41.3.3. Felépítés…………………………………………………………………...212 42. WPF – Események és tulajdonságok……………………………………………215 43. WPF – Vezérl k elrendezése………………………………………………………221 43.1. StackPanel……………………………………………………………………...221 43.2. WrapPanel………………………………………………………………………223 43.3. DockPanel………………………………………………………………………224

Forrás: http://www.doksi.hu

6 43.4. Canvas…………………………………………………………………………..225 43.5. UniformGrid…………………………………………………………………….226 43.6. Grid……………………………………………………………………………….227 44. WPF – Vezérl k………………………………………………………………………231 44.1. Megjelenés és szövegformázás…………………………………………….231 44.2. Vezérl 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 források…………………………………………………………………………...256 45.1. Assembly resource……………………………………………………………256 45.2. Object resource………………………………………………………………..256 46. Stílusok………………………………………………………………………………..260 46.1. Triggerek………………………………………………………………………262 46.1.1. Trigger…………...………………………………………………………262 46.1.2. MultiTrigger……………………………………………………………..263 46.1.3. EventTrigger……………………………………………………………263 47. Sablonok………………………………………………………………………………265 48. Commanding………………………………………………………………………....267 49. Animációk és transzformációk……………………………………………………272 49.1. Transzformációk……………………………………………………………….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. Animációk……………………………………………………………………….285 49.2.1. From-to-by animációk………………………………………………...286 49.2.2. Key Frame animációk…………………………………………………290 49.2.3. Spline animációk………………………………………………………291 49.2.4. Animációk és transzformációk……………………………………...292 50. Vezérl k készítése………………………………………………………………….294 50.1. UserControl…………………………………………………………………….295 51. ADO.NET…………………………………………………………………………….305 51.1. MS SQL Server 2005/2008 Express………………………………………...305 52. SQL Alapok…………………………………………………………………………...307 52.1. Adatbázis létrehozása………………………………………………………...307 52.2. Táblák létrehozása…………………………………………………………….308 52.3. Adatok beszúrása táblába…………………………………………………...309 52.4. Oszlop törlése táblából……………………………………………………….309 52.5. Kiválasztás……………………………………………………………………...309 52.6. Sz rés…………………………………………………………………………...310 52.7. Rendezés………………………………………………………………………..311

Forrás: http://www.doksi.hu

7 52.8. Adatok módosítása……………………………………………………………312 52.9. Relációk…………………………………………………………………………312 52.10. Join…………………………………………………………………………….314 53. Kapcsolódás az adatbázishoz…………………………………………………….314 53.1. Kapcsolódás Access adatbázishoz………………………………………..315 53.2. Kapcsolódás Oracle adatbázishoz………………………………………....316 54. Kapcsolat néküli réteg……………………………………………………………...317 54.1. DataTable………………………………………………………………………..317 54.2. DataGridView…………………………………………………………………...321 54.3. DataView………………………………………………………………………...329 54.4. DataSet…………………………………………………………………………..330 54.4.1. Relációk táblák között………………………………………………331 54.4.2. Típusos DataSet -ek………………………………………………...333 55. Adatbázis adatainak lekérdezése és módosítása……………………………..335 55.1. Connected és Disconnected közti kapcsolat.. …………………………..336 56. XML…………………………………………………………………………………….342 56.1. XML file –ok kezelése…………………………………………………………343 56.2. XML DOM………………………………………………………………………..346 56.3. XML szerializáció………………………………………………………………347 56.4. XML és a kapcsolat nélküli réteg. ………………………………………….349

2 Jogi feltételek A jegyzet teljes tartalma a Creative Commons Nevezd meg!-Ne add el! 2. kerettel Megjegyzés: fehér alap. ha rátalált a válaszra. .0 és 3.html http://devportal.1 A jegyzet jelölései Forráskód: szürke alapon. Szabadon módosítható és terjeszthet .doksi. 1. Néhány fejezet feltételez olyan tudást amely alapját egy kés bbi fejezet képezi. javaslatot illetve hibajavítást szívesen várok a reiteristvan@gmail.hu 8 1. ha valamit nem ért. hogy megismertesse az olvasóval ezt a nagyszer technológiát.5 Magyarország liszensze alá tartozik. mindennem értékesítési kísérlet tiltott és a szerz beleegyezése nélkül történik.0 verziójával is foglalkozik.hu/reiter_i/sharp.hu/groups/fejlesztk/media/p/1122. egyszer en olvasson tovább és térjen vissza a kérdéses fejezethez.Forrás: http://www.elte. Bevezet Napjainkban egyre nagyobb teret nyer a . Bármilyen kérést. A jegyzet ingyenes. Ez a jegyzet abból a célból született. ezért ne essen kétségbe a kedves olvasó.inf. kerettel 1.NET Framework és egyik f nyelve a C#. ha nincs külön feltüntetve akkor az adott nyelvi elem gond nélkül használható a korábbi verzióban. A mindenkori legfrissebb változat letölthet a következ oldalakról: http://people. a forrás feltüntetésével.com email címre. Április 12.aspx A következ frissítés id pontja: 2009. A jegyzet a C# 2.

nevezik CLR -nek (Common Language Runtime) is. . metódusai.NET Framework A kilencvenes évek közepén a Sun MicroSystems kiadta a Java platform els nyilvános változatát.2 Fordítás/futtatás A natív programok ún. stb.1 A . A . Bár gyorsak. 2. az egymással való interakciót.1.NET forráskódokból egy CIL nyelv futtatható állomány keletkezik.NET nem egy programozási nyelv. a memóriában való megjelenést.NET világában az MSIL illetve a szabványosítás után CIL (Microsoft/Common IL) – különbség csak az elnevezésben van. Részben a piac visszaszerzésének érdekében a Microsoft a kilencvenes évek végén elindította a Next Generation Windows Services fed nev projektet. Az addigi programnyelvek/platformok különböz okokból nem tudták felvenni a Java –val a versenyt.doksi. A . hogy a kényelmesebb és sokoldalúbb Java –t választja.NET Framework –nek szóló utasításokat tartalmaz. nem beszélve a számtalan hobbifejlesztésr l.NET. A CLS (Common Language Specification) a CLI kompatibilis nyelvekkel kapcsolatos elvárásokat tartalmazza. Assembly (vagy szerelvény) keletkezik.1 MSIL/CIL A “hagyományos” programnyelveken – mint pl. írja le. amit a processzor már képes kezelni.NET platform Maga a . Gyakorlatilag bármilyen programozási nyelvnek lehet .NET platform a Microsoft. a fordító egy köztes nyelvre (Intermediate Language) fordítja le a forráskódot. JIT (just–in–time) fordító veszi kezelésbe és lefordítja ket gépi kódra. Microsoft . míg a . amelyb l aztán megszületett a . Ez a kód a feltelepített .megírt programok ún.hu 9 2.Forrás: http://www. a Hewlett Packard. natív kódra fordulnak le. Metadata) amelyeket a futtató környezet fel tud használni a futtatáshoz (az osztályok neve. az Intel és mások közrem ködésével megfogalmazott CLI (Common Language Infrastructure) egy implementációja. A CLI egy szabályrendszer. stb…). vagyis a processzor számára – kis túlzással – azonnal értelmezhet ek. Jelenleg kb. a C++ . így számtalan fejleszt döntött úgy. 50 nyelvnek létezik hivatalosan . gépi kódra fordulnak le. Ez a nyelv a . amely maga is több részre oszlik: A CTS (Common Type System) az adatok kezelését. de rengeteg hibalehet ség rejt zik a felügyelet nélküli (unmanaged) végrehajtásban. Ez tartalmazza a felhasznált illetve megvalósított típusok adatait (ez az ún. 2. 2. Ezeknek a nyelveknek az el nye a hátránya is egyben. Amikor “el ször” lefordítjuk a programunkat akkor egy ún. Egy Assembly egy vagy több file – ból is állhat.NET megfelel je.NET implementációja.NET (akárcsak a Java) más úton jár. hanem egy környezet.1. Amikor futtatjuk ezeket az állományokat el ször az ún. A VES (Virtual Execution System) a futási környezetet specifikálja.

2. WCF. A tervezésénél a lehet legnagyobb produktivitás elérését tartották szem el tt. adatbázis kezelés. FreeBSD és Mac OSX rendszereken fut.2 Mono A Mono projekt szül atyja Miguel de Icaza 2000 –ben kezdte meg a fejlesztést és egy évvel kés bb mutatta be ez els kezdetleges C# fordítót. de napjainkban a legnagyobb hatékonyságot a Microsoft implementációja biztosítja. 2. A nyelv elméletileg platformfüggetlen (létezik Linux és Mac fordító is). ezen ismeretek birtokában pedig több független csapat vagy cég is létrehozta a saját CLI implementációját.NET 2.Forrás: http://www. 1999 – ben Anders Hejlsberg vezetésével kezdték meg a fejlesztését. Az SSCLI Windows. A C# tisztán objektumorientált.3.doksi. keresztplatformos változata a .NET Framework jelen pillanatban csak és kizárólag Microsoft Windows operációs rendszerek alatt elérhet .3. általános felhasználású nyelv. 2. Az összes többi könyvtár (ADO. ezért a liszensze engedélyez mindenfajta módosítást. Ett l függetlenül a forráskód és a hozzá tartozó dokumentációk rendelkezésre állnak. adatszerkezetek. jelen pillanatban valamivel a . Ezt a célt nehezíti.2 A C# programozási nyelv A C# (ejtsd: Szí-sárp) a Visual Basic mellett a .3 BCL A .microsoft. ami az alapvet feladatok (file olvasás/ írás. stb…) elvégzéséhez szükséges eszközöket tartalmazza.NET Framework telepítésével a számítógépre kerül – többek közt – a BCL (Base Class Library). Az SSCLI –t kimondottan tanulási célra készítette a Microsoft. Az SSCLI project jelen pillanatban megsz nni – vagy legalábbis id legesen leállni – látszik. hogy a Microsoft id közben számos a specifikációban nem szerepl változtatást végzett a keretrendszeren.NET f programozási nyelve.1.NET Frameworknek (tehát nem az eredeti lebutított változata).com/downloads/details.0 mögött jár. bár eddig még nem sikerült teljes mértékben reprodukálni az eredetit.NET. típusbiztos. letölthet ek a következ webhelyr l: http://www.3 Alternatív megoldások A Microsoft .aspx?FamilyId=8C09FD61-3F264555-AE17-3121B4F51D4D&displaylang=en 2. A Ximian (amelyet Icaza és Nat Friedman alapított) felkarolta az ötletet és 2001 júliusában hivatalosan . egyedül a piaci értékesítést tiltja meg.1 SSCLI Az SSCLI (Shared Source Common Language Infrastructure) vagy korábbi nevén Rotor a Microsoft által fejlesztett nyílt forrású. Ugyanakkor a szabványosítás után a CLI specifikáció nyilvános és bárki számára elérhet lett. stb…) ezekre a könyvtárakra épül.hu 10 2. Ez a rendszer nem szolgáltatja az eredeti keretrendszer teljes funkcionalitását.

NET 2. a project kódneve Olive (http://www. A DotGNU saját CLI megvalósításának a Portable . 2003 –ban a Novell felvásárolta a Ximian –t. BSD. a szó ugyanis spanyolul majmot jelent. A DotGNU hivatalos oldala: http://www.NET jöv beli ellenfele illetve keresztplatformos társa.1) amely 2008 októberében látott napvilágot.mono-project. A Mono hivatalos oldala: http://www.0 –t is támogató rendszer. Ez a projekt – szemben a Mono –val – nem a Microsoft BCL –el való kompatibilitást helyezi el térbe. van LINQ to XML illetve LINQ to Objects). amelynek célja egy ingyenes és nyílt alternatívát nyújtani a Microsoft implementáció helyett. A legutolsó verzió (2.hu 11 is elkezd dött a Mono fejlesztése. A Mono Windows. illetve a C# 3. hanem az eredeti szabvány pontos és tökéletes implementációjának a létrehozását. UNIX. Ugyanez a verzió tartalmazza a Novell SilverLight alternatíváját. mint a Microsoft .0 verzió már Novell termékként készült el.gnu. Ez a változat már a teljes .mono-project. Napjainkban a Mono mutatja a legígéretesebb fejl dést. amit MoonLight –ra kereszteltek.doksi. A jegyzet írásának idején a projekt leállni látszik.NET nevet adta. ennek fejlesztésében a Novell segítségére van a Microsoft is. Linux. Ezzel a változattal párhuzamosan fejlesztés alatt áll a . A Mono saját fejleszt eszközzel is rendelkezik a MonoDeveloppal.3. az 1.3 DotGNU A DotGNU a GNU projekt része.NET 3.0 képességeit is részlegesen támogatja (pl.0.com/Main_Page 2. Mac OSX és Solaris rendszereken elérhet . A Mono emblémája egy majmot ábrázol.Forrás: http://www.org/software/dotgnu/ .com/Olive).0 –át magába foglalja. egy évvel kés bb.

doksi. mint nagyobb testvére. ez a projektünk file –jait tartalmazza. Ez a termékvonal némileg kevesebb funkcionalitással rendelkezik. A Visual Studio termékcsalád része az Express sorozat. amely teljesen ingyenes mindenki számára. Végül a legnagyobb területet a kódszerkeszt ablak foglalja el. . ugyanakkor kiválóan alkalmas hobbifejlesztésre (de akár “rendes” kereskedelmi program készítésére is). azonban egy jó fejleszt eszköz hiánya jelent sen meggátolja a gyors és produktív programozást.Forrás: http://www.NET programozásához. A jegyzet a grafikus felület alkalmazások készítésénél Visual Studio –t használ majd. szélesség. az egyes vezérl elemek tulajdonságait (név. egy Express eszközzel készített program el is adható. magassság. Alatta a Properties ablak.1 Microsoft Visual Studio Valószín leg a legelterjedtebb IDE a . hogy bármiféle liszenszet vásárolnánk. azaz Integrált Fejleszt i Környezetet vizsgáluk meg. Ugyanakkor a jegyzet els felében egy hagyományos szövegszerkeszt vel és a parancssorral is lehet gond nélkül haladni. Természetesen ezek nélkül is lehetséges fejleszteni. stb…) állíthatjuk be. ez f ként a grafikus designer ablaknál használható. A képen a Visual Studio 2008 felülete látható: Jobb oldalon fölül a Solution Explorer látható. 3. Fejleszt i környezetek Ebben a fejezetben néhány IDE –t (Integrated Development Environment).hu 12 3. ezért érdemes tájékozódni az egyes fejleszt eszközök különbségeir l. anélkül.

A legfrisebb verziót idén februárban adták ki. Miel tt lefordítjuk a programunkat.0 verzióját.NET fejlesztéshez. 3. válasszuk ki a New menüpontot azon belül pedig a Projekt –et (vagy használjuk a Ctrl+Shift+N billenty kombinációt). Lefordítani a programot a Build menüpont Build Solution parancsával (vagy az F6 gyorsbillenty vel) tudjuk. kiválaszthatjuk.hu 13 A Visual Studio tartalmazza az ún. Ez utóbbi automatikusan lefordítja a projektet. Ha nem az lenne megnyitva. el bbi tartalmaz néhány plusz információt a hibakeresés el segítéséhez (így egy kicsit lassabb). akkor kattintsunk a bal oldali listában a C# elemre.2 SharpDevelop A SharpDevelop egy nyílt forráskódú ingyenes alternatíva Windows operációs rendszer alatti . amely automatikusan felajánlja az elkezdett metódusok/változók/osztályok/stb. IronPython és F# programozási nyelveket is. ha a legutolsó fordítás után megváltozott valamelyik file. Boo. IntelliSense rendszert. futtatni pedig a Debug menü Start Debugging (vagy az F5 billenty lenyomásával illetve a kis zöld háromszögre való kattintással) parancsával. utóbbi a már kész programot jelenti.NET. . ez már támogatja a C# 3. hogy Debug vagy Release módban fordítsunk. valamint a Visual Basic. Grafikus felület alkalmazás készítéséhez válasszuk ki a Windows Application vagy a WPF Application sablont. nevének kiegészítését.Forrás: http://www.doksi. Konzolalkalmazás készítéséhez (a jegyzet els részében f ként ilyet írunk majd) a Console Application sablonra lesz szükségünk. Új projekt létrehozásához Kattintsunk a File menüre.

CIL. A MonoDevelop –ot akárcsak a Mono –t a Novell (is) fejleszti.mediafire. OpenSolaris és FreeBSD alatt futó változata is.hu 14 A SharpDevelop hivatalos oldala: http://www.com/OpenSource/SD/ Érdekesség. Java. így a következ webhelyr l ez is letölthet : http://www. Visual Basic. C++.sharpdevelop.NET. hogy a fejleszt k egy könyvet is írtak a fejlesztés menetér l és ez a könyv ingyenesen letölthet .com/?thnognidwyj A könyv letöltése nem számít illegális cselekménynek! 3. de annak megsz nése után átkerült az Apress kiadóhoz. de van már Windows. A MonoDevelop a következ programozási nyelveket támogatja: C#.Forrás: http://www. Mac OSX. de a mai változatok már kevéssé hasonlítanak az sre.3 MonoDevelop A MonoDevelop els sorban a Mono –hoz készült nyílt forráskódú és ingyenes fejleszt eszköz. Nemerle. Boo. A MD eredetileg a SharpDevelop –ból származott el. hogy az Apress nem kívánja nyílvánosságra hozni a könyvet. de természetesen az interneten bármit meg lehet találni. C.doksi. . Sajnos úgy t nik. Eredetileg Linux oprációs rendszer alá szánták. Eredetileg a Wrox kiadó oldaláról lehetett letölteni.

.hu 15 A MonoDevelop hivatalos oldala: http://www. a felhasználónak magának kell lefordítania a forráskódot.Forrás: http://www.monodevelop.com/ A MonoDevelop egyetlen nagy hátulüt vel rendelkezik: Windows operációs rendszer alá nem létezik hozzá telepít szoftver.doksi.

) és illesszük be az elérési utat. hogy egy programozási nyelv bevezet jeként ezt a programot mutatják be. “Hello C#!” A híres-hírhedt “Hello World!” program els ként Dennis Ritchie és Brian Kerningham “A C programozási nyelv” cím könyvében jelent meg. Console. mint kiírja a képerny re az üdvözletet.0 kell). írjunk egy pontosvessz t (. Ha van megnyitva konzol vagy PowerShell azt indítsuk újra. ami a programunk belépési pontja lesz. A változó értéke sorban navigáljunk el a végére. Most már fordíthatunk a csc filenev. Nyissuk meg vagy a v2.cs paranccsal (természetesen a szöveges file kiterjesztése „. Másoljuk ki a címsorból ezt a szép hosszú elérést. Az els sor megmondja a fordítónak. Minden egyes C# program a Main függvénnyel kezd dik. A rendszerváltozók listájából keressük ki a Path –t és kattintsunk a Szerkesztés gombra.txt”. vagy meg kell adnunk a fordítóprogram teljes elérési útját (ez a mi esetünkben elég hosszú) vagy a fordítóprogram könyvtárát fel kell venni a PATH környezeti változóba. és azóta szinte hagyomány. A “HelloWorld” osztályon belül definiálunk egy Main nev statikus függvényt. } } Miel tt lefordítjuk tegyünk pár lépést a parancssorból való fordítás el segítésére. ez a C# 3. El bbi kiírja a képerny re a paraméterét. Windows mappa. hogy csc. Mindent OK –zunk le és kész. Azt kell látnunk. hogy így is le tudjunk fordítani egy forrásfile –t. Ez utóbbihoz Vezérl pult/Rendszer -> Speciális fül/Környezeti változók. Semmi mást nem csinál. Vissza a Path –hoz. kezdet mappát (attól függ en. Ahhoz. hogy C# 2. class HelloWorld { static public void Main() { Console. . utána írjuk be.WriteLine("Hello C#!"). hogy Microsoft ® Visual C# 2008 Compiler Version 3. ezt mindenképpen létre kell hoznunk. hogy használja a System névteret.5… Itt az évszám és verzió változhat. ezért ennek megfelel en módosítsuk a forráskódot: using System.ReadKey().cs”). Most nyissuk meg a Sajátgépet. utóbbi vár egy billenty leütést. Mi itt most nem a világot.0 vagy 3.Forrás: http://www. mivel a C# teljesen objektumorientált ezért utasítást csakis osztályon belül adhatunk.0… vagy a v3. C meghajtó.doksi..0 üzenete.NET/Framework. így nevezzük át mivel a C# forráskódot tartalmazó file -ok kiterjesztése „. hanem a C# nyelvet üdvözöljük. Ezután létrehozunk egy osztályt. Végül meghívjuk a Console osztályban lév WriteLine(…) és ReadKey(…) függvényeket. azon belül Microsoft.hu 16 4.5.

blokkokkal jelöljük ki. Ha a fenti kódban Console.1 A C# szintaktikája Amikor egy programozási nyelv szintaktikájáról beszélünk. 4.és nagybet k különböz jelent séggel bírnak. ezért nem fog lefordulni a program: int int. . aminek az “int” nevet akarjuk adni. Ezeket az azonosítókat a saját meghatározott jelentésükön kív l nem lehet másra használni.1 Kulcsszavak Szinte minden programnyelv definiál kulcsszavakat. A C# C-stílusú szintaxissal rendelkezik (azaz a C programozási nyelv szintaxisához hasonlít). 4.) áll . stb. a jegyzet kés bbi fejezeteiben mindenre fény derül majd.doksi. Az “int” egy beépített típus neve is. A C# 77 darab kulcsszót 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 .) ún.WriteLine(…) helyett console. Vegyünk például egy változót. azaz a “program” és “Program” azonosítók különböznek. azaz kulcsszó. }) segítségével. Ez azért fontos.A kis. a kapcsos zárójelek ({. ez három fontos kitételt von maga után: .hu 17 Ebben a bekezdésben szerepel néhány (sok) kifejezés ami ismeretlen lehet.Forrás: http://www.A program egységeit (osztályok. amelyek speciális jelent séggel bírnak a fordító számára.Az egyes utasítások végén pontosvessz (. mert az egyes fordítóprogramok az ezekkel a szabályokkal létrehozott kódot tudják értelmezni. amelyek megszabják a forráskód felépítését. //hiba A legtöbb fejleszt eszköz megszínezi a kulcsszavakat (is). ellenkez esetben a fordító hibát jelez.writeline(…) –t írnánk a program nem fordulna le. metódusok. akkor azokra a szabályokra gondolunk. ezért könny elkerülni a fenti hibát.1.

amelyeket a nyelv nem tart fenn speciális használatra. Ezzel egyrészt üzeneteket hagyhatunk (pl. osztályok létrehozásánál: add ascending by descending equals from get global group in into join let on orderby partial remove var select where set yield value Néhányuk a környezett l függ en más-más jelentéssel is bírhat. hogy a nevek ismétl djenek. másrészt a kommentek segítségével dokumentációt tudunk generálni. a megfelel fejezet b vebb információt ad majd ezekr l az esetekr l. ami szintén az els célt szolgálja csak éppen élvezhet bb formában.2 Megjegyzések A forráskódba megjegyzéseket tehetünk. class HelloWorld { static public void Main() { Console. de különleges jelentéssel bírnak. azonosítót tartalmaznak. /* Ez egy többsoros komment */ } } Az egysoros komment a saját sora legvégéig tart.doksi.hu 18 Ezeken kív l létezik még 23 darab azonosító.1. metódusok. 4. Ekkor egyrészt nehéz eligazodni köztük. Ilyen nagyságrenddel elkerülhetetlen. egy metódus leírása) magunknak.WriteLine("Hello C#"). // Ez egy egysoros komment Console. míg a többsoros a két “/*” –on belül érvényes.Forrás: http://www. 4.ReadKey(). tulajdonképpen a fordítóprogram els lépése. A kommenteket a fordító nem veszi figyelembe. Amennyiben lehetséges kerüljük a használatukat “hagyományos” változók. vagy a többi fejleszt nek. hogy a forráskódból eltávolít minden megjegyzést.NET Framework osztálykönyvtárai szerény becslés szerint is legalább tízezer nevet.2 Névterek A . Megjegyzéseket a következ képpen hagyhatunk: using System. másrészt a fordító is . Utóbiakat nem lehet egymásba ágyazni: /* /* */ */ Ez a “kód” nem fordul le.

Valami A jegyzet els felében f leg a System névteret fogjuk használni. a namespace kulcsszóval: namespace MyNameSpace { } Ezután a névterre vagy a program elején a using –al. Egy névtér tulajdonképpen egy virtuális doboz. ha valamilyen kifejez nev névtértben vannak (System. Ennek a problémának a kiküszöbölésére hozták létre a névterek fogalmát. //vagy MyNameSpace. stb… vannak.Data).doksi. Nyílván könyebb megtalálni az adatbáziskezeléshez szükséges osztályokat. vagy az azonosító elé írt teljes eléréssel hivatkozhatunk: using MyNameSpace. metódusok.hu 19 megzavarodhat. amelyben a logikailag összefügg osztályok. . Névteret magunk is definiálhatunk. ahol más is kell ott jelezni fogom.Forrás: http://www.

Boolean 1 System.Int32 System..Single System.. A változók a memória egy(vagy több) cellájára hivatkozó leírók.32767 El jel nélküli 16 bites egész szám (0. mellettük ott a .4294967295) Egyszeres pontosságú lebeg pontos szám Kétszeres pontosság lebeg pontos szám Fix pontosságú 28+1 jegy szám El jeles 64 bites egész szám El jel nélküli 64 bites egész szám Unicode karakterek szekvenciája Minden más típus se A forráskódban teljesen mindegy. A következ táblázat a C# beépített típusait tartalmazza.NET megfelel jük.Uint64 System. A típus határozza meg.Decimal System.. Ezeket a tárolókat változóknak nevezzük.SByte System.. a többi karakter szám is. hogy a “rendes” vagy a .String System.Int16 System. .NET típus Méret (byte) System. 2147483647). El jel nélüli 32 bites egész szám (0.doksi.Uint16 System.255) Egy Unicode karakter Logikai típus. Lehet leg kerüljük az ékezetes karakterek használatát..Int64 System..1 Típusok A C# er sen (statikusan) típusos nyelv.Byte 1 System.65535) El jeles 32 bites egész szám (– 2147483647. akkor szükség lehet tárolókra. A változónév els karaktere csak bet vagy alulvonás jel (_) lehet. Változók Amikor programot írunk. ami azt jelenti.Double System. hogy egy változó milyen értékeket tartalmazhat illetve mekkora helyet foglal a memóriában. a méretük és egy rövid leírás: C# típus byte char bool sbyte short ushort int uint float double decimal long ulong string object .Forrás: http://www. értéke igaz(1) vagy hamis(0) El jeles 8 bites egész szám (-128. 5.Uint32 System. Egy változót a következ módon hozhatunk létre C# nyelven: Típus változónév.NET néven hivatkozunk egy típusra.hu 20 5. ahová az adatainkat ideiglenesen eltároljuk.Char 1 System.Object 1 2 2 4 4 4 8 8 8 8 NA NA Leírás El jel nélküli 8 bites egész szám (0.127) El jeles 16 bites egész szám (32768. hogy minden egyes változó típusának ismertnek kell lennie fordítási id ben.

de a megfelel típuskonverziók végrehajthatóak. int x = 10. Console.és referenciatípusokra. az úgy fog viselkedni mint az ekvivalens típus. // int típusú változó z = "string". A halom nem adatszerkezet. benne a kiírandó szöveg string message = "Hello C#". hogy értéket rendeltünk a változóhoz (ezt azonnal meg kell tennünk). Ezt az akciót a var szóval kivitelezhetjük. kivenni pedig csak a legfels elemet tudjuk. // fordítási hiba var w. pl.ReadKey(). Ez természetesen nem jelenti azt. az egyik a verem (stack) a másik a halom (heap). és ezen belül szétoszlik érték. A fenti példában a message egy lokális változó. 5. ha össze akarunk adni két számot akkor a CLR lerakja mindkett t a verembe és meghívja a . mint egy típustalan környezetet.WriteLine(message). mivel nehezen olvashatóvá teszi a forráskódot. //fordítási hiba Néhány speciális esett l eltekintve a var használata nem ajánlott. A ilyen változók típusa nem változtatható meg.Object nev típusból származik.és értéktípusok A . vagyis az az elem amit utóljára berakunk az lesz a tetején. akkor a program nem fordulna le. abban a pillanatban. ha egy másik függvényb l vagy osztályból próbálnánk meg elérni. vagyis a program többi részéb l nem látható (úgy is mondhatjuk. A két leggyakoribb felhasználási területe a névtelen típusok és a lekérdezés-kifejezések.hu 21 Alakítsuk át a “Hello C#” programot úgy.Forrás: http://www. Console. class HelloWorld { static public void Main() { //string típusu változó deklarációja.NET minden típusa a System.3 Referencia.0 lehet vé teszi. hogy a változó hatóköre a blokkra terjed ki). amit a CLR tetszés szerint használhat. // int típusú változó var z = 10. Minden m velet a vermet használja. hogy úgy használhatjuk a nyelvet. LIFO (last-in-first-out) adattár. 5. hogy egy metódus hatókörében deklarált változó típusának meghatározását a fordítóra bízzuk. hanem a program által lefoglalt nyers memória. A verem egy ún. } } A C# 3. A CLR két helyre tud adatokat pakolni. hogy a kiírandó szöveget egy változóba tesszük: using System.doksi. A kett közti különbség leginkább a memóriában való elhelyezkedésben jelenik meg.2 Lokális változók Egy blokkon belül deklarált változó lokális lesz a blokkra nézve.

Ez alól a szabály alól kivételt képez a string típus. x+y A verem: |11| |10| -. de elárulom. Mivel minden típus (érték és referencia is) a System.doksi. 5. ezért az értékadáskor létrejön a memóriában (a halomban. mint egy referenciatípus. . nem a veremben) egy referenciatípus karakterisztikájával rendelkez értéktípus. hogy úgy viselkedjen.WriteLine("X erteke: {0}". amely megengedi egy értéktípusnak. hogy értékr l vagy referenciáról van szó. ezért hatékonyabb a halomban eltárolni. Ezzel szemben egy referenciatípus sokkal nagyobb szokott lenni és a memóriában való megjelenése is összetettebb. ahol egyébként nem lehetne. object boxObject = x. valamilyen módon mindkett t be kell tudnunk rakni a verembe.4 Boxing és unboxing Boxing –nak (bedobozolás) azt a folyamatot nevezzük. ezért kényelmesen kezelhetjük a vermen keresztül.hu 22 megfelel utasítást ami kiveszi a verem legfels végeredményt pedig visszateszi a verembe: int x = 10.|21| két elemét összeadja ket. míg referenciatípust a new operátor segítségével hozunk létre. De miért van ez így? Általában egy értéktípus csak egy-négy bytenyi helyet foglal el. Most nem volt szükség a CLR –re. //bedobozolva Console. Az értéktípusok teljes valójukban a veremben vannak. hogy olyan helyen is használhatunk értéktípust. míg a referenciák a halomban jönnek létre és a verembe egy rájuk hivatkozó referencia kerül. hogy függetlenül attól. addig egy értéktípusnál erre nincs feltétlenül szükség.Forrás: http://www. hogyan tudunk “kézzel” dobozolni: int x = 10.WriteLine("X erteke: {0}". A következ forráskód megmutatja.összeadás m velet-. Ennek el nye. Csakhogy az object maga is referenciatípus.Object típusból származik. ezért egy értéktípust értékül adhatunk egy object típusnak. Els re semmi különös. int y = 11. A forráskódban jól megkülönböztethet a kett . hogy a Console. Vegyük a következ példát: int x = 10. Console. x).WriteLine() metódus ebben a formájában második paraméteré l egy object típusú változót vár. a Ez azt is jelenti egyben. boxObject). Vagyis ebben a pillanatban a CLR automatikusan bedobozolja az x változót.

hu 23 Az unboxing (vagy kidobozolás) a boxing ellentéte. //Hiba A konstans változóknak adott értéket/kifejezést fordítási id ben ki kell tudnia értékelni a fordítónak. Ha mást nem adunk meg. Ezután így használhatjuk: Animal a = Animal. int x = int. mégpedig a deklarációnál. A konstansoknak egyetlen egyszer adhatunk (és ekkor kell is adnunk) értéket.5 Konstansok A const típusmódosító segítségével egy változót konstanssá tehetünk. 5.WriteLine("Adjon meg egy szamot: "). Dog.Tiger. Wolf }. Felsorolt típust az enum kulcsszó segítségével deklarálunk: enum Animal { Cat.doksi. //bedobozolva int y = (int)obj. Console. vagyis a bedobozolt értéktípusunkból kivarázsoljuk az eredeti értékét: int x = 0. object obj = x. x nem ismert fordítási id ben 5.6 A felsorolt típus A felsorolt típus olyan adatszerkezet. if(a == Animal.Parse(Console. const int x. Tiger. így visszanyertük az eredeti értéket...").Tiger) //Ha a egy tigris { Console. amely meghatározott értékek névvel ellátott halmazát képviseli. //Ez jó x = 11. //kidobozolva Az object típuson egy explicit típuskonverziót hajtottunk végre (err l hamarosan). //Hiba const int x = 10.WriteLine("a egy tigris.ReadLine()). . Bármely kés bbi próbálkozás fordítási hibát okoz. akkor az alapérelmezés szerint a számozás nullától kezd dik és deklaráció szerinti sorrendben (értsd: balról jobbra) eggyel növekszik. } A felsorolás minden tagjának megfeleltethetünk egy egész értéket.Forrás: http://www. const y = x. //Ez nem jó.

Wolf } Azok a nevek amelyekhez nem rendeltünk értéket explicit módon az ket megel z név értékét l számítva kapják meg azt. int? x = y. 5. Dog = 3. //ez már m ködik Egy nullable típusra való konverzió implicit (külön kérés nélkül) megy végbe. int x = (int)a. Tiger. Ahhoz. Tiger. még az inicializálás el tt is. //ez le sem fordul Ez azért van. Így a a fenti példában Tiger értéke négy lesz. hogy meg tudjuk állapítani. //x == 3 Magunk is megadhatjuk az értékeket: enum Animal { Cat = 1. Dog. amit a “rendes” típus után írt kérd jellel (?) jelzünk: int? i = null. x = (int)a. //implicit konverzió y = (int)x. mert a referenciatípusok rengeteg plusz információt tartalmaznak.Forrás: http://www. //x == 0 a = Animal.hu 24 enum Animal { Cat.7 Null típusok A referenciatípusok az inicializálás el tt nullértéket vesznek fel. Wolf } Animal a = Animal. illetve mi magunk is jelölhetjük ket “beállítatlannak”: class RefType { } RefType rt = null. //explicit konverzió . Ugyanez az értéktípusoknál már nem m ködik: int vt = null.doksi. hogy egy értéktípus még nem inicializált egy speciális típust a nullable típust kell használnunk.Wolf.Cat. míg az ellenkez irányba explicit konverzióra lesz szükségünk (vagyis ezt tudatnunk kell a fordítóval): int y = 10. míg az értéktípusok memóriában elfoglalt helye a deklaráció pillanatában automatikusan feltölt dik nulla értékekkel.

mégpedig azt. ez a végleges programra semmilyen hatással sincs. Ugyanígy a második pontban i és * (vagyis x+y) az operandusok az értékadás m velet (‘=’) pedig az operátor. de nem az összeset. Két kifejezés is van az utasításban: 1. egy speciális részterület kapcsolódik hozzájuk. A legels helyen szerepelnek pl. Egy operátornak nem csak két operandusa lehet. els ként a tömböknél találkozhat vele az olvasó. a kifejezések pedig operátorokból és operandusokból illetve ezek kombinációjából jönnek létre: i = x + y. Ebben a példában egy utasítást adunk. utolsón pedig az értékadó operátor. Ennek oka. hogy egy változónak értéket adunk.(unáris) és háromoperandusu (ternáris) operátorokkal is rendelkezik. .Forrás: http://www. Pl. A jó megoldás az elöbbi. Operátorok Amikor programozunk utasításokat adunk a számítógépnek. A következ néhány fejezetben átvesszünk néhány operátort. hogy i –nek értékéül adjuk x és y összegét. x + y –> ezt az értéket jelöljük * -al 2. ezért ezeket az operátorokat majd a megfelel helyen ismerjük meg. a zárójeles kifejezések.doksi. az indexel operátor most kimarad. hiszen az eredmény ett l is függ.2 Értékadó operátor Az egyik legáltalánosabb m velet amit elvégezhetünk az az. vagy 60. A C# nyelv egy. Ha bizonytalanok vagyunk a végrehajtás sorrendjében mindig használjunk zárójeleket. (Pl.) 6. az operátorok végrehajtásának sorrendjében a szorzás és osztás el nyt élvez. A C# nyelvben ezt az egyenl ségjel segítségével tehetjük meg: int x = 10.1 Operátor precedencia Amikor több operátor is szerepel egy kifejezésben a fordítónak muszáj valamilyen sorrendet (precedenciát) fölállítani köztük. hogy bizonyos operátorok önmagukban nem hordoznak jelentést. a ‘+’ jel pedig az összeadás m velet operátora.: 10 * 5 + 1 Sorrendt l függ en az eredmény lehet 51. Ezek az utasítások kifejezésekb l állnak.hu 25 6. i = * -> i –nek értékül adjuk a * -ot Az els esetben x és y operandusok. A fenti kifejezés tehát így nézzen ki: (10 * 5) + 1 6.

ReadKey(). //Kivonás: z = 10 . //Összeadás: z = 10 + 3 Console.WriteLine(z). //y értéke most 10 6. // Az osztás maradékát írja ki: 1 Console.3 Console. public class Operators { static public void Main() { int x = 10. Természetesen nem kötelez a deklarációnál (amikor tájékoztatjuk a fordítót.WriteLine(z). A numerikus típusokon értelmezve van egy rendezés reláció: . Ett l függetlenül a legtöbb esetben ajánlott akkor értéket adni egy változónak amikor deklaráljuk.WriteLine(z). // 7 z = x * y. x = 10. illetve ha létezik a megfelel konverzió (a típuskonverziókkal egy kés bbi fejezet foglalkozik). //30 z = x / y. //Maradék nélküli osztás: z = 10 / 3.hu 26 Létrehoztunk egy int típusú változót.WriteLine(z).Forrás: http://www.y. ezt el lehet halasztani: int x. hogy van egy valamilyen típusú. int y = x.4 Relációs operátorok A relációs operátorok segítségével egy adott értékészlet elemei közti viszonyt tudjuk lekérdezni. hogy a változó milyen értéket kapjon). ha a két változó azonos típusú.3 Matematikai operátorok A következ példában a matematikai operátorok használatát mutatjuk meg: using System.WriteLine(z). int x = 10. int z = x + y. //Vár egy billenty leütést } } 6. Egy változónak nem csak konstans értéket. //Maradékos osztás: z = 10 % 3 Console. elneveztük x –nek és kezd értékének 10 –et adtunk. int y = 3. de csak abban az esetben. //Kiírja az eredményt: 13 z = x . // 3 z = x % y. de egy másik változót is értékül adhatunk. //Szorzás: z = 10 * 3 Console. adott nev változó) megadni a definíciót (amikor meghatározzuk. Console.doksi.

Minden ilyen operátor logikai típussal tér vissza. } Console. mert egy elütés is nehezen kideríthet hibát okoz.Forrás: http://www. if(l == true && k == false) { Console.WriteLine(x == y). m ködni viszont valószín leg rosszul fog.ReadKey(). amikor egyenl ség helyett az értékadó operátort használjuk.WriteLine(x <= y).5 Logikai/feltételes operátorok Akárcsak a C++. a C# sem rendelkezik „igazi” logikai típussal.WriteLine(x > y). //x kisebb-egyenl mint y: true Console. //x nem egyenl y –al: true Console. Ilyen esetekben figyelni kell.WriteLine("Igaz"). public class RelOp { static public void Main() { bool l = true.WriteLine(x != y). //Kiírja az eredményt: false Console. } } Az els sor egyértelm . Console. Az esetek többségében ugyanis így is le fog fordulni a program. bool k = false.ReadKey(). a másodikban az egyenl séget vizsgáljuk a kett s egyenl ségjellel. int y = 23. public class RelOp { static public void Main() { int x = 10. ehelyett 1 és 0 jelzi az igaz és hamis értékeket: using System. //false Console.doksi.hu 27 using System. } } . A relációs operátorok összefoglalása: 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.

hogy „Igaz”. Pl. hogy ha a feltétel igaz. Ezután egy elágazás következik. Nézzük az „és” igazságtáblázatát: A hamis hamis igaz igaz A második operátor a „vagy”: using System. Ebb l következik az is. if(l == true || k == true) { Console. bool k = false. err l b vebben egy kés bbi fejezetben lehet olvasni. „lusta kiértékelés” (vagy „rövidzár”) módszerével történik. hogy akár az el zö fejezetben megismert relációs operátorokból felépített kifejezések. A fenti példában az „és” (&&) operátort használtuk. } } B hamis igaz hamis igaz Eredmény hamis hamis hamis igaz A „vagy” (||) operátor akkor térít vissza „igaz” értéket.doksi. ha mindkét operandusa „igaz” vagy nullánál nagyobb értéket képvisel. az els nek „igaz” a másodiknak „hamis” értéket adtunk. } Console.Forrás: http://www. A program maga kiírja. A „vagy” igazságtáblázata: A hamis hamis igaz igaz B hamis igaz hamis igaz Eredmény hamis igaz igaz igaz Az eredmény kiértékelése az ún. mint az el z .hu 28 El ször felvettünk két logikai (bool) változót. vagy matematikai formulák is lehetnek operandusok. ha az operandusai közül valamelyik „igaz” vagy nagyobb mint nulla. public class RelOp { static public void Main() { bool l = true. azaz a program csak addig vizsgálja a feltételt amíg muszáj. akkor végrehajt egy bizonyos utasítás(oka)t. „k” biztosan nem „igaz” (hiszen épp el tte kapott „hamis” értéket). Ez a program is ugyanazt csinálja.ReadKey().WriteLine("Igaz"). a lényege az. a különbség a feltételben van. ez két operandust vár és akkor ad vissza „igaz” értéket. a „vagy” .

A „tagadás” (negáció) igazságtáblája: A Eredmény hamis igaz igaz hamis Ez a három operátor ún.WriteLine("X nem egyenlo 11 -el!"). mivel „l” van az els helyen (balról jobbra haladunk) és „igaz”. } A logikai operátorok családjához tartozik (ha nem is szorosan) a feltételes operátor.ReadKey(). akkor ad vissza igaz értéket. A harmadik a „tagadás” (!()): using System. Ez az egyetlen háromoperandusu operátor. } A logikai „és”: if(l == true & k == true) { Console. } Console.Forrás: http://www.WriteLine("Igaz"). A különbség annyi. if(!(x == 11)) { Console. } } Ennek az operátornak egy operandusa van.hu 29 példában a „k” soha nem fog kiértékel dni. hogy a logikai operátorok az eredményt l függetlenül kiértékelik a teljes kifejezést.WriteLine("Igaz"). feltételes operátor. ha az operandusban megfogalmazott feltétel hamis vagy egyenl nullával. a következ képpen m ködik: . nem élnek a „lusta” kiértékeléssel. vagyis a feltétel „k” értékét l függetlenül mindenképpen teljesül. A logikai „vagy” m velet: if(l == true | k == true) { Console. közölük az „és” és „vagy” operátoroknak létezik a „csonkolt” logikai párja is. public class RelOp { static public void Main() { int x = 10.doksi.

akkor az a következ képpen jelenik meg a memóriában: 2 00000010 A bit operátorok ezzel a formával dolgoznak. } } 6. egyébként 0: 01101101 00010001 AND 00000001 Példa: using System. Console.ReadKey(). ha van egy byte típusú változónk (ami egy byte azaz 8 bit hosszú) aminek a „2” értéket adjuk.6 Bit operátorok Az el z fejezetben említett logikai operátorok bitenkénti m veletek elvégzésére is alkalmasak numerikus típusokon.doksi. //1010 & 0010 = 0010 = 2 Console. Az eddig megismert kett mellé még jön négy másik operátor is.hu 30 feltétel ? igaz-ág : hamis-ág.WriteLine(x & 2). class Program { static public void Main() { int x = 10. A m veletek: Bitenkénti „és”: veszi a két operandus bináris alakját és a megfelel bitpárokon elvégzi az „és” m veletet azaz ha mindkét bit 1 állásban van akkor az adott helyen az eredményben is az lesz.WriteLine( (x == y) ? "Egyenlo" : "Nem egyenlo"). így pl.ReadKey(). Console. Console. int y = 10.Forrás: http://www. A számítógép az adatokat kettes számrendszer –beli alakban tárolja. } } . using System. public class RelOp { static public void Main() { int x = 10.

class Program { static public void Main() { int x = 10.ReadKey().hu 31 Bitenkénti „vagy”: hasonlóan m ködik mint az „és”. //1010 | 0010 = 1010 = 10 Console.WriteLine(x << 1). Console. class Program { static public void Main() { int x = 143. Az operátor: <<: 10001111 LEFT SHIFT 100011110 using System. } } Biteltolás balra: a kettes számrendszerbeli alak „fels ” bitjét eltoljuk és a jobb oldalon keletkez üres bitet nullára állítjuk.doksi.Forrás: http://www. //10001111 (=143) << 1 = 100011110 = 286 Console.WriteLine(x | 2). ha a két operandus adott bitje közül az egyik is az: 01101101 00010001 OR 01111101 using System. de a végeredményben egy bit értéke akkor lesz 1. Console. } } .ReadKey().

Ezután ismét kiértékeljük x –et. Az x nev változót megnöveltük tízzel. szebb és hatékonyabb. mégpedig az ún. rövid forma. x++/x--. Szerencsére van megoldás. A fenti sorból ez lesz: x += 10.doksi. Az operátor: >>: using System. Szemmel láthatóan ugyanaz a baj. Console.hu 32 Biteltolás jobbra: most az alsó bitet toljuk el és felül pótoljuk a hiányt. azonban az eggyel való növelésre-csökkentésre van önálló operátorunk: ++x/--x. hogy növeljük vagy csökkentjük az operandust inkrementális illetve dekrementáló operátorról beszélünk. } } 6. Ebb l az operátorból rögtön két verziót is kapunk.Forrás: http://www. A probléma ugyanaz. Mi történik valójában? Els ként értelmezni kell a jobb oldalt. azaz ki kell értékelni x –et. végül visszaadja az átmeneti változót. azaz megnöveli(csökkenti) az operandusát egyel. .ReadKey(). prefixes (++/-.7 Rövid forma Vegyük a következ példát: x = x + 10. de a megoldás más a következ esetben: x = x + 1. els ként létrehoz egy átmeneti változót. Console. de vannak helyzetek amikor lényegesen megkönnyíti az életünket a használa. Az összes aritmetikai operátornak létezik rövid formája. Csakhogy van egy kis baj: ez a megoldás nem túl hatékony. hozzá kell adni tízet és eltárolni a veremben. ezúttal a bal oldalon. Attól függ en. amiben eltárolja az operandusa értékét. A prefixes alak pontosan azt teszi amit elvárunk t le. majd megnöveli eggyel az operandust. Rövidebb.el l) és postfixes formát. class Program { static public void Main() { int x = 143. Ez els re talán nem t nik hasznosnak. A postfixes forma egy kicsit bonyolultabb.WriteLine(x >> 1).

class Program { static public void Main() { int x = 143. .ReadKey().hu 33 6. Csakis el jeles típusokon m ködik. Typeof: az operandusa típusát adja vissza: using System.8 Egyéb operátorok Unáris -/+: az adott szám pozitív illetve negatív értékét jelezzük vele.doksi. } } A változón meghívott GetType() metódus (amit mellesleg minden típus a System.Object –t l örököl) a változó típusát adja vissza.Forrás: http://www.WriteLine("X tipusa int").GetType()) { Console. if(typeof(int) == x. } Console.

if(x == 10) //Ha x == 10 { Console. hogy „x” nem egyenl tízzel? Erre való az „else” ág. a program utasításainak sorrendiségét szabályozó 7. } else //De ha nem annyi { Console. Ezt az ágat nem kötelez megadni: using System.doksi. class Conditional { static public void Main() { int x = 10.1 Szekvencia A legegyszer bb vezérlési szerkezet a szekvencia. ez akkor hajtódik végre. hogy meg kell vizsgálnunk egy állítást.2 Elágazás Gyakran el fordul. } Console.WriteLine("X == 10"). hogy igaz vagy hamis más-más utasítást kell végrehajtanunk. ha azt is jelezni akarjuk. Ilyen esetekben elágazást használunk: using System. } } Mi van akkor. } Console. Vezérlési szerkezetek Vezérlési szerkezetnek konstrukciókat nevezzük.WriteLine("X == 10"). és attól függ en. if(x == 10) //Ha x == 10 { Console. Ez tulajdonképpen egymás után megszabott sorrendben végrehajtott utasításokból áll.Forrás: http://www. ha a program nem talál más feltételt.ReadKey(). 7.ReadKey(). class Conditional { static public void Main() { int x = 10.WriteLine("X nem egyenlo tizzel!").hu 34 7. } } .

class Conditional { static public void Main() { int x = 12. hogy több feltételt is megvizsgáljunk. switch(x) { case 10: Console. } . ha egy változó több lehetséges állapotát akarjuk vizsgálni: using System. hogy egyik sem lesz jó. } else if(x == 12) //Vagy x == 12 { Console. Ilyen esetekben azonban van egy egyszer bb és elegánsabb megoldás. if(x == 10) //Ha x == 10 { Console. mégpedig a „switch-case” szerkezet.ReadKey(). class Switch { static public void Main() { int x = 10.WriteLine("X == 12"). Egy elágazásban pontosan egy darab if bármennyi else-if és pontosan egy else ág lehet. ekkor nem történik semmi). Az utolsó példában egy darab változó értékét vizsgáljuk. } Console.WriteLine("X nem egyenlo tizzel és tizenkettovel sem!"). } else //De ha nem annyi { Console. default: Console. } } A program az els olyan ágat fogja végrehajtani aminek a feltétele teljesül (termszetesen el fordulhat.Forrás: http://www. Egy elágazáson belül is írhatunk elágazást.WriteLine("X == 10"). break. ekkor „else-if” –et használunk: using System.doksi.WriteLine("X == 10").WriteLine("Default"). break.hu 35 Arra is van lehet ségünk. Ezt akkor használjuk.

7. Ezt a Console. stb…): using System. és eltároljuk egy változóban.Parse(…) metódussal tesszük.hu 36 Console. class Program { static public void Main() { Console. a másodikban pedig a default címkére ugrunk a goto utasítással.WriteLine("Default"). Ezt vagy a break –kel tesszük a már látott módon. Az els az ún. megszakítjuk egy elágazás egy ágának végrehajtását). return. } Console. ha pedig nem… nos.WriteLine("Adjon meg egy szamot: "). break.ReadKey(). goto. kiírjuk és a break –kel kiugrunk a switch –b l (egyébként a break minden ilyen esetben alkalmazható. int x = int. Ha igen. A C# négyféle ciklust biztosít számunkra. Ha viszont x nem egyenl tízzel. switch(x) { case 12: Console. Most jön a switch. számlálós ciklus (nevezzük for-ciklusnak): . } } Bekérünk egy számot a felhasználótól. akkor a default –ra ugrunk. elöbbi beolvassa a standard inputot az els sorvége jelig. ami gyakorlatilag megfelel egy else ágnak.3 Ciklus Amikor egy adott utasítássorozatot egymás után többször kell végrehajtanunk. default: Console. akkor cilust használunk.Parse(Console.Forrás: http://www. Az egyes esetek utasításai után meg kell adni. utóbbi pedig numerikussá konvertálja az eredményt (ha tudja.ReadLine()). } } A switch –en belül els ként megvizsgáljuk.doksi.ReadKey(). err l egy kés bbi fejezetben lesz szó).WriteLine("X == 12"). vagy valamilyen „ugró” utasítással (jump. pl. Az els esetet már láttuk. case 10: goto default. break. hogy hol folytassa a program a végrehajtást. hogy x egyenl –e tízzel.ReadLine(…) függvényen meghívott int.

WriteLine("X == {0}". hogy növeljük(csökkentsük. //kezd érték while(i < 10) //ciklusfeltétel .hu 37 using System.++i) Els ként létrehozunk egy ciklusváltozót (i) amivel nyilvántartjuk. mikor mi kell) a ciklusváltozót. } } Miel tt kivesézzük a ciklust nézzük meg a ciklusmagot: Console. formátumstringgel. ezt a már ismert inkrementális operátorral tesszük.++i) { Console.i < 10. hogy „I == „. ami hatással van a ciklusváltozó értékére (pl. Ehhez a szöveghez akarjuk hozzáadni a változó értékét és ezt is tesszük a {0} jelöléssel. ahol a ciklus befejez dik. hogy éppen hol tart a ciklus. tehát addig megy a ciklus amíg i kisebb tíznél. Végül gondoskodnunk kell arról.Forrás: http://www. A ciklusunk kiírja a számokat nullától kilencig. hanem ket vel növeljük az értékét).doksi.ReadKey(). ami onnan kapta a nevét. de szeretnénk egy kicsit kicsinosítani. i). hogy a ciklus egyszer sem fut le: using System. hogy a ciklusmag végrehajtása el tt ellen rzi a ciklusfeltételt ezért el fordulhat az is. Ezt a végpontot (alias ciklusfeltétel) adjuk meg a második helyen. class Program { static public void Main() { for(int i = 0. Most nézzük a ciklusfejet: for(int i = 0. } Console. csak a memóriát foglalná. i).WriteLine("I == {0}". mégpedig a prefix formával mivel nincs szükségünk az átmeneti változóra. Második kliensünk az el ltesztel s ciklus (mostantól hívjuk while-ciklusnak). nem eggyel.i < 10. Természetesen itt bármilyen más kifejezést is használhatunk. class Program { static public void Main() { int i = 0. ezért a szám elé írjuk. hiszen onnan kezdjük a számozást) paramétert helyettesíti be a szövegbe. Ismerkedjünk meg az ún. Ez a formátumstring utáni els (illetve nulladik. Mivel ez egy számlálós ciklus ezért i valamikor eléri (jó esetben) azt az értéket.

} Console. } } A program ugyanazt csinálja mint az el z . ciklusfeltétel. i). class Program { static public void Main() { string str = "abcdefghijklmnopqrstuvwxyz".hu 38 { Console. nem nehéz kitalálni. Ezzel a ciklussal végigiterálhatunk egy tömbön vagy gy jteményen.doksi. helyette végigmegyünk egy string –en: using System. A harmadik versenyz következik.WriteLine("I == {0}". . ami megvalósítja az IEnumerable és IEnumerator interfészeket (interfészekr l egy kés bbi fejezet fog beszámolni. A példánk most nem a már megszokott „számoljunk el kilencig” lesz. } while(i < 10). ++i. ++i. class Program { static public void Main() { int i = 0. így legalább egyszer biztosan lefut: using System. t hátultesztel s ciklusnak hívják (legyen dowhile).ReadKey(). } } Végül.ReadKey().Write(ch).Forrás: http://www. Console. i).ReadKey(). ott lesz szó err l a kett r l is). foreach(char ch in str) { Console. hogy azért kapta ezt a nevet mert a ciklusmag végrehajtása után ellen rzi a feltételt. do { Console. növel/csökkent). viszont itt jól láthatóan elkülönülnek a ciklusfeltételért felel s utasítások (kezd érték.WriteLine("I == {0}". de nem utolsósorban a foreach (neki nincs külön neve) ciklus következik. //ciklusváltozó növelése } Console. illetve minden olyan objektumon.

amelyen a foreach képes végigiterálni (azaz megvalósítjuk a fentebb említett két interfészt). Ha a gépen van a sor. amivel kijelöljük. A kitalált szám legyen pl. hogy mindig felezi az intervallumot (pl.Forrás: http://www.4 Gyakorló feladatok 1. 1 és 100 között.ReadKey(). Öt próbálkozása lehet a játékosnak. . } } static public void Main() { foreach(int x in EnumerableMethod(10)) { Console.i < max. A foreach pontos m ködésével az interfészekr l szóló fejezet foglalkozik. hogy a tippelt szám nagyobb. ha a kitalált szám nagyobb. Készítsünk számkitaláló játékot. 7.doksi. class Program { static public IEnumerable EnumerableMethod(int max) { for(int i = 0. hogy a felhasználó próbálja kitalálni a program által „sorsolt” számot.++i) { yield return i. akkor 75 jön.hu 39 } } A cilusfejben felveszünk egy char típusú változót (egy string karakterekb l áll).el ször 50 –t tippel. using System. vagy fordítva. } } 7. hogy egy metódust használjunk (metódusokról egy késöbbi fejezet szól) foreach ciklussal: using System. vagy kisebb mint a kitalált szám.WriteLine(x). amely lehet séget ad kiválasztani. A gépi játékos úgy találja ki a számot.Collections. utána az in kulcsszó következik. többek között megvalósítunk egy osztályt. akkor használjuk a következ fejezet elején bevezetett véletlenszámgeneráló objektumot (Random).3. A felhasználó egy játék végén rögtönk kezdhessen újabbat. hogy min megyünk át. és így tovább). } Console. minden tipp után írjuk ki.1 Yield A yield kifejezés lehet vé teszi. a szám létrehozására.

Tartsuk nyílván a játék állását és minden forduló után írjuk ki. Készítsünk egy egyszer számológépet! A program indításakor jelenjen meg egy menü. ezután bekérjük a m velet tagjait (az egyszer ség kedvéért legyen csak két szám).hu 40 2. ahol kiválaszthatjuk a m veletet. . vagyis addig tart.doksi.Forrás: http://www. amelyre a játék végetér). amíg a felhasználó kilép (nehezítésképpen lehet egy karakter. A játék folyamatos legyen. Készítsünk k -papír-olló játékot! Ebben az esetben is használjuk a véletlenszámgenerátort. 3.

Létrehoztunk egy 10 darab int típusú elemeket tartalmazó tömböt. ".hu 41 8. míg referenciatípusoknál ugyanez a m velet NullReferenceException kivételt fog generálni. Az egyes elemekre az indexel operátorral (szögeletes zárójel -> [ ]) és az elem indexével(sorszámával) hivatkozunk. de ezt nem kell megtennünk. } Console. Ekkor a tömbelemek null -ra inicializálódnak. A következ példában feltöltünk egy tömböt véletlen számokkal és kiíratjuk a tartalmát számlálós és foreach (ugyanis minden tömb implementálja az IEnumerator és IEnumerable interfészeket) ciklussal: using System. Ilyenkor kényelmetlen lenne mindegyiknek változót foglalnunk (képzeljünk el 30 darab int típusú értéket.++i) { array[i] = r.++i) { Console. .WriteLine("{0}. array[i]).i < array. azonos típusú elemek halmaza.Length. Tömbök Gyakran van szükségünk arra. mivel értéktípusok esetében szimpla nullát kapnánk vissza az általunk nem beállított indexre hivatkozva. foreach(int i in array) { Console. class Program { static public void Main() { int[] array = new int[10].WriteLine("{0}. Minden elemre egyértelm en mutat egy index (egész szám). A C# mindig folytonos memóriablokkokban helyezi el egy tömb elemeit. Ez a szabály referenciatípusoknál (osztályok) kissé máshogy m ködik. Ez nagy különbség. i). for(int i = 0.Length. még leírni is egy örökkévalóság lenne). } Console.Forrás: http://www. Tömbdeklaráció: int[] x = new int[10].Next().WriteLine(). hogy több azonos típusú objektumot tároljunk el. A számozás mindig nullától kezd dik. ". Random r = new Random().ReadKey().i < array. A tömb deklarációja után az egyes indexeken lév elemek automatikusan a megfelel nullértékre inicializálódnak (ebben az esetben 10 darab nullát fog tartalmazni a tömbünk). hiszen rendelkezésünkre áll a tömb adatszerkezet.doksi. így a legutolsó elem indexe az elemek száma mínusz egy. A tömb meghatározott számú. } for(int i = 0.

//tömb rendezése 8. Látható az indexel operátor használata is. Minden tömb a System.hu 42 } } A példánkban egy véletlenszámgenerátor objektumot (Random r) használtunk. 'c' }. Kivételkezelésr l is egy kés bbi fejezet szól. Dinamikusan b víthet adatszerkezetekr l a Gy jtemények cím fejezet szól. 55. Így a 45 indexe: [3. az objektumon meghívott Next() metódussal.doksi. Egy tömb elemeinek száma a deklarációval meghatározott. egydimenziós tömböt. 1 Ez egy kétdimenziós tömbnek (mátrix a neve – ki hinné?) felel meg. 0] (ne feledjük. ezért néhány hasznos m velet azonnal rendelkezésünkre áll (pl.IndexOutOfRangeException kivételt fog dobni a program. 23. még mindig nullától indexelünk). Tulajdonságokkal egy kés bbi fejezet foglalkozik részletesen. az array[i] az array i –edik elemét jelenti. 67. Ekkor az elemek száma a megadott értékekt l függ. azon a kés bbiekben nem lehet változtatni. vagy vektort használtuk. els helyen a sor áll utána az oszlop. Egy tömböt akár a deklaráció pillanatában is feltölthetünk a nekünk megfelel értékekkel: char[] chararray = new char[] { 'b'.Next(100). 'd'. 52 ] 45. ezt a Next() metódus paramétereként megadott fels határral redukálhatjuk: array[i] = r. Lehet ségünk van azonban többdimenziós tömbök létrehozására is.Array osztályból származik. az egyes elemekre két indexxel hivatkozunk. minimum 0 lehet a véletlenszám. A példaprogram valószín leg elég nagy számokat fog produkálni. 2 A = [ 13.1 Többdimenziós tömbök Eddig az ún. ami a nevéhez híven véletlenszámokat tud visszaadni. Egy dolog van még ami ismeretlen. Ekkor maximum 99. 'a'. a tömb Length tulajdonsága.Forrás: http://www. Multidimenziós tömböt a következ módon hozunk létre C# nyelven: . ugyanis a fordító nem ellen rzi fordítási id ben az indexek helyességét. ez a tömb elemeinek számát adja vissza. Az indexeléssel vigyázni kell.Sort(). viszont helytelen indexelés esetén futás id ben System. Vegyük például a matematikából már ismert mátrixot: 12. rendezhetünk egy tömböt a Sort() metódussal): chararray. ekkor nem egy indexxel hivatkozunk egy elemre hanem annyival ahány dimenziós.

Next().3]. azonban a sorok hosszát (az egyes sorok nyílván önálló vektorok) ráérünk kés bb megadni és nem kell ugyanolyannak lenniük: . Ekkor legalább egy dimenzió hosszát meg kell adnunk. {13. {45. A többdimenziós tömbök egy variánsa az ún. Az elemszám most is meghatározott. j] = r.] { {12. Készítettünk egy három sorral rendelkez tömböt. a már ismert véletlenszámgenerátoros példa módosított változata jön: using System. viszont a bels tömbök hossza tetszés szerint megadható: int[][] jarray = new int[3][]. ezt nem okozhat gondot megírni. 1} }. } } Console. Itt is összeköthetjük az elemek megadását a deklarációval.hu 43 int[. A tömbök GetLength() metódusa a paraméterként megadott dimenzió hosszát adja vissza (nullától számozva). 2}. tehát a példában az els esetben a sor.GetLength(1).++j) { matrix[i. } } Most nem írjuk ki a számokat. class Program { static public void Main() { int[. 55.++i) { for(int j = 0. 23.ReadKey(). ez konstans marad. 67.Forrás: http://www.] matrix = new int[3.i < matrix. a másodikban az oszlop hosszát adjuk meg a ciklusfeltételben.] matrix = new int[3. for(int i = 0.j < matrix. Ez itt egy 3x3 –as mátrix. 52}.] matrix = new int[.GetLength(0). egyenetlen(jagged) tömb. bár egy kicsit trükkösebb a dolog: int[.doksi. nem változtatható. 3]. Ez a mátrix már pontosan olyan mint amit már láttunk. Random r = new Random(). Nyílván nem akarjuk mindig kézzel feltölteni a tömböket. olyan mint a fent látható.

persze értelmes kereteken belül.next(1. azon nem lehet változtatni. hiszen használtuk a Length tulajdonságot.Length. 2}.hu 44 using System. így . az el bbi példa így néz ki ekkor: var jarray = new int[][] { new int[] {1. 8.next(100).Length. Random r = new Random(). Azt már tudjuk. Az inicializálás a következ képpen alakul ebben az esetben: int[][] jarray = new int[][] { new int[] {1. A bels ciklusban jól látható. a fix méretre és a meghatározott típusra nyújt megoldást.ReadKey(). 4} }. 5)]. for(int j = 0. hogy a tömb elemei valóban tömbök.++i) { jarray[i] = new int[r. 4} }.i < jarray.0 megengedi a var kulcsszó használatát is. } } Console. new int[] {3. for(int i = 0.Object –b l származik. A C# 3. class Program { static public void Main() { int[][] jarray = new int[3][].++j) { jarray[i][j] = r.j < jarray[i].2 ArrayList A fejezet olvasása el tt ajánlott elolvasni az Örökl dés és Típuskonverziók cím fejezetet. 2}. A fordító fordításkor dönti el a pontos típust. Az ArrayList két problémára.Forrás: http://www.doksi. } } Véletlenszámgenerátorral adjuk meg a bels tömbök hosszát. new int[] {3. hogy minden osztály a System.

a másik pedig. hogy int típusú elemeket tároltunk el.i < 10. hogy nem adunk meg méretet a deklarációnál. Feltételezzük. a programban hivatkoznunk kell a System. Random r = new Random(). akkor nyert ügyünk van. Egész más a helyzet. mert a Console.0 bevezette a típusos változtait a generikus adatszerkezetekkel.WriteLine("{0}. a 2. Miel tt használnánk egy ArrayList –et.Add(i). Egy ArrayList deklarációjánál nem adjuk meg.hu 45 ha van egy olyan tárolónk.Forrás: http://www.WriteLine –nak van olyan túlterhelt változata. ha konkrét típusú változónak akarom értékül adni az adott indexen lév elemet: int x = list[4]. for(int i = 0.i < list. } } Az Add() metódus segítségével tudunk elemeket betenni a listába. ez a programozó dolga.++i) { list.Count. hogy a kivételnél konverzió szükséges. list[i]). Ez azért van így. Ez egy ún.Object típusú elemet vár. A fenti sor így néz ki helyesen: int x = (int)list[4]. Hogy ez miért van így.Collections névtérre. amikor hivatkozunk egy elemére. Egy normális tömb kivételt dob. } Console. } for(int i = 0. Az ArrayList hasonlóan m ködik mint egy tömb. most nem csináltunk ilyet.ReadKey().0 –ban jelent meg. Egy Object típusú elemet ugyanis nem készítettek fel implicit konvertálásra. using System. hogy típuskonverziót kell alkalmaznunk. using System. amely egy System. hány elemet fogunk eltárolni.Collections. és van elem a negyedik indexen. Bár azt mondtam.++i) { Console. A fenti sor már a fordításon fennakad a „Cannot implicitly convert type object to int” kezdet hibaüzenettel . arról az Örökl dés cím fejezet nyújt információt. mint . class Program { static public void Main() { ArrayList list = new ArrayList().doksi. ". az egyik. két különbség van. Az ArrayList a C# 1. explicit típuskonverzió. amely ilyentípusú objektumokat tárol. ha több elemet próbálunk elhelyezni benne. b vebben lásd a Típuskonverziók cím fejezetet.

hogy az aktuális elemek számánál nem kisebb értéket adjunk meg. hogy hány darab páros szám van benne. ekkor figyeljünk arra. a kapcitás 32 –re ugrik (és így tovább). van neki egy alapértelmezett kapacitása (ezt a Capacity tulajdonsággal kérdezhetjük le). 8. hogy mennyi helyre van szüksége? A válasz egyszer . egyébként ArgumentOutOfRange kivétel dobódik. de helyettük érdemesebb a nekik megfelel generikus változatokat használni. de ez nem mindig elég.doksi. így miel tt elhelyeznénk a 17 –ik elemet. Ez a kezdet kezdetén 16.hu 46 amennyit megadtunk. A kapacitást kézzel is beállíthatjuk a már említett tulajdonság segítségével. 2. Töltsünk fel egy tömböt véletlenszámokkal és döntsük el. Az ArrayList –en kív l számos adatszerkezetet is szolgáltat nekünk a C#. Akkor honnan tudja az ArrayList. ha több egyenl legnagyobb szám van). .Forrás: http://www. Töltsünk fel egy tömböt véletlenszámokkal és válasszuk ki közülük a legnagyobbat (az mindegy.3 Gyakorló feladatok 1. ezekr l b vebben a generikus gy jteményekr l szóló fejezetben olvashatunk.

WriteLine("ezegystring"[0]).doksi.ReadKey().hu 47 9. Egy string egyes bet ire az indexel operátorral hivatkozhatunk: using System. class Program { static public void Main() { string s = "ezegystring".WriteLine(s). Console. } } Ekkor a visszaadott típus char lesz. Console. Stringek A C# beépített karaktertípusa (char) egy Unicode karaktert képes eltárolni két byte – on. A foreach ciklussal indexel operátor nélkül is végigiterálhatunk a karaktersorozaton: foreach(char ch in s) { Console. //e Console. //e 9. de a string referenciatípus.Forrás: http://www. } Az indexel operátort nem csak változókon. de emlékezzünk arra.WriteLine(ch). hogy a metódusoknak számos változata lehet. ennek ellenére nem kell használnunk a new operátort. } } Nem úgy t nik.ReadKey(). Most bemutatunk néhányat. Console. de „nyers” szövegen is alkalmazhatjuk: Console. class Program { static public void Main() { string s = "ezegystring".1 Metódusok A C# számos hasznos metódust biztosít a stringek hatékony kezeléséhez. itt a leggyakrabban használtakat mutajuk be: . using System.WriteLine(s[0]). A szintén beépített string típus ilyen karakterekb l áll (tehát char –ként hivatkozhatunk az egyes bet ire).

Keresés: using System. . char[] chs = new char[]{ 'y'.Forrás: http://www. A Contains() metódus igaz értékkel tér vissza. Console.CompareOrdinal(s. 'z'.WriteLine(s.").WriteLine(s. A két metódus Any –re végz d változata egy karaktertömböt fogad paramétereként..WriteLine(s. //3 Console. c) if(x == 0) { Console.WriteLine(s. //16 Console.. a második az azonos indexen lév karaktereket nézi. '0' }.WriteLine("A ket string nem egyenlo. } } Az IndexOf() és LastIndexOf() metódusok egy string vagy karakter els illetve utolsó el fordulási indexét (stringek esetén a kezdés helyét) adják vissza. class Program { static public void Main() { string s = "verylonglongstring". Ha nincs találat.IndexOf('r')). string c = "another". akkor a visszaadott érték -1 lesz. ha a két string egyenl és nullánál kisebbet/nagyobbat. x = s. //2 Console. ha a paramétereként megadott karakter(sorozat) benne van a stringben. Az els változat nagyság szerint sorbarendezi a karaktereket és úgyhasonlítja össze a két karaktersorozatot. ha nem (pontosabban ha lexikografikusan kisebb/nagyobb).. int x = String.").Compare(s. } else if(x > 0 || x < 0) { Console.hu 48 Összehasonlítás: string s = "other". de ez nem statikus hanem példánymetódus. Mindhárom metódus nullát ad vissza..doksi.WriteLine(s.Contains("long")).ReadKey(). míg a harmadik az els höz hasonló.LastIndexOfAny(chs)).CompareTo(c). c). } x = String.LastIndexOf('n')).WriteLine("A ket string egyenlo. //true Console.IndexOfAny(chs)). //3 Console.

//smallstring Console. 3)).Text névtérben található.2 StringBuilder Amikor módosítunk egy stringet akkor automatikusan egy új példány jön létre a memóriában. 'g' }. hogy ezek a metódusok soha nem az eredeti stringen végzik a módosításokat.ToLower()). hanem egy új példányt hoznak létre és azt adják vissza. Fontos megjegyezni.Substring(0.hu 49 Módosítás: using System. Végül a ToLower() és ToUpper() metódusok pedig kis. //allstring Console. //mallstrin Console. //onesmallstring Console. Az Insert()/Remove() metódusok hozzáadnak illetve elvesznek a stringb l.WriteLine(s.WriteLine(s. class Program { static public void Main() { string s = "smallstring". using System. //sma string c = s. hogy ugyanakkora mint az új. char[] chs = new char[]{ 's'. paraméterei a kezd és végindexek (van egyparaméteres változata is.Remove(0. 2)).WriteLine(c. akkor allokál egy nagyobb területet és átmásolja magát oda. 'l')). Ha sokszor van szükségünk erre.WriteLine(s).doksi. a Substring() kivág egy karaktersorozatot. using System.illetve nagybet ssé alakítják az eredeti stringet. } } A Replace() metódus az els paraméterének megfelel karaktert lecseréli a második paraméterre. A Trim() metódus a string elején és végén lév karaktereket vágja le.ToUpper().Text. "one")).Forrás: http://www. Console.Insert(0. ez automatikusan lefoglal egy nagyobb darab memóriát és ha ez sem elég.ReadKey(). akkor használjuk inkább a StringBuilder típust.WriteLine(s.Replace('s'. //SMALLSTRING Console. Console. . A StringBuilder a System. hiszen az eredeti változat nem biztos.Trim(chs)). //lmallltring Console. class Program { static public void Main() { StringBuilder builder = new StringBuilder(50).WriteLine(s. ekkor a csak a kezd indexet adjuk meg és a végéig megy). 9.WriteLine(s.

.ReadKey(). A for ciklust érdekesen használtuk. most nem numerikus típuson iteráltunk.Forrás: http://www. ekkor az alapértelmezett tizenhat karakternek foglal helyet). Azt.hu 50 for(char ch = 'a'.++ch) { builder. Az Append() metódussal tudunk karaktereket (vagy egész stringeket) hozzáf zni. hogy ez miért m ködik a következ fejezetb l tudhatjuk meg.Append(ch).WriteLine(builder). hanem karaktereken. Console.ch <= 'z'. } } A StringBuilder fenti konstruktora (van több is) helyet foglal ötven karakter számára (létezik alapértelmezett konstruktora is. } Console.doksi.

hu 51 10. amihez nincs szükség egy egész blokkra: . byte y = (byte)x. de a többi része nulla lesz. Ha lehetséges a konverzió. szinte minden esetben a sz kebb típusról a tágabbra: int x = 10. akkor végbemegy.1 Ellen rzött konverziók A programfejlesztés alatt hasznos lehet tudnunk. Vegyük a következ példát: int x = 300. Kétféleképpen konvertálhatunk: implicit és explicit módon. //y == 10. a fordító elvégzi helyettünk. hogy ilyen esetekben csak a blokkon belül deklarált. hogy csak egy-egy konverziót szeretnénk vizsgálni.Forrás: http://www. mint egy másiknak. A byte viszont (ahogy a nevében is benne van) egy nyolcbites értéket tárolhat. byte y = (byte)x. egyébként a fordító figyelmeztetni fog. hogy az egyes típusok másként jelennek meg a memóriában. Ilyen helyzetekben típuskonverziót (vagy castolást) kell elvégeznünk. implicit konverzió Ebben az esetben a long és int mindketten egész numerikus típusok. Egy explicit konverzió nem feltétlenül fog m ködni és adatvesztés is felléphet. Egy implicit konverzió minden esetben sikeres és nem jár adatvesztéssel. hogy egy adott típusnak úgy kellene viselkednie. //explicit konverzió A byte sz kebb típus mint az int. és a long a tágabb. ezért explicit konverzitó hajtottunk végre. hogy minden konverzió gond nélkül végbement –e. 10. ami pont 44. ellen rzött konverziót fogunk használni. Vajon mennyi most az y változó értéke? A válasz els re meglep lehet: 44. } Ez a kifejezés kivételt fog dobni. Az el bbi esetben nem kell semmit tennünk. Azonban gyakran kerülünk olyan helyzetbe. ezért a 300 –nak csak az els 8 bitje fog értékül adódni az y –nak. amely kivételt dob (err l hamarosan). Implicit konverzió általában „hasonló” típusokon mehet végbe. long y = x. Ennek ellen rzésére ún. Típuskonverziók Azt már tudjuk.doksi. Figyeljünk arra. A magyarázat: a 300 egy kilenc biten felírható szám (100101100). ezt a változó el tti zárójelbe írt típussal jelöltük. statikus és tag változókat vizsgálhatjuk. ezért a konverzió gond nélkül végbemegy. persze az int kapacitása ennél nagyobb. ha a forrás nem fér el a célváltozóban: checked { int x = 300. El fordul.

10. hogy egy adott osztály megvalósít –e egy bizonyos interfészt (err l kés bb).ch <= 'z'. ekkor a karakter Unicode értékét kapjuk vissza: using System. Az ajánlás szerint ellen rzött konverziókat csak a fejlesztés ideje alatt használjunk. Amennyiben ez a konverzió nem hajtható végre a célváltozóhoz a megfelel érték rendel dik (értéktípusokhoz 0. } Console. Console.hu 52 int x = 300.ReadKey().doksi. byte y = unchecked((byte)x).WriteLine("X tipusa int"). } } . mivel némi teljesítményvesztéssel jár. if(x is int) //ha x egy int { Console.WriteLine(x). } Ennek az operátornak a legnagyobb haszna az.3 Karakterkonverziók A char típust implicit módon tudjuk numerikus típusra konvertálni. byte y = x as y. Az ellen rzés kikapcsolását is megtehetjük az unchecked használatával: int x = 300.2 Is és as Az is operátort típusok futásidej lekérdezésére használjuk: int x = 10.++ch) { int x = ch. hogy le tudjuk kérdezni.Forrás: http://www. referenciatípusokhoz null). Párja az as az ellen rzés mellett egy explicit típuskonverziót is végrehajt: int x = 10. byte y = checked((byte)x). class Program { static public void Main() { for(char ch = 'a'. nulla 10.

doksi.hu 53 Erre a kimenet a következ lesz: 97 98 99 100 … A kis ’a’ bet hexadecimális Unicode száma 0061h. . tehát a konverzió a tízes számrendszerbeli értéket adja vissza. ami a 97 decimális számnak felel meg.Forrás: http://www.

ez a 0 és az 1 legyen. Jó szórakozást! 11.Forrás: http://www. hogy a diszkrimináns egyenl –e nullával. a Double. pozitív számok jelezzék a szárazföldet.3 Felderítés Készítsünk programot. 11. Az els elemet így nyílván meg kell majd adnunk. az egyenl ség csak akkor igaz. Gyakorló feladatok I. a diszkrimináns negatív. A feladata.Epsilon) { //itt csinálunk valamit } Ez már biztosan m ködni fog. n tagját a Fibonacci számoknak.hu 54 11. Ekkor viszont az ellen rzésnél gond adódhat. Nézzük a következ esetet: double y = 0. hogy a feltétel soha nem lesz igaz. if(y == Double. amely egy felderít repül gépet szimulál.2 Fibonacci számok Készítsünk programot amely kiírja az els változót a felhasználótól kérjük be. de gondoljunk a típus nagyságára).Epsilon statikus tulajdonság értéke pont egy double típus által felvehet legkisebb pozitív szám: double y = 0. Itt nem szerepel teljes megoldás. A mérési adatok egy tömbben legyenek eltárolva. Szerencsére van beépített megoldás. viszont a megoldást segít tippek. Az n két Tipp: a Fibonacci sor tagjainak értéke a megel z két tag összege. Nehezítésként használhatunk kétdimenziós tömböt.doksi. 11. Most néhány gyakorló feladat következik. tanácsok igen. de mivel az els egy double ez nem valószín (a meggondolás egyéni feladat. ha a két operandusa bitenként megegyezik. Hogy miért? Nos. stb…). illetve jelzi. if(y == 0) { //itt csinálunk valamit } A meglep az. . ha nem lehet kiszámolni az eredményt (a == 0. hogy megkeresse egy adott területen a legmagasabb szárazföldi.és a legmélyebben fekv vízalatti pont magasságát/mélységét.1 Másodfokú egyenlet Készítsünk programot. amely bekéri az egyenlet három együtthatóját és kiszámolja a gyökeit(gyökét). double) típussal érdemes reprezentálni. Azonban ezt az értéket valamilyen lebeg pontos (pl. Tipp: nyílván megvizsgáljuk. negatívak a vízet.

11. amely egy tömb elemei közül legnagyobbat/legkisebbet.hu 55 Tipp: véletlenszámgenerátorral töltsük következ képpen tudunk sorsolni: Random r = new Random().Forrás: http://www. Az elemeket a felhasználótól kérjük be. fel a tömböt. negatív számot a Ebben az esetben a generált szám -100 és 100 közé esik majd. 100).doksi.Next(-100.4 Maximum/minimumkeresés Készítsünk programot. r. kiválasztja a .

gyakorlati példákkal a következ részekben találkozhatunk (szintén a következ részekben található meg néhány elméleti fogalom amely gyakorlati példákon keresztül érthet bben megfogalmazható.3 Adattag és metódus Egy objektumnak az életciklusa során megváltozhat az állapota.2 Osztály Az OOP világában egy osztály olyan adatok és m veletek összessége. Objektum-orientált programozás .doksi.hu 56 12. súly.). Legyen például a modellünk a kutya állatfaj. stb. Az UML a következ képpen jelöl egy osztályt: Kutya Amikor programot írunk. Ekkoriban még f leg matematikai számításokat végeztek a számítógépekkel. stb. pl. hogy a különböz hajótípusok adatait egy egységként kezeljék. megváltoztak az igények. csóválja a farkát. Ebben a fejezetben az OOP elméleti oldalával foglalkozunk. A m veletek összességét felületnek is nevezik. Az els objektum-orientált programozási nyelv a Simula 67 volt. 12. az adatok pedig túl kompexekké váltak ahhoz.elmélet A korai programozási nyelvek nem az adatokra. Ez egy általános tervez eszköz. életkor. hanem a m veletekre helyezték a hangsúlyt. 12. a cél a paradigma megértése. hogy a procedurális módszerrel kényelmesen és hatékonyan kezelni lehessen ket. ezért ezek csak kés bb lesznek tárgyalva. A következ kben az UML eszközeit fogjuk felhasználni az adatok közti relációk grafikus ábrázolásához. A tulajdonságokat tároló változókat adattagnak (vagy mez nek). Ahogy aztán a számítógépek széles körben elterjedtek. így egyszer sítve a munkát.Forrás: http://www.: polimorfizmus). tulajdonságai. ezt pédányosításnak nevezzük. a m veleteket metódusnak nevezzük. Módosítsuk a diagramunkat: . Tervez i Ole-Johan Dahl és Kristen Nygaard hajók viselkedését szimulálták. a kutya eszik(ez egy m velet).1 UML Az OOP tervezés el segítésére hozták létre az UML –t (Unified Modelling Language). 12. játszik.) és van meghatározott viselkedése (pl. Egy kutyának vannak tulajdonságai (pl. hanem az egyes adatokat (adatszerkezeteket) és köztük lév kapcsolatot (hierarchiát). Ezt az állapotot valahogy el kell tudnunk tárolni illetve biztosítani kell a szükséges m veleteket a tulajdonságok megváltoztatásához (pl. ekkor megváltozik a „jóllakottság” tulajdonsága). Az osztály és példány közti különbségre jó példa a recept (osztály) és a sütemény (példány). amellyel leírhatjuk egy modell (vagy entitás) tulajdonságait és m ködését. Az OOP már nem a m veleteket helyezi a középpontra. akkor az adott osztályból (osztályokból) létre kell hoznunk egy (vagy több) példányt. a célja egy minden fejleszt által ismert közös jelrendszer megvalósítása. ekkor jött az ötlet.

csak az evés ténye számít. ha a közvetlen család hozzáfér a számlámhoz. de nem módosíthatják (a származtatás/örökl dés hamarosan jön) (UML jelölés: -). egységbezárás (encapsulation vagy information hiding). de a leszármazott osztályok módosíthatják is (UML jelölés: #). Az OO paradigma egységbe zárja az adatokat és a hozzájuk tartozó felületet. attól függetlenül. hogy az egy személyautó. A háromféle láthatóság: Public: mindenki láthatja (UML jelölés: +). nem OO programnyelvek (pl. illetve a leszármazott osztályok is láthatják. Ennek egyik nagy el nye.doksi. emésztést is. Az s-OOP szabályai háromféle láthatóságot fogalmaznak meg. Protected: ugyanaz mint a private. illetve hozzáférnek a struktúrákhoz és nem megfelel en használják fel azokat. Ezekkel a fogalmakkal egy kés bbi fejezet foglalkozik. a C) az adatokat és a rajtuk végezhet m veleteket a program külön részeként kezelik. A kett között nincs összerendelés. Bevett szokás ezeket elkülöníteni egy önálló forrásfile –ba. de idegenekkel nem akarom megosztani). egy autót biztosan tudunk kormányozni. de ez még mindig nem elég biztonságos. hogy jó közszemlére bocsátani. hogy a felhasználó csak annyi adatot kapjon amennyi feltétlenül szükséges. ha mindenki hozzájuk fér (az elfogadható. Ugyanígy egy tulajdonság (adattag) esetében sem jó. 12. Private: csakis az osztályon belül elérhet . de err l nem fontos tudnunk. metódusokat nem biztos. ez nyelvt l függ en b vülhet. ezért más programozók gond nélkül átírhatják egyiket vagy másikat.Forrás: http://www. traktor vagy forma-1 –es gép). ez az ún.hu 57 Kutya jollak : int eszik() : void Az adattagokat név : típus alakban ábrázoljuk. . A Kutya osztály most így néz ki: Kutya -jollak : int +eszik() : void 12. mindössze arra kell figyelni. A kutyás példában az eszik() m velet magába foglalja a rágást. hogy a felület ne változzon (pl. Az OOP egyik alapelve.5 Egységbezárás A „hagyományos”.4 Láthatóság Az egyes tulajdonságokat. hogy egy adott osztály bels szerkezetét gond nélkül megváltoztathatjuk. a metódusokat pedig név(paraméterlista) : visszatérési_érték formában. nyelést. a C# láthatóságairól a következ részekben lesz szó.

de ez nem minden programnyelvre igaz. hogy a „Kutya” egy specializáltabb forma. Legyen egy „Állat” osztályunk. de leginkább a felület definiálására koncentrálnak.hu 58 12. csak a felületet szeretnénk átörökíteni. amelynek nincsenek adattagjai csakis a m veleteket deklarálja. Ezen belül megkülönböztethetünk „Eml s” –t vagy „Hüll ” –t. „az-egy” (is-a) reláció áll fenn. A C# nyelvi szinten támogat absztrakt osztályokat is. Ezekben el fordulnak adattagok is. amit az utódok megvalósítanak . Az osztályok között fenálló kapcsolatok összeségét hierachiának nevezzük. hogy az minden szül jének tulajdonságát örökli vagy átfogalmazza azokat. hogy az osztályunkat fel tudja használni a programunk egy másik része (ilyen például a már említett foreach ciklus).egy krokodil nyílván máshogy eszik mint egy hangya. hogy nem fontos számunkra a bels szerkezet. El fordul. mondjuk a „Gerinces Állatok” –ra. . A diagram a fenti példát ábrázolja. Ez egy eléggé tág fogalom. úgy. Egy (vagy több) már létez osztályból hozunk létre egy újat.Forrás: http://www. A legegyszer bben egy példán keresztül érthet meg. Az „Eml s” osztály egy leszármazottja lehet a „Kutya” és így tovább. de az evés az állatokhoz köthet valami. amelyben mondjuk megadunk egy absztrakt „evés” metódust. Az interfészek az osztálytól függetlenek. Ennek a gondolatmenetnek a gyakorlati felhasználás során lesz jelent sége. ezért közös). a kett között ugyanis lényegi különbség van. A specializálás során az osztályok között ún. hogy a „Kutya” az egy „Állat” akkor arra gondolunk. ezért k egy hasonló szerkezetet ún. Így amikor azt mondjuk. amely a specializált osztály fel l mutat az általánosabbra. Ilyenkor nem egy „igazi” osztályról. Az örökl dést specializálásnak is nevezik. UML -ül az örökl dést „üres” nyíllal jelöljük. A C# rendelkezik önálló interfészekkel.6 Örökl dés Az örökl dés vagy származtatás az új osztályok létrehozásának egy módja. hanem egy interfészr l beszélünk.doksi. csakis felületet biztosítanak (például az IEnumerable és IEnumerator a foreach –nek (is) biztosítanak felületet. ezért sz kíthetjük a kört. amelynek megvan a saját karkaterisztikája. de végeredményben egy „Állat”. absztrakt osztályokat használnak. az nem lényeges. hogy milyen osztályról van szó). Az absztrakt osztályok viszont egy shöz kötik az utódokat (erre az esetre példa az „Állat” osztály.

Az adattagok – ha vannak – automatikusan a nekik megfelel nullértékre inicializálódnak(pl. Az osztályunkból a következ képpen tudunk létrehozni egy példányt: Dog d = new Dog(). honnan tudja. hogy az osztálydeklaráció végén nincs pontosvessz . hogy melyik az igazi belépési pont. ett l függetlenül rendelkezik alapértelmezett (azaz paraméter nélküli) konstruktorral. mint a program belépési pontját. Osztályok Osztályt a class kulcsszó segítségével deklarálhatunk: using System. hogy a Main egy speciális metódus. Ez igaz minden olyan osztályra. A fenti példában a lehet legegyszer bb osztályt hoztuk létre. Hasonlóan a C++ nyelvhez itt is a new operátorral tudunk új objektumot létrehozni. A C++ programozók figyeljenek arra. ekkor a fordítóprogramnak meg kell adni (a /main kapcsoló segítségével). class Dog { } class Program { static public void Main() { } } Látható. hogy a fordító. mint helyet foglal a memóriában és létrehoz egy példányt. Bár a fenti osztályunkban nem definiáltunk semmilyen metódust. hogy melyik a „f ” osztály? A helyzet az. bool -> false.Object –b l származik (még akkor is ha erre nem utal semmi). referenciatípusok -> null). ezt a fordító automatikusan felismeri és megjelöli.Forrás: http://www.1 Konstruktorok Minden esetben amikor példányosítunk egy speciális metódus. értékadó operátort illetve másoló konstruktort nem. 13. . A C++ nyelvet ismer k vigyázzanak. Ugyanakkor minden osztály a System.doksi. hogy a f program és a saját osztályunk elkülönül. Ett l függetlenül ezt a megoldást ha csak lehet kerüljük el. Felmerülhet a kérdés. mivel itt csak alapértelmezett konstruktort kapunk automatikusan. amelynek nincs konstruktora (amennyiben bármilyen konstruktort létrehoztunk akkor ez a lehet ség megsz nik). Az alapértelmezett konstruktor semmi mást nem tesz. a konstruktor fut le.: int -> 0. ezért néhány metódust (például a típus lekérdezéséhez) a konstruktorhoz hasonlóan azonnal használhatunk. Igazából lehetséges több Main nev metódust is létrehozni.hu 59 13.

ha sok adattag/metódus van. this. } } A konstruktor neve meg kell egyezzen az osztály nevével és semmilyen visszatérési értéke sem lehet. Egy osztálynak paraméterlistától függ en bármennyi konstruktora lehet és egy konstruktorból hívhatunk egy másikat a this –el: class MyClass { public MyClass() : this(10) //A másik konstruktort hívtuk { } public MyClass(int x) { } } Ilyen esetekben a paraméter típusához leginkább illeszked konstruktor kerül majd hívásra. ezért készítsünk hozzá néhány adattagot és egy konstruktort: using System. a nevet és a kort (metódusokkal és paramétereikkel a következ rész foglalkozik b vebben).Forrás: http://www. A példában a konstruktor törzsében értéket adtunk a mez knek this hivatkozással. illetve ha a paraméterek neve megegyezik az adattagokéval. így mindig tudja mivel dolgozik. 2).hu 60 Jelen pillanatban az osztályunkat semmire nem tudjuk használni.doksi. A mi konstruktorunk két paramétert vár. Nem kötelez használni. azaz most csakis az osztályon belül használhatjuk ket. class Dog { private string name. elméleti rész). public Dog(string n. } } class Program { static public void Main() { Dog d = new Dog("Rex".name = n. ugyanakkor hasznos lehet. például a konstruktorban. Az adattagok private elérés ek (ld. egyébként nem fordul le a program. amelyen meghívták. amely mindig arra a példányra mutat. A fordítóprogram automatikusan „odaképzeli” magának a fordítás során. . ami viszont publikus. Ezeket a példányosításnál muszáj megadni. int a) { this. private int age.age = a.

} } Az inicializálás mindig a konstruktor el tt fut le. Ha a Dog osztálynak ezt a módosított változatát használtuk volna fentebb. Másoló konstruktort általában az értékadó operátorral szoktak implementálni.name = n. akkor a példányosítás során felülírnánk az alapértelmezettnek megadott kort. ez egyben azt is jelenti.Name. int a) { this.age = a. } } public string Name { get { return name.Forrás: http://www. private int age = 5. int a) { this. private int age = 5.hu 61 Nem csak a konstruktorban adhatunk értéket a mez knek. } } } . de az operátortúlterhelés egy másik fejezet témája így most egyszer bben oldjuk meg: class Dog { private string name = "Rex". inicializálókat is: class Dog { private string name = "Rex".name = otherDog.age = a. A konstruktorok egy speciális változata az ún. hogy az felülbírálhatja. Az inicializálás sorrendje megegyezik a deklarálás sorrendjével (föl lr l lefelé halad). } public Dog(Dog otherDog) { this. public Dog(string n. hanem használhatunk ún. this.name = n.age = otherDog.vagy copy – konstruktor. } public int Age { get { return age.Age. this. this.doksi. public Dog(string n. Ez paramétereként egy saját magával megegyez típusú objektumot kap és annak értékeivel inicializálja magát. másoló.

4 Parciális osztályok C# nyelven létrehozhatunk ún. protected: csakis a tartalmazó osztályon és leszármazottain belül látható. 13. internal: csakis a tartalmazó (és a barát) assembly(ke) –n belül látható. Egy konstans mez t nem lehet explicite statikusnak jelölni. Most már felhasználhajuk az új konstruktort: Dog d = new Dog("Rex". tulajdonságokat használtunk fel (err l hamarosan). 13.. ilyenek voltak a Dog osztályon belüli name és age változók.. 13. felesleges minden alkalommal külön példányt készíteni bel le). Az eddigi példáinkban is használtunk már adattagokat.. mivel a fordító egyébként is úgy fogka kezelni (ha egy adat minden objektumban változatlan.Forrás: http://www. osztályok/struktúrák esetében az alapértelmezés. Egy parciális osztály definiciója több részb l (tipikusan több forrásfile –ból) is állhat.*/ } } . //file1. 6). protected internal: a protected és internal keveréke. parciális osztályokat (partial class). private: csakis a tartalmazó osztályon belül látható.2 Adattagok Az adattagok vagy mez k olyan változók. Az adattagokon használhatjuk a const típusmódosítót is.cs partial class PClass { public void DoSomeThing(){ /*.cs partial class PClass { public PClass() { /*. Ezek a mez k pontosan ugyanúgy viselkednek mint a hagyományos konstansok.3 Láthatósági módosítók A C# nyelv ötféle módosítót ismer: public: az osztályon/struktúrán kív l és belül teljes mértékben hozzáférhet .. A mez kön alkalmazható a readonly módosító is. a leszármazottak sem láthatják.*/ } } //file2. amelyeket egy osztályon (vagy struktúrán) belül deklaráltunk.hu 62 Ún.doksi. hasonlóan az el z fejezetben említett inicializáláshoz. ekkor a deklarációnál értéket kell adnunk a mez nek. Dog newDog = new Dog(d). ez két dologban különbözik a konstansoktól: az értékadás elhalasztható a konstruktorig és az értékül adott kifejezés értékének nem kell ismertnek lennie fordítási id ben.

cs partial class PMClass { //definíció partial public void PartialMethod(string param) { /*. adattagokat és más osztályokat is. Egy beágyazott osztály hozzáfér az t tartalmazó osztály minden tagjához (beleértve a private elérés tagokat és más beágyazott osztályokat is). A C# a parciális osztályokat f ként olyan esetekben használja.doksi.*/ } } A partial kulcsszót ilyenkor is ki kell tenni minden el fordulásnál.Forrás: http://www. //a beágyazott osztály nem látható class Outer { private class Inner { } } //de most már igen class Outer { . Ezeket a „bels ” osztályokat beágyazott (nested) osztálynak nevezzük. 13. ekkor a metódus deklarációja és definíciója szétoszlik: //file1. Csakis parciális osztály tartalmazhat parciális metódust.cs partial class PMClass { //deklaráció partial public void PartialMethod(string param).5 Beágyazott osztályok Egy osztály tartalmazhat metódusokat. akkor a „küls ” osztályon kereszt l érhetjük el. a grafikus felület alkalmazásoknál a kezdeti beállításokat az InitializeComponent() metódus végzi. de ha mégis publikus elérés nek deklaráljuk.hu 63 Az osztály összes „darabjánál” kötelez kitenni a partial kulcsszót. ezt teljes egészében a fordító készíti el).. amikor az osztály egy részét a fordító generálja (pl.0 már engedélyezi parciális metódusok használatát is. A következ téma megértését el segítheti a következ Metódusok cím rész elolvasása A C# 3. } //file2.. Egy ilyen osztályt általában elrejtünk.

//az objektumra már nem mutat semmi. A C# objektumai által elfoglalt memóriát a szemétgyüjt (Garbage Collector) szabadítja fel. azaz el re nem tudjuk megmondani.Inner(). } } } Person p = new Person { Name = "Istvan" }.Collect(). de ez nem ajánlott: MyClass mc = new MyClass(). 13. //mc egy MyClass objektumra mutat mc = null.doksi. Ilyen esetekben vagy egy nyílvános tagra. felszabadítható A szemétgyüjt m ködése nem determinisztikus.Forrás: http://www. ha létezik paraméteres konstruktor. 13. ugyanakkor kézzel is meghívható. abban az esetben ha az objektumra nincs érvényes referencia: MyClass mc = new MyClass().hu 64 public class Inner { } } //pélányosítás: Outer. .6 Objektum inicializálók A C# 3. GC. } set { this. akkor is használhatjuk ezt a beállítási módot. vagy egy tulajdonságra hivatkozunk (ez utóbbit használtuk). public Person() { } public string Name { get { return name. hogy mikor fut le. a következ képpen: Osztaly valtozo = new Osztaly(/*parameterek*/) { }. GC.0 objektumok példányosításának egy érdekesebb formáját is tartalmazza: class Person { private string name.WaitForPendingFinalizers().7 Destruktorok A destruktorok a konstruktorokhoz hasonló speciális metódusok amelyek az er források felszabadításáért felelnek. mc = null. Természetesen.name = value.Inner innerClass = new Outer.

neve megegyezik az osztályéval és nem nem lehet semmilyen módosítója vagy paramétere. A GC miel tt megsemmísiti az objektumokat meghívja a hozzájuk tartozó destruktort... hogy elvégezze a gy jtést (de legkés bb akkor m ködésbe lép amikor elfogy a memória).")..Object –t destruktorral felülírunk. Vegyük a következ kódot: class DestructableClass { public DestructableClass() { } ~DestructableClass() { } } A destruktor neve tilde jellel (~) kezd dik. másnéven Finalizert. } } class Derived { ~Derived() { Console. } } Ezután: . A fenti kód valójában a következ formában létezik: protected override void Finalize() { try { } finally { base.doksi. metódusát (ez legalább a System. A Finalize() el ször destruktorban általunk megszabott módon).WriteLine("Derived destruktor.WriteLine("Base destruktor.").hu 65 A GC a hatékonyság szempontjából a legjobb id t választja ki arra.Object –é nem ér: l a Finalize() metódust.Finalize(). amelyet a felszabadítja az osztály er forrásait (a aztán meghívja az sosztály Finalize() lesz) és így tovább amíg a lánc végére class Base { ~Base() { Console. } } Minden típus örökli a System.Forrás: http://www.

hu 66 class Program { static public void Main() { Derived d = new Derived().doksi.Forrás: http://www. ezek a következ ek: Egy osztálynak csak egy destruktora lehet A destruktor nem örökölhet A destruktort nem lehet direkt hívni. ez mindig automatikusan történik Destruktora csakis osztálynak lehet.ReadKey() –nek elindul a GC és a kimenet a következ lesz: Derived destruktor… Base destruktor… Els ként a leszármazott. ahogy az várható is volt. majd az sobjektum semmisül meg. } } Amikor elindítjuk a programot nem történik semmi. Console.ReadKey(). A destruktorokra vonatkozik néhány szabály. struktúrának nem . Amikor viszont lenyomunk egy gombot a Console.

ha a kutyánk nem csak létezne a semmiben. } public void Sleep(int time) { Console. } } Most már tud enni és aludni. ez az ún. is): . Kanyarodjunk vissza az eredeti szálhoz! Az alvás metódusunk nem tér vissza semmivel. azok a „kisegít ” függvények amelyek nem kapcsolódnak közvetlenül az objektum életciklusához."). stb. de szeretnénk lekérdezni az állapotukat is és felhasználni ezeket az értékeket. int a) { name = n.).").. private int age. Mire jó a visszatérési érték? Az objektumainkon nem csak m veleteket végzünk. etc. ezt a void kulcsszóval jelezzük. A függvényeket az objektum neve után írt pont operátorral hívhatjuk meg (ugyanez érvényes a publikus adattagokra.WriteLine("A kutya most eszik. time).. Visszatérési értékkel rendelkez metódust használhatunk minden olyan helyen. public Dog(string n. Ezután a metódus neve és paraméterlistája következik. hogy meghívhassuk.. logikai kifejezések. ahol a program valamilyen típust vár (értékadás. ez publikus lesz. metódus paraméterei.doksi.Forrás: http://www. age = a.WriteLine("A kutya {0} orat alszik.". a két legfontosabb dolgot.. általános típus (a kés bbiekben még lesz róla szó).WriteLine("A kutya {0} orat alszik. Metódusok Bizonyára szeretnénk. Ezután a metódus visszatérési értékének a típusa jön. } public void Eat() { Consol. hanem csinálna is valamit.hu 67 14.. amivel már találkoztunk az int.Parse(…) metódus kapcsán. } El ször megadjuk a láthatóságot.. Nézzük az alvást: public void Sleep(int time) { Console. a metódusokal m velteket tudunk végezni az objektumon): class Dog { private string name. Egy másik lehet ség.. de hasznosak és logikailag az osztály hatókörébe tartoznak. Készítsünk néhány metódust (ugye senki nem felejtette el. tulajdonságokra.

.hu 68 Dog d = new Dog("Rex". //10 } } . de a változás csakis a metóduson belül lesz észlelhet . Utóbbi esetben van azonban egy kivétel. Az értéktípusok alapértelmezetten érték szerint adódnak át.. A C# nyelvben paraméterek átadhatunk érték és cím szerint is.WriteLine(a. míg a referenciatípusoknál a cím szerinti átadás az el re meghatározott viselkedés.. az a program futására nem hat. El bbi esetben egy teljesen új példány jön létre az adott osztályból. Ha ezt mégis megtesszük. tehát az eredeti objektummal dolgozunk. class Program { static public void AllocArray(int[] array.. A metódus nevét és paraméterlistáját aláírásnak. using System. és a paraméter nevével hivatkozunk rájuk.Length). 100). AllocArray(a. Erre a kimenet: A kutya most eszik. A paraméterek a metóduson belül lokális változókként viselkednek.doksi. amelynek értékei megegyeznek az eredetiével. A másik esetben egy az objektumra mutató referencia adódik át valójában.Forrás: http://www. A paraméterek számát és típusait a függvény deklarációjában.és referenciatípusok különböz en viselkednek az átadás szempontjából. vessz vel elválasztva adjuk meg. int size) { array = new int[size]. vagyis paramétereket. A kutya 3 orat alszik. Console. szignatúrának vagy prototípusnak nevezzük. d.Sleep(3). Console. 2).Eat().1 Paraméterek Az objektummal való kommunikáció érdekében képesnek kell lennünk kív lr l megadni adatokat.Length). //100 } static public void Main() { int[] a = new int[10].WriteLine(array. 14. d. hogy míg a referenciatípus értékeit megváltoztathatjuk (és ez az eredeti objektumra is hat) addig magát a referenciát már nem. Egy metódusnak gyakorlatilag bármennyi paramétere lehet. Az érték. mégpedig az.

Ezt a hívás helyén is meg kell tennünk. Dog d2 = new Dog("Rinti". } } A ref kulcsszóval jelöljük meg azokat a paramétereket.Forrás: http://www. A cím szerinti átadás másik formájában nem inicializált paramétert is átadhatunk. } /* már elkészített metódusok */ public void PlayOtherDog(ref Dog other) { Console. d1.. mind a hívás helyén: using System. A használata megegyezik a ref –el.PlayOtherDog(ref d2). } } class Program { static public void Main() { Dog d1 = new Dog("Rex". amelyeknek a címét akarjuk átadni.doksi. mind a metódus prototípusánál.").hu 69 Ha mégis módosítani akarjuk a referenciatípus referenciáját. 3). class Dog { private int age. int a) { name = s. hogy a metóduson belül állítsuk be. class Dog { private int age. 2). Az els esetben az átadott objektum inicializálva kell legyen. . Kétféleképpen adhatunk át paramétert cím szerint. private string name. de ekkor feltétel.WriteLine("Ket kutya jatszik. akkor külön jeleznünk kell azt. A cím szerinti átadást a forráskódban is jelölni kell. public Dog(string s. Console. azaz a szignatúrában és a hívásnál is jelezni kell a szándékunkat. age = a.. hogy cím szerint akarjuk átadni.ReadKey(). A használandó kulcsszó az out (Nomen est omen – A név kötelez): using System.

akkor használhatunk paramétertömböket: static void Func(params int[] paramarray) { /*Itt csinalunk valamit*/ } static public void Main() { int[] array = new int[] { 1. hiszen az objektumok értékeit nem módosítottuk. 4). 3). 2)..ReadKey().WriteLine("Ket kutya jatszik.doksi. Func(array).hu 70 private string name. 3..PlayOtherDog(ref d2). 4 }."). Console. } } class Program { static public void Main() { Dog d1 = new Dog("Rex". } } Ebben a példában nem lett volna muszáj cím szerint átadnunk a paramétereket. int a) { name = s. } public void InitDog(out Dog idog) { idog = new Dog("Lassie". 2. Dog d2 = new Dog("Rinti". Amikor nem tudjuk. age = a. d1. public Dog(string s. Dog d3. } /* már elkészített metódusok */ public void PlayOtherDog(ref Dog other) { Console. } . hogy pontosan hány paramétert akarunk átadni. d2.Forrás: http://www.InitDog(out d3).

A cím és érték szerinti átadásról a következ oldalakon olvashatunk többet: http://msdn.WriteLine(s).microsoft. akkor így módosítjuk a programot: using System.microsoft. ami a parancssori paramétereket tárolja el.aspx 14. class Program { static public void Main(String[] args) { foreach(string s in args) { Console.2 Parancssori paraméterek A Main –nek létezik paraméteres változata is: using System. class Program { static public void Main(String[] args) { } } Ekkor a Main paramétere egy tömb. Pl.com/en-us/library/s6938f28.doksi.3 Kiterjesztett metódusok . } } } Erre a kimenet a következ : elso masodik harmadik 14. mint egy normális tömb.exe elso masodik harmadik Ha ki szeretnénk iratni a paramétereket.hu 71 A paramétertömböt a params kulcsszóval jelezzük.aspx http://msdn.com/en-us/library/9t0za5es. ezután a metódus belsejében pontosan úgy viselkedik. így futtatjuk a programot: program.Forrás: http://www.

Nemcsak osztályt. //10 elem tömb ma. Ha nem így teszünk akkor a speciálisabb paraméter metódus fog meghívódni.4 Gyakorló feladatok 1.WriteLine(s). 2. Készítsünk metódusokat a kutya osztályhoz. stb.0 lehet séget ad arra. amelyekkel elvégezhetjük a szokásos teend ket: pl. Ha két kiterjesztett metódus ugyanazzal a szignatúrával rendelkezik. s. amely meghatározza a kiterjesztett osztály típusát. Egy kiterjesztett metódust nem defiinálhatunk beágyazott osztályban.: fürdetés. A kiterjesztett metódusokról a következ oldalon olvashatunk többet: http://msdn.: MyArray ma = new MyArray(10). Ezután a metódust így használjuk: string s = "abcd".Forrás: http://www. Egészítsük ki a string típust egy metódussal.Print(). //kiírjuk 3. akkor a hagyományos statikus úton kell hívnunk ket. anélkül.aspx 14.com/en-us/library/bb383977. Írjunk hozzá egy metódust. hogy egy már létez típushoz új funkciókat adjunk. . Egy kiterjesztett metódus (extension method) minden esetben egy statikus osztály statikus metódusa kell legyen. amely kiírja a tömböt a konzolra. amely kiírja egy egész számokból (int) álló tömb elemeit (a szükséges statikus osztályokról a következ fejezet szól). //vagy hagyományos statikus metódusként: StringPrinter. de interfészt is b víthetünk. sétáltatás. } } A this módosító után a paraméter típusa következik.doksi.microsoft. Az osztály konstruktorában foglaljuk le a tömböt (a méretét a konstruktor paramétere tartalmazza). amely egy tömböt tartalmaz.hu 72 A C# 3. Készítsünk egy kiterjesztett metódust. Pl.Print(s). ami kiírja a képerny re az adott karaktersorozatot: static public class StringPrinter { static public void Print(this string s) { Console. Készítsünk osztályt. hogy azt közvetlenül módosítanánk.Print().

. pl. akárcsak az adattagokból osztályszinten egy darab létezik. Az összes statikus tagból összesen egy darab létezik. ha szeretnénk számolni. } } A példában a statikus adattag értékét minden alkalommal megnöveljük eggyel. azaz minden példány saját példánnyal rendelkezik.. . Így megkülönböztetünk példány (instance) és statikus tagokat. hogy minden példány egy közös tagot használjon. illetve miel tt a statikus konstruktor – ha van – lefut.1 Statikus adattagok Statikus adattagot (és metódust ld. Animals.2 Statikus metódusok A statikus metódusokból.hu 73 15. Statikus tagok A hagyományos adattagok és metódusok objektumszinten léteznek. ugyanis nem definiálhatunk globális mindenki számára egyformán elérhet tagokat.animalCounter). Statikus metódus létrehozása a következ képpen történik: public class Math { static public void Pi() { /*. A statikus tagokhoz az osztály nevén (és nem egy példányán) keresztül férünk hozzá: Console.WriteLine("A peldanyok szama: {0}". hogy el ször hozzáférnénk.Forrás: http://www. A statikus tagok jelent sége a C# tisztán objektum orientáltságában rejlik. Gyakran van azonban szükségünk arra. hogy hány objektumot hoztunk létre. A statikus tagok azel tt inicializálódnak.Pi()). következ fejezet) a static kulcsszó segítségével hozhatunk létre: public Animals { static public int animalCounter = 0. 15. Vagyis a példányok számát tároljuk el benne.doksi. Math. public Animals() { ++animalCounter. amikor meghívjuk a konstruktort.*/ } } Ezt így tudjuk meghívni: Console. Ezt váltják ki a statikus adattagok és metódusok. 15.WriteLine("Pi erteke: {0}".

A statikus konstruktornak nem lehet láthatóságot adni. A statikus konstruktor azután fut le. Örökl dés).doksi. illetve nincsenek paraméterei sem. ha nem egy példány állapotának a megváltoztatása a cél. static Person() { name = "Anonymus". Mivel ez is egy statikus metódus nem férhet hozzá a példánytagokhoz. Általában osztályokhoz kapcsolódó konstans értékek lekérdezésére használjuk: public class Math { static public double Pi { get { return 3. 15. hanem egy osztályhoz kapcsolódó m velet elvégzése. de azel tt. static class Math { static private double pi = 3. nem lehet példánykonstruktora (de statikus igen) és mindig lezárt (ld.4 Statikus konstruktor A statikus konstruktor a statikus tagok beállításáért felel. A példányon való hívás fordítási hibát jelent. ha csak és kizárólag statikus tagjai vannak. Statikus metódust általában akkor használunk. } } 15.3 Statikus tulajdonságok Ennek a fejezetnek a megértéséhez ajánlott elolvasni a Tulajdonságok cím részt A statikus tulajdonságok a C# egy viszonylag ritkán használt lehet sége. A statikus metódusok nem férhetnek hozzá a példánytagokhoz (legalábbis direkt módon nem). A fordító minden esetben ellen rzi ezeknek a feltételeknek a teljesülését.Forrás: http://www.14. Egy statikus osztályból nem hozható létre példány. } } } 15.141516.hu 74 Bármely statikus taghoz az osztályon – és nem egy példányon – kereszt l férünk hozzá. hogy a program elindult. . hogy egy példány keletkezik. Példa statikus konstruktorra: public class Person { public static string name.5 Statikus osztályok Egy osztályt statikusnak jelölhetünk.

Az összeadás és szorzás metódusok a paramétereiket a params tömbön (ld.*/ } /*Egyéb tagok*/ } 15.6 Gyakorló feladatok 1..Forrás: http://www. amelyek meghatározatlan számú paramétert várnak ciklussal olvassuk be a tagokat! . 2.hu 75 static public double Pi() { return pi. vagyis bármennyi taggal elvégezhet ek legyenek a m veletek. metódusok) kapják meg..doksi. Készítsük el a Math statikus osztályt! Az osztály tartalmazzon metódusokat a négy alapm veletre (illetve tetsz leges matematikai m veletre). } static public double Cos(double x) { /*. Az el z feladat osztályát felhasználva készítsünk egy egyszer számológépet! Azoknál a metódusoknál.

name. Hagyomány . Minden esetben amikor az adott tulajdonságnak értéket adunk egy „láthatatlan” paraméter jön létre. mint a hagyományos változók: using System. A setter már érdekesebb egy kicsit.name = name. hogy megsértenénk az egységbezárás elvét. Console.Forrás: http://www.hu 76 16. } } - Els ként megadtuk a láthatóságot. A tulajdonságok kív lr l nézve pontosan ugyanolyanok.name.name = value.Name). ez fogja tartalmazni a tulajdonság új értékét.name = value. class Person { private string name. aztán a visszatérési értéket és a nevet. } set { this. } } } class Program { static public void Main() { Person p = new Person("Istvan"). anélkül. A tulajdonság törzse két részre az ún.de nem kötelez hogy a tulajdonság neve az adattag nevének nagybet vel kezd d változata. getter –re és setter –re oszlik. aminek a neve value. Tulajdonságok A tulajdonságokat (vagy property –ket) a mez k közvetlen módosítására használjuk. } //Ez a tulajdonság public string Name { get { return this.WriteLine(p. public string Name { get { return this. épp úgy mint egy hagyományos metódus esetében tennénk. El bbivel lekérdezzük az adott értéket.doksi. } } A tulajdonsággal lekérdeztük a „személy” nevét. A settert így használhatjuk: class Program { static public void Main() . } set { this. public Person(string name) { this.

doksi. hogy azonnal visszadjuk/beolvassuk az adttag értékét. } private set { this. automatikus tulajdonságokkal. } } Ebben az esetben a getter megtartja az alapértelmezett publikus láthatóságát. méghozzá az. set. } } A fordító automatikusan létrehoz egy private elérés name nev adattagot és elkészíti hozzá a gettert/settert is. Console.Name). míg a setter private elérés lesz.hu 77 { Person p = new Person("Istvan"). hogy pontosan úgy m ködik mint egy változó. Van azonban egy probléma." + this. amit használhatunk és már létezik: class Person { private string Name { get." + this. Nem kell létrehoznunk sem az adattagot.Forrás: http://www.name = value. } public Person(string n) { . set. Viszont van setter.name). kiv lr l nem lehet meghívni.name). //Dezso } } Látható. } set { this. ekkor csak-írható/csakolvasható tulajdonságokról beszélünk (bár el bbi használata elég ritka). hogy csak az egyiket használjuk. A getter/setter láthatóságát külön megadhatjuk. sem a teljes tulajdonságot. ráadásul ezeknek nem kell egyezniük sem: public string Name { get { return ("Mr.name = value. a fordító mindkett t legenerálja nekünk: class Person { public string Name { get. } } Arra is van lehet ség. Egyik esetben sem vagyunk rákényszerítve.0 rendelkezik egy nagyon érdekes újítással az ún. A C# 3. p. tetszés szerint végezhetünk m veleteket is rajtuk: public string Name { get { return ("Mr.Name = "Dezso". hogy a fordítás pillanatában ez a változó még nem létezik.WriteLine(p.

hu 78 name = n. //Ez viszont igen } } .Forrás: http://www.doksi. //Ez nem m ködik Name = n.

public Names() { nameList = new ArrayList(). nameList.Add("Judit"). } public string this [int idx] { get { if(idx < nameList.ToString().Add("Eszter"). a this mutató mutat az aktuális objektumra.Count) { return nameList[idx]. amin az indexel t definiáltuk. Általában olyan esetekben használják. hogy nem névvel. } } } Ez gyakorlatilag egy „névtelen tulajdonság”. Nemcsak egy indexet adhatunk meg. Indexel k Az indexel k hasonlóak a tulajdonságokhoz..hu 79 17.Add("Istvan").: public int this [int idx1..doksi. nameList. azzal a különbséggel. illetve az index típusa sem kötött. nameList. hanem egy indexxel férünk hozzá az adott információhoz. amely maga is megvalósít egy indexel t). Pl.Add("Bela"). } else return null. Egy indexel t így implementálhatunk: class Names { private ArrayList nameList.*/ } . int idx2] { /*.Forrás: http://www. hanem tetszés szerintit. amikor az osztály/struktúra tartalmaz egy tömböt vagy valamilyen gy jteményt (vagy olyan objektumot. nameList.

rendezés. hogy létez indexekre hivatkoznuk. Gyakorló feladatok II. hogy ellen rizze. Az osztály tartalmazzon metódusokat/tulajdonságokat/indexel ket a szokásos tömbm veletek (kiírás. .hu 80 18.1 Tömb típus Valósítsunk meg egy tömb típust.Forrás: http://www. 18.doksi. Az indexel t úgy készítsük el. stb…) elvégzésére. Ha nem akkor írjon hibaüzenetet a képerny re.

rendelkeznek többek közt egy ún. d. hogy kiírjuk a képerny re a megadott üzenetet. ezért az eggyel feljebbi st kell ."). ugyanakkor megengedi több interfész impementálását (interfészekr l hamarosan). Az egyszer ség kedvéért hagyjuk ki az Állat és Kutya közti speciálisabb osztályokat: class Animal { public Animal() { } } class Dog : Animal { public Dog() { } } class Crocodile : Animal { public Crocodile() { } } A Kutya és Krokodil osztályok egyaránt megvalósítják az szegényes) funkcionalitását. } } sosztály (egyel re Ezt az új metódust az útódosztályok is örökölni fogják. A fenti példában a hívó osztály nem rendelkezik Eat() nev metódussal és nem is definiálja át annak a viselkedését (err l hamarosan). hogy a fordító a fordítás pillanatában megkapja a metódus nevét és elindul „visszafelé” az osztályhierarchia mentén. Persze ezt is meg kell határozni valahogy. Örökl dés Örökl déssel egy már létez típust terjeszthetünk ki vagy b víthetjük tetsz leges szolgáltatással. Az eredmény az lesz. ez nagy vonalakban úgy történik.doksi. hívjuk is meg valamelyik sosztályon: Dog d = new Dog(). metódustáblával. A C# csakis egyszeres örökl dést engedélyez.hu 81 19.Eat(). Készítsük el az elméleti rész példáját (Állat-Kutya-Krokodil) C# nyelven.Forrás: http://www. Honnan tudja vajon a fordító.. hogy az egyes metódushívásoknál melyik metódust kell meghívni. B vítsük ki az sosztályt: class Animal { public Animal() { } public void Eat() { Console.WriteLine("Az allat eszik. ami mutatja.. hogy egy sosztálybeli metódust kell meghívnia? A referenciatípusok (minden osztály az) speciális módon jelennek meg a memóriában.

2 Polimorfizmus Korábban már beszéltünk arról. 19. használhatja annak metódusait. public DerivedClass(int d2) : base(d2) { data2 = data. adattagjait. Például gond nélkül írhatom a következ t: Animal a = new Dog().1 Konstruktor Egy leszármazott konstruktorában meghívhatjuk az sosztály konstruktorát: class BaseClass { protected int data.hu 82 megnéznünk. A new operátor meghívása után a úgy fog viselkedni mint a Dog osztály egy példánya. ha a fordító engedné a visszafelé konverziót az ún. } } Ezt a base függvénnyel tehetjük meg és. public Base(int _data) { this. Abban az esetben ugyanis. ahol egy stípust használunk ott használhatunk leszármazottat is (pl. hogy minden olyan helyen. 19.data = _data. A C++ nyelvben sokszor jelent gondot ez a probléma. leszeletel dés (slicing) effektus lépne fel. mivel ott egy pointeren keresztül megtehet a „lebutítás”. így nem kell aggódnunk miatta. } } class DerivedClass : BaseClass { private int data2. hogy az s és leszármazottak közt az-egy reláció áll fent. Szerencsére a C# nyelvben ezt megoldották.doksi. hogy ez visszafelé nem m ködik. ahogy a példában is látszik a paramétereket is átadhatjuk. azaz az adott objektum elveszítené a speciálisabb osztályra jellemz karakterisztikáját. egy állatkertben állatok vannak. Arra azonban figyeljünk. a fordító hibát jelezne. Ez egészen a lehet „legújabb” metódusdefinícióig megy és amikor megtalálja a megfelel implementációt bejegyzi azt a metódustáblába.Forrás: http://www. Ez a gyakorlatban azt jelenti. de az állatok helyére (nyílván) behelyettesíthetek egy speciális fajt). .

WriteLine("Az allat eszik. aniArray [1].. Amit a fordító lát..Eat(). Virtuális metódust a virtual kulcsszó segítségével deklarálhatunk: class Animal { public Animal() { } public virtual void Eat() { Console.hu 83 19.. aniArray [0] = new Animal(). így az leszármazottai is átdefiniálhatják a m ködését. } } A leszármazott osztályban az override kulcsszóval mondjuk meg a fordítónak. Egy override –al jelölt metódus automatikusan virtuális is lesz. A kimenet: A kutya eszik… Mi történik vajon a következ esetben: Animal[] aniArray = new Animal[2].WriteLine("A kutya eszik. hogy készítettünk egy Animal típusú elemekb l álló tömböt és.doksi. az az. } } class Dog { Public Dog() { } public override void Eat() { Console. aniArray [0].3 Virtuális metódusok A virtuális (vagy polimorfikus) metódusok olyan sosztályok olyan metódusai amelyek viselkedését a leszármazottak átdefiniálhatják. Csakhogy az Eat() egy virtuális . hogy az elemein meghívtuk az Eat() metódust. hogy szándékosan hoztunk létre az sosztályéval azonos nev metódust és a leszármazott osztályon ezt kívánjuk használni mostantól. d.Forrás: http://www.Eat()..Eat().")."). aniArray [1] = new Dog(). Próbáljuk ki az új metódust: Dog d = new Dog().

} } Ezután a Dog utódjai már nem látják az eredeti Eat() metódust.. hogy a fordítás hiba nélkül menne végbe.Forrás: http://www.. azaz a keresés legkés bb az els virtuális változatnál megáll. Ez az ún."). Már beszéltünk arról. a fordító megkeresi a legkorábbi implementációt. de a fordító figyelmezet minket. Viszont készíthetünk bel le virtuális metódust. A kimenet így már nem lehet kétséges: Az állat eszik. A kutya eszik. amikor a fordító felépíti a metódustáblát. visszatérési értéke és láthatósága meg kell egyezzen azzal amit át akarunk definiálni. ráadásul van leszármazottbeli implementációja is.. amely átdefinálja az eredeti viselkedést. Ezt a jelenséget árnyékolásnak (shadow) nevezik. és most már azt is tudjuk. Ezt a new kulcsszóval tehetjük meg: public class Animal { public Animal() { } public virtual void Eat() { Console. Természetesen mi azt szeretnénk.. hogyan épül fel a metódustábla.WriteLine("Az allat eszik.hu 84 metódus.").doksi. és ezáltal ütemezi a metódushívásokat. hogy nem ismerjük az sosztály felületét és a hagyományos módon deklaráljuk az Eat() metódust (ugye nem tudjuk. hogy már létezik). ha meghívnánk akkor az új metódus futna le..WriteLine("A kutya eszik. hogy eltakarjuk az öröklött metódust. Az utódosztály metódusának szignatúrája. Azaz. így tájékoztatnunk kell a fordítót.. Tegyük fel. vagyis a new virtual kulcsszavakkal ellátott metódus lesz az új metódussorozat gyökere. És valóban. } } public class Dog : Animal { public Dog() { } public new void Eat() { Console. kés i kötés (late binding). hogy szándékosan takarjuk el az eredeti implementációt. és ezt explicit jelöltük is az override kulcsszóval. amelyet az utódjai már kedvükre használhatnak. .. Így a fordító el tudja dönteni a futásidej típust. hogy az els ilyen implementáció egy virtuális metódus lesz. a new módosítóval ellátott metódus új sort kezd. Ekkor a program ugyan lefordul..

de ezt nem kell külön jelölni)..WriteLine("Egy allat eszik.WriteLine("Egy kutya eszik. . absztrakt és override –al jelölt tagokat (az utolsó kett egyébként virtuális is lesz.. azaz megtilthatjuk.Forrás: http://www.").. ekkor a leszármazottak már nem definiálhatják át a m ködését: class Animal { public Animal() { } public virtual void Eat() { Console..doksi. 19."). } } class Dobermann : Dog { public Dobermann() { } public override void Eat() //Ez nem fog lefordulni { //valamit csinálunk } } Nem jelölhetünk virtuálisnak statikus.4 Lezárt osztályok Egy osztályt lezárhatunk.hu 85 A következ fejezet lezárt osztályaihoz hasonlóan egy metódust is deklarálhatunk lezártként. hogy új osztályt származtassunk bel le: //Ez már egy végleges osztály public sealed class Dobermann : Dog { } //Ez nem fog lefordulni public class MyDobermann : Dobermann { } A struktúrák (róluk kés bb) alapértelmezetten lezártak. } } class Dog : Animal { public Dog() { } public sealed override void Eat() { Console.

hogy közös felületet biztosítsunk a leszármazottainak: abstract class Animal { abstract public void Eat(). A fordító feladata az.. Egy absztrakt osztály csak a fordítás közben absztrakt. hogy betartassa a rá vonatkozó szabályokat.hu 86 19. hogy mind az osztály. mint a hagyományos nem-virtuális függvények.. public Animal(int _age) { this.. hogy egy absztrakt osztályt nem példányosíthatunk még lehet konstruktora. ugyanakkor a metódus nem virtuális és nincs definíciója.age = _age."). A létrehozásának célja az..5 Absztrakt osztályok Egy absztrakt osztályt nem lehet példányosítani. } class Dog : Animal { public Dog(int _age) : base(_age) { } //Egyéb metódusok… } . mégpedig azért. Annak ellenére. Ezek a szabályok a következ ek: absztrakt osztályt nem lehet példányosítani abszrakt metódusnak nem lehet definíciója a leszármazottaknak definiálnia kell az öröklött absztrakt metódusokat. a lefordított kódban teljesen normális osztályként szerepel. } } Látható.WriteLine("A kutya eszik. mind a metódus absztraktként lett deklarálva. } class Dog : Animal { Animal() { } public override void Eat() { Console. } //Egyéb metódusok. ezek pont úgy viselkednek. hogy beállíthassuk vele az adattagokat: abstract class Animal { protected int age.doksi. Az öröklött absztrakt metódusokat az override kulcsszó segítségével tudjuk definiálni (hiszen virtuálisak. virtuális metódusokkal. Amennyiben egy osztálynak van legalább egy absztrakt metódusa az osztályt is absztraktként kell jelölni.Forrás: http://www. Absztrakt osztály tartalmazhat nem absztrakt metódusokat is. még ha nem is látszik).

//Ez m ködik . akkor az új osztálynak nem kell megvalósítania az absztrakt metódusokat. Ha egy leszármazottjából származtatok. animArray[1] = new Crocodile(100). A fordító csak azt fogja megvizsgálni. animArray[0] = new Dog(2). hiszen ténylegesen egyszer sem példányosítottuk az absztrakt sosztályt. Természetesen a következ esetben nem fordulna le: animArray[0] = new Animal(2).. } } public class Dobermann : Dog { public Dobermann() { } } Dobermann d = new Dobermann().WriteLine("A kutya eszik.Eat(). d. viszont használhatja azokat: public abstract class Animal { public Animal() { } public abstract void Eat().doksi.. hogy mi van a new operátor jobb oldalán."). } public class Dog : Animal { public Dog() { } public override void Eat() { Console. Az absztrakt osztályok rendelkeznek néhány gyenge ponttal. hogyan m ködik a következ példában a polimorfizmus elve? : Animal[] animArray = new Animal[2].Forrás: http://www.hu 87 Vajon. az alaposztály nem érdekli. Ennek a kódnak hiba nélkül kell fordulnia.

A tagoknak nincs külön láthatóságuk. A következ forráskód megmutatja. IFace2 { /*. IFace1. hogy nincs definíció. addig bármennyi intefészt megvalósíthat."). Egy interfész a következ ket tartalmazhatja: metódusok.. csak deklaráció..hu 88 20. Interfészt a következ képpen deklarálunk: public interface IAnimal { void Eat(). indexel k és események. Egy másik el nye az interfészek használatának.Forrás: http://www. hogyan valósíthatjuk meg a fenti interfészt: public interface IAnimal { void Eat(). egy interfész nem köthet közvetlenül egy osztályhoz.doksi. } } Fontos. Interfészek Az interfészek hasonlóak az absztrakt osztályokhoz. mindössze el ír egy mintát. hogy amennyiben egy másik osztályból is származtatunk. abban az értelemben. akkor a felsorolásnál az sosztály nevét kell el revenni.*/ } . A megvalósító osztály dolga lesz majd megvalósítani a tagjait.WriteLine("A kutya eszik. amit meg kell valósítania az osztálynak. } Az interfész nevét általában nagy I bet vel kezdjük. A nagy különbség a kett közt az.. hogy míg egy osztálynak csak egy se lehet. hogy meghatározzák egy soztály viselkedését. hogy míg el bbi eleve meghatároz egy osztályhierarchiát.. ehelyett minden tagra az interfész elérhet sége vonatkozik. felületét. } public class Dog : IAnimal { public Dog() { } public void Eat() { Console. tulajdonságok. utána jönnek az interfészek: public interface IFace1 { } public interface IFace2 { } class Base { } class Derived : Base. Ezen felül interfészt használhatunk struktúrák esetében is. Látható.

IDog { public Dog() { } public void Eat() { Console...*/ } class EnumClass : IEnumerable { /*..").*/ } IEnumerable ie = new EnumClass().hu 89 Egy interfészt származtathatunk más interfészekb l: public interface IAnimal { void Eat()..."). //Ez m ködik Az is és as operátorokkal megtudhatjuk.. } public void Vau() { Console.WriteLine("Vau-vau. } public class Dog : IAnimal.doksi.. . } public interface IDog : IAnimal { void Vau(). hogy egy adott osztály megvalósít –e egy interfészt: public interface IMyIf { } class MyIClass : IMyIf { } MyIClass mic = new MyIClass().WriteLine("A kutya eszik.. } } Egy adott interfészt megvalósító objektumot implicit átkonvertálhatjuk az interfész „típusára”: public IEnumerable { /*.Forrás: http://www.

az as pedig null –t ad vissza.Method(). ITwo { IOne.. ezért konverziót kell végrehajtanunk: MyClass mc = new MyClass(). iface.Method(). 20. így valahogy meg kell ket különböztetnünk. ((IOne)mc).WriteLine("Az osztaly megvalositja az interfeszt.Method(). Ennek kiküszöbölésére explicit módon megadhatjuk a megvalósítani kívánt funkciót: public interface IOne { void Method().WriteLine("Az osztaly megvalositja az interfeszt. az névütközéshez is vezethet. } public class MyClass : IOne."). ((ITwo)mc).Method() { } } A két Method() szignatúrája megegyezik.hu 90 if(mic is IMyIf) { Console.. } Az is használata egyértelm . IOne iface = (IOne)mc. Ekkor explicit konverziót hajtunk végre és a megfelel metódust.Forrás: http://www. } IMyIf test = mic as IMyIf. interfészen hívjuk meg a . Egy másik lehet ség: MyClass mc = new MyClass().doksi. } public interface ITwo { void Method().Method() { } ITwo..1 Explicit interfészimplementáció Ha több interfészt is implementálunk.. ha a konverzió sikertelen volt. Ekkor azonban a függvényhívásnál is problémába ütközünk. if(test != null) { Console.").

..."). } } Ez esetben nyílván használnuk kell a new kulcsszót annak jelölésére.WriteLine("A dobermann eszik."). } public class Dog : IAnimal { public Dog() { } public virtual void Eat() { Console.doksi. de az utódnál is jelöljük a megvalósítást: public class Dobermann : Dog. IAnimal { public Dobermann() { } public new void Eat() { Console.2 Virtuális tagok Egy interfész tagjai alapértelmezés szerint lezártak. amennyiben nemcsak az snél. Ezután az osztály leszármazottjai tetszés szerint módosíthatják a definíciót.WriteLine("A kutya eszik. . de a megvalósításnál jelölhetjük ket virtuálisnak..Forrás: http://www. } } public class Dobermann : Dog { public Dobermann() { } public override void Eat() { Console..WriteLine("A dobermann sokat eszik. hogy eltakarjuk az s megvalósítását. } } Egy leszármazott újraimplementálhatja az adott interfészt."). a már ismert override kulcsszóval: public interface IAnimal { void Eat().hu 91 20..

és annak vissza kell adnia az osztályt IEnumerator –ként (ld.Forrás: http://www. public Animal(string _name) { this. és már tudjuk. Mindkett a System. implicit konverzió). amelyen megvalósítjuk a két interfészt. és ami tartalmaz egy Animal objektumokból álló listát: .name. } } } Most készítsünk egy osztályt.hu 92 20.doksi. Els ként nézzük az IEnumerable interfészt: public interface IEnumerable { IEnumerator GetEnumerator(). } Ez a foreach –nek fogja szolgáltatni a megfelel felületet. hogy csak olyan osztályokon képes végigiterálni. amelyek megvalósítják az IEnumerator és IEnumerable interfészeket. Végül a Current (read-only) tulajdonság az aktuális pozicióban lév elemet adja vissza. hiszen minden típusra m ködnie kell (létezik generikus változata is. ugyanis a ciklus meghívja a metódusát. Használjuk az Animal osztályunk egy kissé módosított változatát: public class Animal { private string name. Ezért kell megvalósítani egyúttal az IEnumerator interfészt is. } public Name { get { return this. de ha a végére ért akkor false értékkel tér vissza. Korábban már találkoztunk a foreach ciklussal.3 Gyakorlati példa 1. object Current { get.Collections névtérben található. void Reset(). de err l kés bb). ami így néz ki: public interface IEnumerator { bool MoveNext().name = _name. } } A MoveNext() a következ elemre mozgatja a mutatót. azaz -1 –re. Ennek object típussal kell visszatérnie. A Reset() alapértelmezésre állítja a mutatót. ha tudja.

} } public void Reset() { currPosition = -1. public AnimalContainer() { container. felvettünk egy ArrayList –et. } Ezután használhatjuk is az osztályt: AnimalContainer ac = new AnimalContainer().Add(new Animal("Rex")).Add(new Animal("Cheetah")). nem pedig külön tettük ezt.hu 93 public class AnimalContainer : IEnumerable.Add(new Animal("Rin-Tin-Tin")). Reset()). } . container. container. } } Ez persze még nem az egész osztály. private int currPosition = -1. IEnumerator { private ArrayList container = new ArrayList(). Készítsük el az IEnumerator által igényelt metódusokat: public bool MoveNext() { return (++position < container.Forrás: http://www.Count) } public object Current { get { return container[currPosition].WriteLine(a.Name). illetve deklaráltunk egy egész számot. amiben eltároljuk az objektumokat.doksi. hogy rögtön a new –al hoztunk létre új objektumokat. ami az aktuális pozicíót tárolja el és kezd értéké l -1 –et adtunk (ld. A konstruktorban látható. } Végül az IEnumerable interfészt valósítjuk meg: public IEnumerator GetEnumerator() { return (IEnumerator)this. foreach(Animal a in ac) { Console.

A generikus List típusmak is van rendez metódusa. list. . ha egyenl és 1 –et ha nagyobb. Az IComparable egyetlen metódussal a CompareTo() –val rendelkezik. amelyre gyakran van szükségünk. Ez a metódus -1 –et ad vissza ha a hívó fél kisebb.i < 10. A használata: List<ComparableClass> list = new List<ComparableClass>().Value).. public ComparableClass(int val) { this. for(int i = 0. hiszen k mind megvalósítják ezt az interfészt.hu 94 20. } public int Value { get { return value.Forrás: http://www.Add(new ComparableClass(r.. } Console. } } Az osztályban a beépített típusok CompareTo() metódusát használtuk. A második gyakorlati példánkban az IComparable interfészt fogjuk megvalósítani.value = val. Random r = new Random().CompareTo(c.doksi.Value). return value.++i) { list.Sort(). amely egy object típust kap paraméteréül: class ComparableClass : IComparable { int value.WriteLine("A rendezett lista:"). } foreach(ComparableClass c in list) { Console.WriteLine(c.4 Gyakorlati példa 2. } } public int CompareTo(object o) { if(o is ComparableClass) { ComparableClass c = (ComparableClass)o.Next(1000))). Ez az interfész általában olyan adatszerkezeteknél követelmény amelyek az elemeiken megvalósítanak valamilyen rendezést. } else throw(new Exception("Nem megfelelo objektum.")). amely ezzel a metódusal dolgozik. 0 –át.

de jóval rugalmasabb az IComparer interfész.hu 95 foreach(ComparableClass c in list) { Console.Value).. A List rendezésénél megadhatunk egy összehasonlító osztályt is. . ez a Compare() amely két object típust vár paramétereként: class ComparableClassComparer : IComparer { public int Compare(object x. amely megvalósítja az IComparer interfészt. hogy ismernénk a bels szerkezetét). } } Ezután a következ képpen rendezhetjük a listát: list. object y) { if(x is ComparableClass && y is ComparableClass) { ComparableClass _x = (ComparableClass)x. return _x. } else throw(new Exception("Nem megfelelo parameter.Sort(new ComparableClassComparer()). Az IComparer el nye. hogy nem köt dik szorosan az osztályhoz (akár anélkül is megírhatjuk.").CompareTo(_y).doksi. } Hasonló feladatot lát el. Most is csak egy metódust kell elkészítenünk.. ComparableClass _y = (ComparableClass)y.WriteLine(c. így többféle megvalósítás is lehetséges.Forrás: http://www.

stb…). amikor egy mátrix típust valósítunk meg. Jó lenne. ugyanis engedi az operátortúlterhelést. ha az összeadás. m veleteket úgy tudnánk végrehajtani.Add(m2). stb.hu 96 21. } } . Matrix m1 = new Matrix(). A túlterhelhet operátorok listája: +(unáris) -% >> >= -(unáris) + & == <= ! | != ~ * ^ > ++ / << < A C# nyelvben a paraméterek statikus metódusok. } public int Value { get { return value. } } public static MyInt operator+(MyInt lhs. Egy egyszer példa: class MyInt { int value. string. //írhatjuk ezt Matrix m4 = m1 + m2. hogy az általunk készített típusok hasonló funkcionalitással rendelkezzenek mint a beépített típusok (int. szorzás. nem pedig metódushívásokkal. //ehelyett Matrix m3 = m1.value = _value.Forrás: http://www. vagyis egy adott operátort tetszés szerinti funkcióval ruházhatunk fel az osztályunkra vonatkoztatva. Vegyük pl.doksi. kivonás. Operátor túlterhelés Nyílván szeretnénk. Szerencsére a C# ezt is lehet vé teszi számunkra.Value + rhs. Matrix m2 = new Matrix(). paramétereik az operandusok. visszatérési értékük pedig az eredmény. azt a példát. public MyInt(int _value) { this.Value). MyInt rhs) { return new MyInt(lhs. mint egy egész szám esetében.

} public int Value { get { return value. Mivel definiáltunk az osztályunkon egy saját operátort így a fordító tudni fogja. hogy a két elem egyenl –e. } } static public bool operator==(MyEqual lhs.Value == rhs. MyEqual rhs) { return !(lhs == rhs). de mi a nem-egyenl ségre vagyunk kíváncsiak. MyInt m2 = new MyInt(11).value = _value. MyInt sum = m1 + m2.Forrás: http://www. utalva a jobb és baloldali operandusra. ami pontosan ezt a választ adja meg).hu 97 A ’+’ operátort m ködését fogalmaztuk át. Ezek egyike az. Tehát ha a következ t írom: MyInt m1 = new MyInt(10). . hogy ha túlterheljük az egyenl ség operátort (==) akkor definiálnunk kell a nem-egyenl (!=) operátort is: class MyEqual { int value. 21. ezért tagadjuk az eredményt.operator+(m1. } static public bool operator!=(MyEqual lhs. els ként megvizsgáljuk.doksi.Value). végül pedig megvalósítjuk a mátrix osztályt és operátorait. hogy azt használja és átalakítja az utolsó sort a következ re: MyOnt sum = MyInt. public MyEqual(int _value) { this. } } A nem-egyenl operátor esetében a saját egyenl ség operátort használtuk fel (a megvalósítás elve nem feltétlenül világos.1 Egyenl ség operátorok A C# nyelv megfogalmaz néhány szabályt az operátortúlterheléssel kapcsolatban. m2). A következ kben megnézünk néhány operátort. A paraméterek (operandusok) nevei konvenció szerint lhs (left-hand-side) és rhs (right-hand-side). MyEqual rhs) { return (lhs.

Természetesen szeretnénk.2 Relációs operátorok Hasonlóan a logikai operátorokhoz a relációs operátorokat is csak párban lehet elkészíteni. ezért meg kell majd gy zödnünk arról. ha a saját típusunk ilyesmire is képes legyen. interfészekkel foglalkozó fejezet nyújt b vebb információt.Forrás: http://www. } return this == (MyEqual)o. ami a CLS kompatibilitást hívatott meg rizni. Az Equals() mellett illik megvalósítani a szintén az object –t l öröklött GetHashCode() metódust is.3 Konverziós operátorok A C# a sz kebbr l tágabbra konverziókat implicit módon (azaz különösebb jelölés nélkül). biteltolás.hu 98 Ezekhez az operátorokhoz tartozik az object típustól örökölt virtuális Equals() metódus is.value = _value. stb…): class MyHash { int value. Mivel ez egy példány tag ezért a this –t használjuk az objektum jelölésére. >=). public MyHash(int _value) { this. Ezekr l az el z . } } 21. } public override int GetHashCode() { return value. míg a tágabbról sz kebbre kovertálást explicite (ezt jelölnünk kell) végzi. err l kés bb még lesz szó. 21. így az osztály használható lesz gy jteményekkel és a HashTable típussal is. és bizony . egyetlen object típusú paramétert vár.doksi. Az Equals azonban egy kicsit különbözik. vagyis (<. Ebben az esetben az IComparable és IComparable<T> interfészek megvalósítása is szükséges lehet a különböz gy jteményekkel való együttm ködés érdekében. amin meghívtuk a metódust. >) és (<=.: hatványozás. A fenti esetben ezt a metódust is illik megvalósítani. hogy valóban a saját objektumunkkal van –e dolgunk: public override bool Equals(object o) { if(!(o is MyEqual)) { return false. } Az is operátorral ellen rizzük a futásidej típust. A legegyszer bb implementáció visszad egy számot az adattag(ok)ból számolva (pl.

public MyConversion(int _value) { this. hogy a konverziós operátorok mindig statikusak.4 Kompatibilitás más nyelvekkel Mivel nem minden nyelv teszi lehet vé az operátortúlterhelést ezért a CLS javasolja.doksi. } public int Value { get { return value. } } . } } Ezt most így használhatjuk: int x = 10. //implicit konverzió MyConversion mc2 = (MyConversion)y.value = _value. hogy készítsük el a hagyományos változatot is: class Operators { private int value. } } public static implicit operator MyConversion(int rhs) { return new MyConversion(rhs). MyConversion mc1 = x. } public static explicit operator MyConversion(long rhs) { return new MyConversion((int)rhs). 21. } public int Value { get { return value. //explicit konverzió Fontos. long y = 12.intValue = _value. public Operators(int _value) { this. Ezeknél az operátoroknál az implicit illetve explicit kulcsszavakkal fogjuk jelölni a konverzió típusát: class MyConversion { int value.Forrás: http://www. amit túlterhelhetünk.hu 99 erre van operátor.

} public int N { get { return matrix. } } static public Matrix operator+(Matrix lhs.i < lhs.5 Gyakorlati példa Most pedig elkészítjük a mátrix típust: class Matrix { int[. } set { matrix[idxn.N != rhs.M) return null.M != rhs. } } .j < lhs.N.Value). for(int i = 0. } } public int M { get { return matrix.Value + rhs. } } 21. Matrix result = new Matrix(lhs. lhs. } static public Operators Add(Operators lhs. Operators rhs) { return new Operators(lhs.N. Matrix rhs) { if(lhs.M). } } public int this[int idxn.M. j]. idxm].++j) { result[i. idxm] = value. Operators rhs) { return new Operators(lhs. } } return result. int m) { matrix = new int[n.Forrás: http://www. int idxm] { get { return matrix[idxn.Value + rhs.Value). public Matrix(int n.GetLength(0).hu 100 static public Operators operator+(Operators lhs. j] + rhs[i.++i) { for(int j = 0.] matrix. m]. j] = lhs[i.doksi.N || lhs.GetLength(1).

Most csak az összeadás m veletet valósítottuk meg.Forrás: http://www. a többi a kedves olvasóra vár. Plusz feladatként indexellen rzést is lehet végezni az indexel nél.doksi. hogy az azonos indexeken lév értékeket öszeadjuk: 123 145 1+1 2+4 3+5 456 + 532 = (stb…) 789 211 Az összeadás m veletet csakis azonos nagyságú dimenziók mellet lehet elvégezni (3x3 –as mátrixhoz nem lehet hozzáadni egy 4x4 –eset).hu 101 Mátrixokat úgy adunk össze. . Ezt ellen riztük is az operátor megvalósításánál.

public MyStruct(int _x. Egy struktúrának nem lehet paraméter nélküli (default) konstruktora Nem használhatnak finalizer –t. } MyStruct ms = new MyStruct(). //hiba. y nem kap értéket } } értékadásáról Egy struktúra tartalmazhat referenciatípust. amelyet nem rejthetünk el. egy struktúra nem származhat más struktúrából (kivétel a System.Object). //ez is m ködik Készíthetünk saját konstruktort.hu 102 22. Ez a konstruktor a struktúra minden mez jét nullértékkel tölti fel: struct MyStruct { public int x. struktúrák esetén csakis értéktípusokkal dolgozzunk.WriteLine(ms. de ekkor minden mez gondoskodnunk kell. amely pedig a System.Forrás: http://www. // x == 0 Console. Struktúrát általában akkor használunk.doksi. viszont van néhány különbség: Egy osztály referenciatípus míg egy struktúra értéktípus Struktúrából nem lehet származtatni ill. . Struktúrák használatának van még egy el nye.Object –b l.ValueType típusból származik. Amennyire lehet kerüljük ezt a megoldást. Nem kötelez használni a new –t. int _y) { x = _x. Minden struktúra közvetlenül a System. //ez hiba int y. ekkor a verembe (az értéktípusok memóriahelyére) egy referencia kerül amely a referenciatípusra hivatkozik a halomban. Minden struktúra alapértelmezetten rendelkezik paraméternélküli konstruktorral. Egy struktúra mez it nem inicializálhatjuk: struct MyStruct { int x = 10. Struktúrák A struktúrák hasonlóak az osztályokhoz. ekkor automatikusan az alapértelmezett konstruktor hívódik meg. de nincs szükségünk egy osztály minden szolgáltatására. MyStruct x. így sok objektum esetében hatékonyabb a használata. ugyanis példányosításnál nem kell helyet foglalni a halomban. ha adatok egyszer összeségével kell dolgoznunk.x).

Ehhez a m velethez három dologra van szükségünk: kijelölni azt a programrészt ami dobhat kivételt. x[2] = 10. e. Console. class Program { static public void Main() { int[] x = new int[2]. ilyennel már találkoztunk a tömböknél. A fenti programra a következ lesz a kimenet: A hiba: Index was outside the bounds of the array.ReadKey(). elkapni azt és végül kezeljük a hibát: using System. } Console. Kivételkezelés Nyílván vannak olyan esetek. így kivételt kapunk. kivételt fog dobni. Kivételt a throw utasítással dobhatunk: .ReadKey(). class Program { static public void Main() { int[] x = new int[2].IndexOutOfRangeException –t.Forrás: http://www. ahogy elképzeltük.hu 103 23. ezért el fogjuk kapni a kivételt.IndexOutOfRangeException e) { Console.doksi. hogy ezek is blokkok. Természetesen mi azt szeretnénk. } } Itt az utolsó érvényes index az 1 lenne. mégpedig egy System. } catch(System. Ezután a program leáll. azaz a blokkon belül deklarált változók a blokkon kív l nem láthatóak). amikor túlindexeltünk: using System. akkor egy ún.Message).WriteLine("A hiba: {0}". } } A try blokk jelöli ki a lehetséges hibaforrást. amikor az alkalmazásunk nem úgy fog m ködni. a catch pedig elkapja a megfelel kivételt (arra figyeljünk. Az ilyen „abnormális” m ködés kezelésére találták ki a kivételkezelést. try { x[2] = 10. Amikor az alkalmazásunk „rossz” állapotba kerül. hogy valahogy kijavíthassuk ezt a hibát.

class Program { static public void Main() { try { throw(new System.hu 104 using System.Exception e) { Console..WriteLine("Kivétel történt…").Forrás: http://www.WriteLine("A hiba: {0}". e. e. e.WriteLine("A hiba: {0}". catch(System..Message)..1 Kivétel hierarchia Amikor kivétel dobódik. A catch –nek nem kötelez megadni a kivétel típusát. } Egyszerre több catch is állhat egymás után. akkor a vezérlést az els alkalmas catch blokk veszi át.IndexOutOfRangeException e) { Console.. Az összes kivétel ugyanis a System. hiszen az mindent elkapna.Message). } catch(System. } catch { Console.WriteLine("A hiba: {0}". } 23.doksi. } catch(System.")).Exception osztályból származik.Exception("Exception.Message).Exception e) { Console. ekkor minden kivételt elkap: try { throw(new System.Message). így ha ezt adjuk meg a catch –nél. akkor az összes lehetséges kivételt el fogjuk kapni vele: catch(System.Exception e) { Console. de ha van olyan amelyik az s kivételt kapja el. e.Exception("Exception. ha az az utolsó helyen áll.")). } . akkor a program csak akkor fog lefordulni. } } } A C++ -tól eltér en itt példányosítanunk kell a kivételt.WriteLine("A hiba: {0}".

hu 105 23. hálózati kapcsolat.doksi. //továbbadjuk throw(new System. e.ArgumentException e) { throw. hogy a kivétel keletkezése után az éppen végrehajtott programrész futása megszakad. hogy nem szabadulnak fel id ben az er források (megnyitott file. inner) { } } Az utolsó konstruktorról hamarosan lesz szó. ebben a formában nem fog lefordulni.Forrás: http://www.2 Saját kivétel készítése Mi magunk is csinálhatunk kivételt. a System. } 23. Most így használhatjuk: try { throw(new MyException("Sajat kivetel. .WriteLine("A hiba: {0}". } catch(MyException e) { Console. stb). 23. Ez hasznos olyan esetekben. amikor feljegyzést akarunk készíteni illetve. így el fordulhat.ArgumentNullException()).4 Finally blokk A kivételkezelés egy problémája. Exception inner) : base(message.Exception { public MyException() { } public MyException(string message) : base(message) { } public MyException(string message. amely hibát okozhat.").Message). illetve objektumok olyan formában maradnak meg a memóriában.Exception –ból származtatva: public class MyException : System. //vagy egy újat dobunk } Természetesen a fenti példában csak az egyik throw szerepelhetne „legálisan”.. ha egy specifikusabb kivételkezel nek akarjuk átadni a kivételt: try { } catch(System..3 Kivételek továbbadása Egy kivételt az elkapása után ismét eldobhatunk.

} } Filekezeléssel egy kés bbi fejezet foglalkozik majd. x).hu 106 Megoldást a finally – blokk használata jelent.. X erteke a kivetel utan: 11 „Valódi” er források kezelésekor kényelmesebb a using – blokk használata: using(StreamReader reader = File. . A kimenet a következ lesz: X erteke a kivetel elott: 10 Kivetel keletkezett.WriteLine("X erteke a kivetel elott: {0}".doksi.Dispose(). amely függetlenül attól. throw(new Exception())..WriteLine("X erteke a kivetel utan: {0}".txt")..txt")) { } Ez pontosan megegyezik ezzel: StreamReader reader = File..Forrás: http://www. try { Console. } catch(Exception) { Console. x). } Console.WriteLine("Kivetel keletkezett."). hogy történt –e kivétel mindig végrehajtódik: int x = 10.OpenText("text. } finally { x = 11.OpenText("text. try { } finally { if(reader != null) { ((IDisposable)reader).

összeadunk két típust a sablonmetódusban.1 Generikus metódusok Vegyük a következ metódust: static public void Swap(ref int x. Kivéve. a másik pedig jelen fejezet tárgya a generikus típusok. ref T y) tmp = x. } Ha szeretnénk. de annál kevésbé hatékonyabbak. A C# generikusai hasonlítanak a C++ sablonjaira. addig a C# ezt a m veletet futási id ben végzi el. Ennek megvalósítására két eszköz áll rendelkezésünkre. cserébe sokkal biztonságosabb a használatuk. y = tmp. hogy ez a metódus más típusokkal is m ködjön. amelyek hibásak. vagyis. míg a C++ fordítási id ben készíti el a specializált metódusokat/osztályokat. ha írunk egy generikus metódust: static { T x y } public void Swap<T>(ref T x.doksi. A T bet fogja jelképezni az aktuális típust (lehet más nevet is adni neki. Swap(ref x. metódusok. A másik eltérés az els b l következik.Forrás: http://www. ezt generikus paraméternek hívják. amelyeken nincs értelmezve összeadás. Generikusok Az objektum – orientált programozás egyik alapköve a kódújrafelhasználás. int y = 8. pl. az egyik az örökl dés. amelyek mindenképpen m ködni fognak. ref y). hogy egy adott kódrészletet elég általánosra írjunk meg ahhoz. A C# ezzel szemben kénytelen az ilyen problémákat megel zni.hu 107 24. x = y. Két fontos különbség van a kett közt. a fordító felismeri. = y. ref y). ref int y) { int tmp = x. hogy melyik típust használjuk (ezt megadhatjuk mi magunk is explicite): int x = 10. akkor bizony sokat kell gépelnünk. 24. hogy minél többször felhasználhassuk. Generikus paramétere csakis osztálynak vagy metódusnak lehet és ebb l többet is használhatnak. = tmp. //vagy Swap<int>(ref x. Ezután a metódust a hagyományos úton használhatjuk. a fordító csakis olyan m veletek elvégzését fogja engedélyezni. eredetileg a Template szóból jött). A következ példa nem fog lefordulni: . mivel a C++ fordításkor ki tudja sz rni azokat az eseteket.

hogy azt a feladatot kaptunk..2 Generikus osztályok Képzeljük el. } pointer = 0. } public object Pop() { --pointer.. pointer = 0. Így a legkézenfekv bb megoldás egy object típusú tömb lesz: class Stack { object[] t. T y) { return x + y. hogy készítsünk egy verem típust. throw(new InvalidOperationException("Ures. hogy sikeres lesz –e a végrehajtás. ++pointer. if(pointer >= 0) { return t[pointer]. ezért a fenti kód az összeadás miatt (nem feltétlenül valósítja meg minden típus) „rossz”. Azt is képzeljük el. public Stack(int capacity) { t = new object[capacity]. hogy még nem hallottunk generikusokról.hu 108 static public T Sum<T>(T x.Forrás: http://www.. } Fordításkor csak azt tudja ellen rizni. 24. readonly int size. } t[pointer] = _in.. amely bármely típusra alkalmazható. int pointer.doksi. size = capacity.")).")). hogy létez típust adtunk –e meg. így nem tudhatja a fordító. } } Ezt most a következ képpen használhatjuk: . } public void Push(object _in) { if(pointer >= size) { throw(new StackOverflowException("Tele van.

.")). a kényelem pedig amiatt. nehogy olyan kasztolással éljünk ami kivételt dob.doksi. } } Ezután akármelyik típuson könnyen használhatjuk: .WriteLine(int. readonly int size.WriteLine(Console. A hatékonyság az érték/referenciatípusok miatt csökken jelent sen (ld.i < 10.hu 109 Stack s = new Stack(10). if(pointer >= 0) { return t[pointer]. int pointer. ++pointer. for(int i = 0. } for(int i = 0. } public void Push(T _in) { if(pointer >= size) { throw(new StackOverflowException("Tele van. } pointer = 0. } t[pointer] = _in.i < 10. } M ködni m ködik. Ezeket a problémákat könnyen kiküszöbölhetjük. size = capacity.Parse(s. public Stack(int capacity) { t = new T[capacity]..Pop())). de se nem hatékony se nem kényelmes..Forrás: http://www. } public object Pop() { --pointer.")). boxing/unboxing). hogy mindig figyelni kell épp milyen típussal dolgozunk. throw(new InvalidOperationException("Ures.++i) { s.Push(i). pointer = 0.. ha genrikus osztályt készítünk: class Stack<T> { T[] t.++i) { Console.

i < 10.Pop(). amelyre normális esetben igen: class Base { } class Derived : Base { } Stack<Derived> s = new Stack<Derived>(10).hu 110 Stack<int> s = new Stack<int>(10).++i) { s.3 Generikus megszorítások Alapértelmezetten egy generikus paraméter bármely típust jelképezheti.Push(i). IFace. } for(int i = 0. c.++i) { Console.Push(i. new() {} . Stack<Base> b = s.ToString()).doksi. A deklarációnál azonban kiköthetünk megszorításokat a paraméterre. azaz egy generikus példányt nem kasztolhatunk olyan típusra. {1}".Forrás: http://www. for(int i = 0.Pop()). } A generikus típusok nem kovariánsak. c.i < 10. A megszorításokat a where kulcsszóval vezetjük be: where where where where where where T T T T T T : : : : : : alaposztály interfész osztály struktúra new() //alapértelmezett konstruktor U Egy példa: public interface IFace { } public class BaseClass { } class NewClass<T> where T : BaseClass. s. //ez nem m ködik 24. Stack<string> c = new Stack<string>(10).WriteLine("{0}.

hu 111 A NewClass osztály csak akkor példányosítható. hogy a paraméternek 24. IFace { public GoodClass() { } } NewClass<GoodClass> nc = new NewClass<GoodClass>(). Az osztály és struktúra megszorítás osztálynak/struktúrának kell lennie. Console. vagy a nyers generikus osztályból: class Base<T> { } class Derived<T> : Base<T> { } //vagy class IntDerived : Base<int> { } 24. //20 .data).data = 20.data = 10. megvalósítja az IFace interfészt és rendelkezik alapértelmezett konstruktorral: class GoodClass : BaseClass.doksi.5 Statikus tagok Generikus típusok esetében minden típushoz külön statikus tag tartozik: class MyClass<T> { public static int data. ekkor vagy az sosztály egy specializált változatából származtatunk. } MyClass<int>.WriteLine(MyClass<int>. MyClass<string>.Forrás: http://www.4 Örökl dés Generikus osztályból származtathatunk is. ha a megadott típusa a BaseClass osztályból származik.data). azt jelenti.WriteLine(MyClass<string>. //10 Console.

A SortedList kulcs – érték párokat tárol el és a kulcs alapján rendezi is ket: SortedList<string.Value).hu 112 24. } .Key.6 Generikus gy jtemények A C# 2. kv. 1). } Az Add() metódus a lista végéhez adja hozzá a praméterként megadott elemet. hiszen ez alapján történik a rendezés.Add("masodik". 3). int> kv in slist) { Console. Használhatjuk rajta az indexel operátort is. Ezeket a típusokat a tömbökhöz hasonlóan használhatjuk.Add("elso". hasonlóan az ArrayList –hez (tulajdonképpen a List<> az ArrayList generikus változata).Generic névtérben találhatóak: List<int> list = new List<int>(). int> kv in dict) { Console.Add(i). int> dict = new Dictionary<string. slist. Ezek a szerkezetek a System.i < 10.Add("elso". 1).WriteLine("Kulcs: {0}. for(int i = 0.doksi. kv. } A lista kulcsai csakis olyan típusok lehetnek. foreach(KeyValuePair<string.Add("harmadik". Value: {1}". többek közt listát és vermet. A SortedList rendezetlen párja a Dictionary<>: Dictionary<string.Add("masodik". akkor mi magunk is definiálhatunk ilyet. dict. kv.++i) { list. hanem a kulcs – érték párokat reprezentáló KeyValuePair<> objektumok.0 bevezetett néhány hasznos generikus adatszerkezetet. 2). amelyek megvalósítják az IComparable interfészt. az Interfészek fejezetet. int>(). slist. Ha ez nem igaz.WriteLine("Key: {0}. Ertek: {1}". A lista elemei tulajdonképpen nem a megadott értékek. dict. kv.Collections.Key.Value). A lista elemeinek eléréséhez is használhatjuk ezeket: foreach(KeyValuePair<string.WriteLine(i).Forrás: http://www. int>(). részletekért ld. } foreach(int i in list) { Console. slist. A következ kben megviszgálunk ezek közül néhányat. int> slist = new SortedList<string. 2).

IList és IDictionary interfészeken alapulnak. csak épp használnunk kell a generikus paraméter(eke)t.hu 113 24. így ezeket megvalósítva akár mi magunk is létrehozhatunk ilyet. Például az IEnumerable és IEnumerator is ilyen: class MyClass<T> : IEnumerable<T>.doksi. A generikus adatszerkezetek a generikus ICollection. IEnumerator<T> { } Ekkor a megvalósítás teljesen ugyanúgy m ködik mint a hagyományos esetben.Forrás: http://www. .7 Generikus interfészek A letöbb hagyományos interfésznek létezik generikus változata is.

} } . for(int i = 0. Random r = new Random(). } A használata: MyDelegate delgt = GoodMethod.hu 114 25.Forrás: http://www.i < array.Length. } } public void Transform(Transformer t) { for(int i = 0. amelyek egy metódusra hivatkoznak. Egy delegate deklarációjánál megadjuk.doksi. int result = delgt(10). hogy nem kell el re megadott metódusokat használnunk. //result == 100 A delegate páldányosítását a hagyományos úton is megtehetjük: MyDelegate delgt = new MyDelegate(GoodMethod). A delegate –ek legnagyobb haszna. Ez a delegate olyan metódusra mutathat amelynek visszatérési értéke int típusú és egyetlen int paramétere van: static public int GoodMethod(int x) { return (x * x).i < length. hogy milyen szignatúrával rendelkez metódusokra mutathat: delegate int MyDelegate(int x). ehelyett dinamikusan adhatjuk meg az elvégzend m veletet: class ArrayTransform { public delegate int Transformer(int _input). Delegate -ek A delegate –ek olyan típusok.++i) { array[i] = r. int[] array. public ArrayTransform(int length) { array = new int[length].++i) { array[i] = t(array[i]).Next(1000).

1 Többszörös delegate -ek Egy delegate nem csak egy metódusra hivatkozhat: delegate void MyDelegate(). at. delegate void DelTwo().WriteLine(). } Console. ami elvégzi a változtatásokat a tömbön. } } Két delegate nem egyenl .: class Program { static public int Square(int _in) { return (_in * _in).Write("{0}.Write("Method One…"). void MethodOne() { Console.Print(). at. } static public void Main() { ArrayTransform at = new ArrayTransform(10). //hiba 25.Transform(Square). ". at.hu 115 public void Print() { foreach(int i in array) { Console. i). DelTwo dlt = dlo. még akkor sem ha a szignatúrájuk megegyezik: delegate void DelOne(). } .ReadKey(). Console. } } A Transform() metódus egy delegate –et kap paraméteréül. Pl.Print().doksi. void Method() { } DelOne dlo = Method.Forrás: http://www.

. void Method(object i) { } MyDelegate md = Method(). Ennek a fordítottja igaz a visszatérési értékre. int Square(int _in) { return (_in * _in). } MyDelegate d = MethodOne. Most d hívásakor mindkét metódus meghívódik.doksi. Ez az ún. d().hu 116 void MethodTwo() { Console.Forrás: http://www. amelyek az eredeti paraméternél általánosabbak: class MyClass { } delegate void MyDelegate(MyClass mc). azaz az átadott metódus visszatérési értéke lehet specifikusabb az eredetinél: class Base { } class Derived { } delegate Base MyDelegate().2 Paraméter és visszatérési érték Egy delegate használhat generikus paramétereket is: delegate T GenericDelegate<T>(T param). d += MethodTwo. Derived Method() { } MyDelegate md = Method(). Egy delegate –nek átadott metódus paraméterei lehetnek olyan típusok. Egy delegate –b l el is vehetünk metódust: d -= MethodOne().Write("Method Two…"). } GenericDelegate<int> gd = Square. 25. kontravariáns (contravariant) viselkedés.

}.Forrás: http://www.doksi. .hu 117 Ezt kovariáns (covariant) viselkedésnek nevezzük. MyDelegate md = delegate(int x) { return (x * x).3 Névtelen metódusok Egy delegate –nek nem kötelez létez metódust megadnunk. 25. akár explicit módon is megadhatjuk azt: delegate int MyDelegate(int x).

Forrás: http://www. class MyListWithEvent { private List<int> list = null.hu 118 26. Ezeket a metódusokat eseménykezel knek nevezzük.Empty). public event ChangedEventHandler Changed. amikor hozzáadunk a listához egy elemet: using System. hogy valami történt (felhasználói aktivitás. amely az EventArgs osztályból származik.doksi. de a Framework eseményei mind ilyenek) két paramétere van. amely akkor hajtódik végre. Egy esemény egy olyan metódust (vagy metódusokat) fog meghívni. } public void Add(int value) { list. amely kiváltotta az eseményt. a második pedig az eseményhez kapcsolódó információk. az els az az objektum. using System. } } } class ListListener { private MyListWithEvent list = null. OnChanged(EventArgs. e). EventArgs e). public delegate void ChangedEventHandler(object sender. A következ példában egy listához kapcsolunk eseményt. Az eseményekhez rendelt delegate –eknek konvenció szerint (ett l eltérhetünk.Add(value). Gyakran találkozunk majd eseményekkel a grafikus felület alkalmazásokkal foglalkozó fejezetekben. stb…). Események Egy osztály eseményeket használhat. public ListListener(MyListWithEvent l) . hogy értesítsen más osztályokat. A második paraméter csakis olyan típus lehet. } protected virtual void OnChanged(EventArgs e) { if(Changed != null) { Changed(this. public MyListWithEvent() { list = new List<int>().Collections.Generic. amelyekre egy delegate hivatkozik.

} } class Program { static public void Main() { MyListWithEvent list = new MyListWithEvent(). llist.Add(value).aspx#list 26. egy ciklussal tesztelhetjük. véletlenszámokat megadva az aktuális h mérsékletnek). Console.1 Gyakorló feladatok 1. amelyek a t zsdét jelképez osztály egy adott eseményére irattkozhatnak fel (vagyis bizonyos részvények árfolyamát figyelik).ReadKey(). Amikor a kiszolgálón történik valami. ennek lényege nagy vonalakban. akik az érkez információkat felhasználják a saját adataik módosítására. amely egy bizonyos h mérséklet alatt vagy fölött riaszt (ezt pl. hogy ún. } } Az események segítségével megvalósíthatjuk (és a . } public void Add(int value) { list.Add(10). . list. amely megváltoztatja az állapotát.Net meg is valósítja) az Observer (Figyel ) tervezési mintát. 2. Tervezési mintákról a következ oldalon olvashatunk többet: http://www. Készítsünk t zsdeszimulátort! Hozzunk létre kliens osztályokat is. ListListener llist = new ListListener(list).doksi..Changed += new ChangedEventHandler(ListChanged).. } private void ListChanged(object sender. akkor értesíti a klienseit.com/Patterns/Patterns.Forrás: http://www. kliensek felíratkozhatnak a kiszolgálóra.dofactory. EventArgs e) { Console. Készítsük el a h mér osztályt! Tartalmazzon egy eseményt.WriteLine("A lista megváltozott.").hu 119 { list = l. illetve annak egyes eseményeire.

A következ kben a lambda kifejezések felhasználási területeit nézzük meg. jobb oldalán a kifejezés áll.Forrás: http://www. //True .WriteLine(d(10. Console. Els ként a Func –ot vizsgáljuk meg: Func<int. y) => x > y. //9 A generikus paraméterek között utolsó helyen mindig a visszatérési érték áll. Itt is használhatunk több (maximum négy bemen ) paramétert is: Func<int.WriteLine(d(2)). bool> BiggerOrLesser = (x. int> square = x => x * x. MyDelegate d = (x. Minden lambda kifejezés tartalmazza az ún. el tte pedig a bemen paraméterek kapnak helyet. amely nem (void).WriteLine(BiggerOrLesser(10. y) => x > y. int. err l egy kés bbi fejezetben olvashatunk. ezzel önálló lambda kifejezéseket hozva létre (ugyanakkor a generikus kifejezések kaphatnak névtelen metódusokat is). MyDelegate d = x => (x * x).1 Generikus kifejezések Generikus kifejezéseknek (tulajdonképpen ezek generikus delegate –ek) is megadhatunk lambda kifejezéseket amelyek nem igénylik egy el z leg definiált delegate jelenlétét. Természetesen nem csak egy bemen paramétert használhatunk: delegate bool MyDelegate(int x. 5)). //4 Vagyis x legyen x*x. Lambda kifejezések A C# 3. A lambda kifejezések els sorban a LINQ miatt kerültek a nyelvbe. Console. ennek jelentése nagyjából annyi.doksi. 5)). azaz a bemen paraméter négyzetét adjuk vissza. hogy az els paraméter nagyobb –e mint a második.0 bevezeti a lambda kifejezéseket. square(3). Az operátor bal oldalán a bemen változók. int y). Kétféle generikus kifejezés létezik a Func amely adhat visszatérési értéket és az Action. 27. //True Ebben a példában megvizsgáltuk. Console. Mivel névtelen metódus ezért egy lambda kifejezés állhat egy delegate értékadásában: delegate int MyDelegate(int _in).hu 120 27. lambda operátort (=>). hogy „legyen”. Egy lambda kifejezés gyakorlatilag megfelel egy névtelen metódusnak.

vagyis az adott változó legutolsó értékadása számít majd.WriteLine(BiggerOrLesserExpression. int.ReadKey(). A felhasznált változókat inicializálni kell miel tt használnánk egy lambda kifejezésben. ha az id közben kifut a saját hatóköréb l: using System. } } A programban el ször IL kódra kell fordítani (Compile()). using System. y) => x > y. és nem lehet visszatérési értéke: Action<int> IntPrint = x => Console. A lambda kifejezés fenntart magának egy másolatot a lokális változóból/paraméterb l.Forrás: http://www. 27. nem pedig a deklaráláskor. 5)). csak azután hívhatjuk meg.hu 121 Amikor Func –ot használunk minden esetben kell visszatérési értéknek lennie. Egy kifejezésfa egy generikus kifejezést kap generikus paraméterként: using System. hogy futási id ben a CLR ki tudja azt értékelni. class Program { static public void Main() { Expression<Func<int.doksi.Linq.2 Kifejezésfák Generikus kifejezések segítségével felépíthetünk kifejezésfákat. 27.Invoke(10.3 Lambda kifejezések változóinak hatóköre Egy lambda kifejezésben hivatkozhatunk annak a metódusnak a paramétereire és lokális változóira amelyben definiáltuk. delegate int MyDelegate(int x). bool>> BiggerOrLesserExpression = (x. még akkor is. Létezik olyan változata is. A küls változók akkor értékel dnek ki amikor a delegate ténylegesen meghívódik. amely csak a visszatérési érték típusát kapja meg generikus paraméterként: Func<bool> RetTrue = () => true. Kifejezésfákról még olvashatunk a LINQ –r l szóló fejezetekben. amelyek olyan formában tárolják a kifejezésben szerepl adatokat és m veleteket. Console. Console. A Func párja az Action amely maximum négy bemen paramétert kaphat. class LambdaTest { .Compile().WriteLine(x).Expressions.

localVar = 100.Forrás: http://www. . Vegyük észre.doksi. mégpedig arra. A lambda kifejezésben létrehozott változók ugyanúgy viselkednek. }. mint a hagyományos lokális változók. d = x => { return (localVar * x).Click += ClickHandler.WriteLine(lt. Vegyük például azt az esetet. Ebben az esetben egyetlen dologra kell figyelni. } } A lokális változók és paraméterek módosíthatóak egy lambda kifejezésben. hogy a kifejezés a típus megadása nélkül is tudja kezelni a pramétereket. //1000 Console. //localVar már kifutott a hatóköréb l. e) => { //itt csinálunk valamit }. amikor egy gomb Click eseményét kivánjuk kezelni (err l a Windows Forms –ról szóló fejezetekben olvashat az olvasó): EventHandler<EventArgs> ClickHandler = null. a delegate minden hívásakor új példány jön létre bel lük.ReadKey().d(10)).4 Eseménykezel k Rövid eseménykezel k megírásához kiválóan alkalmasak a lambda kifejezések. lt. 27. button1. ClickHandler = (sender. hogy az eseménykezel nem az alkalmazás f szálában fog lefutni.Test(). //ez lesz a delegate -ben } } class Program { static public void Main() { LambdaTest lt = new LambdaTest().hu 122 public MyDelegate d. public void Test() { int localVar = 11. de a lambdának még megvan Console.

Attribute { private double version. ezek a megfelel fejezetekben bemutatásra kerülnek majd. amelyeket a futtató környezet (a CLR) felhasznál majd az objektumok kreálása során. mivel a fordító feloldja majd.Forrás: http://www. így a Conditional eredeti neve is ConditionalAttribute.ReadKey().AttributeUsage(System. Attribútumok Már találkoztunk nyelvbe épített módosítokkal.. Egy attribútum a fordítás során beépül a Metadata információkba. } } class Program { static public void Main() { DebugClass. Konvenció szerint minden attribútum neve a névb l és az utána írt „attribute” szóból áll. Ha nem lenne definiálva az adott szimbólum a metódus nem futna le. amely egy el rdító (ld. de ezt elhagyhatjuk. Console.WriteLine("Debug message: {0}".0) { } .Diagnostics névtérben rejt zik: #define DEBUG_ON using System. Ezek általában be vannak betonozva az adott nyelvbe. A Conditional a System."). mi magunk nem készíthetünk újakat – kivéve.Diagnostics. Ahogy az már elhangzott. using System.Class)] public class VersionAttribute : System. Az alapértelmezett telepítés is rengeteg el re definiált attribútumot szolgáltat. mint amilyen a static vagy a virtual.DebugMessage("Main started. ha a .hu 123 28. következ fejezet) által definiált szimbólumhoz köti programrészek végrehajtását. } } Egy attribútumot mindig szögletes zárójelek közt adunk meg. message).doksi. mi magunk is készíthetünk attribútumokat: [System. class DebugClass { [Conditional("DEBUG_ON")] static public void DebugMessage(string message) { Console. Egy tesztelés során általánosan használt attribútum a Conditional.NET Framework –el dolgozunk..AttributeTargets. public VersionAttribute() : this(1.

Ezután (például) a következ képpen használhatjuk: [VersionAttribute(Version = 1. hogy hol használhajuk azt.Forrás: http://www.WriteLine("The version of the class: {0}". ebben az esetben csakis referenciatípusokon.Attribute.hu 124 public VersionAttribute(double _version) { } public double Version { get { return version. } } .Attribute[] attr = System. } } } Az attribútumosztály maga is kapott egy attribútumot.doksi. } set { version = value. foreach(System. a.GetCustomAttributes(typeof(MyVersionedClass)). amellyel azt jelezzük.Attribute a in attr) { if(a is VersionAttribute) { Console.0)] class MyVersionedClass { } Az attribútumok értékeihez pedig így férünk hozzá: System.Version).

amit egy osztálydefinícióhoz hasonlóan kinyithatunk-becsukhatunk: #region LOTOFCODE #endregion . egyébként nem. #endif #undef SYMBOL #define OTHER //ha nincs definiálva #if !SYMBOL Console.doksi. #endif Az elif az else-if rövidítése. hogy definiálva van –e ez a szimbólum.. pl. az else pedig már ismer s kell legyen..WriteLine("Debug symbol exist. ez pedig a region-endregion. #endif Az els sorban utasítjuk a fordítót.").").WriteLine("No symbol defined..hu 125 29.WriteLine("OTHER is defined..").WriteLine("SYMBOL is defined. a fordító számára értelmezhet vé alakítja a forráskódot."). Az el fordító Az el fordító a tényleges fordítás el tt végzi el a dolgát. #else Console.WriteLine("SYMBOL is not defined. amellyel egy kódrészletet jelölünk ki.. A Visual Studio –ban használható egy kakukktojás.").. Ha igen. #elif OTHER //vagy ha egy másik szimbólumot definiáltunk Console. ezután pedig ellen rizzük.. hogy az ún.. szimbólumtáblázatba tárolja el a „DEBUG_ON” szimbólumot. ami nem igazán el fordító utasítás. ezt a lehet séget leggyakrabban feltételes végrehajtásra használjuk: #define DEBUG_ON #if DEBUG_ON Console. Összetett feltételt is készíthetünk: //ha mindkét szimbólum definiálva van #if SYMBOL_1 && SYMBOL_2 #endif Szimbólum eltávolítására az undef utasítást használjuk: #define SYMBOL #if SYMBOL Console. A C és C++ nyelvekhez hasonlóan a C# is támogatja az el fordítónak szánt utasításokat...Forrás: http://www. akkor az adott kódrészlet lefordul.

Console.: amikor együtt kell m ködnünk az operációs rendszerrel. . string* messagePointer = &message. hogy használhassunk mutatókat (pointereket) az adott metódust.doksi.WriteLine(messagePointer). vagy meg kell valósítanunk egy bonyolult és id igényes algoritmust). class Program { static public void Main() { unsafe { string message = "Hello Pointer!".ReadKey(). ezután pedig deklaráltunk egy string objektumra mutató mutatót (minden típushoz létezik a hozzá tartozó mutatótípus) és értékül adtuk neki az eredeti objektumunk memóriabeli címét. } } } Els ként létrehoztunk egy változót az üzenettel. adattagot vagy blokkot az unsafe kulcsszóval kell jelölnünk: class UnSafeClass { public unsafe void UnSafeMethod() { } public void MethodWithUnSafeBlock() { unsafe { } } } Írjuk meg a „Hello Pointer!” programot nevéhez h en mutatók használatával (A C/C++ programozók számára ismer s lesz): using System. utóbbi az adott memóriacím értékét adja visza.hu 126 30. Unsafe kód A C# lehet vé teszi a memória direkt elérését mutatókon keresztül. igaz ritkán fordul el olyan probléma amikor ezt ki kell használnunk (pl. osztályt. Ahhoz.Forrás: http://www. Console. ezt a „címe operátorral” (&) és a „*” operátorral végeztük el.

Forrás: http://www. hiszen a mutatott objektumnak a címét kértük le. ez pedig néhány esetben gondot okozhat (pl.WriteLine(tp->message).hu 127 A programot prancssorból az /unsafe kapcsolóval fordíthatjuk le. } unsafe { Test t = new Test(). Egy pointer azonban mindig egy fix helyre mutat.doksi.WriteLine(tp->message). Console. Visual Studio esetén jobb klikk a projecten. } 30. valamilyen er forrás). csc /unsafe test. Test* tp = &t. Properties és ott állítsuk be.WriteLine((int)messagePointer). Unsafe kód esetén az osztálytagokat is másként érjük el: class Test { public string message = "TestClass". Ha szeretnénk.1 Fix objektumok Normális esetben a szemétgy jt a memória töredezettségmentesítése érdekében mozgatja az objektumokat a memóriában.cs A megfelel explicit konverzióval a memóriacímet is lekérhetjük: Console. Console. amikor hoszabb ideig van a memóriában a mutatott adat. pl. ez pedig nem fog frissülni. hogy az objektumok a helyükön maradjanak a fixed kulcsszót kell használnunk: unsafe { fixed(Test t = new Test()) { Test* tp = &t. } } .

Amikor egy szál ideje lejár a futási adatait eltárolja az ún. p.Diagnostics.NET számos osztályt és metódust bocsájt rendelkezésünkre. foreach(Process p in processes) { Console. az er források elosztása. Jó példa lehet a szálkezelés bemutatására egy szövegszerkeszt használata: amíg kinyomtatunk egy dokumentumot (egy mellékszállal) az alkalmazás f szála továbbra is figyeli a felhasználótól érkez utasításokat. Main). class Program { static public void Main() { //lekérjük az összes futó folyamatot Process[] processes = Process. hiszen a processzor egyszerre csak egy feladatot tud végrehajtani (bár terjednek a többmagos rendszerek. f szállal (primary– vagy main thread). mivel csak egy szál fér hozzá az összes er forráshoz. id osztásos (time slicing) rendszer. amely a belépési pontja a programnak (ld. amely – ha szükséges – betölti a saját adatait a TLS -b l és elvégzi a feladatát. using System. Egy process – t az azonosítója (PID – Process ID) alapján különböztetünk meg a többit l. process jön létre. Ugyanakkor ezek az alkalmazások hajlamosak „elaludni”. hiszen a f szál ekkor nem tud figyelni a felhasználó interakciójára. Minden egyes process rendelkezik egy ún. hogy amikor hozzá sem nyúlunk a számítógéphez is ötven – száz szál fut).hu 128 31. ha egy komplexebb feladatot hajtanak végre.WriteLine("PID: {0}. de gondoljunk bele.ReadKey().GetProcesses(".Diagnostics névtérben vannak. Az ilyen helyzetek elkerülésére a Windows (és a . worker thread) hozzáadását a f szálhoz. amelyekkel az egyes process –eket felügyelhetjük. } Console. Írjunk egy programot amely kiírja az összes futó folyamatot és azonosítójukat: using System. Többszálú alkalmazások Egy Windows alapú operációs rendszerben minden „*. amely teljes mértékben elkülönül az összes többit l. A . ezek a System.exe” indításakor egy ún.doksi. A többszálú programozás legnagyobb kihívása a szálak és feladataik megszervezése. Az egyes szálak (a process –ekhez hasonlóan) önállóan m ködnek a folyamaton belül és „versenyeznek” az er források használatáért (concurrent access).Id. így el kell osztania az egyes feladatok közt a processzorid t (ezt a a szálak prioritása alapján teszi) ez az ún. Thread Local Storage –ben (ebb l minden szálnak van egy) és átadja a helyet egy másik szálnak. } } . Azokat az alkalmazásokat.NET) lehet vé teszi másodlagos szálak (ún.ProcessName)."). hogy valójában a töbszálúság a számítógép által nyújtott illúzió. Fontos megértenünk. Name: {1}". p. amelyek csak a f szállal rendelkeznek thread-safe alkalmazásoknak nevezzük.Forrás: http://www.

"). a meggondolás házi feladat. } } Elég valószín .Diagnostics.GetProcesses(". az MSDN –en megtaláljuk a fenti osztályok további metódusait. várjunk öt másodpercet és állítsuk le: using System.Threads.WriteLine("The process {0}'s threads:". indítsuk el az Internet Explorert. using System.hu 129 Amennyiben tudjuk a process azonosítóját.doksi. pt. akkor használhatjuk a Process. A következ programunk a process –ek írányítását szemlélteti.StartTime. start at {1} and its current state is {2}". pt.WriteLine ( "This thread ({0}).ReadKey().Id. class Program { static public void Main() { Process[] processes = Process. tulajdonságait amelyek az „utazáshoz” szükségesek.ProcessName). A fenti osztályok segítségével remekül bele lehet látni a rendszer „lelkébe”.ThreadState ). pt. hiszen a szálak listájába olyan szál is bekerülhet amely a kiíráskor már befejezte futását (ez szinte mindig az Idle process (a rendszer üresjárati…) esetében fordul el .Threading. A következ programunk az összes futó process minden szálát és azoknak adatait fogja kilistázni: using System. hogy a program futásakor kivételt kapunk.GetProcessById(azonosító) metódust is. foreach(ProcessThread pt in ptc) { Console. using System. class Program { .Forrás: http://www. using System. } } Console.Diagnostics. ProcessThreadCollection ptc = p. foreach(Process p in processes) { Console. akárcsak a program kivételbiztossá tétele). p.

a Thread osztály a System.FriendlyName).CurrentDomain.Sleep(5000). Ezzel a megoldással egyrészt el segítik a platformfüggetlenséget.Kill(). Amikor elindítunk egy . ezredmásodpercben ie.1 Application Domain -ek Egy . A következ program kiírja az aktuális AD nevét: using System. . } } Egyúttal felhasználtuk az els igazi szálkezeléshez tartozó metódusunkat is. } } A képerny n megjelenik az alkalmazás neve. 31. hanem be van ágyazva egy ún. hiszen egyel re nincs is több. Thread. //A f szál pihentetése.exe").ReadKey(). hiszen így csak az Application Domaint kell portolni egy másik platformra. Hozzunk létre egy második AppDomaint: az alapértelmezett AD és using System.Threading névtérben található.CreateDomain("SecondAD").WriteLine(currAD.Start("IExplore. a Thread. ezután – ha szükséges – a CLR további AD –ket hoz létre.NET program nem direkt módon process –ként fut.hu 130 static public void Main() { Process ie = Process. Console. a kommentet). class Program { static public void Main() { AppDomain currAD = AppDomain.Forrás: http://www.doksi. application domain –be a processen belül (egy process több AD –t is tartalmazhat egymástól teljesen elszeparálva). másrészt biztosítja a programok stabilitását ugyanis ha egy alkalmazás összeomlik egy AD –ben.Sleep() –et (ld. class Program { static public void Main() { AppDomain secondAD = AppDomain. a többi még tökéletesen m ködik majd.NET programot els ként az alapértelmelmezett AD (default application domain) jön létre. Console. a benne futó folymatoknak nem kell ismerniük az operációs rendszert.

Forrás: http://www.doksi.hu

131
Console.WriteLine(secondAD.FriendlyName); AppDomain.UnLoad(secondAD); //megszüntetjük Console.ReadKey(); } }

31.2 Szálak
Elérkeztünk a fejezet eredeti tárgyához, már eleget tudunk ahhoz, hogy megértsük a többszálú alkalmazások elvét. Els programunkban lekérjük az adott programrész szálának az azonosítóját:
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 következ lesz:
Hello from thread 1

Azaz az els , a „f ” szálban vagyunk. A program utasításainak végrehajtása szerint megkülönböztetünk szinkron- és aszinkron m ködést. A fenti program szinkron módon m ködik, az utasításait egymás után hatja végre, ha esetleg egy hosszas algoritmusba ütközik akkor csak akkor lép a következ utasításra ha azt befejezte, Az aszinkron végrehajtás ennek épp az ellentéte az egyes feladatokat el tudjuk küldeni egy másik szálba a f szál pedig fut tovább, amíg a mellékszál(ak) vissza nem térnek.

31.3 Aszinkron delegate -ek
A következ kben delegate –ek segítségével fogunk aszinkron programot írni, így tisztában kell lennünk a delegate –ek mibenlétével ezért ha valami nem világos inkább olvassunk vissza. Minden egyes delegate rendelkezik azzal a képességgel, hogy aszinkron módon hívjuk meg, ez a BeginInvoke() és EndInvoke() metódusokkal valósul meg. Vegyük a következ delegate –et:
delegate int MyDelegate(int x);

Forrás: http://www.doksi.hu

132

Ez valójában így néz ki:
public sealed class MyDelegate : System.MulticastDelegate {
//...metódusok...

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

Egyel re ne foglalkozzunk az ismeretlen dolgokkal, nézzük meg azt amit ismerünk. A BeginInvoke –kal fogjuk meghívni a delegate –et, ennek els paramétere megegyezik a delegate paraméterével (vagy paramétereivel). Az EndInvoke fogja majd az eredményt szolgáltatni, ennek visszatérési értéke megegyezik a delegate –éval. Az IasyncResult amit a BeginInvoke visszatérít segít elérni az eredményt és az EndInvoke is ezt kapja majd paraméteréül. A BeginInvoke másik két paraméterével most nem foglalkozunk, készítsünk egy egyszer delegate –et és hívjuk 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(); } }

Forrás: http://www.doksi.hu

133 A kimenet a következ lesz:
Hello from thread 1 BlaBla... Hello from thread 3 144

Látható, hogy egy új szál jött létre. Amit fontos megértenünk, hogy a BeginInvoke azonnal megkezdi a feladata végrehajtását, de az eredményhez csak az EndInvoke hívásakor jutunk hozzá, tehát küls szemlél ként úgy látjuk, hogy csak akkor fut le a metódus. A háttérben futó szál üzenete is látszólag csak az eredmény kiértékelésénél jelenik meg, az igazság azonban az, hogy a Main üzenete el bb ért a processzorhoz, ezt hamarosan látni fogjuk. Többszálú program írásánál össze kell tudnunk hangolni a szálak munkavégzését, pl. ha az egyik szálban kiszámolt eredményre van szüksége egy másik, kés bb indult szálnak. Az ilyen eseteket szinronizálásnak nevezzük. Szinkronizáljuk az eredeti programunkat, vagyis várjuk meg amíg a delegate befejezi a futását (természetesen a szinkronizálás ennél jóval bonyolultabb, err l a következ 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 interfész IsCompleted tulajdonságával oldottuk meg. A kimenet:
Hello from thread 1 BlaBla... Hello from thread 3 BlaBla... BlaBla... BlaBla... BlaBla... 144

Itt már tisztán látszik, hogy az aszinkron metódus futása azonnal elkezd dött, igaz a Main itt is megel zte. Valljuk be elég macerás mindig meghívogatni az EndInvoke –ot, felmerülhet a kérdés, hogy nem lehetne valahogy automatizálni az egészet. Nos, épp ezt a gondot oldja meg a BeginInvoke harmadik AsyncCallback típusú paramétere. Ez egy delegate amely egy olyan metódusra mutathat, amelynek visszatérési értéke void,

Forrás: http://www.doksi.hu

134 valamint egy darab IAsyncResult típusú paraméterrel rendelkezik. Ez a metódus azonnal le fog futni, ha a mellékszál elvégezte a feladatát:
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 következ lesz:
Hello from thread 1 BlaBla... Hello from thread 3 Async thread complete... Result: 144

Egyetlen dolog van hátra mégpedig a BeginInvoke utolsó paraméterének megismerése. Ez egy object típusú változó, azaz bármilyen objektumot átadhatunk. Ezt a paramétert használjuk, ha valamilyen plusz információt akarunk továbbítani:

Forrás: http://www.doksi.hu

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 Szálak létrehozása
Ahhoz, hogy másodlagos szálakat hozzunk létre nem feltétlenül kell delegate –eket használnunk, mi magunk is elkészíthetjük. Vegyük a következ 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 ként elneveztük az els dleges szálat, hogy kés bb azonosítani tudjuk, melyikmelyik. Most készítsünk egy másik objektumot, de ezúttal küldjük el a háttérbe:
Thread first = Thread.CurrentThread; tmp.Name = "FirstThread"; TestClass tc = new TestClass(); tc.PrintThreadInfo();

Forrás: http://www.doksi.hu

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 szép és jó, de mi van akkor, ha a meghívott metódusnak paraméterei is vannak? Ilyenkor a ThreadStart parametrizált változatát használhatjuk, ami igen eredeti módon a ParameterizedThreadStart névre hallgat. A ThreadStart –hoz hasonlóan ez is egy delegate, szintén void visszatérési típusa lesz, a paramétere pedig object típusú lehet:
//az új metódus

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

Most már hívhatjuk:
Thread backgroundThread = new Thread(newParameterizedThreadStart(ct.ParameterizedThreadInfo)); backgroundThread.Name = "SecondThread";
//itt adjuk meg a paramétert

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

31.5 Foreground és background szálak
A .NET két különböz száltípust különböztet meg: amelyek el térben és amelyek a háttérben futnak. A kett közti különbség a következ : a CLR addig nem állítja le az alkalmazást, amíg egy el térbeli szál is dolgozik, ugyanez a háttérbeli szálakra nem vonatkozik. Logikus (lenne) a feltételezés, hogy az els dleges és másodlagos szálak fogalma megegyezik jelen fejezetünk tárgyaival. Az igazság viszont az, hogy ez az állítás nem igaz, ugyanis alapértelmezés szerint minden szál (a létrehozás módjától és idejét l függetlenül) el térben fut. Természetesen van arra is lehet ség, hogy a háttérbe küldjük ket:
Thread backgroundThread = new Thread(new ThreadStart(SomeUndeclaredMethod)); backGroundThread.IsBackground = true; backgroundThread.Start();

31.6 Szinkronizáció

Forrás: http://www.doksi.hu

137

A szálak szinkronizációjának egy primitívebb formáját már láttuk a delegate –ek esetében, most egy kicsit komolyabban közelítünk a témához. Négyféleképpen szinkronizálhatjuk a szálainkat, ezek közül az els a blokkolás. Ennek már ismerjük egy módját, ez a Thread.Sleep metódus:
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 metódus a paramétereként ezredmásodpercben megadott ideig várakoztatja azt a szálat, amelyben meghívták. Amikor egy szálat leblokkolunk az azonnal „elereszti” a processzort és addig inaktív marad, amíg a blokkolás feltételének a környezet eleget nem tesz, vagy a folyamat valamilyen módon megszakad. Egy viszonylag ritkán használt metódusa a Thread osztálynak a SpinWait. Ez hasonlóan m ködik mint a Sleep, de benne marad a processzorban, csak éppen nem csinál semmit, folyamatosan üres utasításokat hajt végre. Látszólag nem sok értelme van egy ilyen metódus használatának, de olyankor hasznos, amikor csak nagyon rövid (néhány ezredmásodperc) id t kell várni, mondjuk egy er forrásra, ekkor nagy költség lenne cserélgetni a szálakat, mint azt a Sleep teszi. A Join metódus addig várakoztatja a hívó szálat, amíg az a szál amin meghívták nem végezte el a feladatát:
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” paramétert (ezredmásodpercben), amely id lejárta után – ha a szál nem végzett feladatával – hamis értékkel tér vissza. A következ példa ezt mutatja meg:

Forrás: http://www.doksi.hu

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(); //megszakítjuk a szál futását } Console.ReadKey(); } }

A példában a szál üzenete már nem fog képerny re kerülni, még el tte megszakítjuk. A következ szinkronizációs módszer a lezárás (locking). Ez azt jelenti, hogy er forrásokhoz, illetve a program bizonyos részeihez egyszerre csak egy szálnak engedünk hozzáférést. Ha egy szál hozzá akar férni az adott dologhoz, amelyet egy másik szál már használ, akkor automatikusan blokkolódik és várólistára kerül, ahonnan érkezési sorrendben lehet hozzájutni az er forráshoz (ha az el z szál már végzett). Nézzük a következ példát:
static int x, y; static public Divide() { if(x != 0) Console.WriteLine(y / x); x = 0; }

Tegyük fel, hogy megérkezik egy szál, eljut odáig, hogy kiírja az eredményt és épp ekkor érkezik egy másik szál is. Megvizsgálja a feltételt, rendben találja és továbblép. Ebben a pillanatban azonban az els ként érkezett szál lenullázza a változót és amikor a második szál osztani akar, akkor kap egy szép kis kivételt. A m veletet lezárhatjuk a következ módon:
static int x, y; static object locker = new object(); static public Divide()

Forrás: http://www.doksi.hu

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

A lock kijelöl egy blokkot, amelyhez egyszerre csak egy szál fér hozzá. Ahhoz azonban, hogy ezt megtehessük ki jelölnünk egy ún. tokent, amelyet lezárhat. A tokennek minden esetben referenciatípusnak kell lennie. Természetesen nem csak statikus metódusokból áll a világ, egy „normális” metódust is lezárhatunk, de ilyen esetekben az egész objektumra kell vonatkoztatni a zárolást, hogy ne változzon meg az állapota egy másik szál keze nyomán:
class LockedClass { public void LockedMethod() { lock(this) {
/*BlaBla*/

} } }

A lock –hoz hasonlóan m ködik a Mutex is, a legnagyobb különbség az a kett közt, hogy utóbbi processz szinten zárol, azaz a számítógépen futó összes folyamat elöl elzárja a használat lehet ségét. Az er forrás/metódus/stb. használata el tt meg kell hívnunk a WaitOne metódust, a használat után pedig el kell engednünk az er forrást a ReleaseMutex metódussal (ha ezt nem tesszük meg a kódból, akkor az alkalmazás futásának végén a CLR automatikusan megteszi helyettünk). A következ példában létrehozunk több szálat és versenyeztetjük ket egy metódus használatáért:
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(); }

Forrás: http://www.doksi.hu

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 hasonlít a lock –ra és a Mutex –re, azzal a különbséggel, hogy megadhatunk egy számot, amely meghatározza, hogy egy er forráshoz maximum hány szál férhet hozzá egy id ben. A következ program az el z átirata, egy id ben maximum három szál férhet hozzá a metódushoz:
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(); }

i <threads. } } . for(int i = 0.hu 141 } static public void Main() { Thread[] threads = new Thread[10].Length. threads[i].Name = i.ReadKey().ToString().++i) { threads[i] = new Thread(new ThreadStart(ThreadProc)). } Console. threads[i].Forrás: http://www.Start().doksi.

GetType(). a Reflection témaköre óriási és a mindennapi programozási feladatok végzése közben viszonylag ritkán szorulunk a használatára (ugyanakkor bizonyos esetekben nagy hatékonysággal járhat a használata). Console.hu 142 32.ReadKey(). class Test { } class Program { static public void Main() { Test t = new Test().CreateInstance(tp). Reflection A programozástechnikában a „reflection” fogalmát olyan programozási technikára alkalmazzuk. tp. using System. Az erre a paradigmára ép l programozást reflektív programozásnak nevezzük.doksi. null. Type tp = t. Console. lépjünk el re egyet: using System. ahol a program (futás közben) képes megváltoztatni saját struktúráját és viselkedését.ReadKey().Forrás: http://www.GetCallingAssembly(). } } Futásid ben lekértük az objektum típusát ( a GetType metódust minden osztály örökli az object –t l). } } .WriteLine(tp). Console.InvokeMember("Method". Activator. Ebben a fejezetben csak egy rövid példát találhat a kedves olvasó.InvokeMethod. class Test { public void Method() { Console.WriteLine("Hello Reflection!"). BindingFlags. Vegyük a következ példát: using System. null).Reflection. } } class Program { static public void Main() { Type tp = Assembly.GetType("Test"). persze a reflektív programozás ennél többr l szól.

így ha elgépeltünk valamit az bizony kivételt fog dobni futáskor (System.hu 143 Ebben az esetben az objektum létrehozása és a metódusa meghívása teljes mértékben futási id ben történik. végül a hívás paraméterei (ha vannak)). hogy mit és hogyan fogunk hívni (a fenti példában egy metódust). hogy az öröklött/túlterhelt tagokat hogyan hívjuk meg. Az InvokeMember els paramétere annak a konstruktornak/metódusnak/tulajdonságnak/… a neve amelyet meghívunk. A második paraméterrel jelezzük. Ezután maga a típus amin a hívást elvégezzük. Következ a sorban a binder paraméter. Megjegyzend .doksi. ezzel beállíthatjuk. . hogy fordítási id ben semmilyen ellen rzés sem történik a példányosítandó osztályra nézve.Forrás: http://www.ArgumentNullException).

class Program { static public void Main() { FileStream fs = new FileStream("Text.IO. Console. akkor azt a következ képpen tehetjük meg: . string s = rs. fs. adatfolyamokat használ az IO m veletek végzéséhez.WriteLine(s).Close(). stream –eket. FileMode. ebben a fejezetben a leggyakrabban használtakkal ismerkedünk meg. Legyen pl. A C# ún.Forrás: http://www. Az els sorban megnyitottunk egy ilyen folyamot és azt is megmondtuk. s = rs.doksi. hogy mit akarunk csinálni vele.Open). } rs. amelyekkel file –okat udunk kezelni.ReadLine(). while(s != null) { Console. A FileStream konstruktorának els paramétere a file neve. Ha nem adunk meg teljes elérési útat.IO névtérben vannak. Kezdjük egy file megnyitásával és tartalmának a képerny re írásával.hu 144 33. a szöveges file tartalma a következ : alma korte dio csakany konyv penz A program pedig: using System.1 Olvasás/írás file –ból/file –ba A . 33.Close(). StreamReader rs = new StreamReader(fs).NET számos osztályt biztosít számunkra. akkor automatikusan a saját könyvtárában fogja keresni a program.ReadLine(). using System. Ha küls könyvtárból szeretnénk megnyitni az állományt.ReadKey().txt". Állománykezelés Ebben a fejezetben megtanulunk file –okat írni/olvasni és a könyvtárstruktúra állapotának lekérdezését illetve módosítását. } } Az IO osztályok a System.

Open). mert az egy ún.txt". FileAccess. minden ilyen esetben a backslash –t kell használnunk). ahogy más folyamatok férnek hozzá a file –hoz: . Ugyanaz mint az el z . escape karakter. Az els a FileAccess.txt". Ha nem létezik létrehozza.doksi. ekkor nincs szükség dupla karakterekre.Forrás: http://www. FileMode. de ha már létezik a file. ha nem létezik kivételt dob. magában nem lehetne használni (persze ez nem azt jelenti. Megnyit egy létez file –t és törli a tartalmát. FileMode. Olvasásra nyitja meg. Írásra nyitja meg. hogy az „at” jelet (@) használjuk az elérési út el tt. Azért használunk dupla backslash –t (\). Olvasásra és írásra nyitja meg Végül a FileShare –rel azt állítjuk be.txt". Egy másik lehet ség. Megnyit egy file –t és automatikusan a végére pozícionál.Open. hogy pontosan mit akarunk csinálni az állománnyal: Read Write ReadWrite A fenti példát így is írhattuk volna: FileStream fs = new FileStream("text.Read). de ha nem létezik akkor létrehozza a file –t. amellyel beállítjuk. mindkett enum típusú. Megnyit egy file –t.Open). A FileMode enum –nak a következ értékei lehetnek: Create CreateNew Open OpenOrCreate Append Truncate Létrehoz egy új file –t. mivel minden karaktert normális karakterként fog értelmezni: FileStream fs = new FileStream(@"C:\Egymasikkonyvtar\Masikkonyvtar\text. akkor kivételt dob. ha már létezik a tartalmát kitörli Ugyanaz mint az el z . hogy minden ilyen karaktert kett zve kellene írni. A FileStream konstruktorának további két paramétere is lehet amelyek érdekesek számunkra (tulajdonképpen 15 féle konstruktora van). new FileStream("C:\\Egymasikkonyvtar\\Masikkonyvtar\\text.hu 145 FileStream fs = FileMode. Ebben a módban a file tartalmát nem lehet olvasni (egyébként kivételt dob).

doksi. FileShare. using System. Most írjunk is a file –ba. Más folyamat törölhet a file –ból (de nem magát a file –t).WriteLine("Operation success!").Close(). Más folyamat olvashatja a file –t.bin")). class Program { static public void Main() { FileStream fs = new FileStream("Text. FileAccess. } } Házi feladat következik: módosítsuk a programot.++i) { sw. sw.IO.Open. StreamWriter sw = new StreamWriter(fs).Write("\n"). using System.ToString()). FileMode. Random r = new Random().Next().Create("file.Forrás: http://www.IO. és az írás után azonnal írjuk ki a képerny re az új tartalmát.Close(). hogy ne dobja el az eredeti tartalmát a file –nak. Bináris file –ok kezeléséhez a BinaryReader/BinaryWriter osztályokat használhatjuk: using System.Write(r.txt". . Más folyamat írhatja a file –t. A gyermek processzek is hozzáférhetnek a file –hoz. Erre a feladatra a StreamReader helyett a StreamWriter osztályt fogjuk használni: using System. fs.None).ReadKey(). Console. Console.Write. } sw.i < 10. class Program { static public void Main() { BinaryWriter bw = new BinaryWriter(File. Más folyamat írhatja és olvashatja is a file –t.hu 146 None Read Write ReadWrite Delete Inheritable Más folyamat nem férhet hozzá a file – hoz. amíg azt be nem zárjuk. for(int i = 0.

class Program { static public void Main() { foreach(string s in Directory.bin".IO névtér ebb l a szempontból két részre oszlik: információsés operációs eszközökre.) végeznek a filerendszeren.Close(). hogy számokat írtunk ki.IO. hiszen tudjuk. A PeekChar() metódus a soron következ karaktert (byte –ot) adja vissza. . Console. 33.2 Könyvtárstruktúra kezelése A file –o kkezelése mellett a .WriteLine(br. míg az utóbbiak (többségükben statikus metódusok) bizonyos m veleteket (új könyvtár létrehozása. de vigyázni kell vele. illetve -1 –et.i < 100. ha a ReadString metódust használtuk volna akkor kivétel (EndOfStreamException) keletkezik.Write(i). ha elértük a file végét. } br. using System. A System. BinaryReader br = new BinaryReader(File.doksi.PeekChar() != -1) { Console.ReadKey().GetDirectories("C:\\")) { Console. } Console. akkor hibázni fog. } bw.hu 147 for(int i = 0.Close(). A folyambeli aktuális pozíciót nem változtatja meg. A cikluson belül van a trükkös rész. mert ha nem megfelel méret a beolvasandó adat. El bbiek (ahogyan a nevük is sugallja) információt szolgáltatnak. while(br.NET a könyvtárstruktúra kezelését is széleskör en támogatja.++i) { bw. A ReadValami metódus a megfelel típusú adatot adja vissza. mivel a kett nem ugyanakkor mennyiség adatot olvas be. Az integer –eket beolvasó metódus nyílvná m ködni fog.Open("file. és kiírtuk belé a számokat egyt l százig. Ezután megnyitottuk a már létez file –t és elkezdjük kiovasni a tartalmát. A fenti példában.Forrás: http://www.WriteLine(s).ReadInt32()). } } Készítettünk egy bináris file –t. törlése.ReadKey(). stb.Open)). FileMode. Els példánkban írjuk ki mondjuk a C meghajtó gyökerének könyvtárait: using System.

} Console. di.hu 148 } } Természetesen nem csak a könyvtárakra. annak meggondolása az olvasó feladata. A programunk módosított változata némi plusz információval együtt ezeket is kiírja nekünk: using System.FullName.GetFiles("C:\\")) { PrintFileSystemInfo(new FileInfo(s)). created at {1}". } } static public void Main() { foreach(string s in Directory.WriteLine("Directory {0}. így a metódusban csak meg kell vizsgálni.CreationTime). Console. Ugyanazzal a metódussal írjuk ki az információkat kihasználva azt.Forrás: http://www.FullName. using System. } foreach(string s in Directory. hogy DirectoryInfo és a FileInfo is egy közös st l a FileSystemInfo –ból származik (mindkett konstruktora a vizsgált alany elérési útját várja paraméterként).Attributes & FileAttributes. Eddig csak információkat könyvtárstruktúrát: kértünk le.doksi. de a file –okra is kíváncsiak lehetünk.Directory) != 0) { DirectoryInfo di = fsi as DirectoryInfo.CreationTime).GetDirectories("C:\\")) { PrintFileSystemInfo(new DirectoryInfo(s)). } else { FileInfo fi = fsi as FileInfo. A vizsgálatnál egy „bitenkénti és” m veletet hajtottunk végre. Console.WriteLine("File {0} created at {1}". majd a file –okon megyünk végig. di. most megtanuljuk módosítani is a . class Program { static public void PrintFileSystemInfo(FileSystemInfo fsi) { if((fsi.IO. } } Els ként a mappákon. fi.ReadKey(). hogy éppen melyikkel van dolgunk és átkonvertálni a megfelel típusra. hogy ez miért és hogyan m ködik. fi.

txt".IO.Forrás: http://www. mivel lehet séget ad közvetlenül file –ba írni a tartalmát. } FileInfo fi = new FileInfo(filePath). amellyel memóriabeli adatfolyamokat írhatunk/olvashatunk.CreateDirectory(dirPath).IO.3 In – memory stream –ek A . using System.Exists(dirPath) == false) { //akkor megcsináljuk Directory.CreateText(). class Program { static public void Main() { . A MemoryStream osztály jelent sen megkönnyíti a dolgunkat. el bbi rugalmatlan. //ha nem létezik a file if(fi. ha közvetlenül a memóriában tároljuk el az adatokat. string filePath = dirPath + "\\file.WriteLine("Alma"). hogy összegy jtsünk nagy mennyiség adatot. sw. utóbbi memóriaigényes. ezért a legegyszer bb.doksi. sw.hu 149 using System. } Console. 33. sw. A következ program erre mutat példát: using System. amelyeket majd a folyamat végén ki akarunk írni a merevlemezre.ReadKey().Close().WriteLine("Dio"). amellyel írhatunk egy szöveges file –ba.Exists == false) { //akkor elkészítjük és írunk bele StreamWriter sw = fi. } } A FileInfo CreateText metódusa egy StreamWriter objektumot ad vissza. Nyílván egy tömb vagy egy lista nem nyújtja a megfelel szolgáltatásokat. class Program { static public void Main() { string dirPath = "C:\\test".NET a MemoryStream osztályt biztosítja számunkra. Mire is jók ezek a folyamok? Gyakran van szükségünk arra. using System. //ha nem létezik a könyvtár if(Directory.

++i) { sw. fs.WriteTo(fs). Miután minden adatot a memóriába írtunk a Flush() metódussal. for(int i = 0.hu 150 MemoryStream mstream = new MemoryStream().Close().Flush(). . mstream. } sw.Close().WriteLine(i). sw. amely egyúttal ki ríti a StreamWriter –t is.i < 1000.Close(). mstream.doksi. StreamWriter sw = new StreamWriter(mstream). Ezután létrehoztunk egy file -t és a MemoryStream WriteTo metódusával kiírtuk belé azadatokat. FileStream fs = File. Console.txt").Forrás: http://www.Create("inmem. } } A MemoryStream –re „ráállítottunk” egy StreamWriter –t.ReadKey().

Size(200.Controls. utána pedig a WPF következik.Size = new System.5 (LINQ.Forms.button. A WF az eredeti Windows API metódusati hívja meg menedzselt környezetben.Size(100.NET els grafikus interfész API (Application Programming Interface) –jának neve.Drawing.doksi. 100).NET 2. de a késöbbiekben már jobb lesz a helyzet. } protected void Button_Click(object sender. Els ként a Windows Forms –ot vesszük górcs alá (lévén inkább kap helyet a fels oktatásban mint fiatalabb társa).Drawing.Click += new EventHandler(Button_Click). EventArgs e) { MessageBox. Készítsük hát el els ablakos programunkat.Show("Hello WinForms!"). } public static void Main() .button.button. a következ fejezetekben ez a párhuzamosság már nem fog helyet kapni.Text = "Click Me!".Forrás: http://www. 30).Add(button). vagy is a nyelv elemeinek megértésére.NET) igazi erejét olyan alkalmazások készítésével sajátíthatjuk el amelyek sokkal jobban kihasználják a platform képességeit. this. Grafikus felület alkalmazások fejlesztése Az eddigi fejezetekben konzolalkalmazásokat készítettünk. this. ezért ez az egyszer program is bonyolultnak fog t nni els re. A következ két alfejezet egy-egy egyszer grafikus felület alkalmazás készítését mutatja be a két technológia segítségével. this. megtanulunk adatokat és adatbázisokat kezelni a . this. így arra tudtunk koncentrálni amire kell. A feladat a következ : az ablakban (hívjuk mostantól formnak) legyen egy gomb és rákattintva jelenjen meg egy MessageBox valamilyen üzenettel: using System.Size = new System.0 és a 3.hu 151 34.Point(50. this.button. 200). Entity Framework) eszközeivel.button = new Button(). Azonban a C# (és a .Text = "FirstWindow" this.Windows.Drawing. 34. A jegyzet hátralév részében megismerkedünk a Windows Forms és a Windows Presentation Foundation alapelemeivel. this. class FirstWindow : Form { private Button button. public FirstWindow() { this.1 Windows Forms . using System.Location = new System.Bevezet A Windows Forms a . Most még parancssorból fogunk fordítani és a Visual Studio beépített tervez jét sem fogjuk használni.

az ablakon belüli helyzetét (Location). Végül hozzáadtuk a gombot a formhoz ( a this szócska végig az osztályra azaz a formra mutat).Button). majd a kattintás (Click) eseményéhez hozzárendeltük az eseménykezel t. az els az eseményt kiváltó objektum (most a gomb). Ez utóbbinak több specializált változata van (pl. de ha nincs különösebb szükségünk ezekre az adatokra nyugodtan használjuk az sosztályt (EventArgs). ez mindig object típusú.Run(new FirstWindow()).Forms.: külön a billenty zet és egér által kiváltott eseményekhez).Forrás: http://www. A form konstruktorában beállítjuk az ablak címsorát és méretét. ezt a System. ezután példányosítjuk a gombot. beállítjuk a szövegét (Text). Az eredmény ez lesz: Ha pedig rákattintunk a gombra: Jöjjön a magyarázat: a FirstWindow lesz az ablakunk osztálya. t elneveztük button –nak. Az ablakon lesz egy gomb (System. } } A programot a következ képpen fordíthatjuk le: csc /target:winexe file. amely majd a MessageBox –ot fogja megjeleníteni a képerny n. a méretét (Size). a második pedig egy az esemény információit tároló változó.Windows.Form osztályból származtattuk.Forms. .cs A kapcsoló nélkül is lefordul.doksi. de a futáskor egy konzolablak is megjelenik.hu 152 { Application.Windows. Az eseménykezel metódusok visszatérési típusa mindig void és két paraméterük van.

ez egyrészt hídat képez a .doksi. hogy az XAML –t bár a WPF vezeti be. az adatkötések (stb…) létrehozásához bevezeti az ún. Media Integration Layer –en (MIL) keresztül. A WinForms –tól eltér en a WPF a WinAPI helyett közvetlenül a DirectX –xel kommunikál az ún.NET és a MIL közt. Ugyanakkor nem az XAML az egyetlen útja a WPF programozásának. mondjuk írjuk ki a küld nevét is.Name = "FirstButton". adatkötéseket. Az eseménykezel ben típuskonverziót kell végrehajtanunk. itt jelenítjük meg a formot.NET képes használni) PresentationCore –nak hívják. . viselkedését.0 verziójában megjelent grafikus alrendszer. az animációk.NET 3.Name). A WPF és a MIL közti menedzselt réteget (amelyet a .hu 153 A sender paraméter segítségével felhasználhatjuk a küld objektum adatait. EventArgs e) { Button mp = (Button)sender. A WPF a grafikus felület. írjuk a konstruktorba: this. ami az animációkat. Egyúttal azt is lehet vé teszi. hogy elérjük a gomb tulajdonságait: protected void Button_Click(object sender. Azt sem árt tudni.Bevezet A WPF (kódneve Avalon) a . vagyis egy designer is elkészítheti azt. } Most ez lesz a kimenet: A Main függvény lesz a program belépési pontja. Ehhez el ször nevet kell adnunk a gombnak. 34. gyakorlatilag bármely környezet átveheti azt. másrészt definiálja a WPF m ködését. mégsem köt dik hozzá szorosan. stb. hiszen forráskódból is gond nélkül létrehozhatunk vezérl ket a már ismert úton (és persze az XAML is hagyományos CLR kódra fordul le).button.Show("Hello WinForms! Sender: " + mp. hogy specifikus programozói készség nélkül lehessen definiálni a felületet. amely egy natív könyvtár. amely az XML –en alapul.2 Windows Presentation Foundation . Az i –re a pontot a PresentationFramework teszi fel. mégpedig az Application osztály Run metódusával amely paramétereként egy példányt kap a form –ból. MessageBox. XAML (eXtensive Application Markup Language) nyelvet. Ez a metódus elindít egy programot az aktuális programszálban és irányítja a form és WinAPI közti kommunikációt. definiálja. Az XAML segítségével deklaratív úton (vagyis hagyományos programozás nélkül) állíthatjuk be az egyes komponenesek kinézetét.Forrás: http://www.

File menü. adjunk nevet a projectnek. mint amit a Winforms –nál csimáltunk. A feladat ugyanaz lesz. A gombnak a tervez nézetben is meg kell jelennie: .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 eseményhez tartozó kezel t kérésre létrehozza nekünk.doksi. alatta pedig ott a hozzá tartozó XAML kód.Forrás: http://www. A következ t kell látnunk: Láthatjuk az ablakot (illetve még nem.Window1" xmlns="http://schemas. Most még mindent kézzel írunk be. mivel nincs rajta semmi).microsoft. akár Express is). ehhez már szükség van a Visual Studio –ra (lehet leg 2008 legyen. a Windows fülnél válasszuk ki a WPF Application sablont. navigáljunk a két Grid tag közé és készítsük el. tehát el ször szükségünk van egy gombra. New -> Project. Az IntelliSense segítségével könny dolgunk lesz: <Window x:Class="WPfDemo. elérési útat és OK.hu 154 Készítsük el els WPF programunkat.

hu 155 Most váltsunk át a Windows1.xaml. ennek a résznek a megírása az olvasó feladata.doksi. hogy készítse el): private void firstWPFButton_Click(object sender. itt már benne kell legyen az eseménykezel (ha kértük.Forrás: http://www. Futtatni az F5 gomb megnyomásával tudjuk a kész programot.cs file –ra. . RoutedEventArgs e) { } A MessageBox megjelenítése pontosan úgy zajlik. mint amit már láttunk.

A legtöbb osztály amit felhasználunk majd. amelyet „közösen használnak”. File menü. A következ kben már szükség lesz a Visual Studio –ra (2008 vagy 2005 ( ebben nem használható a C# 3. itt a Visual C# fülnél válasszuk ki a Windows Forms Application sablont: Adjunk nevet a projectnek.Forms névteret fogjuk használni. megismerkedünk a leggyakrabban használt vezérl kkel. Ezután a designer fog megjelenni. ekkor válasszuk a „Load Project Normally” lehet séget). ebben a fejezetben pedig mélyebbre ássuk magunkat a témában. majd kattintsunk az OK gombra (a VS 2008 ezután biztonsági okokból felajánlja – alapértelmezés szerint. készítünk saját vezérl t. A Windows Forms fejlesztéshez a leggyakrabban a System.Forrás: http://www. elérési utat. rajta az ablakkal: . végül pedig egy összetett alkalmazást fogunk elkészíteni. New -> Project. Windows Forms Az el z részben már elkészítettük els WF alkalmazásunkat. Ebben a részben megtanulunk bánni a Visual Studio –val. Expressek is). a névtér Controls osztályából származik.0). így rengeteg olyan tulajdonság/esemény/stb… lehet.doksi.Windows.hu 156 35. hogy csak „megnézni” nyissuk meg a projektet.

Data. Text: a vezérl felirata.doksi.0).Form) van.ComponentModel.Forrás: http://www. Kattintsunk a jobb egérgombbal a designerre és válasszuk ki a View Code –ot. Most nézzünk egy kicsit a dolgok mélyére. Egy Form esetében a Location értékei csak akkor érvényesek. ezek a legtöbb vezérl esetében használhatóak: Name: ez lesz a vezérl neve. minél kisebb az érték annál átlátszóbb a vezérl . Az ablak fels sorában a vezérl neve (Form1) és típusa (System. a bal föls sarok koordinátái: (0.Collections. . System. hogy a vezérl aktív –e. System. Size: a vezérl mérete.Drawing. Nézzük a legfontosabb tulajdonságokat.hu 157 A jobb föls sarokban a Solution Explorer található.Generic. System. Itt az adott vezérl tulajdonságai vannak (ezek a valóságban is tulajdonságok).Windows. Ez nálam a következ képpen néz ki (VS 2008): using using using using using System. Enabled: azt jelöli. Alatta pedig egy sokkal érdekesebb dolog: a Properties ablak. System. Opacity: átlátszóság. Ekkor elénk tárul az ún. a programban ezzel az azonosítóval hivatkozunk majd rá. code-behind file.Forms. ha a StartPosition tulajdonságot Manual –ra állítjuk. itt vannak a project file –jai. Location: a vezérl helyezete az öt tartalmazó vezérl ben.

utóbbit pedig OkButton –nak. this. El bbit nevezzük el (a Properties ablakban) MessageTextBox –nak.AutoScaleMode.Linq.Forms. namespace JegyzetTest { public partial class Form1 : Form { public Form1() { InitializeComponent(). using System.Windows. az az. azért nézzük meg a konstruktort.Manual. this. this. // // Form1 // this. Ez egy InitializeComponent nev metódust hív meg.Text = "Form1". 13F).Name = "Form1".Font. ezután húzzunk a formra egy TextBox –ot és egy Buttont. a második pedig az. ha nincs.ResumeLayout(false). a definícióját azonban itt nem látjuk. akkor a View menüb l válasszuk ki).FormStartPosition.Windows. hogy beállítsa a vezértl k kezdeti tulajdonságait. this. az InitializeComponent valahogy így kell kinézzen: private void InitializeComponent() { this. Most már készen állunk.Text. hogy ez egy parciális osztály.Forrás: http://www. Nyissuk ki az All Windows Forms feliratú fülecskét (ha még nincs nyitva).Windows. } } } Két dolgot kell észrevennünk: az els .AutoScaleDimensions = new System. hogy ezt a metódust ne nagyon piszkáljuk (minden ami itt beállításra kerül beállítható a Properties ablakban). } Amit fontos megjegyeznünk.doksi. hogy elkészítsük a második WF programunkat. Ha megnézzük a forráskódot akkor látható. Nézzük meg.Size(292. ehhez jobb klikk a metódushíváson és válasszuk ki a Go To Definitiont. this.Drawing.Forms. this. a feladata pedig az. hogy ez miért van. hogy ez a parciális osztályunk másik fele.ClientSize = new System.SuspendLayout().hu 158 using System. A gomb felirata legyen: „OK”. using System.SizeF(6F. Ezt a metódust a Visual Studio automatikusan generálja. hogy az osztály a Form osztályból származik. A jobb oldalon található a Toolbox.Drawing. amely a felhasználhatzó vezérl ket tartalmazza (alapértelmezés szerint ott kell lennie. 266).StartPosition = System. ezt a Visual Studio fordításkor minden esetben újragenerálja. Végül a form feliratát is állítsuk .AutoScaleMode = System.Forms. Térjünk vissza a tervez nézethez. az esetleges általunk eszközölt módosítások elvesznek.

doksi. A legutólsó lehet ségünk pedig az. ekkor valamilyen üzenet jelenjen meg a Textbox –ban.Text = "Ez egy üzenet". Most valahogy így kell kinézzen (a formot átméreteztem): A feladat a következ : rákattintunk a gombra. A program futtatásához nyomjuk le az F5 gombot. hogy szintén a villámjelnél duplán kattintunk az eseméyn nevére. } A Text tulajdonság egy string –et vár (vagy ad vissza. ekkor az adott vezérl alapértelmezett eseményéhez (ez a gomb esetében a Click) létrejön a kezel . EventArgs e) { MessageTextBox. A második módszer. létezik setter is). Ehhez a feldathoz a gomb már ismert Click eseményét fogjuk használni.Forrás: http://www. keressük meg a Click eseményt és írjuk be a kezel nevét. mondjuk WinFormsTest –re. Az ehhez tartozó eseménykezel t háromféleképpen tudjuk létrehozni. a kezel metódusba írjuk a következ t: private void OkButton_Click(object sender.hu 159 át. hogy tervez nézetben duplán kattintunk egy vezérl n. els és legegyszer bb módszer. Akárhogy is. hogy megírjuk. amely a TextBox szövegét fogja beállítani. ekkor a VS lérehozza a metódust. ezután tervez nézetben válasszuk ki a gombot és a Properties ablakban kattintsunk a kis villámjelre. ezután így fog m ködni: .

ez gyakorlatilag egy hivatkozás Load: a form megjelenése el tt fut le. Closing: akkor fut le. Ezeket összefoglaló néven a Form életciklusának nevezzük. Ennek az állomásai a következ ek: HandleCreated: egy kezel jön létre a formhoz. ez az a hely ahol a legcélszer bb a form által használt er források lefoglalása. amikor bezárjuk az ablakot. miel tt visszatér az eredeti ablakhoz (ilyen pl. 36. Miel tt és miután a Form és gyermekei megjelennek számos esemény fut le. Deactivated: az ablakon nincs rajta a fókusz. illetve a teljes méretre nagyításnak a gombjai. A Form osztály a ContainerClass osztályból származik. Windows Forms – Vezérl k Vezérl k alatt azokat az elemeket értjük. mivel csak egy másik vezérl vel együtt játszik szerepet.ShowDialog(). Helyezzünk egy formra egy gombot. amelyek alkalmassá teszik ket egy adott feladatra. amikor a form megjelenik fut le.hu 160 36. azaz olyan párbeszédablak amely megköveteli a felhasználótól. Néhány vezérl nem kap önálló fejezetet. A Form alapértelmezett eseménye a Load. Minden egyes vezérl nek vannak egyedi tulajdonságai. és a Click eseményében jelenítsünk meg egy modális ablakot: private void button1_Click(object sender. így tartalmazhat más vezérl ket is. a MessageBox is). Activated esemény: a form és vezérl i megjelennek és a formrakerül a fókusz (vagyis van az el térben. eseményei. HandleDestroyes: az ablakhoz tartozó kezel megsemmisül. Closed: miután bezártuk az ablakot fut le. amelyekb l egy grafikus alkalmazás felépül. Shown esemény: az els alkalommal. Ezeket a MinimizeBox illetve a MaximizeBox tulajdonságokkal tudjuk tetszés szerint megjeleníteni/elrejteni. illetve az adattagok kezdeti beállítása.1 Form Ez az osztály fogja képezni a felhasználói felület alapját. amely akkor fut le miel tt a Form el ször megjelenik. modalForm. Egy Form lehet modális ablak is. . EventArgs e) { Form modalForm = new Form(). Ezt a hatást a ShowDialog metódussal tudjuk elérni. Az ablak méretezésének letiltását a FormBorderStyle tulajdonság valamelyik Fixed el taggal rendeljez értékével tudjuk jelezni. } Alapértelmezetten egy form címsorában megjelennek a tálcára helyezéshez.Forrás: http://www. A következ kben megnézünk néhány vezérl t. hogy használja.doksi.

vagy a tervez t használjuk és egyszer en ráhúzzuk. a f -Formra pedig húzzunk egy gombot. } Az egyes eseményeket módosítsuk úgy.doksi. vagy pedig futás közben dinamikusan. amely egy ControlCollection típusú objektumot ad vissza. egy MessageBox –ban egy üzenet.Forrás: http://www.Shown += new EventHandler(form_Shown). Egy Form –hoz kétféleképpen adhatunk hozzá vezérl t (tulajdonképpen a kett egy és ugyanaz). stb…). 36. form.Size = new Size(100.Controls.HandleCreated += new EventHandler(form_HandleCreated). vagyis tömbként kezelhet . b.Add(b). valamint foreach ciklussal is használható.hu 161 Készítsünk egy programot ezeknek a folyamatoknak a szemléltetésére! A Formunk egyik adattagja legyen egy Form. Az eseménykezel k hozzáadásánál a += operátor után nyomjunk dupla TAB –ot.Activated += new EventHandler(form_Activated).2 Button . 10).Load += new EventHandler(form_Load). form. b. b. A ControlCollection megvalósítja az ICollection és IEnumerable interfészeket is. public Form1() { InitializeComponent(). Utóbbihoz tudni kell. 30). EventArgs e) { this.form = new Form(). } } A Click eseményéhez pedig írjuk ezt: private void button1_Click(object sender. form. form. ekkor automaikusan kiegészíti a sort és létrehozza a kezel t is. A Controls tulajdonság használható foreach ciklussal. Ehhez tudjuk majd hozzáadni a vezérl ket: Button b = new Button().Text = "Gomb". public partial class Form1 : Form { Form form = null. vagy a f – formon egy ListBox. Itt a this egy formra vonatkozik.Location = new Point(10. form. hogy lássuk mi történik (pl. this. hogy minden ContainerClass leszármazott rendelkezik egy Controls nev tulajdonsággal.Show().

amely hasonlóan m ködik mint egy weboldalon található hivatkozás. 36. Készítsünk egy programot.microsoft. A leggyakrabban iránymutatásra használjuk. Értéket a Text tulajdonságán keresztül adhatunk neki. hogy elindítsunk valamilyen folyamatot (pl.Text = "Ez egy Label. a példánkban ezt a form konstrukorában tesszük meg: public partial class Form1 : Form { public Form1() { InitializeComponent().Forrás: http://www.doksi. els dlegesen arra használjuk.".com"). ekkor kell átirányítanunk a felhasználót a megfelel oldalra. Ennek alapértelmezett eseménye a LinkClicked. beolvasása.Process.4 TextBox Szintén egy szöveges vezérl .Start("http://www. adatok elküldése. } A Form pedig így néz ki: 36.. LinkLabelLinkClickedEventArgs e) { System. stb. A LinkLabel szövegét szintén a Text tulajdonsággal tudjuk beállítani: private void linkLabel1_LinkClicked(object sender.3 Label Szöveget jelenítünk meg vele. Alapértelmezett eseménye a kattintás.).Diagnostics.. } } És az eredmény: A Label egy specializált változata a LinkLabel. label1. amelyet a felhasználó nem módosíthat. de ez már képes fogadni a felhasználótól érkez adatokat. amely felhasználja az eddig megismert három .hu 162 A gombot már ismerjük.

A TextBox –ok nevei legyenek pl.Text = LastNameText. Most valahogy így kellene kinéznie a formnak: Kezeljük a gomb kattintás eseményét: private void OKButton_Click(object sender. EventArgs e) { if (LastNameText. Most már m ködnie kell a programnak. három Label –t és egy Button –t.Text. } else { FullNameText.Text + " " + FirstNameText. Kérjük be a felhasználó kereszt.Forrás: http://www. a gombot pedig nevezzük OKButton –nak.Text == "" || FirstNameText.hu 163 vezérl t.doksi. } } Egy kis hibakezelést is belevittünk a dologba. Húzzunk a formra három TextBox -ot. amikor lenyomunk egy gombot.Text == "") { MessageBox. méghozzá így: .és vezetéknevét külön TextBox –ban. FirstNameText és FullNameText.Show("Kérem töltse ki az összes mez t!"). majd egy harmadikba írjuk ki a teljes nevet. LastNameText.

Forrás: http://www. A Properties ablakban keressük meg az Items tulajdonságot. Generáljunk a FirstTextBox –hoz egy TextChanged eseménykezel t: private void FirstTextBox_TextChanged(object sender. ez akkor váltódik ki. kezdjük az egyszer bbel. Készítsünk egy programot ennek szemléltetésére! Legyen a formunkon két TextBox. Legyen a két TextBox neve FirstTextBox és SecondTextBox.5 ListBox A ListBox egy olyan vezérl .hu 164 A TextBox MultiLine tulajdonságát igazra állítva több sorba is írhatunk. Egy ListBox –ot kétféleképpen tölthetünk fel adatokkal. amelynek megadva a kívánt karaktert ezután minden beírt karakter helyett a megadott karaktert jeleníti meg.Text.doksi. egy sorba egyet: Ha futtatjuk a programot. az jelenjen meg a másikban. jellemz en egy lista stílusában. kattintsunk a pontocskákra és írjuk be az adatokat. ha az els ben írunk valamit. amelyben adatokat tárolunk. azaz úgy viselkedik. mint egy jelszómez : -> A TextBox alapértelmezett eseménye a TextChanged.Text = FirstTextBox. Egy másik érdekes tulajdonság a PasswordChar. amikor a TextBox tartalma megváltozik. } 36. EventArgs e) { SecondTextBox. akkor az eredmény a következ lesz: .

hanem egy saját osztály példányait akarom megjeleníteni: class MyString { public string data. "Viktória". hogy néhány (adatokat tároló) vezérl nek megadhatunk adatforrásokat (egy tömb.doksi. tehát el ször generáljuk le azt. ha nem stringeket. hogy adatkötést használjon. Ez tulajdonképpen annyit tesz. hogy milyen adatokat akarunk. hogy el re tudnunk kell. egy lekérdezés eredménye. "Béla". stb…).hu 165 Ennek a módszernek a hátránya az. } public string Data { get { return data. EventArgs e) . Írjuk át a fenti programot. akkor vajon mi történik? private void Form1_Load(object sender.DataSource = t.Forrás: http://www. } } } Ha egy ilyen elemekb l álló tömböt kötünk a ListBox –ra. "Dániel" }. EventArgs e) { string[] t = new string[] { "István". listBox1. A dinamikus feltöltéshez bevezetjük az adatkötések (databinding) fogalmát. public MyString(string s) { data = s. majd írjuk: private void Form1_Load(object sender. A ListBoxot a Form Load eseményében fogjuk feltölteni. } Mi van azonban akkor. "Judit". amelyekb l feltölti magát.

hogy elkészítjük a metódust a saját osztályunkhoz is: class MyString { public string data. } } Most már kifogástalanul kell m ködnie. hogy a kiválasztott elemre kattintva jelenjen meg egy MessageBox a megfelel szöveggel: . listBox1. new MyString("Dániel") }. public MyString(string s) { data = s.hu 166 { MyString[] t = new MyString[] { new MyString("István").Forrás: http://www. A megoldás tehát az.DataSource = t. } public override string ToString() { return data. } Hát ez: A ListBox a rákötött elemeinek a ToString() metódus által nyert adatát jeleníti meg. A ListBox alapértelmezett eseménye a SelectedIndexChanged. mi viszont nem terheltük túl ezt a metódust. new MyString("Béla"). Módosítsuk a programot. new MyString("Judit"). ha megváltozik a kiválasztott elem. amelyet az vált ki. new MyString("Viktória"). ezért az eredeti változat hívódik meg.doksi. ami pont a fent látható csúfságot adja vissza.

…) is használható. A CheckBox alapértelmezett eseménye a CheckedChanged.Add(i. A SelectionMode egy enum típus. hogy ilyenkor nem jön létre új objektum. hogy hozzáférjünk az eredeti adatokhoz.SelectedItem. Nos.Forrás: http://www. A Sorted tulajdonság beállításával a ListBox elemei ABC sorrendbe rendez dnek. A ListBox BeginUpdate és EndUpdate metódusai segítségével anélkül tölthetjük fel a lsitát futásid ben. csak egy már létez re mutató referencia. } A küld objektumon explicit típuskonverziót hajtottunk végre. ez egy gy jteményt ad vissza. A használandó eszközök: Items tulajdonság. MultiSimple (a Ctrl billenty nyomva tartásával lehet több elemet kiválasztani) illetve MultiExtended (az egér nyomva tartása mellett lehet „kijelölni” az elemeket). Ez a metódus a hasonló vezérl knél (CheckedListBox. Az Items gy jtemény megcalósítja az IEnumerable és IEnumerator interfészeket így foreach ciklust is használhatunk. illetve az Add metódust. amely felajánlja.ToString()).i < 100. A felhasználói felület nagyon egyszer lesz: . 36.Items.ToString()).++i) { listBox1.Show("A kiválasztott elem: " + lb. } listBox1. EventArgs e) { ListBox lb = (ListBox)sender. hogy legközelebb felismer minket és azonnal beléptet. for(int i = 0. Készítsünk egy egyszer programot. vagyis használhatjuk az indexel operátor.6 CheckBox A CheckBox vezérl a választás lehet ségét kínálja fel. hogy minden egyes alkalommal újrarajzolná azt: listBox1.BeginUpdate(). értékei lehetnek None (nem lehet kiválasztani egy elemet sem). A ListBox elemeihez az Items tulajdonságon keresztül is hozzáférhetünk. ezt a bejelölés/visszavonás váltja ki. A ListBox SelectionMode tulajdonságát megváltoztatva több elemet is kijelölhetünk egyszerre. Bizonyára mindenki találkozott már olyan beléptet rendszerrel.doksi. azt a választást egy CheckBox –xal implementálták (természetesen ilyen más környezetekben is van). amellyel egy lámpát szimulálunk. Fontos tudni. MessageBox.hu 167 private void listBox1_SelectedIndexChanged(object sender. foreach ciklus. Házi feladat következik: húzzunk a formra egy másik ListBox -ot és a form Load eseménykezel jében töltsük fel ezt a vezérl t is a másik lista elemeivel. ComboBox.EndUpdate().

Ennél a vezérl nél nem használhatunk adatkötést. egyszerre több elemet az Items tulajdonságának AddRange metódusával tudunk átadni neki (illetve egy elemet az Add metódussal): private void Form1_Load(object sender. "Judit". a foreach ciklus. ez egy gy jteményt ad vissza. checkedListBox1.AddRange(t). EventArgs e) { string[] t = new string[] { "István". "Viktória". "Béla".doksi. . EventArgs e) { if (checkBox1. } 36.Forrás: http://www. "Dániel" }.hu 168 Az eseménykezel pedig a következ : private void checkBox1_CheckedChanged(object sender.Text = "A lámpa le van kapcsolva". A kiválasztott elemekhez a CheckedItems tulajdonságon kereszt l férhetünk hozzá.Checked) { label1.Text = "A lámpa fel van kapcsolva". } Alapértelmezetten dupla kattintással tudunk megjelölni egy elemet. az elemeit egy – egy CheckBox –xal lehet kijelölni.Items. } else label1.7 CheckedListBox Ez a vezérl a ListBox és a CheckBox házasságából született. amelyen használhatjuk pl. ezt a CheckOnClick tulajdonság True értékre állításával szimpla kattintásra változtathatjuk.

Name == "radioButton1") { label1. hogy minden RadioButton egy adott tárolón belül (pl. mint a CheckBox.Add(s).Text = "A lámpa le van kapcsolva". A két RadioButton használja ugyanazt az eseményt (mindkett kijelöl. if (rb. egy gombot és egy ListBox –ot. EventArgs e) { RadioButton rb = (RadioButton)sender.CheckedItems) { listBox1. a különbség annyi. hogy be legyen –e jelölve. A RadioButton alapértelmezett eseménye a CheckedChanged.Items. amely tartalmaz egy CheckedListBox –ot. közülük pedig egy id ben csak egyet lehet kiválasztani. egy form) egy csoportot alkot.hu 169 Készítsünk egy programot. A gomb Click eseményében fogjuk megoldani: private void button1_Click(object sender. A gomb megnyomásakor a kiválasztott elemek jelenjenek meg a ListBox –ban. A lámpakapcsolgató program: . ehhez a Checked tulajdonságot kell megváltoztatni (itt is érvényes az egy id ben egy lehet bejelölve szabály).doksi. 36. Vegyük el a jól bevált lámpakapcsoló programunkat és alakítsuk át. a CheckedListBox alapértelmezett eseménye is a SelectedIndexChanged. EventArgs e) { foreach (object s in checkedListBox1. } } Hasonlóan a ListBox –hoz. A RadioButton –nak tervezési id ben megadhatjuk. } else label1.Forrás: http://www.8 RadioButton A RadioButton hasonló funkcionalitást kínál. utána Properties ablak villámjel és ott állítsuk be): private void radioButton2_CheckedChanged(object sender. } A form Load eseményében is állítsuk be a Label kezdeti szövegét. hogy használja ezt a vezérl t. ezért erre fogunk ráülni.Text = "A lámpa fel van kapcsolva".

} else label2.hu 170 A megjelenését átállíthatjuk gombszer vé az Appearance tulajdonsággal: Nyílván egy formon belül el fordulhatnak különálló RadioButton csoportok. if (p == null) { if (rb. mivel minden vezérl rendelkezik egy Parent nev tulajdonsággal. Nincs nehéz dolgunk. hogy ne zavarják egymást el kell különíteni ket valahogy.Text = "A lámpa fel van kapcsolva". A négy RadioButton ugyanazt az eseménykezel t fogja használni.Text = "A lámpa le van kapcsolva". A kett közt a különbségek a következ k: a Panel scrollozható. Panel p = rb.Name == "radioButton3") { label2. Alakítsuk át a progrmunkat. nincs felirata sem állandó kerete. a Panel –t és a GroupBox –ot. } else label1. hogy melyik csapat küldte a jelzést. Erre a célra két vezérl t használhatunk. ekkor. így meg kell tudnunk különböztetni.doksi.Text = "A lámpa le van kapcsolva". hogy két külön lámpa legyen rajta. Egy egyszer típuskonverzióval fogjuk eldönteni a kérdést: private void radioButton2_CheckedChanged(object sender. } } . amely az t tartalmazó vezérl t adja vissza.Parent as Panel.Text = "A lámpa fel van kapcsolva". EventArgs e) { RadioButton rb = (RadioButton)sender. } else { if (rb. a GroupBox pedig nem scrollozható van felirata és kerete is.Forrás: http://www.Name == "radioButton1") { label1. az egyik csoport egy Panel –ban legyen a másik egy GroupBox –ban.

amely egy ComboBox –ból és egy gombból áll. Készítsünk egy alkalmazást ennek szemléltetésére! Húzzunk a fomra egy TextBoxot. hogy melyik csapattól érkezett a jelzés. A ComboBox –xal használhatunk adatkötést is. hogy a szöveg módosítása nem igazán elegáns. a gombot megnyomva megjelenik egy MessageBox. ha a konverzió sikertelen volt. ez fogja tartalmazni a keresend szöveget.doksi. A ComboBox –ot a már ismert módon töltöttük fel a form Load eseményében. A FindString és FindExactString metódusokkal a ComboBox elemei között kereshetünk. amelyek a használandó metódusokat jelölik. így biztosan tudjuk. a keresést fogja elindítani: .9 ComboBox A ComboBox a TextBox és a ListBox keresztezéséb l jött létre. amely a paramétereként megadott stringgel kezd dik. de ezt beállíthatjuk a BorderStyle tulajdonsággal. Ugyanakkor meg kell jegyeznünk. Készítsünk egy programot. ezért az olvasó feladata lesz. El bbi a legels olyan elem indexét adja vissza.hu 171 Kihasználtuk azt. illetve két RadioButton –t.Forrás: http://www. hogy egy szebb megoldást találjon. 36. amely a kiválasztott elemet jeleníti meg. hogy az as operátor nullértéket ad vissza. A gomb funkcionalitása most megváltozik. utóbbi pedig pontos egyezést keres. a beírt szöveg alapján kiválaszthatunk egy elemet egy el re megadott listából. A program most már szépen fut: A Panel –nek alapértelmezetten nincs körvonala.

A vezérl t a form Load eseményében töltöttük fel adatokkal: .Text).FindStringExact(textBox1.doksi. 36.FindString(textBox1. A fa egyes elemei TreeNode típusuak.Text) : comboBox1. egy filerendszer elemei).SelectedIndex = radioButton1. EventArgs e) { comboBox1.Forrás: http://www. } Ezek a metódusok a többi hasonló vezérl vel (ListBox.hu 172 A Click esemény kódja pedig ez lesz: private void button1_Click(object sender.10 TreeView A TreeView vezérl adatok hierarchikus megjelenítésére szolgál (pl.Checked == true ? comboBox1. stb…) is használhatóak.

ez akkor fut le. break.hu 173 private void Form1_Load(object sender.Items. Készítsünk egy programot.ByKeyboard: listBox1. ha valamelyik Node –ot kiválasztjuk. Ezután az eseménykezel kódja: private void treeView1_AfterSelect(object sender. ebben fogjuk az eseményeket megjeleníteni.Items. break. TreeViewEventArgs e) { switch (e. ++i) { treeView1. case TreeViewAction.Nodes. for (int i = 0.EndUpdate().ByMouse: listBox1. TreeViewEventArgs e) { } A TreeViewEventArgs a kiválasztás módjáról ad tájékoztatást. } } treeView1.Add("Parent" + i.Action) { case TreeViewAction. EventArgs e) { treeView1. } } A program pedig: . i < 10. for (int j = 0.Forrás: http://www. Az eseménykezel a következ képpen néz ki: private void treeView1_AfterSelect(object sender. } A TreeView alapértelmezett eseménye az AfterSelect.Add("Az esemény kiváltója a billenyt zet"). Húzzunk a formra egy ListBoxot.ToString()). j < 3.doksi.BeginUpdate().Add(j.Add("Az esemény kiváltója az egér"). default: break. ++j) { treeView1. amely ezekr l az eseményekr l ad információt.ToString()).Nodes[i].Nodes.

} . ezek az AfterExpand és AfterCollapse események: private void treeView1_AfterCollapse(object sender. A bejelölést az AfterCheck eseménnyel tudjuk kezelni: private void treeView1_AfterCheck(object sender.hu 174 Ez az eseménykezel azonban csak a Node kiválasztását figyeli. TreeViewEventArgs e) { listBox1.Text + ") bezárult").Add("Node (" + e.doksi.Items. de mi esetleg szeretnénk a kinyit/becsuk eseményre is feliratkozni. } private void treeView1_AfterExpand(object sender.Text + ") kinyilt").Node.Items.Add("Node (" + e.Node.Items.Node.Add("Node (" + e.Text + ") bejelölve"). } A CheckBoxes tulajdonság bejelölésével minden egyes Node –hoz egy CheckBox is megjelenik.Forrás: http://www. TreeViewEventArgs e) { listBox1. TreeViewEventArgs e) { listBox1.

Items. azaz végigiterálható egy foreach ciklussal. EventArgs e) { domainUpDown1. Készítsünk programot. TextBox –ban.11 DomainUpDown A DomainUpDown egy stringérték megjelenítésére szolgál.doksi. amely ezt megoldja (tipp: a TreeView –on meghívott Nodes[x] tulajdonság az x –edik gyökérelemet fogja visszaadni.Add("Harmadik"). vagy tetszés szerint beállítani. amely megvalósítja az IEnumerable és IEnumerator interfészeket.: egy Node bejelölése nem jár együtt a gyermekei bejelölésével.Items.hu 175 Egy Node szül jére a Parent tulajdonságával tudunk hivatkozni. private void Form1_Load(object sender. amelyet egy listából választhatunk ki. tipp2: akár az egész lerendezhet két egymásba ágyazott foreach ciklussal (kulcsszó: TreeNodeCollection)).Add("Második"). Házi feladat 2. domainUpDown1. ellenkez esetben indításnál a domainUpDown1 felirat jelenik meg. akárhol) a bejelölt Node –ok nevét és szül jét (a gyökérelemek Parent tulajdonsága null –t ad vissza.Add("Els "). A Wrap tulajdonság igaz értékre állításával ha elértük a lista végét akkor a következ elemnél automatikusan a lista elejére ugrik. 36.Forrás: http://www.Items. } A Text tulajdonságot nem árt kitisztítani. . A bejelölést a Node Checked tulajdonságával ellen rizhetjük. A TreeView Nodes tulajdonsága egy TreeNodeCollection típusú gy jteményt ad vissza. ennek pedig van Nodes tulajdonsága. Házi feladat 1. domainUpDown1. mivel nekik nincs szül jük).: gombnyomásra listázzuk ki (egy másik ListBox –ban.

12 NumericUpDown Hasonló mint az el z vezérl .hu 176 36.Sleep(100). } Ebben az esetben a típuskonverzióhoz nem hoztunk létre új azonosítót. egy növekv téglalap illetve egyetlen mozgó blokk) állítható be.doksi.Show(((NumericUpDown)sender). de ezúttal számokat jelenítünk meg.PerformStep().Value. amely akkor indul el. A Minimum és Maximum tulajdonságokkal a legkisebb és legnagyobb lehetséges értéket adjuk meg. amikor megváltozik a mutatott érték: private void numericUpDown1_ValueChanged(object sender. } MessageBox. i < 100. a megfelel zárójelezéssel is elérhet ek a vezérl adatai.ToString()).Forrás: http://www.Thread. míg az Increment tulajdonság a lépték méretét adja meg.13 ProgressBar A ProgressBar vezérl t általában egy folyamat állapotának jelölésére használjuk: A Minimum és Maximum tulajdonságai a felvehet értékek intervallumát jelölik ki. Alapértelmezett eseménye a ValueChanged. A gomb Click eseményében indítjuk el a folyamatot: private void button1_Click(object sender. ++i) { progressBar1. 36. A Style tulajdonsággal a vezérl kinézete (blokkos. EventArgs e) { MessageBox. A fenti program elszámol százig és ezt a folyamatot egy ProgressBar –on jelzi. System.Show("100%"). } .Threading. EventArgs e) { for (int i = 0. ezek alapértelmezetten 0 és 100.

Most be tudjuk állítani. stb…). átmásolva a saját könyvtárba. Ha a képet egy resource állomány tartalmazza. válasszuk ki és nyissuk meg a Properties ablakot. ez a Scroll. Az értéket kézzel is állíthatjuk a Value tulajdonsággal: progressBar1. hogy a Local Resource –t választjuk. Ennek a file –nak is vannak beállítási. A Minimum és Maximum tulajdonságokkal az alsó és fels határt lehet állítani. csakis a programon belül érhetjük el.resx nev tag. 36. hogy látványosabb legyen az eredmény és a folyamat végét egy MessageBox –xal jelezzük. amikor megváltozik a vezérl értéke (vagyis elmozdul a mutató): private void trackBar1_Scroll(object sender.15 PictureBox Ez a vezérl – ahogyan azt a neve is mutatja – képek megjelenítésére illetve grafikus feladatokra való. A fordítás után a Solution Explorerben megjeleni egy Resources. a TickFrequency a vezérl beosztását módosítja. EventArgs e) { label2.hu 177 Minden lépésnél pihentetjük kicsit a f szálat.doksi. hogy hogyan kezelje az er forrásokat (beágyazva. akkor a kép belefordul a futtatható állományba (vagy osztálykönyvtárba). amely a Step tulajdonság értéke szerint lépteti az aktuális értéket (a Step alapértelmezetten 1 –re van állítva). amely akkor aktiválódik. A másik lehet ség. ezt az Import gombra kattintva tehetjük meg.14 TrackBar Ezzel a vezérl vel a felhasználó ki tud választani egy értéket egy intervallumból.Value.Text = ((TrackBar)sender). A ProgressBar léptetését a PerformStep metódussal végeztük el.Value += 10. hogy a vezérl vízszintesen (Horizontal) vagy függ legesen (Vertical) jelenjen meg. ekkor a kép csak simán belefordul a kimenetbe. } Az Orientation tulajdonsággal beállíthatjuk. Az Image tulajdonságát kiválasztva egy ablak jelenik meg és két lehet séget kínál nekünk.illetve SmallChange pedig azt az értéket. amikor az egérrel vagy a billenty zettek módosítjuk az aktuális értéket: A programban a TrackBar alapértelmezett eseményére ültünk rá. . a Large. Természetesen ekkor is el re/vissza lép a vezérl . ez fogja tartalmazni az er forrásokat (most a képet).Forrás: http://www. 36. Alapértelmezetten nincs kép hozzárendelve a programhoz.ToString().

Forrás: http://www.doksi.hu

178 Természetesen arra is van lehet ség, hogy a forráskódból állítsuk be a képet, ekkor a kép elérési útját kell megadnunk (ha a futtatható állománnyal egy helyen van, akkor csak a nevét):
private void Form1_Load(object sender, EventArgs e) { Image img = new Bitmap("Naplemente.jpg"); pictureBox1.Image = img; }

Az Image egy absztrakt osztály, a Bitmap pedig egy leszármazottja. Utóbbi számos konstruktorral (12 –vel) rendelkezik, a fent bemutatott a legegyszer bb. A PictureBox Image tulajdonsága pedig egy Image leszármazottat vár.

36.16 RichTextBox
A RichTextBox szöveg bevitelére és manipulálására alkalmas, de jóval rugalmasabb és több szolgáltatást nyújt, mint a TextBox. Emellett kapacitásában is túlmutat rajta. Szöveget rendelhetünk hozzá a Text tulajdonságán kereszt l, egyszer szöveges fileból vagy RTF (Rich Text File) formátumú szövegb l:

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

Egy másik módja a szöveggel való feltöltésnek az AppendText metódus:

Forrás: http://www.doksi.hu

179
private void Form1_Load(object sender, EventArgs e) { richTextBox1.AppendText("Ez a hozzáf zött szöveg"); }

Ekkor a paraméterként átadott karaktersorozat a vezérl tartalmának végéhez f z dik hozzá (gyakorlatilag a Text += „szoveg” rövidített formája). RTF vagy szöveges állományt olvashatunk be a LoadFile metódusával:
private void Form1_Load(object sender, EventArgs e) { richTextBox1.LoadFile("test.rtf"); }

A LoadFile párja a SaveFile, amellyel RTF –be menthetjük a vezérl tartalmát (a példában egy gombhoz rendeltük a mentést):
private void button1_Click(object sender, EventArgs e) { richTextBox1.SaveFile("savedfile.rtf"); }

aktuális

A RichTextBox rengeteg metódust állít rendelkezésünkre a szöveg formázásához. Ehhez el ször ki kell jelölni a szöveget, ezt vagy kézzel tesszük meg, vagy a Find metódussal. Húzzunk a formra három CheckBox –ot, ezekkel fogjuk beállítani, hogy félkövér, d lt vagy aláhúzott legyen a szöveg. A három vezérl használja ugyanazt az eseményt:
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;

Forrás: http://www.doksi.hu

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

Hasonlóan kell kezelni a SelectionColor, SelectedIndent, stb. tulajdonságokat is. A Find metódussal megkereshetünk és kijelölhetünk egy adott karaktersorozatot. Húzzunk egy gombot és egy TextBox –ot a formra, a gomb Click eseményéhez pedig írjuk a következ t:
private void button1_Click(object sender, EventArgs e) { int idx = richTextBox1.Find(textBox1.Text); richTextBox1.Focus(); }

Vissza kell adnunk a fókuszt a vezérl nek, különben nem látszana az eredmény:

Kijelölni a Select metódussal is tudunk, mindössze meg kell adnunk a kezd indexet és a hosszt:
richTextBox1.Select(0, 10);

Ekkor a szöveg elejét l kezdve (nulladik index) tíz karakter hosszan jelöljük ki a vezérl tartalmát. Ha a vezérl tartalmaz egy internetes hivatkozást, akkor azt automatikusan felismeri és megjelöli:

Forrás: http://www.doksi.hu

181

Ahhoz azonban, hogy a link m ködjön is kezelnünk kell a RichTextBox LinkClicked eseményét:
private void richTextBox1_LinkClicked(object sender, LinkClickedEventArgs e) { System.Diagnostics.Process.Start(e.LinkText); }

A LinkClickedEventArgs a hivatkozásról tulajdonsága a címet adja vissza.

tartalmaz

információkat,

LinkText

Hasonlóan a TextBox –hoz a RichTextBox alapértelmezett eseménye is a TextChanged.

36.17 DateTimePicker
Ez a vezérl dátumot: lehet vé teszi a felhasználó számára, hogy kiválasszon egy adott

Számos tulajdonság ál lrendelkezésünkre, amelyekkel megváltoztathatjuk a vezérl megjelenítését, pl. a CalendarForeColor, CalendarFont, stb… A MinDate és MaxDate tulajdonságaival beállíthatjuk, hogy milyen intervallumban választhat a felhasználó. Alapértelmezett eseménye a ValueChanged, amely a kiválasztott dátum megváltozásakor aktivizálódik. Írjunk egy programot, amely egy MessageBox –ban megjeleníti a kiválasztott dátumot:
private void dateTimePicker1_ValueChanged(object sender, EventArgs e)

Forrás: http://www.doksi.hu

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

A Value tulajdonság egy DateTime típusú értéket ad vissza. A fenti példában a ToShortDateString metódust használtuk, ez csak a napra lebontott dátumot adja vissza (vagyis év-hónap-nap), amennyiben másodpercre pontos értéket akarunk használjuk a sima ToString metódust. A DateTimePicker tulajdonképpen két vezérl b l áll, egy TextBox leszármazottból illetve a dátum kiválasztásához egy MonthCalendar –ból, ez utóbbi önállóan is felhasználható:

A FirstDayOfWeek tulajodnságával a hetek kezd napját adhatjuk meg, ez egyes országokban eltér lehet, alapértelmezetten az operációs rendszer nyelve alapján kap kezd értéket. Kijelölhetünk napokat, amelyek a többit l kiemelkednek, ezt a BodledDates, AnnuallyBoldedDates illetve MonthlyBoldedDates tulajdonságokkal érhetjük el, mindegyikük egy DateTime típusú tömböt vár:
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),

Forrás: http://www.doksi.hu

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

Az eredmény pedig ez lesz:

Két fontos eseménye van, az els a DateChanged, amely akkor aktivizálódik, amikor kiválasztunk az egérrel egy dátumot:
private void monthCalendar1_DateChanged(object sender, DateRangeEventArgs e) { MessageBox.Show(e.End.ToShortDateString()); }

A DataRangeEventArgs End illetve Start tulajdonságai a felhasználó által utolsóként és el ször kiválasztott dátumokat adják vissza. Természetesen ebben az esetben csak egy id pontot tudunk visszaadni, a második paramétert igazából a másik DateSelected eseményben tudjuk felhasználni (és ez a vezérl alapértelmezett tulajdonsága is). Az egérrel kijelölhetünk több dátumot és ekkor már fel tudjuk használni az End és Start tulajdonságokat:
private void monthCalendar1_DateSelected(object sender, DateRangeEventArgs e) { MessageBox.Show(e.Start.ToShortDateString() + " " + e.End.ToShortDateString()); }

36.18 MenuStrip
Ezzel a vezérl vel a legtöbb programban megszokott menüsort tudunk létrehozni:

Forrás: http://www.doksi.hu

184

Kétféleképpen tudjuk feltölteni a menüpontokat, a legegyszer bb a Visual Studio tervez nézetét használni, ekkor csak szimplán be kell írni a szöveget. A másik lehet ség, hogy kódból hozzuk létre, ez a következ képpen néz 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); } }

Látható, hogy a MenuStrip minden egyes menüpontja egy ToolStripItem típusú objektum. A MenuStrip Items és a ToolStripMenuItem DropDownItems tulajdonsága is egy ToolStripItemCollection típusú listát ad vissza, amelynek tagjai mind ToolStripMenuItem típusuak. A fenti kód kétféle módját mutatja be a menühöz való hozzáadásnak: vagy a hagyományos úton létrehozzuk az objektumot és hozzáadjuk, vagy helyben az Add metódus paramétereként megadott string értékkel. Utóbbi esetben automatikusan meghívódik a ToolStripMenuItem konstruktora. A menü megvan, most kellene valami funkcionalitást is hozzáadnunk, ezt két módon tudjuk megoldani. Kezelhetjük a MenuStrip alapértelmezett eseményét, az ItemClick –edet, amely értelemszer en akkor sül el, amikor rákattintunk valamelyik menüpontra. Azt is tudnunk kell ekkor, hogy melyik menüpont volt a „b nös”. Nézzük meg az eseményt:
private void menuStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e) { }

A ToolStripEventArgs ClickedItem tulajdonsága visszaadja azt a ToolStripMenuItem objektumot, amelyre kattintottuk. Viszont van egy probléma, méghozzá az, hogy ez a MenuStrip eseménye, nem pedig a gyermek objektumoké. A legfels bb szint elemekre kiváltódik, de az almenükre nem. Ilyenkor a ToolStripMenuItem ItemClicked eseményét (ez bizony ugyanaz, vajon miért?) fogjuk használni. Miel tt továbbmegyünk gondolkodjunk egy kicsit: tegyük fel, hogy a MenuStrip eseményét

amikor egy kétállású tulajdonságot akarunk implementálni): . akkor rögtön meg is adhatjuk neki az eseménykezel jét: ToolStripMenuItem item = new ToolStripMenuItem("Menu".*/ default: break. case "masodik": //itt is csinálunk valamit break. hogy egy MessageBox –ban jelenítse meg a menüpont nevét: private void menu11ToolStripMenuItem_Click(object sender. hogy az egyes menüpontok kiválasztásakor mit tegyünk. Módosítsuk az eseménykezel t.Show(((ToolStripMenuItem)sender). }. de ezt most nem adtuk meg.Forrás: http://www. és el kell dönteni.. null. Írhatunk mondjuk valami ilyesmit: private void menuStrip1_ItemClicked(object sender.Text). amikor a menü csak egyszint . EventArgs e) { } Itt már nem jár a speciális eseményargumentum. csak az utasítás a lényeg.hu 185 használjuk. ToolStripItemClickedEventArgs e) { switch (e. /*.ClickedItem. new EventHandler(Click_EventHandler)). } Ez a megoldás nem valami szép. Olyan esetekben azonban jó. } A menüpontok CheckOnlick tulajdonságának igazra állításával beállíthatjuk.. mivel ilyen esetekben nem érdekel minket. Amennyiben a menüpontot kódból hoztuk létre. ehhez az adott menüponton kattintsunk duplán: private void menu2ToolStripMenuItem_Click(object sender. hogy kattintásra változtassa az állapotát (ez olyankor jön jól. hogy ki küldte a parancsot.Name) { case "elso": //itt csinálunk valamit break. A konstruktor második paramétere a menüponthoz rendelt kép lenne. Átláthatatlan és nehezen módosítható. Bonyolultabb helyzetben viszont sokkal egyszer bb az egyes menüpontok ItemClicked eseményét használni. EventArgs e) { MessageBox.doksi.

hogy a menüpont be van –e jelölve.Show(((ToolStripMenuItem)sender). azaz a form teljes élettartama alatt ugyanazt az ablakot használjuk. } 36. amikor csak ritkán használjuk a dialógust elegánsabb.OK) { . EventArgs e) { if (colorDialog1. vagy a kódból hozzuk létre.ShowDialog() == DialogResult. EventArgs e) { MessageBox.ToString()). ha gyakran van szükségünk az adott ablakra. amivel megnyithatunk egy szöveges file –t. összesen hat darab van (a PrintDialog a Printing fül alatt lakik). A gomb Click eseményét fogjuk használni: private void button1_Click(object sender. Egy ilyen dialógusablakot kétféleképpen használhatunk fel. Jöjjön egy gyakorlati példa: húzzunk a formra egy gombot és egy PictureBox –ot.hu 186 A Checked tulajdonság visszadja.doksi. Az el bbi esetben egy (alapértelmezetten) private elérés ColorDialog típusú adattag jön létre.19 Általános párbeszédablakok A . ha szükség van rá. Vagy a tervez nézetben ráhúzzuk a formra (ekkor alul megjelenik egy külön sávban). Ezeket az el re legyártott elemeket a Visual Studio tervez nézetében a ToolBox ablak Dialogs füle alatt találjuk. illetve egy ColorDialog –ot.Checked.NET –ben lehet ségünk van el re megírt dialógusablakok használatára. Els ként a legegyszer bbet a ColorDialog –ot fogjuk megvizsgálni. Az ellenkez esetben. vagy sem: private void menu11ToolStripMenuItem_Click(object sender.Forrás: http://www. Ez olyankor jön jól. mint pl. ha lokális változóként hozzuk létre.

EventArgs e) { ColorDialog colorDialog1 = new ColorDialog(). akkor tudjuk. hogy a felhasználó mit választott (az IntelliSense mutatja a többi lehet séget is.BackColor = colorDialog1.ShowDialog() == DialogResult.Color. hogy a felhasználó választott valamilyen színt.Color.Forrás: http://www. ha az eredmény OK. } } A ShowDialog metódus egy DialogResult típusú felsorolt értéket ad vissza. kísérletezzünk).hu 187 pictureBox1. attól függ en. } } . A fenti példában. ezt pedig lekérdezhetjük a Color tulajdonsággal. akkor a Click esemény így néz ki: private void button1_Click(object sender.BackColor = colorDialog1. Ha az ablakot tisztán kódból akarjuk létrehozni. if (colorDialog1.doksi.OK) { pictureBox1.

hogy milyen típusú file –okat akarunk megnyitni. ez – mint azt a neve is mutatja – arra való. hogy milyen típusú file – t akarunk a RichTextBox –ba beolvasni. } } Ebben az esetben a LoadFile metódusban meg kell adnunk. amit majd a RichTextBox –ban megjelenítünk.PlainText). most azonban csak a Filter –t vizsgáljuk. RichTextBoxStreamType. EventArgs e) { if (openFileDialog1.OK) { richTextBox1. A Filter tulajdonság kódból is megadható: openFileDialog1. ez utóbbit arra használjuk. A dialógus FileName tulajdonsága a file elérési útját adja vissza. . Ezzel megadhatjuk.txt|".Forrás: http://www. Akár többféle sz r t is megadhatunk: openFileDialog1.: Text files|*.Filter = "Text files|*. mivel a lokális változók hatóköre a metódus végén lejár. Az OpenFileDialog –nak rengeteg érdekes tulajdonsága van.doksi. Következ a sorban az OpenFileDialog. hogy megnyissunk egy szöveges állományt.hu 188 Ekkor viszont minden egyes alkalommal új objektum jön létre.FileName.txt|All Files|*.Filter = "Text files|*.*".ShowDialog() == DialogResult. Húzzunk egy formra egy RichTextBox –ot illetve egy gombot.LoadFile(openFileDialog1. hogy file –okat nyissunk meg. mivel az alapértelmezetten RTF formátumúakat olvas. A programunkban gomb Click eseménye: private void button1_Click(object sender. pl.txt.

.OpenFile().Filter = "Text files|*. hogy stream –b l is képes olvasni.hu 189 Az OpenFile metódus megnyit egy stream –et csakis olvasásra a kiválasztott file –ra: private void button1_Click(object sender.ShowDialog() == DialogResult. } } Itt kihasználtuk a RichTextBox azon tulajdonságát.LoadFile(openFileDialog1.Forrás: http://www.PlainText).OK) { richTextBox1.txt|All Files|*.*".doksi. RichTextBoxStreamType. EventArgs e) { openFileDialog1. if (openFileDialog1.

A példa ugyanaz lesz itt is.ShowDialog() == DialogResult. amelybe a RichTextBox SaveFile metódusával írjuk ki az adatokat. Az el z példát fogjuk használni. ez lesz a legegyszer bb. RichTextBoxStreamType.PlainText).SaveFile(saveFileDialog1. az ingyenes PDF995 –öt. de gombnyomásra el jön az ablak és megváltoztatjuk a RichTextBox bet típusát: private void button1_Click(object sender. } } A PrintDialog –gal kinyomtathatunk egy dokumentumot.ShowDialog() == DialogResult.OK) { printDocument1.Font. Következ a sorban a FontDialog. A tényleges nyomtatást a PrintDocument fogja végezni. EventArgs e) { if (fontDialog1. de most a gomb lenyomásakor elmentjük a RichTextBox tartalmát: private void button1_Click(object sender. amely bet típus beállítására használandó. EventArgs e) { if (printDialog1. még mindig a példánknál maradva a RichTextBox tartalmát. amely a nyomtatandó adatokról tartalmaz megfelel formátumú információkat. Most is gombnyomásra fogjuk elindítani a folyamatot: private void button1_Click(object sender. Ezt az objektumot akár a ToolBox –ból is a formra húzhatjuk. Az OpenFileDialog párja a SaveFileDialog. amely file –ok elmentésére szolgál. } } Az OpenFile metódus egy írható/olvasható stream –et nyit meg. Ehhez szükségünk lesz egy PrintDocument típusú objektumra. pl. a fenti megoldás az érthet ség miatt született. a PrintDialog a beállítások miatt kell. } } .doksi.OK) { richTextBox1. EventArgs e) { if (saveFileDialog1.hu 190 Természetesen a Filter tulajdonságot elegánsabb és ésszer bb a konstruktorban vagy a form Load eseményében beállítani.OK) { richTextBox1.Forrás: http://www. Miel tt továbbmegyünk érdemes a teszteléshez beszerezni valamilyen PDF nyomtatót.OpenFile().ShowDialog() == DialogResult.Font = fontDialog1.Print().

Graphics. System. A ciklusban kiszámoljuk.SelectedPath. e. Brushes.Text = folderBrowserDialog1.MarginBounds.Printing. A gomb Click eseménye következ lesz: private void button1_Click(object sender.GetHeight(e. font. } } . Most egy while ciklust indítunk.GetHeight(e. float maxLine = e. hogy a RichTextBox Lines tulajdonsága az egyes sorokat adja vissza egy stringtömb formájában.Length.PrintPageEventArgs e) { Font font = new Font("Arial".Left. ++i. Az el re megírt párbeszédablakok közül az utolsó a FolderBrowserDialog. y. a negyedik és ötödik a kezdés x illetve y koordinátája.Drawing. Ezután kiszámoljuk.MarginBounds.Lines. amelynek segítségével kiválaszthatunk a könyvtárstruktúrából egy mappát. A következ lépésben az esemény paraméterét használjuk. amely gombnyomásra megjelenít egy FolderBrowserDialog –ot. vagyis azt.OK) { textBox1. Az esemény legvégén beállítjuk a HasMorePages logikai (bool típusú) tulajdonságot. és miután a felhasználó kiválasztott egy mappát egy TextBox –ban jelenítsük meg a mappa elérési útját. ennek els paramétere a nyomtatandó sor.Height / font. végül pedig a formátumstring. amely vagy addig megy amíg teleírja alapot. int i = 0.Black. hogy van –e még nyomtatandó szöveg. annak is a DrawString metódusát. new StringFormat()). pl margó. ahol a tényleges nyomtatást fogjuk megcsinálni: private void printDocument1_PrintPage(object sender. ez a bet típust fogja jelenteni.Forrás: http://www. vagy az adatforrás végéig. vagyis elosztjuk a lap magasságát a bet k magasságával. Ha van.hu 191 Amikor meghívjuk a Print metódust kiváltódik a PrintDocument PrintPage eseménye.Graphics).Lines. } Az esemény paramétere a nyomtatásról tartalmaz információkat.ShowDialog() == DialogResult. amelyet üresen hagytunk. ezt az értéket pedig hozzáadjuk a lap tetejének értékéhez.Graphics) + e. 10). akkor az esemény automatikusan újrakezd dik (pontosabban folytatódik). EventArgs e) { if (folderBrowserDialog1.Lines[i]. hogy a beállított bet típus és méret mellett hány sor fér el egy oldalra. hogy hol kezd dik az adott sor (vagyis az y koordinátát): megszorozzuk a bet k magasságát az aktuális sor számával. Készítsün kegy programot. a második a bet típus. while (i < maxLine && i < richTextBox1.MarginBounds.HasMorePages = i < richTextBox1. Els ként létrehoztunk egy Font típusú objektumot. de ezt fogjuk használni a tényleges nyomtatáshoz is. a harmadik a bet k színe.Top.DrawString(richTextBox1. Itt kihasználtuk.doksi. } e.Length) { float y = i * font. e.

20 TabControl Ezzel a vezérl vel több párhuzamosan létez részekb l egyszerre csak egy látszik: részre oszthatjuk a formot.Forrás: http://www.doksi. ez a vezérl pontosan err l szól. A csónak kétszemélyes és semelyik parton nem lehet több kannibál. A két partot jelezheti mondjuk két ListBox. 36. ha duplán kattintunk a vezérl re (ezt minden lapnál külön kell megtennünk). amelyek lekérhet ek a TabControl TabPages tulajdonságával. 36. 2. A ContextMenuStrip tulajdonképpen a már ismert MenuStrip „mobilizált” változata. minden amit ott tanultunk itt is hasznosítható. hogy egy csónakban mindenkit átjuttasunk a túlpartra. a lista egyes elemei nyílván TabPage típusúak). amely egy TabPageCollection típusú listával tér vissza (ez felhasználható egy foreach ciklusban. ha beállítjuk a ContextMenu tulajdonságát. betölteni és formázni is. amely a „Misszionárius . egyébként a kannibálok megeszik ket. Készítsünk programot. Egy folyó egyik partján 3 kannibál és 3 misszionárius áll. . A TabControl –nak nincs alapértelmezett eseménye.kannibál” problémát modellezi. amely Az egyes lapok TabPage típusúak.22 Gyakorló feladatok 1. Bármelyik vezérl höz hozzáköthetjük.21 ContextMenuStrip Biztosan mindenki ismeri a jobb egérgombra el ugró helyi menüt. mint misszionárius. A cél az. Lehessen menteni.hu 192 36. Készítsünk egyszer szövegszerkeszt t a RichTextBox és a menüvezérl k segítségével. ehelyett az adott lap Click eseményét kezeljük.

2 ErrorProvider Ezzel a komponenssel egy hibaüzenetet köthetünk egyes vezérl khöz. ha pedig odaért akkor megállítjuk a Timer –t. } A Timer nincs felkészítve többszálú végrehajtásra. akkor használjuk a System. amelyek közvetlenül nem látszanak a formon. amely az eseményünk gyakoriáságt fogja jelölni ezredmásodpercben (tehát ha az Interval 1000. A ToolBox –ból húzzunk egy Timer –t a formra (a Component fül alatt található). } else { timer1. EventArgs e) { if (button1. amely lehet vé teszi. Az aktivált esemény pedig a Timer alapértelmezett eseménye a Tick lesz.Location.1 Timer A Timer egy olyan komponens. } } A gombot addig mozgatjuk balra.Location = new Point(button1. amely automatikusan mozgat egy vezérl t (mondjuk egy gombot) egy formon.X + button1.Windows Forms – Komponensek A komponensek olyan osztályok. hogy Windows Forms környezetben m ködjenek a legjobban. hogy bizonyos id közönként végrehajtsunk egy utasítást.Location. erre pedig a legjobb hely a gomb Click eseménye: private void button1_Click(object sender. ez alapértelmezetten – egy ikonnal jelenik meg: - . akkor másodpercenként fog aktiválódni az esemény).Y).Width) { button1.Forrás: http://www. és egy tetsz leges vezérl t is (a példában gomb lesz). 37.Stop().X + 10. EventArgs e) { timer1.hu 193 37.Timers névtér Timer osztályát. amíg eléri a form szélét.Location. A programunkban a Tick kódja ez lesz: private void timer1_Tick(object sender.doksi. de speciálisan arra tervezték ket. Valahol azonban el is kell indítanunk. ha erre vágyunk.Start(). Ebben a fejezetben néhány fontosabb komponenssel ismerkedünk meg. A Timer tulajdonságai közt találjuk az Interval –t.Width + 10 < this. Készítsünk egy programot. button1. 37.

EventArgs e) { if (users. A felhasználóneveket és jelszavakat egy Dictionary gy jteményben fogjuk eltárolni: private Dictionary<string.3 BackGroundWorker Ezzel a komponenssel egy bizonyos feladatot egy háttérben futó szálban végezhetünk el. egy file letöltése) és nem engedhetjük meg. users.doksi. hogy addig a form ne tudja fogadni a felhasználó utasításait. } else { errorProvider1. string>(). "judit"). Az utasítás végrehatásához létre kell hoznunk a komponens DoWork eseményének a kezel jét.Show("Helyes felhasználónév és jelszó!").textBox2.nagyon – egyszer „beléptet rendszer”.Add("Istvan". users. ha a feladat id igényes (pl.Forrás: http://www.textBox1.Text) { MessageBox. A fenti program egy . EventArgs e) { users. private void Form1_Load(object sender. Ez olyankor hasznos. els paramétere a vezérl amihez kötünk. } Gombnyomásra fogjuk ellen rizni a megadott adatok helyességét: private void button1_Click(object sender. "ahmed"). "Nem létez felhasználó!"). string> users = new Dictionary<string. Húzzuk rá a formra a megfelel vezérl ket és egy ErrorProvider –t is (ez alul fog megjelenni). A folyamat állapotát lekérdezhetjük a ProgressChanged esemény .hu 194 A vezérl höz kötést az ErrorProvider SetError metódusával tudjuk megtenni. } } else { errorProvider1. } } 37.SetError(this. a második pedig a hibaüzenet.ContainsKey(textBox1.Text)) { if (users[textBox1. "Rossz jelszó!").Add("Judit".Add("Ahmed".Text] == textBox2. Ez az esemény a BackGroundWorker RunWorkerAsync metódusának meghívásakor váltódik ki (ennek túlterhelt változata paramétert is kaphat). "istvan").SetError(this.

A BackGroundWorker nem támogatja a szálak közötti kommunikációt. ++i) { System.Forrás: http://www. illetve a feladat teljesítését a RunWorkerCompleted esemény kezeli le. Ugyanígy nem lehetséges Application Domain-ek közötti kommunikációt sem végrehajtani. Ez a metódus paramétereként a folyamat állapotát kapja százalékos értékben (a ciklusváltozó értékéhez azért adtunk egyet. A példaprogramban egy file letöltését fogjuk szimulálni. Ezt a ProgressChangedEventArgs UserState tulajdonságával kérdezhetjük le. amely második paramétereként egy object típusú változót vár. Végül a ProgressChanged és a RunWorkerCompleted esemény: private void backgroundWorker1_ProgressChanged(object sender. label1. ProgressChangedEventArgs e) { ++progressBar1. EventArgs e) { backgroundWorker1. .hu 195 kezelésével. ellenkez esetben InvalidOperationException típusú kivételt kapunk. hogy a valós értéket mutassa.ReportProgress(i + 1). Húzzunk a formra egy ProgressBar –t. A kész program így néz ki: A „letöltést” gombnyomásra indítjuk: private void button1_Click(object sender.Sleep(200).Text = "A folyamat állapota: " + e.RunWorkerAsync(). Ennek a metódusnak van egy túlterhelt változata is.ProgressPercentage.doksi. hiszen nulláról indul). egy gombot és egy Label –t. így a felhasználói felület nem módosítható a DoWork eseményb l. } A DoWork eseménykezel ez lesz: private void backgroundWorker1_DoWork(object sender. backgroundWorker1. amely tetsz leges adatot tartalmazhat.ToString() + "%". i < 100. DoWorkEventArgs e) { for (int i = 0. Ahhoz. } } A ReportProgress metódus fogja kiváltani a ProgressChanged eseményt.Value. hogy használhassuk a ProgressChanged eseményt a komponens WorkerReportsProgress tulajdonságát igaz értékre kell állítanunk.Thread.Threading.

A komponensben beállíthatjuk. felhasználhatunk egy TreeView vezérl höz. megadva az egyes Node –okhoz rendelt ikont. ezután az eredmény: Természetesen nem csak egy képet rendelhetünk az összes Node –hoz. hanem az ImageIndex tulajdonságon keresztül egyesével is beállíthatjuk azt: treeView1. a képek méreteit és színmélységét is.ImageIndex = 0.Nodes[0]. A többszálúságról szóló fejezetben már talákoztunk vele. A komponensnek el re megadhatjuk az elemeit. erre kattintva beállíthatjuk a képek formátumát és itt adhatjuk hozzá ket: A TreeView ImageList tulajdonságánál állíthatjuk be a használandó komponenst. } 37. .doksi.Forrás: http://www.4 Process Helyi és távoli folyamatok vezérlésére ad lehet séget ez a komponens.hu 196 } private void backgroundWorker1_RunWorkerCompleted(object sender. 37. Egy ImageList –et pl. Húzzunk a formra egy ImageList komponenenst és egy TreeView vezérl t. hasonlóan mint egy gy jteménnyel. RunWorkerCompletedEventArgs e) { MessageBox. Adjunk néhány elemet a vezérl höz.Show("A letöltés kész!"). ekkor azok „belefordulnak” a futtatható állományba.5 ImageList Ennek a komponesnek a segítségével több képet tudunk kezelni. Az ImageList „tetején” megjelenik egy kis nyilacska.

de teljesen mindegy) adott id közönként változtatja a helyét. illetve beilleszthet ek egyéb események is a játékmenetbe (pl. ha elvégezték a dolgukat. hogy milyen gyorsan mozogjon a vezérl . Nehezítésképpen akár egy egyszer gazdasági játékot is csinálhatunk. Ez a szám fogja jelezni. Nehezítésképpen a játék elején lehessen fogadni a gy ztesre. hogy ez túl lassú legyen. és jelezzük. . a játékos feladata pedig.Forrás: http://www. hogy hány mez t léphet el re. ha nem.doksi. 2.hu 197 37. csapda). Készítsünk „tekn sversenyt”! Az állatokat szimbolizáló vezérl k egyenes vonalban mozogjanak és a Timer minden Tick –jénél döntsük el. a Timer komponens segítségével! A formon egy vezérl (mondjuk egy PictureBox. 3. Készítsünk „irodaszimulátort”! A játkos a fönök. akkor az adott körb l kimarad.6 Gyakorló feladatok 1. hogy mi történik: ha a tekn s épp fáradt (ezt is mérjük). A felhasználó állíthassa be. Készítsünk „reflexjátékot”. hogy rákattintson. Az alkalmazottak BackgroundWorker –ek legyenek. aki elküldi az alkalmazottait különböz feladatok elvégzésére. de ne engedjük. akkor sorsoljunk neki egy véletlenszámot.

. System. akkor a legjobb lesz.Új vezérl k létrehozása Ha saját vezérl létrehozásán törjük a fejünket két út áll el ttünk.1 Származtatás Ebben az esetben rendkívül egyszer dolgunk lesz. A (nagyon egyszer ) példaprogramban a ListBox vezérl t fogjuk kib víteni oly módon. Most már minden adott a fejlesztéshez.Generic. Egy harmadik lehet ség.Forrás: http://www. System. System. Az osztály most így néz ki (hozzáadtam a szükséges névtereket és elkezdtem írni az osztályt is): using using using using using using using System.Collections.Windows.Forms assemblyt és adjuk hozzá. A Solution Explorer –ben kattintsunk jobb egérgombbal a Solution –ra. mivel hozzá kell adnunk a megfelel könyvtárakat. Ezt a metódust ez a jegyzet nem részletezi.NET feliratú fülén keressük meg a System. tehát ne a projectekre.Drawing. Amikor létrejött az osztály kattintsunk jobb gombbal az újonnan létrejött projectre és válasszuk az Add Reference menüpontot. Vagy egy már létez t b vítünk ki származtatással. hogy közvetlenül a Control osztályból származtatunk. hanem a legfels „bejegyzésre”.Text. System.Windows.Data. kib vítjük azt a szükséges metódusokkal. System. A beépített vezérl k csakis paraméternélküli konstruktorral rendelkeznek. hogy a konstruktorban ne felejtsük el meghívni az sosztály konstruktorát. 38. Ha viszont olyan vezérl t szeretnénk. azon belül pedig az New Project –et. hogy melyik módszert mikor használjuk? Ha van olyan vezérl . amely minden más vezérl se. System. ezért az új vezérl t is ennek szellemében fogjuk megírni. ami több más vezérl tulajdonságait olvasztja magába. ugyanis egy szimpla származtatást kell végrehajtanunk. egy ún. vagy teljesen újat hozunk létre. UserControl –t. A Windows fülnél válasszuk ki a Class Library sablont. namespace ExtendedListBox { public class ExtendedListBox : ListBox { public ExtendedListBox() : base() { } } } Fontos.hu 198 38. Válasszuk az Add menüpontot.Forms. tulajdonságokkal.ComponentModel. A megjelen ablak . Windows Forms .doksi. hogy lekérdezhessük a leghosszabb elemét. akkor UserControl –t kell létrehoznunk. Felmerülhet a kérdés. amely rendelkezik a szükséges képességek nagy részével.

Next(). próbáljuk is ki: private void Form1_Load(object sender. A vezérl máris használható állapotban van. foreach (string s in this. Ezután jó eséllyel a ToolBox –ban megjelenik a vezérl . ennek a Click eseményében keressük majd meg a leghosszabb elemet: private void button1_Click(object sender. Ezután ugyanúgy használható a vezérl . } } Húzzunk egy gombot a formra. ++i) { extendedListBox1.hu 199 Készítsük el a metódust: public string MaxLengthItem() { string max = "". azon belül pedig a Bin mappába.ToString()). ehhez els ként fordítsuk le az új vezérl projectjét. } } return max. Menjünk el az új vezérl könyvtárába. EventArgs e) { Random r = new Random(). EventArgs e) { MessageBox. } Ez a metódus persze csakis az els leghosszabb elemet adja vissza. és kattintsunk a megjelen ablakban a Browser fülre. mint az összes többi.Items) { if (s.Length > max. Válasszuk az Add Reference menüpontot. ha mégsem.Add(r. de egyel re csakis futási id ben adhatjuk hozzá a formhoz.Forrás: http://www. akkor azt nem veszi figyelembe. } Az eredmény: . hogy ezt tervezési id ben tegyük meg.Length) { max = s. Természetesen lehet ség van arra. Ezután navigáljunk el a form tervez nézetéhez és kattintsunk jobb egérgombbal a projectre. nevezzük el valahogy. akkor jobb klikk a ToolBox –on Add Tab menüpont. A .Show(extendedListBox1.MaxLengthItem()). Itt a fordítás „típusától” függ en a Debug vagy Release mappában találjuk a megfelel dll kiterjesztés file –t.Items. majd szintén jobb klikk az új fülre és Choose Items. Adjuk hozzá a projecthez. ha több ugyanolyan hosszú is van a ListBox –ban.doksi. a Build menüponttal. i < 10.NET Framework Components fül alatt keressük meg a vezérl nket és pipáljuk ki (beírva a nevét gyorsabban megtaláljuk). for (int i = 0. ekkor létrejön a vezérl t tartalmazó assembly.

Generic. A vezérl kódját megnyithatjuk.Forrás: http://www. ha jobb egérgombbal kattintva a View Code menüpontot választjuk: using using using using System. System. . System. de ezúttal válasszuk a Windows Forms Control Library sablont.Drawing.2 UserControl -ok A UserControl példa egy „beléptet rendszer” lesz.hu 200 38. ami jelzi majd a hibákat. System.doksi. erre húzhatjuk rá a vezérl ket: A vezérl valahogy így nézzen ki: Ezenkív l legyen egy ErrorProvider is. Adjunk egy új projectet a Solution –höz. akkor egy üres alapot kell látnunk. Amikor létrejött a project.ComponentModel.Collections.

Linq. System. System. System. System. string> LoginData { set { loginData = value.Forms.Windows. A kész osztály így néz majd ki: using using using using using using using using System. } . akárcsak a f programunk. namespace WindowsFormsControlLibrary2 { public partial class UserControl1 : UserControl { public UserControl1() { InitializeComponent(). System.Linq. System. } } }public partial class LoginControl : UserControl { private Dictionary<string. public LoginControl() { InitializeComponent(). így rendelkezik az összes olyan tulajdonsággal. System.Text.ComponentModel. A vezérl t úgy fogjuk elkészíteni.Data.doksi. hanem egy tulajdonságon keresztül adjuk meg azokat. } public Dictionary<string. System. ezenkív l rendelkezik egy a tervez által generált metódussal. System.Collections. hogy újrafelhasználható legyen. System.Generic. A UserControl osztály a ContainerControl leszármazottja. amely ahhoz szükséges. string> loginData = null.Windows. } } } Látható.Drawing.hu 201 using using using using System.Text.Forms. ezért nem égetjük bele a felhasználók adatait.Data. hogy az új osztály a UserControl –ból származik.Forrás: http://www. namespace WindowsFormsControlLibrary2 { public partial class UserControl1 : UserControl { public UserControl1() { InitializeComponent(). hogy kezelni tudja a rajta („benne”) elhelyezett vezérl ket (többek közt a Form osztály is az utódja).

string>(). amelyen keresztül megadjuk a név – jelszó párokat. } else errorProvider1. mint a származtatott vezérl k esetében. string> logData = new Dictionary<string. "Nem létezik a felhasználó!").SetError(textBox1. logData.Forrás: http://www.Show("Sikeres belépés!").Text)) { if (loginData[textBox1.Text) { MessageBox. logData. Házi feladat: módosítsuk a programot. } És az eredmény: .Add("Judit".Add("Balazs". Próbáljuk ki: private void Form1_Load(object sender. logData. EventArgs e) { if (loginData.doksi. } } A LoginData lesz az a tulajdonság.LoginData = logData. loginControl1.ContainsKey(textBox1. "balazs"). EventArgs e) { Dictionary<string. Kulcsszó: delegate. hogy tetsz leges metódust hívhasssunk sikeres belépés esetén. "istvan").hu 202 } private void button1_Click(object sender. } else errorProvider1.SetError(textBox2. A tervezési id ben való támogatást pontosan ugyanúgy érhetjük el. "judit").Add("Istvan". "Rossz jelszó!").Text] == textBox2.

Els programunkban rajzoljunk ki egy kört egy formra.CreateGraphics(). hogy hogyan kell végrehajtania az utasításokat.Forrás: http://www. ezek nem négyzetes alakzat esetén a síkidom köré rajzolt legsz kebb négyzet bal fels sarkát jelölik (az ábrán a pirossal jelzett pont): Az utolsó két paraméterrel a szélességet és magasságot állítjuk be. Használatához nem szükséges ismerni a hardvert. amikor a PrintDialog –gal foglalkoztunk már találkoztunk ezekkel az osztályokkal). amely kezeli az adott vezérl grafikus megjelenését (ilyet bármelyik vezérl t l kérhetünk). ez tartalmazza a megfelel információkat az objektumról). A program eredménye: . Ehhez szükségünk lesz egy Graphics típusú objektumra (ún. 30. A . amelynek szolgáltatásait használja fel az operációs rendszer a felhasználói felület megjelenítéséhez. A GDI+ a GDI Windows XP –ben megjelent utódja. a form Load eseményében nem lehet ezt megtenni (ha mégis „függetlenül” akarunk rajzolni. a második és harmadik pedig az alakzat kezd koordinátáját. ez már fejlettebb grafikus képességekkel rendelkezik. 0.Drawing névtér osztályain keresztül (korábban. Ezt az objektumot a vezérl n meghívott CreateGraphics metódussal kaphatjuk meg: private void button1_Click(object sender. } Az els paraméterrel a színt állítjuk be. a System. akkor válasszuk a Paint eseményt vagy a konstruktort). Device Context.hu 203 39.CreateGraphics().doksi. a GDI API automatikusan „kitalálja”.Red). A kört a DrawEllipse metódussal fogjuk megjeleníteni: private void button1_Click(object sender. EventArgs e) { Graphics g = this. 30). 0. } A rajzolást gombnyomásra indítjuk. EventArgs e) { Graphics g = this. Rajzolás: GDI+ A GDI (Graphics Device Interface) a Microsoft Windows grafikus alrendszere.DrawEllipse(new Pen(Brushes. g.NET Framework menedzselt felületet nyújt a GDI+ használatához.

0. A kört kirajzoló sort így is írhattuk volna: g.Red). } } . 30)).Forrás: http://www.DrawEllipse(new Pen(Brushes. ahol a második egy Rectangle objektum. 30. 0. 30).hu 204 A Graphics objektumot biztos felszabadítása érdekében érdemes using –gal együtt használni: private void button1_Click(object sender. 30)). EventArgs e) { using (Graphics g = this.Red). new Rectangle(0. 30)). 30. EventArgs e) { using (Graphics g = this. 0.DrawEllipse(new Pen(Brushes.CreateGraphics()) { g. 0.Red.doksi. g. amely a fentebb már említett négyzetet jelképezi. new Rectangle(0.CreateGraphics()) { g. 0. 30.Red). } } A DrawEllipse (és más metódusok is) egy változata csak két paramétert vár.FillEllipse(Brushes. Egy alakzatot ki is színezhetünk a megfelel körünkkel: Fill* metódussal. 30. new Rectangle(0. Tegyük ezt meg a private void button1_Click(object sender.DrawEllipse(new Pen(Brushes.

0)). Húzzunk a fomra egy PictureBox –ot a form Load eseményébe pedig írjuk: private void Form1_Load(object sender. new Point(0. Egy másik – függetlenebb – módja a közvetlen rajzolásnak az Image osztály használata: private void button1_Click(object sender. A GDI+ segítségével közvetlenül a fomra (vagy bármely vezérl re) is kihelyezhetünk képeket.DrawImageUnscaled(img. new Point(0. használhatjuk ehhez pl.hu 205 A Fill* metódusok a kitöltend területet várják második paraméterül. } A Bitmap elfogad stream –et is. 0). EventArgs e) { using (Graphics g = this. } } A Draw metódus els paramétere a megfelel Graphics objektum. az ImageList komponenst: private void button1_Click(object sender.Forrás: http://www. EventArgs e) { using (Graphics g = this. 0).CreateGraphics()) { imageList1. } } Ez a metódus a képet eredeti nagyságában jeleníti meg.FromFile("Naplemente. második helyen áll a kezd koordináta végül pedig a kirajzolandó kép indexe.doksi.CreateGraphics()) { Image img = Image.jpg").jpg"). g.Image = bm. pictureBox1.Drawing névtérben található). .Draw(g.1 Képek kezelése Képek beolvasására használhatjuk a Bitmap osztályt (amely a System. amennyiben ezt módosítani akarjuk használjuk a DrawImage metódust. 39. EventArgs e) { Bitmap bm = new Bitmap("Naplemente.

Add("Istvan"). listBox1.BackColor = Color. EventArgs e) { listBox1. Mondjuk szinezzük át a hátteret. hogy jelezni akarjuk ha az éppen „húzásban” lév elemmel egy vezérl fölé érünk.hu 206 40. hogy a vezérl k AllowDrop tulajdonságát igaz értékre állítjuk. ezzel engedélyezve a fejezet címében is szerepl metódust. } private void listBox2_DragLeave(object sender.SelectedItem.Items.ToString(). hogy engedélyezzük –e.Move). Drag and Drop A feladat a következ : adott két ListBox. DragEventArgs e) { ((ListBox)sender). Ha most elindítjuk a programot. hogy vissza kell állítani az eredeti színt.White.Add("Viktoria"). amin a két vezérl osztozhat).Items. Ezt az adott vezérl DragEnter eseményében tehetjük meg (ez szintén egy olyan eseménykezel lesz. hogy tetszés szerint húzzunk elemeket egyikb l a másikba. A DoDragDrop metódus els paramétere az az érték lesz.Items.Add("Judit"). A két metódus: private void listBox2_DragEnter(object sender. akkorr „megragadva” egy elemet az egérmutató megváltozik.BackColor = Color. } A DragOver esemény fogja eldönteni. } A Drag and Drop m veletet akkor indítjuk el.DoDragDrop(((ListBox)sender). DragEventArgs e) { . Azt se felejtsük el.Red. DragDropEffects. Tegyük fel. ezt a DragLeave eseményben fogjuk megcsinálni.Add("Balazs"). amely lehet vé teszi. hogy a form fogja levezérelni a vonszolást. Az els dolog amit meg kell tennünk (persze a ListBox –ok formra helyezése után). a második paraméter pedig a milyenséget fogja meghatározni. vagyis a MouseDown eseményben (mindkét vezérl használhatja ugyanazt az eseményt). listBox1. private void listBox2_MouseDown(object sender. hogy ledobhassuk az elemet: private void listBox2_DragOver(object sender. Adjunk hozzá néhány elemet az els ListBox – hoz: private void Form1_Load(object sender. amikor az egérgombot nyomva tartjuk egy elem felett.Forrás: http://www.Items.doksi. készítsünk programot. MouseEventArgs e) { this. EventArgs e) { ((ListBox)sender). listBox1. amit mozgatni akarunk. vagyis a kiválasztott elem. } Az már látható.

mert ebben az esetben a DragLeave esemény már nem következik be és piros maradna.Forrás: http://www. } A háttérszínt vissza kell állítani.BackColor = Color.White.hu 207 e.ToString()).GetData(DataFormats. } Most már az egérmutató is megváltozik a ListBox –ok fölött.doksi.Items. Egyetlen dolog van még hátra. akkor a DragDrop esemény lép m ködésbe: private void listBox2_DragDrop(object sender.Text). DragEventArgs e) { ((ListBox)sender).Data.Move. az információ elengedése.Effect = DragDropEffects.Add(e. méghozzá a dolog lényege. . Amikor ezt megtesszük. ((ListBox)sender).

Rendering Tier 0. mégpedig az. de ezt nem er lteti. a videókártya legalább 120 MB memóriával rendelkezik. .0 vagy fölötte. míg eddig a Windows API függvényeit hívtuk meg és a GDI segítségével szoftveresen rajzoltuk ki az alkalmazások kezel felületét. A WPF megjelenésével minden megváltozott. További információkkal az MSDN megfelel oldala szolgál: . .: alap (részleges) hardveres gyorsítás.: teljes kör hardveres gyorsítás. hiszen a DirectX egyetlen céllal jött világra. amit a WPF elvár (Pixel Shader. természetesen ez kicsit lassabb lesz. mégpedig villámgyors vizuális megjelenítésre. Természetesen a WPF sem váltja meg a világot. Ennek aztán meg is volt ára.Forrás: http://www.Rendering Tier 1. Ennek megfelel en a videókártya képességei alapján háromféle besorolása lehet egy rendszernek: .0 alatt. DirectX 7.aspx Nézzük meg .0 –t illetve legalább négy textúrázó egységgel bír. Személtetésként lássuk a Rendering Tier 2. Windows Presentation Foundation A WPF teljesen új alapokra helyezi a grafikus megjelenítést. Ez a váltás az elmúlt tizegynéhány év egyik legnagyobb jelent ség fejlesztése.microsoft. DirectX 7.: nincs hardveres gyorsítás.com/en-us/library/ms742196.hu 208 41.0 vagy felette. Vertex Shader. stb. hogy a lehet legtöbb munkát bízza a célhardverre.mit kínál a megjelenítés mellett a WPF: Animációk Adatkötések Audio. a WPF képes szoftveresen is megjeleníteni az alkalmazást. hardverigényét: DirectX 9. Egy fontos kérdés felmerülhet.doksi. platformokon fejlesztettünk.0 vagy fölötte.Rendering Tier 2. mivel teljesen új platformot kapunk. de a megjelenítést teljes egészében a WPF veszi át a GDI –t l. amelyek nem rendelkeznek „er s” videókártyával? Nos. Eddig amikor Visual Basic. ugyanazt tettük.http://msdn.0 vagy alatta. gyengeségeit egyik sem tudta kiküszöbölni. A WPF arra törekszik. emellett támogatja a Pixelés Vertex Shader 2. most közvetlenül a DirectX fogja „hardverb l” megtenni ezt. csak éppen mindig fejlettebb környzetben.). DirectX 9. . MFC stb.és videó vezérlés Stílusok és sablonok Stb. erre is van megoldás. a kártya mindent tud. hiszen a rendszer alapvet hiányosságait. hogy mi van azokkal a számítógépekkel. Ez a technológia egyúttal függetlenít minket (elvileg) az operációs rendszert l is. például a felhasználó interaktivitását még mindig a Windows API kezeli. de 9.

hu 209 Még két a WPF –hez köt d technológiáról is meg kell emlékeznünk. mivel egy ún.Forrás: http://www. ezért álljon itt egy ismertet : Természetesen mint minden mese ez is az Object osztállyal kezd dik. k fogják továbbítani a kéréseket a Media Integration Layer –nek (MIL).dll és WindowsCodecs. alatta eggyel pedig egy absztrakt osztály a DispatcherObject tartózkodik. 41. A MIL már egy natív felületet biztosít és közvetlenül hívja a DirectX szolgáltatásait. Végül a WPF architechtúra részét képezi a Windows API is. cserében platformfüggetlen (Macintosh és Windows rendszerek alá Silverlight néven érkezik. Ez a milcore.NET Framework –öt használja (ugyanakkor a nagytestvérhez képest vannak korlátai.NET FW telepítését igényli (~5MB). nyelven megírt menedzselt kód tanyázik. akkor a kérések a WPF API –hoz futnak be. amelyet a számítógépen a PresentationFramework.dll és a WindowsBase. A megvalósítás azonban alapjaiban különbözik: az XBAP a WPF teljes funkciónalitását nyújtja. míg Linux alatt a Novell szolgáltatja az alternatívát. Els ránézésre mindkett ugyanazt teszi: böngész b l érhetünk el egy felhasználói felületet.2 A WPF osztályhierarchia A WPF meglehet sen túlburjánzott osztályhierarchiával jött a világra. hiszen ugyanazt a . amely az összeköt kapcsot jelképezi a . sandbox –ban fut – kés bb). viszont ennek ismerete elengedhetetlen a késöbbi fejlesztéshez. Ezzel szemben a Silverlight a Flash ellenlábasaként egy miniat r . 41. Amikor egy ilyen porgramot futtatunk.1 A WPF architectúra Legfels bb szinten természetesen a C#/Visual Basic. Ezek teljes egészében menedzselt kódon alapulnak. amelyet korábban már említettünk. így a lehet ségek némileg korlátozottabbak. ezek az XBAP és a Silverlight.NET és a DirectX között. Ez az osztály a . PresentationCore.dll szerelvények jelképeznek.doksi.dll.dll fileokban él (a milcore –t használja a Windows Vista is a grafikus felület megjelenítésére).NET/stb. amelyet Moonlight –nak hívnak).

amelyeket a felhasználó is használhat – ennek alapján amíg a Windows Forms –ban a Label vezérl volt. Következ a sorban a DependencyObject.UIElement többek között az eseményekért felel s. néhány ezek közül pedig vezérl (pl. kép. amely ténylegesen megvalósítja azokat a szolgáltatásokat. pl.doksi. amely segítségével definiálni tudjuk a WPF elemeinek elrendezését (illetve még más dolgokat is. stb. Dependency Property –k lehet ségét adja hozzá a csomaghoz (err l hamarosan b vebben). minden vezérl . de intézi a vezérl k elrendezését és a felhasználó interaktivitásának kezelését. A System. ellenben a WF megoldásától. stb. ami azt jelenti.Windows névtérben van. forráskódból állt. amit a Windows Forms esetében már láttunk.. Igen ám. minden elemet element –nek nevezünk. A Visual –ból származó System. 41.3 XAML Az XAML egy XML –b l elszármazott leírónyelv. Van azonban egy nagy különbség.). tehát a felhasználói felületet elválasztjuk a tényleges forráskódtól. ezek közül a ContentControl az ami újdonság lesz: ez az osztály azokat a vezérl ket jelképezi. amelynek szolgáltatásait meghívhatjuk más szálból is. A DependencyObject a System. itt már nem lenne az).hu 210 System. Az UIElement leszármazottja a FrameworkElement. A WPF világában ez nem így van. amely fókuszba kerülhet illetve azok.: az UIElement felel s az elrendezésért. hogy a felhasználói felületet egyetlen „f szál” irányítja (és ezért a vezérl ket is csak a saját szálukból lehet módosítani). maximum azt nem kézzel írtuk. .Windows.Forrás: http://www. de a FrameworkElement implementálja az ehhez szükséges tulajdonságokat. az vezérl . ahol minden C#/VB:NET/stb. amiket a WPF használ (pl. Az ItemsControl azoknak a vezérl knek az se. a Control osztály. StackPanel).: Margin). amelyek képesek gyermek vezérl ket kezelni (pl. a billenty zet vagy az egér megmozdulásait.Windows.) se. utóbbi pedig azon vezérl k atyja. A Shape és a Panel abszrakt osztályok. el bbi a primítív alakzatok (kör. Single-Thread Affinity (STA) modellel dolgozik. pl. amelyek maguk is több elemet tartalmaznak. de valahogyan a többi szálból érkez üzeneteket is fogadni kell pl.Media névtérbeli Visual absztrakt osztály felel s a tényleges megjelenítésért és a képezi a kapcsolatot a MIL –lel is. amely az ún. hanem a fejleszt eszköz generálta. És itt jön a képbe a DispatcherObject. Ennek az osztálynak is van két leszármazottja.Threading névtérben található és fogja kontrollállni a szálak közti kommunikációt. mégpedig az. Most jön az érdekes rész. négyszög. amelyeknek van „tartalma” (szöveg. a ListBox. Az XAML hasonló ahhoz. hogy minden ami egy formon lehet. Miért van erre szükség? A WPF az ún. A Windows Forms esetében megszokhattuk. de róluk a megfelel id ben). is. hogy ezúttal valóban különválasztva él a kett .

hu 211 XAML –t többféleképpen is létrehozhatunk. ezek grafikus felületet nyújtanak a tervezéshez.Window1" xmlns="http://schemas.microsoft. A logikai fa a következ lesz: A magyarázat el tt lássuk a vizuális fa egy részletét is: .és Vizuális fa Vegyük az alábbi egyszer XAML kódot: <Window x:Class="JegyzetWPF. hogy egyszer en kézzel begépelünk mindent (a Visual Studio nyújt IntelliSense támogatást ehhez).3. hogy jól olvasható és könnyen megérthet legyen.Forrás: http://www.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Grid> <ListBox> <ListBoxItem Content="item1" /> <ListBoxItem Content="item2" /> </ListBox> </Grid> </Window> A formon létrehozunk egy ListBox –ot.3. az els módszer.doksi. A BAML tokenizált.1 Fordítás Mi történik vajon az XAML –lel. 41. ezért az XAML a fordítás után BAML –lé (Binary Application Markup Language) alakul. hogy a hosszú utasításokat lerövidíti. ami nagy vonalakban azt jelenti. ami a forrás bináris reprezentációja. Hogy a programok futását felgyorsítsák valahogyan meg kell kurtítani ezt.2 Logikai. A másik kett a Visual Studio tervez nézete (de csak az SP1) illetve a Microsoft Expression Blend nev program. ennek megfelel en azonban egy kicsit „b beszéd ”. amelyhez hozzáadunk két elemet.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. 41. amikor lefordítunk egy WPF alkalmazást? Az XAML – t úgy tervezték. amit a jegyzet is használni fog az az. így jóval gyorsabban tud betölt dni a file mint a nyers XAML.

ami alkalmas erre. egy ún. hogy a két fa megléte nem igényli az XAML –t. top-level-element. a fenti esetben a code-behind osztályt adtuk meg. stb… (természetesen a fenti ábra messze nem teljes). Ha szeretnénk ket megjelölni.microsoft.3 Felépítés Nézzük meg még egyszer az el z kódot: <Window x:Class="JegyzetWPF. tehát a top-level-element lezárása (</Window>) után már nem állhat semmi. ha mindent forráskódból hozunk létre. akkor az x:Name attribútumot kell használnunk: . amelyek ténylegesen a szemünk el tt vannak. Ez egy nagyon érdekes dolog: a code-behind osztályt a fordító fogja generálni. err l hamarosan. Fontos megjegyezni. Az x névtér prefixxel a project számára biztosítunk egy sablont.Forrás: http://www.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Grid> <ListBox> <ListBoxItem Content="item1" /> <ListBoxItem Content="item2" /> </ListBox> </Grid> </Window> Minden XAML dokumentum tetején egy olyan elem foglal helyet.egyetlen gyökérelemet engedélyez.hu 212 Hasonlítsuk össze a kett t: az els esetben az alkalmazásnak azon elemeit láttuk. 41. A vizuális fa esetében már bejönnek a részletek is. Alapértelmezés szerint az elemek nem rendelkeznek külön azonosítóval (ahogy azt a Windows Forms esetében megszokhattuk). vagyis a logikai felépítést. azaz az egyes elemek kerete.Window1" xmlns="http://schemas. színe. Lépjünk eggyel tovább. Ezzel a prefixxel több más helyen is fogunk találkozni. akkor is léteznek. A WPF több ilyet is definiál: Window Page Application Minden XAML dokumentum – ahogyan az XML dokumentumok is .doksi.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. de hála a parciális osztályok intézményének mi magunk is hozzáadhatjunk forráskódot.3.microsoft.

hogy más is használná). ezeknek a célját nem nehéz kitalálni. A gyökérelem rendelkezik még néhány attribútummal is.com a Microsoft tulajdonában áll. amely kiválóan alkalmas a vezérl k elrendezésére (ezt majd látni fogjuk). Következik egy Grid objektum. Az xmlns és xmlns:x attribútumok els ránézésre egy weboldalra mutatnak. hogy hol keresse a számára szükséges osztályokat. Ami fontos. az els a WPF alapértelmezett névterére mutat. Lássunk mindkét esetre egy példát: <TextBlock Text="Inline definíció" /> <TextBlock> <TextBlock. mert egy hagyományos XML dokumentum is így épül fel. A fordító ezekb l a „címekb l” tudni fogja. ennek oka pedig az. így lehetnek gyermekei. hogy egy XAML dokumentumban minden „element” egy létez WPF osztályra mutat. Ezenkív l néhány osztály amely rendelkezik a RunTimeNameProperty attribútummal (például a FrameworkElement és így az összes leszármazotta) birtokol egy Name nev tulajdonságot. amely segítségével futásid ben is beállíthatjuk az elem nevét: ListBoxWithName. tehát ez is egy helyes megoldás: <ListBox Name=" ListBoxWithName " > <ListBoxItem Content="item1" /> <ListBoxItem Content="item2" /> </ListBox> Egy vezérl tulajdonságait beállíthatjuk inline és explicit módon is. hogy az egyes „gyártók” gond nélkül használhassák a saját elnevezésüket (a schemas. de valójában nem. .Text> Ez pedig az explicit definíció </TextBlock.Text> </TextBlock> Térjünk vissza a fejléchez. Miért van ez így? Els dlegesen azért. és minden attribútum az adott osztály egy tulajdonságára.hu 213 <ListBox x:Name="ListBoxWithName"> <ListBoxItem Content="item1" /> <ListBoxItem Content="item2" /> </ListBox> Mostantól kezdve a „hagyományos” forráskódból is ezen a néven hivatkozhatunk a ListBox –ra. az az. Ezenkív l ez egy Panel leszármazott.doksi. Ezek névterek.Name = "NewName". Ekkor XAML –b l is használhatjuk ezt a tulajdonságot. és elég valószín tlen. a második pedig az XAML kezeléshez szükséges dolgok névterére.Forrás: http://www.microsoft.

Uri("/JegyzetWPF.. az az. Jobb egérgombbal klikkeljünk rajta és válasszuk a Go To Definition lehet séget. hogy a vezérl ket nem itt adja hozzá.doksi. Ezek az utasítások betöltik majd kibontják az er forrásként beágyazott BAML –t. Ahhoz. resourceLocater). amit már láttunk akkor. } } } Ez eléggé hasonlít arra.. Alapértelmezés szerint a Visual Studio ezt generálja: namespace JegyzetWPF { /// <summary> /// Interaction logic for Window1.Uri resourceLocater = new System.Windows. ebb l pedig már fel tudja a WPF építeni a felhasználói felületet.Application.xaml /// </summary> public partial class Window1 : Window { public Window1() { InitializeComponent(). hogy megtudjuk hogyan jön létre az alkalmazás vizuális felülete nézzük a következ sorokat: System. amikor Windows Forms –szal dolgoztunk. .xaml" System.UriKind. most nézzük meg a code-behind kódot. Ami el ször felt nhet. mint ahogy azt a WF tette.hu 214 Rendben.component/window1. Létrejön minden egyes vezérl objektuma és hozzácsatolja az eseménykezel ket is. System. #line 1 ".xaml". Az InitializeComponent metódus azonban más funkcionalitással bír.\Window1.Relative).\.Forrás: http://www.LoadComponent(this.

Window1" xmlns="http://schemas. Az XAML: <Window x:Class="JegyzetTestWPF. most ez is megteszi. amit a Windows Forms esetében már láttunk.ha az közvetlenül nincs kezelve – „felutaztassuk” (vagy le) a vizuális fán. Nézzük meg a következ példát. ami lehet vé teszi.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <Grid> . Természetesen ez is egy EventArgs leszármazott.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 tulajdonság a vertikális (függ leges) helyzetet állítja be.microsoft.Forrás: http://www.hu 215 42.microsoft.Window1" xmlns="http://schemas. Nézzük is meg: private void MessageButton_Click(object sender. routed events bevezetésével. hogy az üzenetet az az objektum küldi. RoutedEventArgs e) { MessageTextBox. vagyis a TextBox felül lesz. A gomb megnyomásakor helyezzünk a TextBox –ba valamilyen szöveget.Text = "Hello WPF!". de utal valamire aminek sokkal nagyobb a jelent sége: eddig megszoktuk azt. a gomb pedig alul (létezik HorizontalAlignment is a vízszintes beállításokhoz). A gombhoz már hozzá is rendeltünk egy eseménykezel t (ezt a Visual Studio kérésre létrehozza). amelyikkel történik valami. } Gyakorlatilag nem különbözik attól. A késöbbiekben megismerkedünk hatékonyabb rendezésekkel is. Az XAML a következ lesz: <Window x:Class="JegyzetWPF.microsoft. hogy egy objektum egy eseményét . WPF – események és tulajdonságok Készítsünk egy egyszer programot: egy TextBox és egy Button vezérl vel.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. A második paraméter típusa RoutedEventArgs lesz. egy különbség azonban mégis van. mégpedig a Click eseményhez.microsoft. A WPF megváltoztatja ezt a helyzetet az ún.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.doksi. amíg valaki le nem kezeli.

amíg el nem éri a forrásobjektumot (vagy talál egy kezel t). hogy annak pedig lekérdezhetjük a nevét. MouseButtonEventArgs e) { MessageBox. hogy minden vezérl egyben FrameworkElement is és azt is. ahogyan azt a Windows Forms esetében csak usercontrol –ok segítségével érhettük el. amely az egérrel való klikkelést kívánja megszemélyesíteni. akkor közvetlenül hívhatjuk a RoutedStrategy –t). . egyetlen feltétel van. Amikor regisztrálunk egy ilyen eseményre. Tunneling: az esemény a gyökérelemben kezd és lefelé vándorol. az választ egyet a három lehetséges stratégia közül: Direct: ez a legáltalánosabb.Name). hogy az a vezérl amibe beágyazunk ContentControl leszármazott legyen. Nézzük az eseménykezel t: private void label1_MouseDown(object sender. ilyen a MouseDown is.Source). Gyakorlatilag bármilyen vezérl t beágyazhatunk ezen a módon. amely egy felsorolt típusú érték (értelemszer en. Amikor rákattintunk a Label –re. azt tudjuk. olyan módon. } Ez is nagyon egyszer . míg az OriginalSource a vizuális fát veszi alapul.Show("Event source: " + ((FrameworkElement)e. - Az esemény milyenségét a megfelel RoutedEventArgs leszármazottól kérdezhetjük meg. Bubbling: az esemény el ször a forrásobjektumban váltódik ki.hu 216 <Label Name="label1" MouseDown="label1_MouseDown"> <TextBlock x:Name="innerText" Text="Hello" /> </Label> </Grid> </Window> Miel tt rátérnénk az eseménykezelésre vegyük észre. Most nézzük az eseményeket: az UIElement osztálytól minden leszármazottja örököl egy nagy csomó eseményt az egér és a billenty zet kezeléséhez. a RoutedEvent tulajdonság RoutedStrategy tulajdonságán kereszt l. ha az eseménykezel második paramétere RoutedEventArgs típusú. amíg talál egy kezel t (vagy a gyökérhez nem ér). majd elkezd felfelé vándorolni a vizuális fában. akkor felugrik egy MessageBox: Igaz. A Source tulajdonság a logikai fa szerinti forrást adja vissza. mégpedig az. hogy a Label eseményét kezeltük (volna) le. az esemény csakis a forrásobjektumban váltódik ki és csakis kezelheti. mégis a TextBlock volt a forrás.doksi.Forrás: http://www. hogy a Label felépítését tetszés szerint alakíthatjuk. Nézzük meg egy kicsit közelebbr l a routed event –eket. A MouseButtonEventArgs egy RoutedEventArgs leszármazott.

hogy olyan eseményt kapjon el.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300" ButtonBase. amíg talál egy barátságos eseménykezel t (ez volt a Label –é). Most nézzünk egy érdekesebb példát: <Window x:Class="JegyzetWPF.hu 217 A példában a TextBlock MouseDown eseménye a Bubbling stratégiát használja. azaz elkezd felfelé vándorolni a vizuális fában. Attached event –et kódból is hozzáadhatunk egy vezérl höz: .Window1" xmlns="http://schemas. RoutedEventArgs e) { messageTextBox. mégis el tudja kapni a Click eseményt.microsoft. attached events (~ csatolt események) intézménye miatt van. RadioButton) küld.doksi. Nézzük az eseménykezel t: private void Window_Click(object sender.Forrás: http://www. amelyet egy ButtonBase leszármazott (Button.Name.Source).Text = ((FrameworkElement)e.microsoft.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 eseményt el fog kapni. } Mit l olyan érdekes ez a példa? Gondolkodjunk: a Window nem ismerheti a Button – t. Ez az ún. ami lehet vé teszi egy vezérl nek.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. amivel maga egyébként nem rendelkezik. CheckBox.

így olyan kód is kezelheti ezeket.Window1" xmlns="http://schemas. Ami érdekes. vagyis amit betettünk a gépbe azt kapjuk vissza. mint az el z .hu 218 window1. A FontSize tulajdonság így néz ki a TextBlock esetében: . amit elvárunk.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.AddHandler(Button. az pedig az.microsoft. az operációs rendszer). mivel az aktuális érték több tényez t l függ. hogy a tulajdonság értéke függhet más tulajdonságoktól illetve a környezet sajátosságaitól (pl. Egy másik fontos dolog a routed event –ek mellett a dependency property –k jelenléte. A DP ezt a helyzetet megváltoztatja. new RoutedEventHandler(Window_Click)).com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <Grid> <Label FontSize="42"> Hello WPF! </Label> </Grid> </Window> Semmi különös. A dependency property –k (~függ ségi tulajdonság) valódi jelensége az.doksi.microsoft.Forrás: http://www. A hagyományos tulajdonságokat általában privát elérés adattagok lekérdezésére és értékadására használjuk. hogy mi a titok. Ezek speciális tulajdonságok. hogy a program pontosan ugyanazt csinálja. amelyek nem ismerik a WPF –et.microsoft. amelyek hagyományos tulajdonságokba vannak becsomagolva. vagyis megjeleni a szöveg 42 –es bet mérettel: Csavarjunk egyet a dolgon: <Window x:Class="JegyzetTestWPF.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300" FontSize="42"> <Grid> <Label> Hello WPF! </Label> </Grid> </Window> Az egyetlen ami változott. pontosan azt teszi.Window1" xmlns="http://schemas.ClickEvent. az az. Nézzük a következ példát: <Window x:Class="JegyzetWPF.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. hogy a bet méretet a Window –nak adtuk meg. Nézzük meg.

Forrás: http://www.FontSize = 10. akkor elvileg nem kapna semmit.doksi. ez az ún. tehát amikor lekérdezné a FontSize tulajdonságot. RoutedEventArgs e) { window1.microsoft. minthogy megváltoztatjuk a Window bet méretét: private void changeFontSize_Click(object sender. mi az a FontSizeProperty? A definíciója a következ : [CommonDependencyProperty] public static readonly DependencyProperty FontSizeProperty. .microsoft. vagy pedig a WPF alapértelmezése (vagy pedig. nézzük sorban. value).hu 219 [TypeConverter(typeof(FontSizeConverter)).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 mást nem teszünk. } } Koncentráljunk csak a getter/setter párosra. Els ként. Localizability(LocalizationCategory. hogy milyen osztályra hivatkozunk a base –zel? Ez pedig a DependencyObject.None)] public double FontSize { get { return (double) base.GetValue(FontSizeProperty). Ekkor a tulajdonság az s DependencyObject –t l lekérdezi a tulajdonság aktuális értékét. Amire még szükségünk van az az. hogy mi is történik valójában a második példában: létrejön a Label és be kell állítania a bet méretet. lokális érték (local value)). akkor azt. ezt a következ példán láthatjuk is: <Window x:Class="JegyzetTestWPF. ami vagy egy a fában nála magasabban elhelyezked (és t magába foglaló) vezérl megfelel értéke lesz. Csakhogy ilyet mi nem adtunk meg. ha van általunk beállított értéke. Rendben.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. } set { base. Ez fordítva is hasonlóan m ködik.SetValue(FontSizeProperty.Window1" x:Name="window1" xmlns="http://schemas.

Dock="Left" Width="100" Height="30" Content="Left" /> <Button DockPanel. Ilyen például a szöveget megjeleníteni képes vezérl k FontFamily tulajdonsága is. amely lehet vé teszi. . hogy egy másik vezérl dependency property –jét használjuk. Tehát a SetValue meghívásakor az alárendelt objektumok bet mérete is változik (amennyiben nem definiált sajátot).hu 220 } Amikor a gombra kattintunk.Dock="Right" Width="100" Height="30" Content="Right" /> </DockPanel> </Window> Most a Grid helyett egy DockPanel –t használtunk. egyúttal a Label bet mérete is megváltozik. Hasonlóan a routed event –ekhez a dependency property –k is definiálnak egy attached property nev képességet.Window1" x:Name="window1" xmlns="http://schemas. Léteznek ún. A kés bbiekben még fogunk találkozni a fejezet mindkét szerepl jével. a gombok pozicíóit pedig attached property –vel adtuk meg. többek között megtanulunk majd saját WPF vezérl t készíteni és hozzá a megfelel DP –ket is. amelyek tulajdonképpen mind a TextElement osztály DP –jét használják.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. amelyek bár különböz osztályokban definiáltak mégis ugyanarra a DP –re hivatkoznak. Ezzel leggyakrabban az elemek pozicíonálásánál találkozunk majd: <Window x:Class="JegyzetWPF.doksi.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <DockPanel> <Button DockPanel.microsoft.Forrás: http://www. megosztott tulajdonságok (shared property).

1 StackPanel <Window x:Class="JegyzetWPF. UniformGrid: minden elem ugyanolyan méret cellákban foglal helyet. ezek a következ ek: Egy elem méretét nem szükséges explicit megadni.doksi.Window1" x:Name="window1" xmlns="http://schemas.Forrás: http://www. az minden esetben megfelel nagyságú lesz a tartalmához (de beálílthatunk minimum és maximum méreteket is). StackPanel: vízszintesen vagy függ legesen helyezkednek el az elemei.microsoft. - A következ kben megnézzük ezeknek a konténereknek a használatát. de nem az a standard). hogy a Panel absztrakt osztályból származnak (lásd: WPF osztályhierarchia). ami tartalmaz egy StackPanel –t. WrapPanel: egyenes vonalban helyezkednek el az elemei (az Orientation tulajdonságán kereszt l vízszintes vagy függ leges lehet a vonal). A konténer elemek elosztják a gyermekeik közt a felhasználható teret. amelyeket a WPF automatikusan alkalmaz (természetesen ezeket felülbírálhatjuk). Hat darab ilyen osztályunk van. ami tartalmaz egy Canvas –t. nézzük meg a jellemz iket: Grid: a leggyakrabban használt. Canvas: koordináta alapú elhelyezkedést tesz lehet vé - Az egyes konténerek egymásba ágyazhatóak. El bbi a vezérl k koordinátáján alapult. az alkalmazás kisebb szekcióinak kialakítására használjuk. az elemeit sorok és oszlopok szerint osztja be. A top-level-element (pl. Az egyes elemek elhelyezkedésére és méretére is van néhány szabály. vagyis készíthetünk egy Grid –et. általában egy másik konténerben foglal helyet. hogy az az egy elem tudjon gyermek vezérl ket kezelni. ami szükségessé teszi azt.hu 221 43. Ezeket az elemeket konténernek (container) nevezzük és közös jellemz jük. a Window) egyetlen elemet képes magában hordozni.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <StackPanel> <Button>Button1</Button> . míg a WPF egy leginkább a HTML –hez hasonló (de annál jóval összetettebb) megoldást kapott (ugyanakkor koordinátákon alapuló elrendezésre is van lehet ség. WPF – Vezérl k elrendezése A WPF a Windows Forms –tól eltér en egy mer ben más módszert vezet be a vezérl k poziciónálásához.microsoft. viszonylag ritkán használt.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. 43. DockPanel: az elemeit az egyes oldalaihoz rendelhetjük. és így tovább.

hogy az elemeit ömlesztve.Forrás: http://www.Window1" x:Name="window1" xmlns="http://schemas. A StackPanel és WrapPanel sajátja.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.microsoft.: <Window x:Class="JegyzetWPF.com/winfx/2006/xaml" . Az Orientation tulajdonság megváltoztatásával vízszintes elrendezést kapunk: <StackPanel Orientation="Horizontal"> Az látható.doksi. Ezen a problémán segíthetünk „margók” beállításával.hu 222 <Button>Button2</Button> <Button>Button3</Button> </StackPanel> </Window> Az eredmény: Az elemek függ legesen követik egymást. az elemek a lehet legkisebb méretet veszik föl (vagyis igazodnak a tartalomhoz): <StackPanel> <Button HorizontalAlignment="Left">Button1</Button> <Button HorizontalAlignment="Center">Button2</Button> <Button HorizontalAlignment="Right">Button3</Button> </StackPanel> Az Alignment tulajdonságokból az Orient tulajdonságnak megfelel en mindig csak egy használható fel. hogy a vezérl k minden esetben teljes mértékben kitöltik a saját helyüket. Amennyiben megadjuk a Horizontal/VerticalAlignment tulajdonságot. térköz nélkül helyezi el.

lent (ez eltér a CSS –t l. Azért ennyi.54 cm). csak itt egy sorban/oszlopban több elem is lehet: <Window x:Class="JegyzetWPF. 10. mert az alapértelmezett Windows DPI (dot-per-inch. Egy DIU egy angol hüvelyknek az 1/96 –od része: ~0.2 mm (egy hüvelyk: 2. Valójában a DPI szám a megjelenít eszközt l függ. fönt.microsoft. 5. margó. ugyanis a fenti esetben a térköz nagysága mind a négy oldalra érvényes.Window1" x:Name="window1" xmlns="http://schemas. meghatározására ún.com/winfx/2006/xaml/presentation" .doksi. balra). stb. 43. egy nagyfelbontású és nagyméret monitornak sokkal nagyobb a DPI értéke. az egy hüvelyken elhelyezked pixelek száma) éppen 96.hu 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 szélesség. magasság. Beállíthatjuk ezt persze egyenként is: <Button Margin="5. jobbra. mivel a WPF a következ képletet használja egy egység meghatározására: Fizikai méret = DIU x DPI Ahol a DPI az éppen aktuális rendszer DPI –nek felel meg. vagyis ebben az esetben egy DIU pontosan egy pixelnek felel meg. A fenti példában a gombok körül 5 egység méret eredmény: térközt állítottunk be. 10" >Button1</Button> Az oldalak sorrendje balról jobbra: balra. Az A CSS –t ismer k ismers nek fogják találni a helyzetet. ahol a sorrend: fönt. lent. jobbra. device independent unit –okat használ (~rendszerfüggetlen egység).Forrás: http://www.2 WrapPanel A WrapPanel hasonlóan m ködik mint a StackPanel.

Forrás: http://www. de semmi mást nem állítottunk.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 elhelyeztünk három gombot. Ekkor ez történik: Az els két gomb függ legesen kitölti a helyet. hogy van még hely és automatikusan megnyújtja).microsoft.hu 224 xmlns:x="http://schemas.microsoft.doksi. 43.3 DockPanel Vegyük a következ XAML –t: <Window x:Class="JegyzetWPF.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. Nem így tesz az utolsó gomb. Nézzünk megy egy másik példát is: . Ez azért van. mert az elemei minden esetben ki kell töltsék a lehet legnagyobb helyet. de vízszintesen a tartalomhoz igazodik a méretük. ami mindkét irányban elfoglalja a maradék helyet.Window1" x:Name="window1" xmlns="http://schemas. hacsak mást nem adunk meg (tehát az els kett gomb még normális. de a harmadikhoz érve látja.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> Hasonlóan mint a társánál az Orientation tulajdonságon kereszt l tudjuk a vízszintes/függ leges elrendezést állítani.

vagyis a (0. akkor azt maradéktalanul kitölti (persze.attached property –t használtunk. Szintén attached property –kel tudjuk megadni a pontos helyet: <Canvas> <Button Canvas.Forrás: http://www. lent sorrendben rajzolja ki.0) pont.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <Canvas> <Button>Button1</Button> </Canvas> </Window> Látható. hogy a gombok a DockPanel melyik oldalához igazodjanak.Dock="Top">Top</Button> <Button DockPanel. hogy meghatározzuk. hogy az alapértelmezett koordináták szerint az elem helye a bal fels sarok.az el z fejezetben megismert .microsoft. jobbra.4 Canvas A Canvas lehet vé teszi.Left="10" Canvas.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. Margin tulajdonság. és ha az utolsó elemhez érve még van hely. akkor azt figyelembe veszi).Dock="Right">Right</Button> <Button>Middle</Button> </DockPanel> Most egy .Dock="Left">Left</Button> <Button DockPanel.microsoft. Az eredmény ez lesz: Most már könnyen meghatározhatjuk a viselkedését: az elemeket balra. fönt.Top="10">Button1</Button> </Canvas> .hu 225 <DockPanel> <Button DockPanel. ha van beállítva pl. 43.Window1" x:Name="window1" xmlns="http://schemas.doksi. hogy a rajta koordinátarendszer segítségével poziciónáljuk: elhelyezked vezérl ket egy <Window x:Class="JegyzetWPF.

Left="100" Width="170" Height="70">Button1</Button> <Button Panel. és csakis azon kereszt l érhetjük el. de ekkor két lehet ség van: vagy kisebb mint amekkora egy cella.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <Canvas> <Button Canvas.com/winfx/2006/xaml/presentation" . Nézzünk ezekre néhány pédát: A „normális” eset: <Window x:Class="JegyzetWPF.doksi. 43.ZIndex="1" Width="170" Height="70">Button2</Button> </Canvas> </Window> A ZIndex –et a Panel –tól örökli. vagy pedig nagyobb mint egy cella. Minden egyes vezérl ugyanannyi helyet foglal el.Window1" x:Name="window1" xmlns="http://schemas.microsoft.hu 226 A példában a gombot a bal oldaltól és a fels részt l is tíz egységgel toltuk el (a viszonyítást mindig az adott vezérl adott oldalához számoljuk): Ha több egymást esetleg eltakaró elem van a Canvas –on. ekkor a ZIndex –e módosul és a „kilógó” része a többi vezérl t alatt lesz.5 UniformGrid Az UniformGrid el re definiált oszlopokba és sorokba (cellákba) rendezi a vezérl it (valójában a méret megadására sincs szükség.Window1" x:Name="window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. és a WPF el ször mindig a legalacsonyabb z-index elemet rajzolja ki (tehát a magasabb index ek eltakarják az alacsonyabbakat): <Window x:Class="JegyzetWPF. akkor az érvényesülni is fog. akkor a ZIndex tulajdonságát is beállíthatjuk. automatikusan is elrendezi ket). Ez alapértelmezetten minden elemnek nulla. Ha valamelyik elemnek explicit megadjuk a szélességét vagy magasságát.Forrás: http://www. ekkor a cella közepén foglal helyet és körülette megfelel méret térköz lesz. pontosabban minden cella mérete egyenl .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 méretmegadás. azt vele is reprodukálni tudjuk. de nagyobb mint kellene: <UniformGrid> <Button Width="170" Height="20">Button1</Button> <Button>Button2</Button> <Button>Button3</Button> <Button>Button4</Button> </UniformGrid> 43.microsoft. .Forrás: http://www.hu 227 xmlns:x="http://schemas.doksi. Amit az el z ekkel meg tudunk csinálni. de kissebb: <UniformGrid> <Button Width="100" Height="20">Button1</Button> <Button>Button2</Button> <Button>Button3</Button> <Button>Button4</Button> </UniformGrid> Explicit megadás.6 Grid A Grid a WPF leger teljesebb (és leggyakrabban használt) container osztálya.

Az nem meglepetés. méretét illetve egy adott cellába több elemet is tehetünk. de most megadhatjuk azok számát.microsoft. Ezeket legyakrabban XAML –b l állítjuk be.microsoft. ekkor pontosan akkora lesz. hogy az egyes oszlopok/sorok definíciója Column/RowDefinition típusú. amelyekkel a sorok/oszlopok mérethatárait állíthatjuk be.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.Forrás: http://www. hiszen nem lenne túlságosan vonzó egy olyan alkalmazás. akkor semmit sem látnák. hogy az adott sor/oszlop ki fogja tölteni a megmaradó összes helyet.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <Grid> <Grid.ColumnDefinitions> </Grid> </Window> Két sort és három oszlopot készítettünk.Window1" x:Name="window1" xmlns="http://schemas. Viszont nem kötelez megani ezeket az értékeket. MinHeight/MaxHeight tulajdonások. Ha most elindítanánk a programot. amelyek Row/ColumnDefinitionCollection típusú gy jteményeket kezelnek. amekkora kell (vagyis a benne lév vezérl méretéhez igazodik). Szerencsére a fejlesztés ellen rzésének céljára lehet ségünk van bekapcsolni. a következ módon: <Window x:Class="JegyzetWPF. amelynek négyzetrácsos a felülete. Ha ezeknek az értékeiknek a helyére csillagot írunk. hogy látszódjon ez a rács. a soroknál csakis a magasságot állíthatjuk.RowDefinitions> <RowDefinition Height="40" /> <RowDefinition Height="*" /> </Grid.hu 228 Hasonlóan a UniformGrid –hez itt is sorok és oszlopok szerint rendezzük a területet.doksi. ha a szélesség/magasság értéknek „auto” –t adunk.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="100" /> <ColumnDefinition Width="150" /> <ColumnDefinition Width="*" /> </Grid. a sorok és oszlopok számának függvényében. a Grid ShowGridLines tulajdonságán keresztül: <Grid ShowGridLines="True"> Ekkor az eredmény: . Az oszlopoknál csakis a szélességet. ekkor a Grid egyenl nagyságú cellákra osztja a felületet. Egy Grid sorait illetve oszlopait a RowDefinitions és a ColumnDefinitions tulajdonságain kereszt l tudjuk beállítani. ami nem meglep . Ez utóbbi két esetben hasznosak lehetnek a MinWidth/MaxWidth. az azt jelenti. Ezenkív l rábízhatjuk a Grid –re is a méret beállítását.

mind a sorok számozása nullától kezd dik.Column="0" Grid.microsoft. hogy minden a Grid –en elhelyezni kívánt vezérl nek be kell állítani a helyét.microsoft. A Grid. ekkor a WPF a hiányzó értékhez automatikusan nullát rendel). Most így néz ki a programunk: Fontos.Column="2" Grid.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <Grid ShowGridLines="True"> <Grid.doksi.RowSpan és a Grid. hogy mind az oszlopok.ColumnSpan tulajdonásgokkal azt adhatjuk meg.ColumnSpan="2">Button1</Button> .Window1" x:Name="window1" xmlns="http://schemas.ColumnDefinitions> <Button Grid. hogy a vezérl hány oszlopot illetve sort foglaljon el: <Button Grid.hu 229 Helyezzünk el most néhány vezérl t: <Window x:Class="JegyzetWPF.Column="1" Grid.RowDefinitions> <Grid.megadni.Column="0" Grid.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.RowDefinitions> <RowDefinition Height="40" /> <RowDefinition Height="*" /> </Grid. különben nem jelenik meg (de elég csak az egyiket – oszlop vagy sor .Row="1">Hello Grid</Label> <ListBox Grid.ColumnDefinitions> <ColumnDefinition Width="100" /> <ColumnDefinition Width="150" /> <ColumnDefinition Width="*" /> </Grid.Forrás: http://www.Row="1"> <ListBoxItem Content="Grid" /> </ListBox> </Grid> </Window> Ismét attached property –ket használtunk. Látható.Row="0" Grid.Row="0">Button1</Button> <Label Grid.

Forrás: http://www.doksi. .hu 230 Most a gomb két oszlopot kapott.

hogy nem minden elem vezérl a WPF világában. hogy a vezérl k a System. míg el bbi általában a szöveg. de most rendelkezésünkre áll valami sokkal jobb.Control osztályból származnak. Azt is tudjuk.1 Megjelenés és szövegformázás Megjelenés szempontjánból a WPF jóval flexibilisebb mint a WinForms. Ebben a fejezetben a vezérl k vizuális megjelenését fogjuk „kicsinosítani”. mivel valós haszna csak egy kés bbi fejezet tudásanyagával lesz. hanem csak azok. ugyanezt alakzatoknál a Fill tulajdonság fogaj szolgáltatni. a létrehozásuk már nem biztos). Két általános vezérl t fogunk ehhez használni. legalábbis célját és m ködését tekintve (a használatuk.hu 231 44.microsoft. hogy több gyermeke is van.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. mindegyikükre jellemz képességgel (ilyen pl.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> .Forrás: http://www. az. A lehet legegyszer bb esetben használhatjuk úgy a színeket. a Button –t és a Label –t (illetve a szinezés demonstrálásához egy egyszer alakzatot a Rectangle –t). A kett ben a közös. csak most ugyanazt a funkciót a Brushes osztály tölti majd be. szöveges tartalmát). mint ahogy azt a Color osztály esetében tettük. Vezérl k esetében a hátteret a BackGround tulajdonságon kereszt l állíthatjuk be. 44. WPF – Vezérl k Ebben a fejezetben megismerkedünk a WPF vezérl ivel.Windows. Már korábban megtanultuk. A vezérl k bemutatásánál néhány elem kimarad.Window1" x:Name="window1" xmlns="http://schemas.doksi. Windows Forms –ban ezeket a Color osztály segítségével állítottuk. mégpedig a Brush. Készítsünk egy négyzetet: <Window x:Class="JegyzetWPF. amelyek mindegyike más-más lehet séget bíztosít számunkra. Sokuk ismer s lesz a korábbi WinForms –szal foglalkozó fejezetb l. így majd a megfelel helyen ismerkedünk meg velük. Utóbbi a vezérl tényleges felszíne. amelyek képesek felhasználói interaktivitást kezelni. hogy Brush típusú (vagy leszármazott) objektumot várnak. ami el revetíti. hogy formázhatjuk a vezérl „feliratát”.microsoft. ami felvértezi ket néhány alapértelmezett. Ez egy absztrakt osztály. Minden vezérl magáévá teszi az el tér és a háttér fogalmát.

doksi. Az a Brush leszármazott.Fill = scb. 10.Color> <Color A="255" R="100" G="10" B="100" /> </SolidColorBrush. brushRectangle.Fill> <SolidColorBrush Color="Cyan" /> </Rectangle. a maximálisan felvehet értéke 255).FromRgb(100. Ugyanezt megtehetjük XAML –b l is: <Rectangle x:Name="brushRectangle" Width="70" Height="70" VerticalAlignment="Center"> <Rectangle.Fill> </Rectangle> A LinearGradientBrush két (vagy több) színt „olvaszt” össze. .Fill = Brushes. 100). scb. Ha el re definiált színeket akarunk használni.Color = Color.Forrás: http://www.Fill> <SolidColorBrush> <SolidColorBrush.Cyan.Color> </SolidColorBrush> </Rectangle. akkor egyszer bb a dolgunk: <Rectangle x:Name="brushRectangle" Width="70" Height="70" VerticalAlignment="Center"> <Rectangle. Például írhatnánk ezt is a fenti helyett: SolidColorBrush scb = new SolidColorBrush(). amelyet itt használtunk SolidColorBrush névre hallgat.hu 232 Ugyanezt megtehetjük kódból is: brushRectangle. A színnel feltöltött területeknek megadhatunk egy d lészszöget (gradiens tengely) amely a színek haladási irányát jelöli majd. hogy ARGB értéket kell megadnunk. ahol A az átlátszóság mértékét jelzi (ha 0 akkor lesz teljesen átlátszó.Fill> </Rectangle> Itt csak arra kell figyelni.

Fill = lgb.Fill> A RadialGradientBrush hasonló mint az el z .0. . itt is GradientStop objektumokkal tudjuk szabályozni a színek határait: RadialGradientBrush rgb = new RadialGradientBrush().Add( new GradientStop(Color. 0" EndPoint="0.FromRgb(255.FromRgb(255.Fill> <LinearGradientBrush StartPoint="0.GradientStops. 0.0)). A tengelyt a StartPoint illetve EndPoint tulajdonságokkal tudjuk beállítani: <Rectangle. 0.5. a bal fels sarkot jelöli a 0.Add( new GradientStop(Color. 0. brushRectangle.0" /> <GradientStop Color="Red" Offset="0.Fill> <LinearGradientBrush> <GradientStop Color="Blue" Offset="0. 0).1. 0).FromRgb(0.5" /> </LinearGradientBrush> </Rectangle. rgb. 90). 1"> <GradientStop Color="Blue" Offset="0. 0.Forrás: http://www. míg a jobb alsót az 1. Alapértelmezetten a gradiens tengely ezt a két vonalat köti össze. A harmadik paraméter a tengely d lésszöge: XAML: <Rectangle.GradientStops. Hasonlóan mint a LinearGradientBrush –nál.5)).5. 0.hu 233 LinearGradientBrush lgb = new LinearGradientBrush( Color. 255). de a színeket kör alakzatban olvasztja össze.Fill> Az Offset tulajdonság a színek átmenetének a választóvonala. brushRectangle. 0.doksi.Fill = rgb.FromRgb(0. 255). rgb.0" /> <GradientStop Color="Red" Offset="0.5" /> </LinearGradientBrush> </Rectangle. Color.

Relative)). ib.doksi. mivel ezek a Control osztálytól öröklik a Background és Foreground tulajdonságukat.Fill> <RadialGradientBrush> <GradientStop Color="Red" Offset="0. A vezérl knek egyszer bben állíthatjuk be a színezését.jpg".Forrás: http://www.5" /> </RadialGradientBrush> </Rectangle.jpg" /> </Rectangle. közvetlenül a GradientStops tulajdonságot használva (eddig a konstruktorban adtuk meg ket).Fill> <ImageBrush ImageSource="test. XAML: <Rectangle.ImageSource = new BitmapImage(new Uri("test.Fill = ib. UriKind.hu 234 Most egy kicsit másként adtuk hozzá színeket.0" /> <GradientStop Color="Blue" Offset="0. Ugyanez XAML – ben: <Rectangle.Fill> Ha rosszul adtuk meg a kép nevét XamlParseException –t kapunk. Ezek valamilyen Brush leszármazottat várnak: <Button Background="Blue" Foreground="Red" Width="100" Height="30" Content="Click" /> A következ weboldalon több példát és leírást találunk: .Fill> Az ImageBrush –sal képeket tudunk kezelni: ImageBrush ib = new ImageBrush(). brushRectangle.

amelyek viszont rendelkeznek ilyennel: <Label BorderBrush="Coral" BorderThickness="5"> Hello Border! </Label> A BorderBrush tulajdonság a keret színét. vagy vezérl ket színezhetünk.doksi. amely szintén kaphat egyedi színezést: <Border BorderBrush="Coral" BorderThickness="5"> <Rectangle x:Name="brushRectangle" Width="70" Height="70" VerticalAlignment="Center"> </Rectangle> </Border> Ez a keret különnálló a Rectangle –t l.aspx Nemcsak alakzatokat.) egy Panel –be tegyük ket és azt jelöljük meg kerettel.hu 235 http://msdn. hanem megadhatunk nekik keretet (Border) is. hanem a CornerRadius tulajdonsággal kerekíthatjük is: <Border BorderBrush="Black" BorderThickness="5" CornerRadius="45"> <Button Width="100" Height="30" Content="Click" /> </Border> . meg. mivel annak nincs saját kerete.Forrás: http://www.microsoft. Vannak vezérl k. Egy Border objektum csakis egyetlen gyermek vezérl vel rendelkezhet. Nem csak szögletes keretet adhatunk. ezért ha többet akarunk összefogni akkor (pl.com/en-us/library/aa970904. míg a BorderThickness a keret vastagságát jelöli.

hiszen a vezérl k képességei nagyjából ugyanazok. a Button például ilyen: <Button BorderBrush="Black" BorderThickness="5" Width="100" Height="30" Content="Click" /> Hasonlóan Windows Forms –hoz a WPF is a jól ismert tulajdonságokat kínálja a szövegformázásokhoz: <Label FontFamily="Arial" FontSize="30"> Hello Border! </Label> 44. Funkciójuk szinte semmit nem változott a WinForms –hoz tapasztaltakhoz képest.hu 236 Egyes vezérl k rendelkeznek saját kerettel is. mint amit már ismerünk. de valójában nagy különbség van közöttük.1 TextBlock és Label Ez a két vezérl els ránézésre ugyanazt a célt szolgálja. de a Label –hez való hasonlósága miatt itt a helye.2.2 Vezérl k Ebben a fejezetben megismerkedünk az alapvet vezérl kkel. de tulajdonságaik illetve deklarációjuk igen. 44. A példákban jobbára a küls megjelenéssel foglalkozunk.doksi. hiszen nem vezérl . a TextBlock kakukktojás ebben a fejezetben.Forrás: http://www. S t. A TextBlock –ot .

Window1" x:Name="window1" xmlns="http://schemas. hanem mert <Window x:Class="JegyzetWPF. Az adatkötésekr l (amelyet a Binding osztály vezérel majd) kés bb beszélünk majd.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. hogy kis mennyiség szöveges (akár formázott) tartalmat jelenítsen meg: <Window x:Class="JegyzetWPF. de támogatja a gyorsbillenty ket: t nem ezért szeretjük.</Bold><LineBreak /> Ez pedig <Italic>d lt bet s.microsoft.Window1" x:Name="window1" xmlns="http://schemas.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 tulajdonságának megadhatjuk a hozzá kötött vezérl nevét. amelyre a megfelel billenty kombinációval a fókusz kerül.microsoft.microsoft.microsoft. . a példában az ALT+t kombináció azt eredményezi.doksi.hu 237 arra találták ki.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.</Italic> </TextBlock> </Grid> </Window> A szövegformázást a Label is támogatja. hogy a fókusz a TextBox –ra kerül.Forrás: http://www.com/winfx/2006/xaml" xml:lang="hu-HU" Title="Window1" Height="100" Width="200"> <Grid> <TextBlock> Ez a szöveg <Bold>félkövér. A gyorsbillenty t a kiválasztott karakter elé írt alulvonással tudjuk kijelölni.

így mindig csak egyet tudtunk megjelelölni. a CheckBox –ot nem lehet . 44.2 CheckBox és RadioButton Vegyük a következ XAML kódot: <Window x:Class="JegyzetWPF.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. err l a következ fejezetben olvashatunk többet.Window1" x:Name="window1" xmlns="http://schemas.RowDefinitions> <Label x:Name="label1" Content="Napszak: " Grid. akkor megszokhattuk.com/winfx/2006/xaml" Title="Window1" Height="150" Width="200"> <Grid> <Grid.hu 238 A Visual Studio –ban az IntelliSense nem támogatja az adatkötéseket.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. így a Binding –et sem fogja felajánlani. hogy egy GroupBox –ban elhelyezett CheckBox –oknak megadhattunk csoportot. A WPF máshogy m ködik.Row="1"> <GroupBox.2.RowDefinitions> <RowDefinition Height="30" /> <RowDefinition Height="*" /> </Grid.Header> Milyen napszak van? </GroupBox.microsoft.Forrás: http://www. Ezenkív l a Label egy ContentControl leszármazott is.doksi.Row="0"/> <GroupBox Grid. nekünk kell beírni.

Az állapotoknak megfelel en mindegyikhez létezik eseményvezérl .hu 239 csoporthoz rendelni. ez egy még specializáltabb eszköz. Írjuk át a programot és használjuk ki inkább a RadioButton csoportosíthatóságát. ez pedig (pl. de nem nyúltunk még hozzá. hogy hogyan lehetne megoldani. így ha többet szeretnénk egy olyan eszközt kell bevetnünk. unchecked (amikor levesszük róla a jelölést) és indeterminated (amikor nincs bejelölve. így könnyedén átformálhatjuk: <CheckBox> <StackPanel> <Label>Hello. hogy egyszerre csak egy CheckBox –ot jelölhessünk meg? Ez viszont az olvasó feladata lesz. vagyis. Ezt kétféleképpen tehetjük meg. amely erre képes.doksi.Row="1"> <GroupBox. ez egy CheckBox!</Label> <Button>OK</Button> </StackPanel> </CheckBox> A ContentControl leszármazottak csakis egy gyermeket tudnak kezelni. hogy a napszaknak megfelel en megváltoztassuk a Label feliratát. lévén van a legközelebb: .Header> Milyen napszak van? </GroupBox. vagy pedig a csoportosítandó RadioButton –okat egy sz l alá rendeljük (pl. vagy a GroupName tulajdonságon kereszt l. Kanyarodjunk vissza az eredeti problémához. mégpedig az.Header> <StackPanel> <RadioButton x:Name="rb1" Content="Nappal" /> <RadioButton x:Name="rb2" Content="Éjszaka" /> </StackPanel> </GroupBox> Hasonlóan a CheckBox –hoz. vagyis lehet valamilyen fejléce. Ezt egy attached event segítségével fogjuk megtenni. egy HeaderedContentControl. vagyis ilyen az állapota pl. A CheckBox a ContentControl osztály leszármazottja. többféle megoldás is létezik (egy kulcsszó: attached event). a RadioButton is háromállású. Már csak egy dolog van hátra. A CheckBox –nak három állapota lehet: checked (amikor bejelöljük). és van még egy nagy különbség. ami feltételezni engedi – és nem téved -. hogy az is egy ContentControl leszármazott. GroupBox): <GroupBox Grid. S t. amikor elindítjuk az alkalmazást). amit a StackPanel –hez ragasztunk.) a StackPanel lesz ( t már ismerjük).Forrás: http://www.Ezt használtuk a GroupBox esetében is.

doksi. } Mivel ez egy routed event. Az eseménykezel ez lesz: private void StackPanel_Checked(object sender.OriginalSource). mind a RadioButton a ToggleButton osztályból származik.3 TextBox. (hiszen az ebben az esetben az az objektum lesz.RowDefinitions> <RowDefinition Height="30" /> <RowDefinition Height="30" /> <RowDefinition Height="*" /> </Grid.2.hu 240 <StackPanel ToggleButton.Checked="StackPanel_Checked"> <RadioButton x:Name="rb1" Content="Nappal" /> <RadioButton x:Name="rb2" Content="Éjszaka" /> </StackPanel> Mind a CheckBox.Row="0" Grid.microsoft.Column="1" /> <Label Content="Jelszó:" Grid.Row="1" Grid.microsoft.Row="0" Grid.ColumnDefinitions> <ColumnDefinition Width="90" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Label Content="Felhasználónév:" Grid. PasswordBox és RichTextBox Ebben a fejezetben a szöveges inputot kezel vezérl ket vesszük górcs Els ként készítsünk egy egyszer beléptet alkalmazást: <Window x:Class="JegyzetWPF. ezért a második paramétert l kérjük el az esemény eredeti forrását. ezért nem konvertálhatjuk közvetlenül az els paramétert. .Forrás: http://www.com/winfx/2006/xaml" Title="Window1" Height="150" Width="220"> <Grid> <Grid.Window1" x:Name="window1" xmlns="http://schemas.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.Column="0" /> <TextBox x:Name="usernamebox" Width="100" Height="20" Grid. ami elkapta az eseményt.Content. 44.Content = "Napszak: " + ((RadioButton)e. és a checked/unchecked/indeterminate eseményeket is innen öröklik. vagyis most a StackPanel).RowDefinitions> <Grid.Column="0" /> alá. RoutedEventArgs e) { label1.

com/winfx/2006/xaml" Title="Window1" Height="150" Width="220"> <Grid> <RichTextBox x:Name="richtb1" Width="200" Height="100" /> </Grid> </Window> Amikor elindítjuk az alkalmazást.Row="1" Grid. hogy a PasswordBox ugyanazt csinálja.Column="0" Click="loginbutton_Click" /> </Grid> </Window> Az eredmény: Ha elindítjuk az alkalmazást látható lesz.Forrás: http://www.Window1" x:Name="window1" xmlns="http://schemas. mint a WF -beli TextBox beállított PasswordChar tulajdonsággal. írhatunk bele.hu 241 <PasswordBox x:Name="passwordbox" Width="100" Height="20" Grid. akár formázott tartalmat is. egy teljesen átlagos RichTextBox jelenik meg. Adjunk a Grid – hez egy gombot is.microsoft.doksi. hogy mostantól nem tudunk közvetlenül menteni a vezérl b l. A gomb eseménykezel jét már könnyen meg tudja írni az olvasó.microsoft.Column="1" /> <Button x:Name="loginbutton" Content="Belépés" Margin="5 0 0 0" Width="80" Height="20" Grid. Következ vizsgálati alanyunk a RichTextBox. stb… Van azonban néhány változás is. Az egyik legjelent sebb. El ször a hagyományos használatával ismerkedünk meg: <Window x:Class="JegyzetWPF.Row="2" Grid.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. ezt nekünk kell intéznünk. amivel ezt majd megtesszük: .

A ContentStart és ContentEnd tulajdonságok TextPointer objektumokat adnak vissza. most nézzük meg az eseménykezel t: private void savebutton_Click(object sender. } } A TextRange osztállyal valamilyen szöveges objektumból tudunk szöveget kiemelni (ez akár egy szimpla strig is lehet).Save(fstream. DataFormats. méghozzá igen sokféle formátumban.ContentStart.Row="0"/> <Button x:Name="savebutton" Width="100" Height="20" Content="Mentés" Grid.RowDefinitions> <RowDefinition Height="150" /> <RowDefinition Height="*" /> </Grid.com/winfx/2006/xaml" Title="Window1" Height="220" Width="220"> <Grid> <Grid. Küls file t.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. range.Window1" x:Name="window1" xmlns="http://schemas.RowDefinitions> <RichTextBox x:Name="richtb1" Width="200" Height="100" Grid. Ennek segítségével akár el re megadhatunk egy megformált szöveget az XAML kódban: <RichTextBox x:Name="richtb1" Width="200" .hu 242 <Window x:Class="JegyzetWPF. Ennek az osztálynak a segítségével tudunk menteni.Document.Rtf). és a TextRange objektumnak a Load metódusát használjuk. FileMode.doksi.microsoft. amelyet a TextRange tud használni. RoutedEventArgs e) { using (FileStream fstream = new FileStream("rtb. még akkor is. Egy RichTextBox minden esetben tartalmaz pontosan egy FlowDocument objektumot.Row="1" Click="savebutton_Click" /> </Grid> </Window> Rendben.Create)) { TextRange range = new TextRange( richtb1.Forrás: http://www. richtb1. hanem megnyitni kell a forrást.ContentEnd). ha ez a forráskódban nem jelenik meg.Document. két különbség van. A RichTextBox Document tulajdonsága egy FlowDocument objektumot ad vissza. a stream –nek nem létrehozni.microsoft. ami tulajdonképpen a tényleges adattárolásért és megjelenítésért felel a RichTextBox –on belül.rtf".ugyanígy tudunk beolvasni.

2. ezt megtehetjük mondjuk a Thread. 44. Mind a Pragraph mind a Bold elemek tartalmazhatnak egyéb formázásokra utasító parancsot (pl. hogy egy a f szállal szinkronban m köd szálban csináljuk meg a feladatot.doksi. hogy a WPF csakis a saját szálában hajlandó módosítani a felhasználói felületet és ez a tudásunk most hasznos lesz. de ez egy másik fejezet(ek) témája lesz. egy id után pedig a vezérl megtelik és ismét aktív lesz az alkalmazás.Sleep metódussal. Ez azért van. A következ oldalon b vebb leírást talál az olvasó err l az osztályról: http://msdn. A megoldás az lesz. A feladat a szokásos lesz.hu 243 Height="100" Grid. ScrollBar és Slider Ezzel a két vezérl vel különösebb gondunk nem lesz. gombnyomásra elkezdjük szép lassan növelni a ProgressBar értékét. így most meg kell elégednünk a „hagyományos” módszerekkel. mivel a WPF más területeinek lehet ségeivel felvértezve ezek a vezérl k is rendkívül érdekesek lesznek. amelyek vízszintes vagy függ leges görget sávot eredményeznek. mert csak így tudjuk azt blokkolni. Azért kell ennek a szálnak az alkalmazás szálával szinkronban m ködnie.: karakterkészlet. hiszen egyikük sem kínál semmilyen extra lehet séget. Az nyílvánvaló. s t ilyen esetben jelent sen korlátozottak a képességei. annyi ideje azonban nem volt. hogy a ProgressBar meg se mukkan. csak azt amit már ismerünk. akkor felt nhet az. Maga a FlowDocument nem köt dik a RichTextBox –hoz. hogy megjelenítsünk egy szöveget. egy kis trükköt igényel.aspx A RichTextBox és a TextBox egyaránt képes helyesírásellen rzésre. Bár ez így nem teljesen igaz. Azt már tudjuk.microsoft. mert az alkalmazás saját szálát blokkoltuk. Amikor ezt használjuk. Ugyanígy mindkét vezérl rendelkezik helyi menüvel.4 ProgressBar. ezt nekünk kell bekapcsolni a Horizontal/VerticalScrollBarVisibility tulajdonságokkal. A ProgressBar –ral kezdünk és egyúttal megismerkedünk a WPF szálkezelésével is egy kicsit közelebbr l. és a felhasználói felület sem használható. hogy az alkalmazás nem reagál küls beavatkozásra.Row="0"> <FlowDocument> <Paragraph> <Bold>Hello RichTextBox!</Bold> </Paragraph> </FlowDocument> </RichTextBox> A FlowDocument –en belül Paragraph elemeket használtunk. bet méret). ezt a SpellCheck. hogy a gombnyomásra elindított ciklust le kell lassítanunk.Forrás: http://www. hogy két cikluslépés közt frissítse a felületet. Az eredmény az lesz.IsEnabled attached propeprty segítségével állíthatjuk be.com/en-us/library/aa970909. . A RichTextBox alapértelmezetten nem támogatja a görget sávot. Ez viszont. amelyet a jobb egérgombbal való kattintással hozhatunk el és a kivágás/másolás/beillesztés m veleteket tudjuk vele elvégezni. és csak a futás végén „telik” meg a ProgressBar.

Ez az osztály minden egyes alkalmazáshoz biztosít egy Dispatcher objektumot. a gomb Click eseménye így fog kinézni: private void startbutton_Click(object sender.Value. } } Az Invoke els paramétere a hívás prioritását jelenti. Nézzük meg közelebbr l. A ProgressBar Value tulajdonsága egy dependency property. hogy jelezzük.ToString() + "%".Thread.Windows. } Az eseménykezel második paramétere is érdekes számunkra. akár egy másik szálból is.Invoke( System.hu 244 A WPF osztályhierarchiájáról szóló fejezetben megtanultuk azt is. } private SleepDelegate sleepdlgt. mint az alkalmazás felületét kirajzoló folyamat. ++i) { Dispatcher.Content = progressbar1. Készítünk még egy Label –t is. többféle prioritás létezik és ebben az esetben többet is használhatunk. hogy a legtöbb WPF beli vezérl a DispatcherObject osztály leszármazottja. hogy éppen hány százalékánál járunk a folyamatnak. Szükségünk lesz egy delegate –re is. RoutedEventArgs e) { for (int i = 0. ami nekünk most tökéletesen megfelel. Ez a . Ne felejtsük el hozzáadni a metódust. ++progressbar1.Threading. ami pontosabb képet ad a folyamatról. Egy kés bbi fejezetben fogunk tanulni a WPF adatkötéseir l is. public static void SleepMethod() { System. Ennek az osztálynak az Invoke és BeginInvoke metódusaival. sleepdlgt). RoutedPropertyChangedEventArgs<double> e) { progresslabel.Sleep(100). mivel ez a paraméter egy adott tulajdonság változásának körülményeit mutatja. Most már nagyon egyszer dolgunk lesz.Value. mondjuk a konstruktorban: sleepdlgt += new SleepDelegate(SleepMethod). i < 100.Forrás: http://www. Mi most az Invoke –ot fogjuk használni.Threading.Render. amellyel a f szálat tudjuk vezérelni. a ValueChanged pedig egy routed event.doksi. ezt a ProgressBar ValueChanged eseményében fogjuk frissíteni: private void progressbar1_ValueChanged(object sender. A neve árulkodó lehet. mert mindkét metódus azt vár paramétereként: public delegate void SleepDelegate(). A Render értékkel hívott delegate ugyanazzal a prioritással rendelkezik.DispatcherPriority. tudunk szinkron illetve aszinkron hívásokat végezni.

Row="0" /> <Button x:Name="button1" Content="Start" Width="100" Height="30" Grid.Window1" x:Name="window1" xmlns="http://schemas.RowDefinitions> <RowDefinition Height="100" /> <RowDefinition Height="*" /> </Grid.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.hu 245 paramétertípus egy generikus osztály. a fenti esetben például double értékekre specializálódott.doksi. Az XAML így néz majd ki: <Window x:Class="JegyzetWPF.RowDefinitions> <ProgressBar x:Name="progressbar1" Minimum="0" Maximum="100" Width="150" Height="30" Grid.Row="1" Click="button1_Click" /> </Grid> </Window> Az eredmény pedig: .Row="0" ValueChanged="progressbar1_ValueChanged"/> <Label x:Name="progresslabel" FontSize="20" Foreground="Cyan" Content="0%" Grid.com/winfx/2006/xaml" Title="Window1" Height="200" Width="300" Background="Black"> <Grid> <Grid.microsoft.Forrás: http://www. amelyek értelemszer en a régi és az új értéket hordozzák magukban. A paraméter tulajdonságai közt megtaláljuk a NewValue és az OldValue tagokat.

EventArgs e) { if (progressbar1. A DispatcherTimer igazából azért jó nekünk. hogy ideje cselekedni: timer = new System. hogy nem –e lehetne ezt egyszer bben megoldani. } Az Interval tulajdonság egy TimeSpan típusú objektumot vár. míg a ValueChanged csakis a mutatott érték változását tudja kezelni.hu 246 Felmerülhet a kérdés. Ha megnézzük a WPF ToolBox –át. a ScrollBar –t mozgatva kiírjuk az aktuális értékét egy Label –re. van helyette más: DispatcherTimer. a Tick eseménye fogja jelezni.Windows. Az XAML: . timer. Az eseménykezel pedig ez lesz: void timer_Tick(object sender. ennek (pl.Windows. a Scroll esemény a változás körülményeit írja le.Threading. illetve az aktuális pozíciót egy Thumb objektum jelképezi.DispatcherTimer().Stop().Threading. így módosíthatja a felhasználói felületet minden további nélkül.Forrás: http://www. Ez a timer hasonlóan m ködik.DispatcherTimer timer.Value < 100) { ++progressbar1. akkor ilyet bizony nem fogunk találni (és más komponenst sem). Következzen a ScollBar. Semmi vész.Interval = new TimeSpan(10000). } else timer. mint a már ismert komponens. a két végpontján egy-egy RepeatButton tartózkodik. igazából a WinForms -os Timer -t itt nem is igazán ajánlott használni. Készítsünk egy új adattagot: private System. timer. Ez a vezérl több részb l áll.Tick += new EventHandler(timer_Tick). például használhatnánk a korábban már bemutatott Timer osztályt is. S t.) nanoszekundumban adhatunk értéket. A mostani példa hasonló lesz az el z höz. mert a f szálban fut.Value. Az értékváltozást kétféleképpen is kezelhetjük.doksi. Ezek együttesen egy Track objektumot alkotnak.

doksi. Kezdjük a ComboBox –xal: .RowDefinitions> <ScrollBar x:Name="scrollbar1" Minimum="0" Maximum="100" Orientation="Horizontal" Grid.hu 247 <Window x:Class="JegyzetWPF.com/winfx/2006/xaml" Title="Window1" Height="150" Width="300"> <Grid> <Grid.Content = "Value: " + scrollbar1.Forrás: http://www.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. az XAML definíciója: <Slider x:Name="slider1" Width="200" Height="30" Minimum="0" Maximum="100" Grid.microsoft. amelyek segítségével jóval egyszer bben oldhatjuk meg az ebben a fejezetben szerepl feladatokat.RowDefinitions> <RowDefinition Height="75" /> <RowDefinition Height="*" /> </Grid.ToString(). Hamarosan foglalkozunk a WPF adatkötéseivel.5 ComboBox és ListBox Ez a két vezérl is a hagyományos szolgáltatásokat nyújtja számunkra.Row="0" ValueChanged="slider1_ValueChanged" /> Az állapotváltozásokat szintén a ValueChanged esemény segítségével tudjuk kezelni.microsoft. mint a ScrollBar –t. 44.Row="0" ValueChanged="scrollbar1_ValueChanged"/> <Label x:Name="valuelabel" Content="Value: 0" Grid. RoutedPropertyChangedEventArgs<double> e) { valuelabel. } Végül foglalkozzunk a Slider –rel is.Row="1" /> </Grid> </Window> Nézzük az eseménykezel t: private void scrollbar1_ValueChanged(object sender.2.Value. Ezt a vezérl t gyakorlatilag ugyanúgy kezeljük.Window1" x:Name="window1" xmlns="http://schemas.

Forrás: http://www. RoutedEventArgs e) { combobox1.Items. ha így csináljuk: private void window1_Loaded(object sender. így összetettebb elemei is lehetnek a vezérl nek (pl.Items. ilyen pl.Items.doksi. combobox1. amely megvalósítja az IEnumerable interfészt. } Ezeken kív l XAML –b l is létrehozhatjuk az elemeit: <ComboBox x:Name="combobox1" Width="100" Height="30" > <ComboBoxItem Content="István" /> <ComboBoxItem Content="Béla" /> <ComboBoxItem Content="Judit" /> <ComboBoxItem Content="Márta" /> </ComboBox> A ComboBoxItem a ListBoxItem –b l származik és leszármazottja a ContentControl oszálynak. a Loaded eseményben adjuk hozzá: private void window1_Loaded(object sender. combobox1.Window1" x:Name="window1" xmlns="http://schemas. "Béla". a fentivel tökéletesen megegyez eredményt kapunk. egy gomb): <ComboBox x:Name="combobox1" Width="100" .hu 248 <Window x:Class="JegyzetWPF.ItemsSource = names. } Az ItemSource tulajdonságot az ItemsControl sosztálytól örökli a vezérl és olyan adatszerkezetet vár. combobox1.microsoft. egy hagyományos tömb. "Márta" }.Items.Add("Márta").com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.Add("Judit").Add("István").Add("Béla"). RoutedEventArgs e) { string[] names = new string[] { "István". combobox1. "Judit".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 ször) az ablak betöltésekor.microsoft. Elemeket az Items tulajdonságon kereszt l is megadhatunk.

doksi. amely nem szöveges tíusú. de keresni tud az alapján. Ha látni is szeretnénk.hu 249 Height="30" > <ComboBoxItem> <Button>István</Button> </ComboBoxItem> <ComboBoxItem Content="Béla" /> <ComboBoxItem Content="Judit" /> <ComboBoxItem Content="Márta" /> </ComboBox> Ugyanakkor nem vagyunk rákényszerítve a ComboBoxItem használatára: <ComboBox x:Name="combobox1" IsEditable="True" TextSearch. amivel az elemek egy tulajdonságának értékét tehetjük kereshet vé: <ComboBox x:Name="combobox1" IsEditable="True" TextSearch.Text="István">István</Button> </ComboBoxItem> <ComboBoxItem Content="Béla" /> <ComboBoxItem Content="Judit" /> <ComboBoxItem Content="Márta" /> </ComboBox> Hasonló célt szolgál a TextSearch. amit írunk.TextPath="Name" Width="100" Height="30" > <Button>István</Button> <Button>Béla</Button> <Button>Judit</Button> <Button>Márta</Button> </ComboBox> A ComboBox alapértelmezetten nem jeleníti meg az általunk beírt szöveget. A ComboBoxItem. Ekkor is rákereshetünk.TextPath="Name" Width="100" Height="30" > <Button Name="István" >István</Button> <Button Name="Béla">Béla</Button> <Button Name="Judit">Judit</Button> <Button Name="Márta">Márta</Button> </ComboBox> . akár olyan objektumot is tartalmazhat.TextPath tulajdonság. ha beállítottuk a TextSearch. akkor az IsEditable tulajdonságát kell true értékre állítanunk.Text attached property –t: <ComboBox x:Name="combobox1" IsEditable="True" Width="100" Height="30" > <ComboBoxItem> <Button TextSearch.Forrás: http://www.

AddedItems illetve RemovedItems tulajdonsága az éppen kijelölt. Multiple (több elem is kiválasztható. vagy kijelölésb l eltávolított elemeket tartalmazza. Nagyon kevés dologban tér el a ListBox a ComboBox –tól. a legszembet n bb. Három mód létezik Single (ez az alapértelmezett.com/winfx/2006/xaml" Title="Window1" Height="120" Width="120"> <Grid> <ListBox x:Name="listbox1" Width="100" Height="80" > <ListBoxItem Content="István" /> <ListBoxItem Content="Béla" /> <ListBoxItem Content="Judit" /> <ListBoxItem Content="Márta" /> </ListBox> </Grid> </Window> A SelectionMode tulajdonságával meghatározhatjuk. Ennek második paramétere SelectionChangedEventArgs típusú.doksi. ItemsControl leszármazott. A kiválasztást vagy annak visszavonását a ListBox SelectionChanged eseményével tudjuk kezelni. amikor megváltozik a kiválasztott elem. nem igényel külön billenyt leütést) illetve Extended (a SHIFT billenty nyomvatartásával egymást követ elemeket vagy a CTRL billenty nyomvatartásával önálló elemeket választhatunk ki). 44. Ez utóbbi a HeaderedItemsControl osztályból származik. hogy ez a vezérl nem tesz lehet vé keresést.2.microsoft. elemeinek típusa TreeViewItem.microsoft. hogy hány elemet választhat ki a felhasználó.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"> .6 TreeView is egy A TreeView vezérl vel hierarchikus sorrendbe állíthatjuk az elemeit.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. vagyis egy adott elemnek lehetnek gyermek elemei (mivel ItemsControl leszármazott) illetve minden elem rendelkezik a Header tulajdonsággal. amely akkor aktiválódik. illetve az elemei típusa ListBoxItem: <Window x:Class="JegyzetWPF.Forrás: http://www.Window1" x:Name="window1" xmlns="http://schemas.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. egyszerre egy elem).microsoft.Window1" x:Name="window1" xmlns="http://schemas. amellyel az egyes elemek feliratát állíthatjuk be: <Window x:Class="JegyzetWPF.hu 250 A ComboBox számunkra fontos eseménye a SelectionChanged.

Ez a vezérl ItemsControl leszámazott.1" /> </MenuItem> <MenuItem Header="Menu2" /> is a .hu 251 <TreeViewItem Header="Tavasz" /> <TreeViewItem Header="Nyár" /> <TreeViewItem Header=" sz" /> <TreeViewItem Header="Tél" /> </TreeViewItem> </TreeView> </Grid> </Window> Mivel a TreeViewItem egyben ItemsControl is.microsoft. ha nem tartalmaz más elemeket. amelyek HeaderedItemsControl osztályból származnak: <Window x:Class="JegyzetWPF.Gomb</Button> </TreeViewItem> <TreeViewItem Header="Nyár" /> <TreeViewItem Header=" sz" /> <TreeViewItem Header="Tél" /> </TreeViewItem> A vezérl ItemSource tulajdonságán kereszt l akár gy jteményeket hozzáadhatunk.doksi. 44.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft. elemeinek típusa MenuItem.7 Menu A vezérl vel egy hagyományos menüt tudunk létrehozni. de csakis akkor.Window1" x:Name="window1" xmlns="http://schemas. is Az Items tulajdonság hagyományos gy jteményként viselkedik. hozzáadhatunk vagy akár törölhetünk is bel le.2. az egyes csomópontok szövegen kívül mást is tartalmazhatnak: <TreeViewItem Header="Évszakok"> <TreeViewItem Header="Tavasz" > <Button>Tavasz . így indexelhetjük.com/winfx/2006/xaml" Title="Window1" Height="130" Width="150"> <Grid> <Menu x:Name="menu1"> <MenuItem Header="Menu1"> <MenuItem Header="SubMenu1.Forrás: http://www.

doksi.Content> <TextBlock> Ezt a szöveget elrejthetjük. } A MenuItem attached event -jeinek segítségével több eseményt is tudunk kezelni.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. a Click eseménnyel (amely egyébként a MenuItem osztályhoz tartozik) tudjuk kezelni (van egy sokkal elegánsabb módszer is.Forrás: http://www..microsoft. mint pl. hogy a tartalmazott elemeket képes elrejteni/megjeleníteni: <Window x:Class="JegyzetWPF. egy almenü megnyitása.1" /> </MenuItem> <MenuItem Header="Menu2" /> </Menu> Az eseménykezel : private void menu1_Click(object sender..Header.microsoft.hu 252 </Menu> </Grid> </Window> Egy menüpont kiválasztását és így egy parancs végrehajtását pl. de az egy másik fejezet témája lesz): <Menu x:Name="menu1" MenuItem.ToString()). ezért tartalmazhat más elemeket is.Show(((MenuItem)e. </TextBlock> </Expander. Ezt a tulajdonságát kiegészti azzal.OriginalSource). RoutedEventArgs e) { MessageBox.8 Expander és TabControl Az Expander a HeaderedContentContorl osztályból származik.Content> .com/winfx/2006/xaml" Title="Window1" Height="130" Width="150"> <Grid> <Expander x:Name="expander1" Width="130" Height="100" > <Expander.Click="menu1_Click"> <MenuItem Header="Menu1"> <MenuItem Header="SubMenu1.2.Window1" x:Name="window1" xmlns="http://schemas. 44.

Content> <StackPanel> <TextBox x:Name="textbox1" Width="100"/> <Button x:Name="button1" Width="100" Height="20" Content="OK" /> </StackPanel> </Expander. amely képes ket kezelni (pl.hu 253 </Expander> </Grid> </Window> Az Expander (a ContentControl osztályból való származása miatt) csak egyetlen gyermekelemet képes kezelni. Az egyes oldalak TabItem tulajdonságúak és a HeaderedContentControl osztályból származnak.Content> </Expander> A vezérl nyitott vagy csukott állapotát az IsExpanded tulajdonsággal kérdezhetjük le (illetve állíthatjuk be).microsoft.com/winfx/2006/xaml/presentation" . <Window x:Class="JegyzetWPF. stb…): <Expander x:Name="expander1" Width="130" Height="100" > <Expander.Forrás: http://www. így ha több vezérl t akarunk elhelyezni benne szükségünk lehet egy olyan elemre.: Grid. A TabControl vezérl több „oldalt” tartalmaz. ennek alapértelmezett értéke true. A nyitás „irányát” az ExpandDirection tulajdonsággal állíthatjuk.doksi. amelyek közül mindig csak egy látszik teljes valójában. StackPanel.Window1" x:Name="window1" xmlns="http://schemas.

stb…) elvégzéséhez szükséges metódusokkal.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. Kezelése rendkív l egyszer (viszonylag könnyen készíthetünk egy egyszer lejétszó programot). <Window x:Class="JegyzetWPF. megállítás.hu 254 xmlns:x="http://schemas.2.Forrás: http://www.doksi.microsoft.9 Image és MediaElement Az Image vezérl képek megjelenítésére szolgál (gyakorlatilag megfelel a WinForms –os PictureBox –nak). amelyet a Windows Media Player 10 is.jpg" /> </Grid> </Window> A MediaElement lehet vé teszi különböz médiafile –ok lejátszását.com/winfx/2006/xaml" Title="Window1" Height="130" Width="150"> <Grid> <Image x:Name="image1" Width="100" Height="100" Source="image. A megjelenítend képet a Source tulajdonságán kereszt l tudjuk megadni: <Window x:Class="JegyzetWPF. Minden olyan típust támogat.Window1" x:Name="window1" xmlns="http://schemas.microsoft.Window1" x:Name="window1" . rendelkezik az alapvet m veletek (lejátszás.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.microsoft.

Pause – a lejátszás megállítása. Stop – a lejátszás megállítása és az aktuális pozicíó a file eleje lesz. tulajdonságokat és eseményeket: Play – ez a metódus elindítja média lejátszását.hu 255 xmlns="http://schemas. . Volume – ez a tulajdonság a hanger beállításáért felel s.Forrás: http://www.wmv" /> </Grid> </Window> Nézzük meg a fontosabb metódusokat.microsoft. MediaOpened és MediaEnded – ezek az események a média megnyitásakor illetve a lejátszás végén lépnek akcióba.doksi. ismételt Play után az aktuális pozíciótól folytatja a lejátszást.microsoft.com/winfx/2006/xaml" Title="Window1" Height="130" Width="150"> <Grid> <MediaElement x:Name="mediaelement1" Source="file.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.

kattintsunk jobb egérgombbal a solution.Relative)). Ezután a kiválasztott elem megjelenik a project file –jai közt is. ekkor a teljes elérési utat oda kell írnunk). akkor a következ t írjuk: image1. Helyezzünk el az alkalmazásban egy Image vezérl t is: <Image x:Name="image1" Source="picture.Relative)). UriKind.jpg". UriKind. Készen vagyunk. amikor BAML –lé alakul.vagy assembly resource –nak nevezzük.GetResourceStream( new Uri("picture. Egy projecthez nagyon könnyen tudunk er forrást hozzáadni. ha az er forrás egy másik assembly –ben van? A következ példához tegyük fel. Ennek két módja van.Relative)).jpg" Width="100" Height="100" /> Látható. hogy egyszer en csak a file nevével hivatkoztunk képre (persze elhelyezhetnük er forrást mappában is. mi van akkor. A WPF bevezet egy másikfajta er forrást. még ha nem is tudtunk róla.Forrás: http://www.doksi. egy kép.hu 256 45.jpg".component/picture. a neve legyen picture. akkor egy az alkalmazásba „belefordított” bináris adatra (pl. ezt binary. UriKind. Er források Általában amikor valamilyen er forrásról beszélünk. most lássuk. Ha ugyanezt forráskódból akarjuk elérni. el ször az egyszerübbikkel foglalkozunk: tegyük fel. hogyan használhatjuk fel ket. 45. vagy hang) gondolunk. Ebben a fejezetben mindkét típust megvizsgáljuk.Resources névtérben van): StreamResourceInfo sri = Application. Az XAML is ilyen er forrás lesz a fordítás során.jpg. hogy ennek a másik assembly –nek a neve ImageAssembly: image1.Windows. Rugalmasabban tudjuk kezelni az er forrásokat egy StreamResourceInfo objektummal (ez a System. a ContentType tulajdonsággal ez er forrás típusát (ez most image/jpg) a Stream tualjdonsággal pedig az er forrásra mutató stream –et kapunk (ennek típusa UnmanagedMemoryStream). Ezután a Build Action –t állítsuk Resource –ra. Az elérési út nem mindig ilyen egyértelm . majd válasszuk az Add menü Existing Item lehet ségét.ön. hogy ebben az esetben .jpg". Ezután két dolgot tudunk kinyerni ebb l az objektumból. 45.2 Object resource .NET objektumokról beszélünk. amelynek neve: object resource. Jobb gombbal kattintsunk rajta és válasszuk a Properties menüpontot.1 Assembly resource Ezzel a típussal már találkoztunk.Source = new BitmapImage( new Uri("ImageAssembly. hogy egy kép file –t raktároztunk el. A név alapján könny kitalálni.Source = new BitmapImage(new Uri("picture.

amikor az er forrás ténylegesen használatra kerül. Emellett ezzel a megoldással könnyen használhatunk egy adott objetumot több helyen (pl. Minden ilyen osztály rendelkezik a Resources nev tulajdonsággal amely egy ResourceDictionary típusú listát ad vissza. vagyis a WPF kikeresi az összes rendelkezésre álló er forráslistából a megfelel tagot. mint a már ismert Dictionary osztály. Er forrást természtesen kódból is hozzáköthetünk egy vezérl höz: private void Window_Loaded(object sender. így azt nekünk kell beírnunk.7" /> </LinearGradientBrush> </Grid. ekkor ezt bárhol felhasználhatjuk késöbb).microsoft.hu 257 Az object resource koncepciója új számunkra.0" /> <GradientStop Color="Red" Offset="0. Er forrást bármely FrameworkElement vagy FrameworkContentElement leszármazott tartalmazhat. Egy er forrásra kétféleképpen statikusan és dinamikusan hivatkozhatunk. Nézzünk egy példát: <Window x:Class="JegyzetWPF.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. míg a másodiknál dinamikusan (DynamicResource) használtuk fel. A gyakorlatban a legtöbb esetben a top-level-element fogja az alkalmazás (pontosabban az aktuális form) er forrásait tartalmazni.Resources> <Button Width="100" Height="30" Background="{StaticResource buttoncolor}" VerticalAlignment="Top" /> <Button Width="100" Height="30" Background="{DynamicResource buttoncolor}" VerticalAlignment="Bottom" /> </Grid> </Window> A két gomb ugyanazt az er forrást használja. de az els esetben statikusan (StaticResource).: stílusok).microsoft. RoutedEventArgs e) { . TextBlock objektumokhoz definiálunk egy stílust. amely megvalósítja (többek közt) az IDictionary interfészt. ha az adott er forrás tulajdonságai megváltoztak. ebb l következik az egyik legfontosabb jellemz je is: vagyis minden tartalmazott er forrásnak egyedi azonosítójának kell lennie. és ezt minden egyes alkalommal megteszi. elöbbi esetben az alkalmazás indításakor értékel dik ki a kifejezés.doksi. A Visual Studio az er források kezeléséhez nem nyújt IntelliSense támogatást.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Grid> <Grid. de a WPF számos lehet sége épít rá (pl.Resources> <LinearGradientBrush x:Key="buttoncolor"> <GradientStop Color="Black" Offset="0.Window1" xmlns="http://schemas. vagyis reagál arra. A dinamikus esetben ez akkorra halasztódik. így hasonlóan m ködik.Forrás: http://www.

Az App.FindResource("buttoncolor").hu 258 resbutton.Resources> <LinearGradientBrush x:Key="buttoncolor"> <GradientStop Color="Black" Offset="0.Forrás: http://www.0" /> <GradientStop Color="Red" Offset="0.com/winfx/2006/xaml" StartupUri="Window1.Window1" xmlns="http://schemas.Resources> </Application> Még jobban kiterjeszthetjük ezt. helyette írhatjuk ezt (vagy adhatunk nevet a Grid –nek): <Window x:Class="JegyzetWPF.Background = (Brush)this. ha ResourceDictionary –t használunk.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 használhatjuk a Resource tulajdonság indexel jét is: resbutton.microsoft.xaml file –ban elhelyezhetünk „globális” er forrásokat. Add/New Item és a WPF fül alatt válasszuk a Resource Dictionary –t): .microsoft. amely null értéket ad vissza. amelyek az alkalmazás bármely pontjáról elérhet ek: <Application x:Class="JegyzetWPF.Resources> <LinearGradientBrush x:Key="buttoncolor"> <GradientStop Color="Black" Offset="0.Background = (Brush)this.doksi. A FindResource biztonságosabb változata a TryFindResource.App" xmlns="http://schemas.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Window.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. } Persze a megfelel objektumon kell meghívnunk a FindResource metódust. tehát az el tte lév példával nem fog m ködni. a fenti esetben a this a legfels szint elemre a Window –ra mutat. ha az er forrás nem található. ezt egy hagyományos XAML file –ba helyezhetjük (projecten jobb klikk.microsoft.microsoft.0" /> <GradientStop Color="Red" Offset="0.Resources["buttoncolor"].7" /> </LinearGradientBrush> </Application.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.xaml"> <Application.7" /> </LinearGradientBrush> </Window.

ezt az App.microsoft.MergedDictionaries> </ResourceDictionary> </Application. csak írjuk a MergedDictionaries alá.microsoft. . Ahhoz.MergedDictionaries> <ResourceDictionary Source="AppRes.microsoft.7" /> </LinearGradientBrush> </ResourceDictionary> Ezt a file -t nevezzük el mondjuk AppRes.xaml" /> </ResourceDictionary.xaml"> <Application.Resources> </Application> Természetesen bármennyi forrást felvehetünk.Resources> <ResourceDictionary> <ResourceDictionary.microsoft.xaml –ben kell megtennünk: <Application x:Class="JegyzetWPF.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.0" /> <GradientStop Color="Red" Offset="0.hu 259 <ResourceDictionary xmlns="http://schemas.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. hogy a tartalmát használni tudjuk be kell olvasztanunk az alkalmazásunkba. azt is megtehetjük.App" xmlns="http://schemas.xaml –nek.Forrás: http://www.com/winfx/2006/xaml" StartupUri="Window1.com/winfx/2006/xaml"> <LinearGradientBrush x:Key="buttoncolor"> <GradientStop Color="Black" Offset="0.doksi. illetve ha magunk akarunk itt er forrásokat felvenni.

mégpedig az. vagyis megadott tulajdonságokhoz rendelünk értékeket. amely még er teljesebbé teszi a WPF stílusait. A koncepció nagyon hasonló a CSS –éhez.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <Window. hogy egy elemhez csak egyetlen stílust rendelhetünk hozzá.doksi.microsoft.Resources> <Style x:Key="labelfontstyle"> <Setter Property="Control.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.Style = (Style)this. Kezdjük rögtön egy példával: <Window x:Class="JegyzetTestWPF. Azt sem árt tudni.hu 260 46.microsoft. A Style tulajdonságot minden vezérl a FrameworkElement osztálytól örökli.Forrás: http://www. hogy explicit módon megadjuk a vezérl adott tulajdonságának az értékét. Látható.Resources> <Grid> <Label Style="{StaticResource labelfontstyle}" Content="Hello Style!" /> </Grid> </Window> Az eredmény: A kód önmagáért beszél. hogy egy stílus bármely értékét felülbírálhatjuk. tehát több stílus összeollózása nem fog m ködni – legalábbis így nem.Window1" xmlns="http://schemas. típusa pedig .FindResource("labelfontstyle"). Vegyük például a fent létrehozott stílust és rendeljük hozzá egy Label vezérl höz: . Van azonban egy dolog.mint az a fenti példában is létszik – Style lesz. azzal. velük is megismerkedünk ebben a fejezetben.FontFamily" Value="Arial Black" /> <Setter Property="Control. hogy a segítségükkel bármely dependency property –t beállíthatjuk – de csakis azokat. Ebb l a példából az is kiderül. Stílusok Az el z fejezetben bemutatott er források egyik leggyakrabban használt területe a stílusok. hogy a stílusokat az er forrásokhoz hasonlóan kezelhetjük (lévén maga is egy er forrás.FontSize" Value="42" /> </Style> </Window. Stílust nem csak XAML –b l rendelhetünk egy elemhez: label1. A stílusok témaköréhez tartoznak a triggerek és a sablonok is. csak van saját neve).

Foreground" Value="Red" /> </Style> Ezt alkalmazva az eredmény: <Label Style="{StaticResource style2}" Content="Hello Style!" /> Eddig a pontig az általános Control tulajdonságokra hivatkoztunk. Gyakran el fordul. így lehet vé téve több stílus összeolvasztását: <Style x:Key="style1"> <Setter Property="Control.75" /> </RadialGradientBrush> </Setter.Click" Handler="Button_Click" /> </Style> A BasedOn tulajdonság beállításával megadhatunk egy másik stílust. de eseményeket is hozzárendelhetünk egy vezérl höz az EventSetter objektumokkal: <Style x:Key="eventstyle"> <EventSetter Event="ButtonBase.Value> <RadialGradientBrush> <GradientStop Color="Red" Offset="0. amely felülbírálja a stílust.FontFamily" Value="Arial Black" /> <Setter Property="Control. amelynek értékeit az új stílus is használja.Value> </Setter> </Style> Nem csak tulajdonságokat.Background"> <Setter.Forrás: http://www.0" /> <GradientStop Color="Black" Offset="0. hogy egy tulajdonság értékét nem tudjuk egy egyszer értékkel megadni. mivel beállítottuk a bet méretet. a TargetType tulajdonság segítségével viszont egy adott vezérl típusra érévnyesíthetjük a stílust: . ekkor a következ t tehetjük: <Style x:Key="backgroundstyle"> <Setter Property="Control.FontSize" Value="42" /> </Style> <Style x:Key="style2" BasedOn="{StaticResource style1}"> <Setter Property="Control.hu 261 <Label Style="{StaticResource labelfontstyle}" FontSize="10" Content="Hello Style!" /> Ebben az esetben csakis a bet típust fogja a stílus meghatározni.doksi.

46. a többiek az adatkötések témaköréhez tartoznak: Trigger: a legegyszer bb. amelyek segítségével beállíthatjuk. amikor az értéke igaz lesz: <Style x:Key="triggerstyle"> <Style.Triggers> . Amennyiben megadunk a vezérl nek egy másik stílust. hogy azt az elem definíciójánál külön jeleznénk. amelyek mindegyikének teljesülnie kell. MultiTrigger: hasonló az el z höz. egy dependency property –t figyel és aszerint változtatja meg a stílust. hogy eltávoltunk minden stílust egy vezérl r l: <Label Style="{x:Null}" Content="Hello Style!" /> 46. Ebben az esetben a stílust definiáló elem összes megfelel típusú gyermekelemére vonatkozni fognak a beállítások: <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 példában a Label vezérl – bár ezt explicite nem jeleztük rendelkezni fog a stílus beállításaival (ugyanígy az összes többi Label objektum is). hogy hogyan reagáljon egy vezérl egy esemény bekövetkeztével. ami az IsMouseOver tulajdonságot figyeli és akkor aktiválódik.1 Triggerek Egy stílus rendelkezhet triggerekkel. Ahogyan azt a WPF –t l megszokhattuk.1 Trigger Készítsünk egy egyszer trigger –t.doksi. anélkül. Ebb l a felsorolásból kimaradt a DataTrigger és a MultiDataTrigger.Forrás: http://www.Triggers tulajdonságon kereszt l. de több feltételt is megadhatunk. velük egy késöbbi fejezetben lehet majd találkozni.hu 262 <Style x:Key="typestyle" TargetType="Label" > <Setter Property="FontFamily" Value="Arial Black" /> <Setter Property="FontSize" Value="42" /> </Style> Egy stílust hozzárendelhetünk egy adott típushoz. az felülbírálja a „globálisat”. a hozzárendelt tulajdonságok csakis dependency property –k lehetnek. vagy egy tulajdonság megváltozásakor. ezek közül most hárommal fogunk megismerkedni. Ötféle trigger létezik. Azt is megtehetjük. a megadott esemény hatására lép akcióba. EventTrigger: a fent már említett esemény trigger. Trigger –t köthetünk közvetlenül egy vezérl höz is a FrameworkElement.1. de ekkor csak és kizárólag esemény trigger –t használhatunk.

de ha lekerül róla a fókusz visszatér a rendszerértékhez.hu 263 <Trigger Property="Control.Foreground" Value="Red" /> </MultiTrigger. Természetesen egy stílus több trigger –t és egy trigger több Setter –t is tartalmazhat.3 EventTrigger Az EventTrigger eseményekre reagál.Triggers> </Style> Rendeljük hozzá a stílust egy Label –hez: <Label Style="{StaticResource triggerstyle}" Content="Hello Style!" /> Ezután ha a Label fölé visszük az egeret.IsMouseOver" Value="true"> <Setter Property="Control.Forrás: http://www.Triggers> <EventTrigger RoutedEvent="MouseEnter"> .1. Nem marad viszont úgy. azaz minden feltételnek teljesülnie kell ahhoz. most csak egy egyszer példa következik): <Style TargetType="{x:Type Button}"> <Style.Triggers> </Style> Ebben a példában az aktív és fókuszban lév legyen ez egy TextBox: vezérl ket célozzuk meg.Conditions> <MultiTrigger.1. akkor a bet méret megn .Setters> <Setter Property="Control.Conditions> <Condition Property="Control.2 MultiTrigger A MultiTrigger segítségével összetettebb feltételeket is felállíthatunk. akkor a szöveg pirosra vált. amíg az aktiválását kiváltó tulajdonság a megfelel értéken áll. mondjuk <TextBox Style="{StaticResource multitriggerstyle}" Width="100" Height="20" /> Amikor gépelni kezdünk a TextBox –ba. 46. 46.Triggers> <MultiTrigger> <MultiTrigger. Ezek között a feltételek közt ÉS kapcsolat van. a Trigger által definiált viselkedés csakis addig tart. hogy a trigger aktiválódjon.IsFocused" Value="true" /> </MultiTrigger.Setters> </MultiTrigger> </Style. de eltér en társaitól egy animációt fog elindítani (ez a témakör egy következ fejezet tárgya lesz.IsEnabled" Value="true" /> <Condition Property="Control.doksi.FontSize" Value="42" /> </Trigger> </Style. <Style x:Key="multitriggerstyle"> <Style.

Triggers> </Style> A példában egy egyszer animációt definiáltunk.hu 264 <EventTrigger.doksi. .Actions> <BeginStoryboard> <Storyboard> <ColorAnimation Storyboard.(SolidColorBrush. amely akkor kezd dik.Background).Forrás: http://www.TargetProperty="(Button.Color)" From="White" To="Black" Duration="0:0:5" /> </Storyboard> </BeginStoryboard> </EventTrigger. amikor az egeret egy gomb fölé visszük és egy fehérb l feketébe tartó színátmenetet eredményez.Actions> </EventTrigger> </Style.

Indítsuk el és készítsünk egy egyszer ListBox vezérl t egy ListBoxItem –mel. egy ilyen akció kivitelezéséhez User Control –t kellet gyártanunk és rengeteg munkával átírnunk a renderelésért felel s metódusokat. Amikor Windows Forms –szal dolgoztunk. stb… Most már könny kitalálni. úgy. akár egy teljesen eltér elemet is készíthetünk). Fölül találunk egy ikont. sablontípussal (DataTemplate és Sablonok segítségével gyakorlatilag bármilyen változást eszközölhetünk egy vezérl kinézetén. Egy példán kereszt l világosabb lesz a dolog egy kissé.doksi. Sablonok Ez a fejezet a vezérl k sablonjaival foglalkozik. Kapcsoljuk be és nézzük meg az eredményt: Amit látunk az nem más. A WPF – felépítéséb l adódóan – változtat ezen. amely a Windows SDK –val érkezik (általában beépül a Start menübe).hu 265 47. mint a ListBox vezérl alapértelmezett sablonja. egy Grid –et egy Rectangle –t. Tartalmaz pl. Egy korábbi fejezetben már megismerkedtünk a logikai. Segítségünkre lesz az XamlPad nev program. a WPF még két másik HierarchicalDataTemplate) rendelkezik.Forrás: http://www. ezért készítsünk egy a megszokottól eltér formájú gombot! Sablont az er forrásoknál megszokott módon a Resources „tulajdonságon” belül a ControlTemplate osztállyal tudunk definiálni: <ControlTemplate x:Key="circlebuttontemplate" TargetType="{x:Type Button}"> <Grid Width="50" Height="50"> <Ellipse Fill="Red"> </Ellipse> <ContentPresenter VerticalAlignment="Center" . hogy miként tudunk új sablont készíteni: ezt a fát kell átdefiniálni.és vizuális fák intézményével. most ezt a tudásunkat fogjuk egy kicsit b víteni. róluk egy másik fejezetben esik szó majd. amely megmutatja a vizuális fát. hogy az semmit sem veszítsen funkcionalitásából (s t.

doksi. Ez egyrészt konvenció másrészt elengedhetetlen a helyes m ködéshez.Triggers> <Trigger Property="IsMouseOver" Value="true"> <Setter TargetName="back" Property="Fill" Value="Black" /> <Setter TargetName="main" Property="TextElement. akkor azt látni is fogjuk. mint megoldani ezt a problémát. A TemplateBinding segítségével ki tudjuk nyerni a „sablonozott” vezérl tulajdonságainak értékét jelen esetben ez a Content lesz. Mi sem könnyebb. de nem fog látszani.Foreground" Value="White" /> </Trigger> </ControlTemplate. fogja tárolni a „tartalmazott” adatokat. amely rendesen le is fut majd. Egyébként pont ugyanúgy m ködik mint egy mezei gomb. Ugyanígy létrehozhatjuk a többi esemény stílusát is. Ez utóbbihoz szükséges a ContentPresenter osztály is amely a sablonunk alján helyezkedik el. hogy rákattintottunk. rendelhetünk hozzá pl. legalábbis vizuálisan nem. Trigger –eket fogunk használni: <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. Click eseménykezel t. mivel a ContentControl leszármazottak – amilyen a Button is – enélkül nem jelenítenék meg a tartalmukat.Triggers> </ControlTemplate> Mostantól ha az egér a gombunk fölké kerül. . A TemplateBinding m ködésér l az adatkötésekr l szóló fejezet többet is mesél majd. hogy ez a sablon gombok számára készült. A gombunk egyel re nem csinál túl sokat.Forrás: http://www.hu 266 HorizontalAlignment="Center" Content="{TemplateBinding Content}" /> </Grid> </ControlTemplate> <Button Content="Hello" Template="{StaticResource circlebuttontemplate}" /> Az eredmény: A sablon definiciójának elején rögtön megmondtuk.

} Az els metódus a parancs végrehajtásáért felel. beilleszteni csak akkor. A forrás (command source). A cél (command target). a második a parancs végrehajthatóságát adaj vissza. hogy bármelyiket válasszuk is ki az ugyanazt a metódust fogja meghívni. parancs objektumokat. másolni csak akkor lehet ha van kijelölt szöveg. nézzük meg. Miel tt rátérünk a gyakorlatiasabb részre fontos megismernünk egy kicsit mélyebben a commanding m ködését. helyi menüb l. Minden egyes parancs objektum megvalósítja az ICommand interfészt amely így néz ki: public interface ICommand { void Execute(object parameter). hogy egy amúgy egyszer alkalmazás elkészítése rémálommá váljon. bool CanExecute(object parameter). míg a harmadik tag egy esemény. amin a parancs végrehajtja a feladatát (pl. a vezérl amely kiváltja az adott parancs végrehajtását. A kötés (command binding) vagyis egy parancsot hozzáköthetünk egy vagy több vezérl höz. amelyeket vezérl khöz köthetünk. hogy ezeket a funciókat többféleképpen is elérhetjük: menüb l. Szerencsére a WPF erre is ad választ a command model bevezetésével. ezért ennek megfelel en változtatni kell a felület elemeinek állapotát is.hu 267 48. a példánkban ez lehet egy RichTextBox. nincs beépített lehet ség a parancsok „követésére” és ezért vissza sem tudjuk vonni azokat. hiszen nem mindegyik funkció érhet el mindig (pl. Ezen a ponton már belefutunk abba a problémába. Commanding Képzeljük el a következ helyzetet: egy szövegszerkeszt programot fejlesztünk és éppen a kivágás/másolás/beillesztés funkciókat próbáljuk megvalósítani. amibe beillesztünk). A command model négy f szerepl vel bír. ez reprezentálja a valódi kódot a parancs mögött és számontartja annak aktuális állapotát (aktív/passzív). hogy ideje cselekedni (ez lesz a valódi kód). stb…).doksi. Mit is takar ez? Definiálhatunk ún. hogy írnunk kell egy rakás eseménykezel t. ha van valami a vágólapon. A hasonló programok esetében megszokhattuk. ezek a következ ek: Maga a parancs (command). Ennek a megoldásnak is vannak hiányosságai. billenty kombinációval. amely a parancs állapotának megváltozásakor aktivizálódik. event EventHandler CanExecuteChanged. ami újabb eseménykezel k készítését jelenti. stb… Az nyílvánvaló. A .Forrás: http://www. Ez persze csak egy leegyszer sített magyarázat. amelyek állapota a parancs elérhet ségét l függ en változik. És ez még nem minden. hogy mi történik valójában: az Execute metódus meghívásakor egy esemény váltódik ki. amely jelzi az alkalmazás egy másik részének. pl. hiszen a felhasználói felületet is módosítanunk kell. Ennyi probléma épp elég ahhoz. amelyek mind ugyanazt csinálják.

ami kiváltja ezt a parancsot. amely a parancs vizuális reprezentációja lesz. amely megvalósítja az ICommandSource interfészt. Ezenkív l a RoutedCommand valósítja meg a tényleges parancskezelést. Ezek a parancsok öt csoportba oszthatóak: ApplicationCommands: a legáltalánosabb parancsok tartoznak ide. hogy a gyakorlatban is felhasználjuk tudásunkat. Miért van ez így? Az osztály nevének el tagja megadja a választ: azért. hogy minden parancs megvalósítja az ICommand interfészt.doksi. EditingCommands: a dokumentumok kezelésével kapcsolatos parancsok. vele részletesebben meg fogunk ismerkedni. hiszen ha a felületen történik valami akkor az összes parancsot végig kell ellen rizni. a másolás menüpont ne legyen aktív). vagyis nem tud arról. Ez bármelyik vezérl lehet. hanem az leszármazottjából a RoutedUICommand –ból.Forrás: http://www. hogy t most WPF alatt szeretnénk használni. most kell egy forrás. . amikor saját parancsot készítünk. amelyek eltakarják azokat.) is. de ez már túlmutat a jegyzeten. A WPF jónéhány el re definiált paranccsal rendelkezik szám szerint 144 –gyel. stb… MediaCommands: multimédiás tartalmakat irányítanak ezek a parancsok (lejátszás. amit a paranccsal küldünk. stb. egy ListBox elemei vagy egy menüben a menüpontok. kivágás. Az EditingCommands csoportot fogjuk ehhez felhasználni. az összes ButtonBase leszármazott. NavigationCommands: a navigációért felel s parancsok gy jteménye. a vágólaphoz kapcsolódóak (másolás.hu 268 CanExecuteChanged pedig jelzi az adott parancshoz kötött összes vezérl nek. mozgás a szövegben.: kijelölés. akkor csak egy helyen kell a lokalizációról gondoskodnunk. Ez azért jó. mert ha többnyelv megvalósítást szeretnénk. CommandParameter: az esetleges paraméter. Eljött az ideje. ComponentCommands: ezek a parancsok nagyon hasonlóak az EditingComands csoporthoz. beillesztés. Az el bb azt mondtuk. pl. Ez az interfész három tulajdonságot definiál: Command: ez az alkalmazandó parancsra mutat. szünet. Ilyen pl. azzal a különbséggel. tehát ez fog megjelenni pl. A legtöbb parancs mégsem ebb l az osztályból származik. pl. Ez az osztály egy extra Text tulajdonsággal rendelkezik.). Az ICommand egy „platformfüggetlen” megoldás. hogy meg kell hívniuk a CanExecute metódust és ennek megfelel en módosítani magukat (pl. Ez persze finomítható. parancsunk már van. Valójában a WPF egyetlen olyan osztállyal rendelkezik amely direkt módon megvalósítja ezt az interfészt (és ide értjük a saját magunk által készített parancsokat is) ez pedig a RoutedCommand. egy menüben. de ez nem teljesen igaz. szövegformázás. hogy k a felhasználói felületen fejtik ki a hatásukat. velük egy külön fejezetben fogunk foglalkozni. stb. a parancsoknak és az ket kiváltó vezérl kek viszont tudniuk kell kommunikálni egymással. Ez a m ködés a Commanding egy nagy hátránya is egyben. hogy támogatni tudja a routed event –eket. Ez az osztály csal egy kicsit ugyanis az ICommand metódusait privátként implementálja és saját metódusokat készit. Tehát.

A gomb Command tulajdonságánál névterest l adtuk meg a parancs nevét.microsoft. A parancsok végrehajtásához rendelhetünk eseménykezel ket.CommandBindings> <Grid> <Button Command="ApplicationCommands. másrészt elkerüljük a névütközéseket (ugyanakkor nem kötelez így használni). ez egyrészt az átláthatóságot szolgálja. A Visual Studio 2008 XAML szerkeszt je sajnos nem ad IntelliSense kiegészítést ehhez a feladathoz. ami hasznos lehet pl. CommandBinding –et fogunk használni.Close" Executed="CommandBinding_Executed" /> </Window.RowDefinitions> <RowDefinition Height="200" /> <RowDefinition Height="*" /> </Grid.com/winfx/2006/xaml" Title="Window1" Height="300" Width="400" Loaded="Window_Loaded" > <Grid> <Grid.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.hu 269 CommandTarget: az az elem. hogy melyik parancs legyen elérhet .CommandBindings> <CommandBinding Command="ApplicationCommands. Nézzük a következ XAML –t: <Window x:Class="JegyzetWPF.doksi.com/winfx/2006/xaml" Title="Window1" Height="150" Width="150" Loaded="Window_Loaded" > <Window. a szövegszerkeszt programunkban. amelyet a következ képpen definiálhatunk: <Window x:Class="JegyzetWPF. amikor meghatározzuk.Window1" xmlns="http://schemas.ToggleBold" CommandTarget="{Binding ElementName=rtbox1}"/> </Grid> </Window> Amikor rákattintunk a gombra akkor a RichTextBox –ban a bet típus félkövérre módosul. Ehhez ún.microsoft.microsoft.RowDefinitions> <RichTextBox Name="rtbox1" Width="300" Height="200" Grid.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. amelyen a parancs kifejti hatását.Row="1" Command="EditingCommands.Window1" xmlns="http://schemas.Forrás: http://www.Row="0" /> <Button Name="boldbutton" Width="100" Height="30" Content="Félkövér" Grid.Close" .

Paste" /> </Menu> <RichTextBox Width="250" Height="150" Grid. de a stratégiája tunneling. hogy mit csinál: <Button Command="ApplicationCommands. amikor a hozzárendelt vezérl meghívja a CanExecute metódust. PreviewExecuted: ugyanaz mint az el z .doksi. Már beszéltünk a RoutedUICommand Text tulajdonságáról.Forrás: http://www. Az eseménykezel k hozzárendelését csakis egyzer kell megtennünk. Path=Command.RowDefinitions> <Menu Grid. CanExecute: akkor aktivizálódik.Window1" xmlns="http://schemas. egy gombra. ha az adott parancsot hozzárendeltük legalább egy vezérl höz azután automatikusan meghívódnak ezek is. PreviewCanExecute: ugyanaz mint az el z de a stratégiája tunneling. vagyis a forrástól indul.Row="1"/> </Grid> </Window> . Nézzünk meg egy másik példát: <Window x:Class="JegyzetWPF.Text}" Width="100" Height="30" /> Els re egy kicsit ijeszt lehet ez a megoldás és most nem is foglalkozunk vele majd csak az adatkötésekr l szóló fejezetben. Ezt felhasználhatjuk a vezérl inken. hogy „ráírjuk” pl.microsoft.hu 270 Content="Close" Width="100" Height="30" /> </Grid> </Window> Négy eseményt kezelhetünk: Executed: a parancs végrehajtásakor aktivizálódik.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.com/winfx/2006/xaml" Title="Window1" Height="250" Width="300" Loaded="Window_Loaded" > <Grid> <Grid.RowDefinitions> <RowDefinition Height="30" /> <RowDefinition Height="*" /> </Grid.microsoft. így azzal sem kell törödnünk.Row="0"> <MenuItem Command="ApplicationCommands.Cut" /> <MenuItem Command="ApplicationCommands. a stratégiája bubbling.Close" Content="{Binding RelativeSource={RelativeSource Self}. a stratégiája bubbling.

a mi esetünkben ezek a szöveges vezérl k (RichTextBox és TextBox). hogy ez csakis a ToolBar és Menu vezérl kre vonatkozik.Forrás: http://www.doksi. Ha elindítjuk a programot minden m ködni is fog: Parancsok készítésével egy kés bbi fejezetben foglalkozunk. . hogy bizonyos vezérl k automatikusan tudják használni a RoutedUICommand Text tulajdonságát. mégpedig az. az egyik. A második eset egy kicsit bonyolultabb: bizonyos parancscsoportok egy adott vezérl csoporthoz köthet ek. az csoportja által használható vezérl höz lesz kötve. hogy a menüpontoknál nem adtunk meg szöveget.hu 271 Két dolog is felt nhet. hogy látszólag nem kötöttük rá a parancsot a RichTextBox –ra. Ha mást nem adunk meg. pl. akkor a parancs mindig az éppen fókuszban lév . minden más esetben explicit meg kell adnunk a kötést. Az els esetben azt használtuk ki. Van még egy kikötés. a másik.

míg transzformációk alkalmazása esetén alacsonyabb szintre visszük a dolgot és a koordinátarendszerben elfoglalt pozíciót módosítjuk.hu 272 49.Media. stb…) manipulálásával dolgozik.1 Transzformációk Minden transzformációs osztály a System.Windows. 0] OffsetY. 1 A harmadik oszlop – mivel a WPF csak lineáris transzformációkat használ – állandó lesz. Ilyen lehet ség a vezérl ink „életrekeltése” is. hogy a WPF mer en új grafikus rendszerrel rendelkezik.Transform osztályból származik. 49.: helyzet. A transzformációs mátrix segítségével kiszámolhatjuk az alakzat új koordinátáit a következ képletekkel (X és Y a régi koordinátákat jelölik X’ és Y’ pedig az újakat): X’ = X * M11 + Y * M21 + OffsetX Y’ = X * M12 + Y * M22 + OffsetY Nézzünk egy gyakorlati példát! Legyen a koordinátarendszer a következ : . M12. A maradék négy változó a szándékunktól függ en kap szerepet. Ennek az osztálynak hat leszármazottja van.doksi. Animációk és transzformációk Már megtanultuk. amelyek megvalósítása eddig embertpróbáló feladat vagy éppen lehetetlen volt. ennek egyik hatalmas el nye. az OffsetX és OffsetY tagok pedig az X illetve Y tengelyen való eltolás mértékét jelzik. Ebben a részben csakis 2D –s transzformációkat és animációkat nézünk meg. szélesség. Ennek gyakorlatilag csak a MatrixTransform esetén lesz szerepe. OffsetX. animációk és transzformációk képében. ebb l négy el re definiált viselkedést hordoz. hogy olyan tulajdonságokkal vértezhetjük fel az alkalmazásainkat.Forrás: http://www. 0 M22. transzformációs mátrixxal: M11. El bbi a vezérl k tulajdonságainak (pl. A következ fejezetekben mindegyikükkel megismerkedünk. A transzformációs mátrix kiindulási helyzete a következ : 100 [010] 001 Tehát az egységmátrix. egy lehet vé teszi tetsz leges transzformáció létrehozását míg az utolsó több transzformációból állhat. [ M21. A WPF rögtön két módszert is felkínál. El tte viszont megnézünk egy kis elméletet is (bár ez nem feltétele a megértésnek): Minden transzformáció leírható egy ún.

Az új koordináták ábrázolva (zölddel): 49. (1. A transzformációs mátrix igen egyszer lesz.hu 273 A pirossal jelölt háromszög csúcsainak a koordinátái: (1. Vagyis egy egységgel eltoltuk a háromszöget az Y tengelyen pozitív irányba. Vegyük a következ példát: .2).doksi. Természetesen ugyanezt megtehettük volna sokkal egyszer bben is az OffsetY változó állításával. mindössze az M12 változót írtuk át 1 –re: 110 [010] 001 Most alkalmazzuk a képletet és számítsuk ki a három új koordinátát: X’1 = 1 * 1 + 1 * 0 + 0 = 1 Y’1 = 1 * 1 + 1 * 1 + 0 = 2 X’2 = 1 * 1 + 2 * 0 + 0 = 1 Y’2 = 1 * 1 + 2 * 1 + 0 = 3 X’3 = 2 * 1 + 1 * 0 + 0 = 2 Y’3 = 2 * 1 + 1 * 1 + 0 = 3 Az új koordináták így: (1.2) és (2.1 MatrixTransform Az új ismereteinknek megfelel en az els vizsgált alany a MatrixTransform lesz.3) és (2.1). Gyakorlatilag az esetek kilencvenkilenc százalékában nem lesz szükségünk erre a transzformációra.3).1). amikor szükségünk van a transzformációs mátrixra. (1. a többiek ezt a részletet elrejtik el lünk.Forrás: http://www. mivel ez az egyetlen olyan WPF transzformáció.1.

75 50. hogy miért van ez így: .doksi.50 50.hu 274 <Polygon Points="100.Fill> <SolidColorBrush Color="Red"/> </Polygon.Fill> <Polygon.RenderTransform> <MatrixTransform Matrix="1 0 0 1 0 0" /> </Polygon. lehet vele kísérletezni.50" Stroke="Black" StrokeThickness="2"> <Polygon.Forrás: http://www.RenderTransform> </Polygon> Létrehoztunk egy Polygon objektumot. hogy a fönti példa eredménye egy háromszög lesz. egyel re így is jó lesz. így hatása nincsen. mégpedig egy ilyen háromszög: Már meg is adtuk a transzformációs mátrixot.95 100. amely épp alaphelyzetben – az egységmátrixon – áll. ebben a sorrendben köti össze ket a WPF. a Points tulajdonságnál megadott számpárok a poligon pontjait jelzik. A képlet segítségével elég könny rájönni. A transzformációk megadásának késöbb megtanuljuk praktikusabb módjait is. nemcsak eltoltuk az X tengelyen pozitív irányba. de a kétszeresére is n tt a síkidom. Például legyen a mátrix a következ : <MatrixTransform Matrix="2 0 0 1 0 0" /> Az eredmény egészen drasztikus. Viszonylag könny kitalálni.

Forrás: http://www.microsoft.ColumnDefinitions> <ColumnDefinition Width="150" /> <ColumnDefinition Width="*" /> </Grid. A kész alkalmazás így néz ki: Az XAML kód: <Window x:Class="JegyzetWPF.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.Window1" xmlns="http://schemas.doksi.hu 275 Most készítsünk egy egyszer programot.com/winfx/2006/xaml" Title="Window1" Height="300" Width="500"> <Grid> <Grid.Column="0" Margin="5"> <WrapPanel> <TextBlock Text="M11: " /> <TextBox Name="m11" Width="30">0</TextBox> . amely segítségével egyszer en állíthatjuk a mátrix értékeit.microsoft.ColumnDefinitions> <StackPanel Background="Cornsilk" Height="150" VerticalAlignment="Top" Grid.

m.RenderTransform> <MatrixTransform x:Name="polytransform" /> </Polygon.Parse(m22.Column="1"> <Polygon Points="100.Text).95 100.Parse(offsetx.hu 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. m.Parse(m11.doksi. m.M11 = double.Text).Fill> <Polygon.RenderTransform> </Polygon> </Canvas> </Grid> </Window> Végül a gombhoz tartozó eseménykezel : private void applybutton_Click(object sender.Text).Text).Parse(offsety. m.Forrás: http://www. m.Fill> <SolidColorBrush Color="Red"/> </Polygon.50 50. RoutedEventArgs e) { Matrix m = new Matrix().Parse(m21.Text).OffsetY = double.M21 = double.M22 = double.Parse(m12.75 50.Text).OffsetX = double. . m.50" Stroke="Black" StrokeThickness="2"> <Polygon.M12 = double.

hu 277 polytransform. Az értékeket megváltoztatva elérhetjük a WPF összes lehetséges transzformációját.doksi. csak a poligon definíciót cseréltük le: <Button Content="Ez egy gomb" Width="100" Height="100"> <Button.RenderTransform> <MatrixTransform x:Name="polytransform" /> </Button.: Rectangle) illetve FrameworkElement leszármazottat.RenderTransform> </Button> 49. hogy a WPF adja nekünk a megfelel mátrix adatszerkezetet. ami a példánkban egy Rectangle lesz: .1. Természetesen nem csak az el re megadott poligont használhatjuk. } Látható.Forrás: http://www. hanem bármely Shape (pl.Matrix = m. Ez utóbbira egy példa: Az XAML nagyon egyszer .2 RotateTransform Ezzel a transzformációval egy adott pont körül adott szögben el tudjuk forgatni a megcélzott objektumot.

RenderTransform> <RotateTransform Angle="{Binding ElementName=angleslider.microsoft. A bal fels sarok koordinátája ekkor (0.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Grid> <Grid. amelyen alkalmazzuk a transzformációt.RowDefinitions> <RowDefinition Height="240" /> <RowDefinition Height="*" /> </Grid.Row="1" /> </Grid> </Window> Most csakis a szöget adtuk meg.Row="0"> <Rectangle.RenderTransform> </Rectangle> <Slider Name="angleslider" Minimum="0" Maximum="360" Grid.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.RowDefinitions> <Rectangle Width="100" Height="100" Fill="Red" Grid.0) lesz. Path=Value}" /> </Rectangle.doksi.Forrás: http://www.hu 278 Nézzük meg a hozzá tartozó XAML –t is: <Window x:Class="JegyzetWPF. Ilyenkor automatikusan az objektum bal fels sarkát tekinti kiindulópontnak.microsoft. vagyis az objektumon belüli koordinátára gondolunk. a forgatás középpontját – tehát azt a pontot ami körül forog – nem.Window1" xmlns="http://schemas. Mivel most tudjuk a négyzet méreteit nem nehéz kitalálni a valódi középpontot: . Adjuk meg ezeket az értékeket is! A középpont koordinátái értelemszer en arra az objektumra értend ek.

Forrás: http://www.3 TranslateTransform Ezzel a transzformációval a transzformációs mátrix OffsetX és OffsetY tagjait tarthatjuk kézben. amelyek hatnak az objektumra: X és Y.Row="0"> <Rectangle. Ennek megfelel en két olyan tulajdonsággal rendelkezik. Nézzük a példát: Az XAML: .RenderTransform> </Rectangle> Az eredmény: 49.RenderTransform> <RotateTransform Angle="{Binding ElementName=angleslider.doksi.hu 279 <Rectangle Width="100" Height="100" Fill="Red" Grid.1. Path=Value}" CenterX="50" CenterY="50" /> </Rectangle.

RenderTransform> </Rectangle> </Canvas> <Slider Name="xslider" Background="Aqua" Minimum="0" Maximum="100" Height="20" Width="130" Grid.ColumnDefinitions> <ColumnDefinition Width="40" /> <ColumnDefinition Width="130" /> </Grid.com/winfx/2006/xaml" Title="Window1" Height="200" Width="180"> <Grid> <Grid.Column="1" /> <Slider Name="yslider" Background="Aqua" IsDirectionReversed="True" Orientation="Vertical" Width="20" Height="130" Minimum="0" Maximum="100" Grid.hu 280 <Window x:Class="JegyzetWPF. Path=Value}" Y="{Binding ElementName=yslider.Left="0"> <Rectangle.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.RowDefinitions> <RowDefinition Height="130" /> <RowDefinition Height="40" /> </Grid.doksi.Row="1" Grid.microsoft.4 ScaleTransform . Path=Value}" /> </Rectangle.ColumnDefinitions> <Canvas Background="SlateGray" Grid.Top="0" Canvas.Forrás: http://www.RenderTransform> <TranslateTransform X="{Binding ElementName=xslider.Window1" xmlns="http://schemas.Row="0" Grid.RowDefinitions> <Grid.Column="1"> <Rectangle Width="30" Height="30" Fill="Red" Canvas.1.microsoft.Column="0" /> </Grid> </Window> 49.Row="0" Grid.

Top="45" Canvas.RenderTransform> <ScaleTransform ScaleX="{Binding ElementName=xslider. A tulajdonságok értékei szorzóként funkciónálnak.Forrás: http://www.RowDefinitions> <Grid.com/winfx/2006/xaml" Title="Window1" Height="200" Width="180"> <Grid> <Grid. Path=Value}" CenterX="15" CenterY="15" /> </Rectangle.Window1" xmlns="http://schemas.microsoft. ezeket a ScaleX és ScaleY tulajdonságain keresztül állíthatjuk.doksi.RenderTransform> </Rectangle> </Canvas> <Slider Name="xslider" Background="Aqua" Minimum="1" Maximum="10" Height="20" Width="130" . Ezenkívül megadhatjuk a transzformáció középponttját is a CenterX és CenterY tulajdonságokkal.microsoft.Row="0" Grid.Column="1"> <Rectangle Width="30" Height="30" Fill="Red" Canvas.ColumnDefinitions> <Canvas Background="SlateGray" Grid.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. így alapértelmezett értékük 1 lesz.Left="45"> <Rectangle.RowDefinitions> <RowDefinition Height="130" /> <RowDefinition Height="40" /> </Grid.hu 281 A ScaleTransform az X illetve Y tengelyeken való nyújtásért felel. Path=Value}" ScaleY="{Binding ElementName=yslider.ColumnDefinitions> <ColumnDefinition Width="40" /> <ColumnDefinition Width="130" /> </Grid. A példa: <Window x:Class="JegyzetWPF.

Row="0" Grid.Window1" xmlns="http://schemas.RowDefinitions> <RowDefinition Height="130" /> <RowDefinition Height="40" /> </Grid.Column="1" /> <Slider Name="yslider" Background="Aqua" IsDirectionReversed="True" Orientation="Vertical" Width="20" Height="130" Minimum="1" Maximum="10" Grid. Az XAML: <Window x:Class="JegyzetWPF.com/winfx/2006/xaml" Title="Window1" Height="200" Width="180"> <Grid> <Grid.ColumnDefinitions> <Canvas Background="SlateGray" Grid.RowDefinitions> <Grid.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.1.ColumnDefinitions> <ColumnDefinition Width="40" /> <ColumnDefinition Width="130" /> </Grid.Row="1" Grid.hu 282 Grid.Row="0" Grid.microsoft.doksi.5 SkewTransform Ez a transzformáció objektumok „elhajlítására” szolgál: Látható.Column="1"> <Rectangle Width="30" Height="30" .Column="0" /> </Grid> </Window> 49.Forrás: http://www. hogy ez egyfajta 3D imitáció is lehet.

egyszerre alkalmazzuk a Rotate.Row="0" Grid.Column="0" /> </Grid> </Window> 49.és TranslateTransformation –t: . A következ példában pl.hu 283 Fill="Red" Canvas.6 TransformGroup Ennek az osztálynak a segítségével több transzformációt tudunk összefogni.Top="45" Canvas.Forrás: http://www.Left="45"> <Rectangle.RenderTransform> <SkewTransform AngleX="{Binding ElementName=xslider.RenderTransform> </Rectangle> </Canvas> <Slider Name="xslider" Background="Aqua" Minimum="0" Maximum="180" Height="20" Width="130" Grid. Path=Value}" CenterX="15" CenterY="15" /> </Rectangle.1. Path=Value}" AngleY="{Binding ElementName=yslider.Row="1" Grid.doksi.Column="1" /> <Slider Name="yslider" Background="Aqua" IsDirectionReversed="True" Orientation="Vertical" Width="20" Height="130" Minimum="0" Maximum="180" Grid.

hu 284 Az XAML: <Window x:Class="JegyzetWPF. Path=Value}" /> <TranslateTransform X="{Binding ElementName=xslider.Row="1" /> </Grid> </Window> 49. Path=Value}" /> </TransformGroup> </Rectangle.Window1" xmlns="http://schemas.microsoft.RenderTransform> <TransformGroup> <RotateTransform CenterX="15" CenterY="15" Angle="{Binding ElementName=xslider.Left="0"> <Rectangle. Path=Value}" /> </TransformGroup> </Window.RowDefinitions> <Canvas Background="SlateGray" Grid.7 Transzformáció mint er forrás Természetesen egy transzformációt nem kell helyben kifejtenünk.Forrás: http://www. Készítsük el az el zö program er forrást használó változatát! Sok dolgunk igazából nincsen.Row="0"> <Rectangle Width="30" Height="30" Fill="Red" Canvas.Top="70" Canvas.doksi.1. megadhatjuk er forrásként is így növelve a kódújrafelhasználást.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. mindössze két helyen kell változtatnunk: <Window. Path=Value}" /> <TranslateTransform X="{Binding ElementName=xslider.com/winfx/2006/xaml" Title="Window1" Height="260" Width="410"> <Grid> <Grid.RenderTransform> </Rectangle> </Canvas> <Slider Name="xslider" Background="Aqua" Minimum="0" Maximum="360" Height="20" Width="400" Grid.Resources> .RowDefinitions> <RowDefinition Height="200" /> <RowDefinition Height="*" /> </Grid.Resources> <TransformGroup x:Key="transformresource"> <RotateTransform CenterX="15" CenterY="15" Angle="{Binding ElementName=xslider.microsoft.

Ezt a típust from-to-by animációnak is nevezzük. Path-Based Animation: ebben az esetben az animálandó objektumot mozgásra bírjuk úgy.Top="70" Canvas. Szinte az összes vezérl eleget tesz a feltételeknek. - - Miel tt továbblépünk a gyakorlati dolgok felé. amelyek megfelelnek három feltételnek: Az animálandó tulajdonság egy dependency property Az osztály aminek a példányán az animációt alkalmazzuk megvalósítja az IAnimatable interfészt és a DependencyObject osztályból származik (ld. Hogy még pontosabbak legyünk csakis olyan objektumokat animálhatunk. hogy „alkalmas animációkra” van szükségünk. a dependency property értéke egy kezd . Mit is jelenthet ez? Mondjuk inkább úgy: kompatibilis.hu 285 /* … */ <Rectangle Width="30" Height="30" Fill="Red" Canvas.és végpont között fokozatosan változik. Fogjuk majd látni. szóval a legnagyobb akadályt a képzeletünk lehet. addig itt ez nincs megkötve. hogy a legtöbb beépített típushoz létezik a megfelel animáció. Említettük. Egész egyszer en arról van szó. Key Frame Animation: míg az el z típus megadott kezd .doksi. els pont) Kell legyen alkalmas animációtípus a tulajdonsághoz (err l mindjárt) Ezek a feltételek els ránézésre szigorúnak t nnek. Ilyen pl. hogy egy általunk meghatározott útvonalat kövessen.és végértékkel dolgozott. – ahogyan azt majd látni is fogjuk – a DoubleAnimation. de nem azok. még két osztályról kell megemlékeznünk.Left="0" RenderTransform="{StaticResource transformresource}" /> Ez a lehet ség egyúttal azt is jelenti.Forrás: http://www. Az utótag: AnimationUsingPath. hogy akár stílusokhoz is rendelhetünk transzformációt. Az ide tartozó animációk neve Animation utótagot kap. A WPF animációit három csoportra oszthatjuk és ennek megfelel en az elnevezési konvenciók is különböz ek lesznek: Linear Interpolation: a legegyszer bb mind közül. A Key Frame animációk utótagja: AnimationUsingKeyFrames.2 Animációk A WPF esetében az animációk tulajdonképpen tulajdonságok manipulációjával jönnek létre. 49. hogy az adott tulajdonság típusát kezelni tudú animációs objektumra van szükségünk. Minden egyes . azaz a dependency property értéke tetsz legesen megváltoztatható egy adott pillanatban. de ha ez nem elég mi magunk is készíthetünk ilyet (és fogunk is a fejezet végén). Az els minden animációk satyja a TimeLine.

amely az egérmutató Rectangle fölé érésekor szép lassan megváltoztatja az objektum színét. XAML kódban nem is definiálhatunk animációt StoryBoard nélkül.MouseEnter"> <BeginStoryboard> <Storyboard> <ColorAnimation Storyboard. hogy tudásunkat a gyakorlatban is kamatoztassuk. stb… A másik fontos osztály a StoryBoard. készítünk egy Rectangle objektumot és hozzákötünk egy animációt. ez képes több TimeLine kezelésére) leszármazottja.TargetName="brush" Storyboard. A ColorAnimation To tulajdonsága a célszínt fogja jelölni.microsoft.Fill> <Rectangle. hogy a neki beállított id szelet viselkedését felügyelje: meghatározza a hosszát. vagyis a fenti példában az animáció három másodperc alatt lesz kész. .Triggers> </Rectangle> </Grid> </Window> Akárcsak a transzformációk az animációk is tökéletesen használhatóak stílusokkal vagy sablonokkal (erre már láttunk is példát).Window1" xmlns="http://schemas. A következ példák az egyszer ség kedvéért csakis „helyi” animációkkal dolgoznak.1 From-to-by animációk Eljött az ideje.hu 286 animációtípus ebb l az osztályból származik. Ezenkív l a StoryBoard fogja szolgáltatni azokat a tulajdonságokat. egy TimeLine leszármazott (a ParallelTimeLines. Ez utóbbi a klasszikus óra-perc-másodperc formátumot követi.com/winfx/2006/xaml" Title="Window1" Height="200" Width="200"> <Grid> <Rectangle Width="150" Height="150"> <Rectangle.doksi. hányszor ismételjen. Bonyolultabb elmondani. amelyek az animáció céljának kijelölésére szolgálnak.Forrás: http://www.TargetProperty="Color" To="Black" Duration="0:0:3" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle. Ez az osztály közvetlenül kontrollálja a „gyermekanimációit”.Fill> <SolidColorBrush x:Name="brush" Color="Red" /> </Rectangle. A Storyboard.TargetType/Property tulajdonságok attached property –k. A szerepe az. mint megcsinálni.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. vagyis akár a StoryBoard deklarációban megadhattuk volna ket. 49.microsoft. Az XAML: <Window x:Class="JegyzetWPF. míg a Duration az id tartamot amíg eljutunk oda.Triggers> <EventTrigger RoutedEvent="Rectangle.2. Az esemény bekövetkezését természetesen egy EventTrigger fogja jelezni. Egy egyszer példával fogunk kezdeni.

TargetProperty="Height" To="170" Duration="0:0:1" /> </Storyboard> </BeginStoryboard> </EventTrigger> <EventTrigger RoutedEvent="Rectangle. az szép lassan megn .TargetProperty="Width" To="170" Duration="0:0:1" /> <DoubleAnimation Storyboard. Ezenkív l hozzáadtunk egy ugyanolyan animációt. Ugyanezt eljátszottuk arra az esetre is. Létezik ugyanis a FillBehavior nev tulajdonság. A másik lehetésges érték a Stop. Ez utóbbi valójában felesleges volt és csak a példa kedvéért szerepelt.TargetProperty="Height" To="150" Duration="0:0:1" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle. Magyarul.Forrás: http://www.Fill> <Rectangle. haladjunk sorjában: els ként átírtuk az animációt.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.Window1" xmlns="http://schemas. vagyis semmi nem történik. hogy double értékeket kezeljen. amely színekhez köt d tulajdonságokra specializálódott.Triggers> </Rectangle> </Grid> </Window> Sokmindennel kiegészítettük a kódot.doksi.MouseLeave"> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard. ha az egérmutató az objektum fölé ér. ha az egeret elvesszük. nézzünk meg egy általánosabb típust: <Window x:Class="JegyzetWPF.Triggers> <EventTrigger RoutedEvent="Rectangle.TargetProperty="Width" To="150" Duration="0:0:1" /> <DoubleAnimation Storyboard. az animáció által módosított értékek úgymaradnak.microsoft. hogy egy olyan animációtípussal ismerkedtünk meg. vagyis mégsem .Fill> <SolidColorBrush Color="Red" /> </Rectangle. Az alapértelmezett értéke HoldEnd. hogy azonnal visszaállnak az értékek. amely azt határozza meg.microsoft. a magasságra vonatkozóan. hogy mji történjen ha az animáció véget ért.com/winfx/2006/xaml" Title="Window1" Height="250" Width="250"> <Grid> <Rectangle Width="150" Height="150"> <Rectangle.hu 287 Azt bizonyára mindenki kitalálta. Van azonban egy probléma is ezzel a megoldással.MouseEnter"> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard. mégpedig az. Ez eléggé lesz kíti a lehetséges célpontokat. vagyis az animáció végén visszaállnak az eredeti értékek. majd a céljának megadtuk a Rectangle szélességét.

ilyenrk pl.microsoft. Miel tt továbbmennénk nézzünk meg egy másik példát is: <Window x:Class="JegyzetWPF. a „visszaállás” a tényleges animáció megfordítása annak minden tulajdonságával és id tartamával.Fill> <SolidColorBrush Color="Red" /> </Rectangle. azaz az nem egyenletes sebességgel megy végbe hanem vagy az elején vagy a végén gyorsabb.microsoft. a logikai és a felsorolt típusok (elöbbi csak két értéket vehet föl. Az AccelerationRatio és DeccelerationRatio nemlineárissá teszik az animációt. de attól kevésbé direktebb.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.Left" . Mindkét tulajdonság egy 0 és 1 közötti (double típusú) értéket vár.Fill> <Rectangle.hu 288 volt olyan felelsleges a fenti megoldás. hogy az animáció az id tartamának els húsz százalékában fog gyorsítani. XAML kódban kissé nehézkes ennek a tulajdonságnak a kezelése. hogy hányszor ismétl djön az animáció. hamarosan készíteni is fogunk egyet.TargetProperty="Canvas. A FillBehavior –hoz hasonló szerepet lát el az AutoReverse. Tehát ha az AccelerationRation tulajdonságnak 0. ugyanakkor vannak olyan típusok amelyeket szándékosan kihagytak.Triggers> <EventTrigger RoutedEvent="Rectangle. mivel így tudtunk az egér elmozdulására reagálni. Említést érdemel még a RepeatBehavior tulajdonság is. amellyel meghatározhatjuk. hogy az egyes tulajdonságokat a megfelel típust kezelni tudó animációtípusokkal animálhatjuk.com/winfx/2006/xaml" Title="Window1" Height="70" Width="250"> <Canvas> <Rectangle Width="20" Height="20" Canvas.Left="0"> <Rectangle. amely a gyorsulás id tartamára vonatkozik százalékos arányban.MouseDown"> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.2 értéket adunk az azt jelenti. A következ deklarációk mindegyike helyes: //az animáció kétszer ismétl dik <DoubleAnimation RepeatBehavior="2x" /> //az animáció 5 másodpercig ismétl dik <DoubleAnimation RepeatBehavior="0:0:5" /> //folyamatosan ismétl dik <DoubleAnimation RepeatBehavior="Forever" /> A két példa alatt már biztosan körvonalazódik a tény. A WPF a legtöbb beépített típushoz tartalmaz támogatást.doksi. Természetesen adott a lehet ség saját animációs osztályok létrehozására. hogy eseményekhez kössük az animációk élettartamát. míg utóbbi értékkészlete nincs el re definiálva).Forrás: http://www.Window1" xmlns="http://schemas. Tulajdonképpen ez az egyetlen módszer. mivel többféle viselkedést definiálhatunk.

Ha a To mellett a By is jelen van. azt jeleznünk kell a fordítónak is. el is indul.hu 289 To="220" Duration="0:0:5" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle. Ha ennek értékét „Compose” –ra állítjuk akkor a két animáció egyesül. Amikor ilyen tulajdonságot használunk. A megoldást a BeginStoryBoard HandoffBehavior tulajdonsága jelenti. de amikor el akarjuk indítani az animációt egy InvalidOperationException –t kapunk. két stílus)). Az els és legfontosabb információ róla az az. Lefordul. By csakis From –mal vagy önmagában állhat. Ezt viszonylag ritkán használjuk. hogy nem használhatjuk egyszerre mindhárom tulajdonságot. egészen addig amíg az animált elemet a GC el nem takarítja. Ennek egyetlen hátulüt je a memóriaigénye. Egy objektum egyszerre több animációt is tud kezelni (itt nem a fenti példában látott „több animáció egy StoryBoard objektumban” esetre gondolunk. ez pedig nem túl szép.Triggers> </Rectangle> </Canvas> </Window> Els ránézésre ez egy teljesen jól m köd program.doksi. ezt pedig a CLR nem tudta feloldani. akkor a tulajdonság nevét zárójelek közé kell írni. hanem arra amikor több forrásunk van (pl.DesiredFrameRate="30"> Egyetlen dolog van amir l még nem beszéltünk ez pedig a By tulajdonság. ugyanakkor az eredeti animáció(k) is a memóiában marad(nak). Az emberi szem nagyjából 25-30 frame/sec –et lát folytonos mozgásnak. de érdemes tudni róla. <Storyboard Timeline. A fenti példát nagyon egyszer en kijavíthatjuk: <DoubleAnimation Storyboard.TargetProperty="(Canvas. hiszen nem tudja róla.Left)" To="220" Duration="0:0:5" /> Tehát a szabály: ha attached property –t akarunk animálni. mivel a második animáció megérkezésekor az els azonnal leáll. de ha kímélni akarjuk a processzort vagy szeretnénk ha gyengébb számítógépen is jól teljesítsen az alkalmazásunk akkor érdemes ezt az értéket lejjebb venni. Van azonban egy kis probléma. Nézzük meg mégegyszer a kódot! Mi az ami más az eddigiekt l? Hát persze! Egy attached property –t használtunk. vagy amíg egy új animációt alkalmazunk az objektumon. Teljesítménynövelés céljából szabályozhatjuk az animáció framerate tulajdonságát vagyis az egy másodperc alatt megjelenített – kiszámolt – képek számát. hogy nem a Rectangle tulajdonságai közt kell keresgélnie. A WPF alapértelmezés szerint 60 frame/sec értékkel dolgozik. ugyanis ekkor egy teljesen új animáció jön létre. míg utóbbi esetben az alapértékt l a By –ig. el bbi esetben az animált tulajdonság a From értékét l a From és a By összegéig tart. . hogy tudja mit hol kell keresnie. akkor utóbbi semmilyen hatással nem lesz.Forrás: http://www.

Fill> <Rectangle. amelyek az egyes mozgásokat rögzítik.hu 290 49.Triggers> <EventTrigger RoutedEvent="Rectangle. Egy példa: <Window x:Class="JegyzetWPF.Window1" . ez kijelöli azt az id szeletet ahol az adott KeyFrame m ködik (a példában az els az els másodpercben. Ez a típus „Linear” el tagot kap. Ezt már láttuk az el z példában is.microsoft.Triggers> </Rectangle> </Grid> </Window> A KeyTime tulajdonság a kakukktojás.2 Key Frame animációk A from/to/by animációk meghatározott úton lineárisan m ködtek.Window1" xmlns="http://schemas. vagyis az animált tulajdonság értéke konstans mód. egy kezd ponttal és egy végponttal.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.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. vagyis a megszabott végértéket a az animációnak járó id legvégén egyetlen „mozdulattal” éri el. A Key Frame animációk nevének utótagja UsingKeyFrames.com/winfx/2006/xaml" Title="Window1" Height="200" Width="200"> <Grid> <Rectangle Width="40" Height="40"> <Rectangle. A Key Frame típusú animációknál nincs ilyen megkötés. Az els és leggyakrabban használt ilyen típus a lineáris interpolációt (itt interpoláció alatt értjük azt.Fill> <SolidColorBrush Color="Red" /> </Rectangle. folyamatosan növekszik/csökken a neki kiszabott id alatt. diszkrét interpoláció mechanizmust használja.Forrás: http://www.doksi. hogy miként érik el a számukra kijelölt végértéket.2. a második a másodikban és így tovább…). aszerint. Ez az animációtípus maga is többfelé oszlik. ami nem egyértelm .MouseDown"> <BeginStoryboard> <Storyboard> <DoubleAnimationUsingKeyFrames Storyboard. ahogyan a tulajdonságok értéke változik) használ. Ezeknek a típusoknak van egy KeyFrameCollection tulajdonsága.microsoft. A következ példában egy primitív „lüktetés” effektet implementálunk: <Window x:Class="JegyzetWPF. A második az ún.

és a jegyzet nem is fogja részletesen tárgyalni. hogy az animáció az id szelet egyes pillanataban mennyire gyorsoljun/lassuljon.3 Spline animációk Végül a harmadik típus a Spline interpolációk családja.MouseDown"> <BeginStoryboard> <Storyboard> <DoubleAnimationUsingKeyFrames Storyboard.microsoft.Fill> <Rectangle.Forrás: http://www.doksi.microsoft.2.com/winfx/2006/xaml" Title="Window1" Height="200" Width="200"> <Grid> <Rectangle Width="40" Height="40"> <Rectangle.hu 291 xmlns="http://schemas. hiszen a teljes megértéséhez er s matematikai ismeretek szükségesek.Triggers> </Rectangle> </Grid> </Window> Most a DoubleKeyFrame diszkrét interpolációt használó változatát vettük el .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. Ez már bonyolultabb.Triggers> <EventTrigger RoutedEvent="Rectangle. Az egyszer típusok legtöbbjéhez rendelkezésünkre áll mindhárom altípus (a harmadikkal is mindjárt megismerkedünk).com/winfx/2006/xaml" Title="Window1" Height="200" Width="200"> <Grid> <Rectangle Width="40" Height="40"> <Rectangle.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. A diszkrét interpolációt használó animációk el tagja értelemszer en „Discrete” lesz.microsoft. 49.Fill> <SolidColorBrush Color="Red" /> </Rectangle. Az animáció a Bezier görbén alapszik.Window1" xmlns="http://schemas.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. két kontrollponttal tudjuk szabályozni. Egy egyszer példa: <Window x:Class="JegyzetWPF.Fill> .microsoft.Fill> <SolidColorBrush Color="Red" /> </Rectangle.

0. Hozzuk össze a két fogalmat és készítsünk egy „forgó” gombot.Forrás: http://www.MouseDown"> <BeginStoryboard> <Storyboard> <DoubleAnimationUsingKeyFrames Storyboard.0. az animációban pedig erre a névre tudunk hivatkozni: <Button Width="100" Height="30" Content="Anitrans button" RenderTransformOrigin="0. Már csak egyvalami kell.0 1.0.hu 292 <Rectangle.0.RenderTransform> <RotateTransform x:Name="transform" /> </Button.0. hogy ezt a képzeetbeli koordinátarendszert egy 1x1 egységnyi nagyságú négyzetnek vesszük. Ezt úgy tehetjük meg.0. össze kell kötnünk a kett t.4 Animációk és transzformációk Egy transzformáció aktuális állapota egy vagy több tulajdonságának értékét l függ. 0.Triggers> <EventTrigger RoutedEvent="Button.RenderTransform> <Button. akkor azt animálni is lehet.0. Els ként szükségünk van egy már létez transzformációra.TargetProperty="Angle" FillBehavior="Stop" To="360" Duration="0:0:5" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Button.0" /> <SplineDoubleKeyFrame Value="40" KeyTime="0:0:4" KeySpline="0. márpedig ha tulajdonság van.Triggers> <EventTrigger RoutedEvent="Rectangle. oly módon.0 1. mint a RotateTransform.1.0. Másodszor egy animációra lesz szükségünk. 49.MouseEnter"> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.0 1.0.0.0" /> <SplineDoubleKeyFrame Value="50" KeyTime="0:0:6" KeySpline="0.TargetName="transform" Storyboard.5"> <Button.0.0" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle> </Grid> </Window> A KeySpline tulajdonsággal jelöljük ki a kontrollpontokat.0 1.1. ez pedig mi más is lehetne most.5.1. hogy a transzformációt elnevezzük.doksi. méghozzá egy DoubleAnimation –ra.Triggers> .2.1.0" /> <SplineDoubleKeyFrame Value="40" KeyTime="0:0:8" KeySpline="0.0.TargetProperty="Width" Duration="0:0:8"> <SplineDoubleKeyFrame Value="50" KeyTime="0:0:2" KeySpline="0.

hogy a gomb a középpontja körül forogjon. hogy az animáció befejeztével az eredeti értékek visszaálljanak és ismét lehet ségünk legyen elindítani a mozgást.hu 293 </Button> A RenderTransformOrigin tulajdonságot azért állítottuk be.doksi. míg az animációban a FillBehavior tulajdonságot Stop – ra állítottuk. .Forrás: http://www.

amelyek tökéletesen illeszkednek a WPF filozófiájához. pl.doksi. hogy hogyan hozzunk létre olyan vezérl ket. Az egész WPF filozófia alapja az. hiszen ezt a módszert többnyire (de nem kizárólagosan) több már létez vezérl összefogására használjuk. Gondoljunk pl. Ugyanakkor továbbra is rendelkezésünkre áll a lehet ség. amely bizonyos formátumú adatot vár. Itt bizony már szükségünk van arra. erre az esetek nagy többségében nem lesz szükségünk. sablonok. illetve. vagy közvetlenül a Control osztályból származtattunk. A WPF nagyban megváltoztatta ezt az ideológiát. hogyegy olyan TextBox –ra van szükségünk. megtanuljuk. hogyan vértezzük fel ket a szükséges tulajdonságokkal (dependency property. routed event. stb. megjelenésével borzasztó egyszer en testre szabható a vezérl k viselkedése és kinézete. A következ fejezetekben mindkét úttal megismerkedünk majd. akkor kénytelenek voltunk származtatással és rengeteg kód írásával készítenünk egy „új” gombtípust. . vagyis a vezérl k megjelenését és funkcionalitását teljesen szétválasztották. egy nem téglalap alakú gombra volt szükségünk. Épp ezért általános jótanács. akkor fordulunk ehhez a megoldáshoz. ha pl. vagy pedig a UserControl -ból. Windows Forms –szal való munka során ez egy viszonylag gyakran alkalmazott módszer volt. a WPF felépítése miatt azonban kicsit háttérbe szorult. mivel a legtöbb esetben jobb egy speciálisabb alapról indulni. így több lehet ségünk is van. hiszen a stílusok. hiszen a WPF ebb l a szempontból eddig elképzelhetetlen lehet ségeket nyújt. hogy az összes vezérl ún.Forrás: http://www. ezzel is b vítve a fegyvertárunkat. arra. stb…). A kés bbiekben megismerkedünk majd az adatkezeléshez sablontípusokkal is. Nos. Innent l viszont a játékszabályok teljesen megváltoznak: a UserControl –ból származtatás lesz a „nem is igazi vezérl ” típus.hu 294 50. funkcionalitása már más tészta. szükséges Felmerülhet a kérdés. hogy új alapokra helyezzük a TextBox viselkedését. addig a viselkedése. Vezérl k készítése Az eddigi fejleszt platformok viszonylag nagy hangsúlyt fektettek saját vezérl k készítésére. nagy részben a limitált lehet ségei miatt (err l a megfelel fejezetben). attól függ en. A WPF szerteágazó „családfával” rendelkezik. Gondoljunk csak a WPF el d Windows Forms –ra! Ott. A következ kben megnézzük a f bb sosztályokat: FrameworkElement: ez a legalacsonyabb szint. lookless „állapotú”. egy telefonszámot. hogy „akkor mikor van szükségünk sajátkészítés vezérl kre?”. hogy mire van szükségünk. hogy ha csak lehet hagyatkozzunk az új elemekre és csak a legvégs esetben nyúljunk a „k kori” eszközökhöz. Ugyanakkor ez egy egy viszonylag ritkán alkalmazott módszer. Windows Forms alatt általában három célpontunk volt amikor új vezérl t készítettünk: vagy valamelyik már létez vezérl t terjesztettük ki. Igaz. Hasonlóan el djéhez a WPF is alapvet en két választási lehet séget ad: származtatunk vagy pedig UserControl –t készítünk. ha a felületet teljes mértékben magunk akarjuk kialakítani. hogy készítsünk egy teljesen új vezérl t. míg a megjelenése egy vezérl nek gyorsan megváltoztatható.

Mi is tulajdonképpen az ok? Eddig jónéhány WPF alkalmazást készítettünk és a felhasználói felület felépítését megszabtuk az XAML kódban. Selector: mindkét osztály olyan vezérl k se. ha csak adatbekérésre lenne szükség.Forrás: http://www.doksi. ha megadunk neki egy sablont. illetve majd meg is jeleníthessük vele azokat. Ezt az osztályt használjuk a leggyakrabban. hogy az rlapot feltölthessük mondjuk egy adatbázisból. . Panel: olyan vezérl t hozhatunk létre a használatával.hu 295 Control: ez az osztály már rendelkezik azzal. felruházzuk a dependency property –kkel. mégpedig a sablonok támogatásával. hogy a meglév vezérl ket különösebben átvariáljuk. hogy egy már létez vezérl t egészítünk ki. amelyet viszonylag nehezen lehet módosítani (de nem lehetetlen. hogy bár az ItemsControl nem definiál vizuális megjelenést. animjációk jelenlétének köszönhette.2 UserControl Els ként a „gyengébb” UserControl osztályból való származtatással ismerkedünk meg. de a WPF felépítése miatt mostanra háttérbe szorult. mivel így megtanuljuk. amellyel a felhasználó megadhatja az adatait (név. akkor ez a dinamizmus kicsit felborul. hogy a Selector (amely maga is az ItemsControl leszármazottja) lehet vé teszi elemek kiválasztását. - - A fentieken kív l meg kell említsük a lehet legegyszer bb lehet séget is. Amikor egy WPF usercontrol –t készítünk. A válasz ebben az esetben igen lesz (persze. telefonszám). amelyet egy Border objektum foglal magába. Vannak azonban korlátai. Az XAML kód pedig adta a könny kezelhet séget. Érdekesség. Már most el kell döntenünk. ContentControl: olyan helyzetekben használjuk. err l majd a megfelel fejezetben. hogy a vezérl maga is tartalmazhasson más elemeket. stb. amikor arra van szükség. UserControl: tulajdonképpen egy ContentControl. A szóban forgó vezérl egy rlap lesz. 50. mégpedig azt. Az alkalmazás „dinamikus” m ködését a sablonok. cím. ItemsControl. amelyek képesek gyermekvezérl k kezelésére. amivel a FrameworkElement nem. mivel a WPF az ilyen tulajdonságokkal szeret adatkötni (err l hamarosan). ezt is megnézzük). stílusok. hogyan tehet a megjelenése dinamikussá. routed event –ekkel. amelyek képesek gyermekobjektumok kezelésére. A kett között a f különbség. hogy szeretnénk –e támogatni adatkötéseket. Korábban ez a módszer nagyjából egyenérték volt a „hagyományos” származtatással. hogyan kell dependency property –t készíteni. attól még használhatjuk közvetlenül. mivel egy usercontrol eleve megszabott kinézettel rendelkezik. Ebben a fejezetben els ként elkészítünk egy viszonylag egyszer usercontrol –t.. míg az ItemsControl neves leszármazottja a TreeView. A Selector osztályból származik többek között a ComboBox és a ListBox. hogy grafikus felületen tudjuk megtervezni az új vezérl t. akkor nemet is mondhatunk). így nem volt rá szükség. végül pedig megnézzük. Kifejezetten arra készítették. vagyis.

mivel a Form osztály egy parciális osztály megvan a lehet ségünk.Documents. namespace JegyzetTestWPF { public partial class Form { } } Ne felejtsük el a névtereket is hozzáadni. System. System. hogy gyakorlatilag ugyanolyan felületen dolgozhatunk.Windows. Minden dependency property statikus tag kell legyen. A név tulajdonságánál azért nem csak simán NameProperty lett. System. nélkülük nem használhatjuk a WPF lehet ségeit.Windows. valamint a nevük konvenció szerint Property utótaggal rendelkezik.Media. Most készítsük el a három DP –t.Generic. System. System.cs néven. hogy a legfels bb szint elem most a UserControl lesz.Shapes.Windows. Az „új osztályunk” fogja tartalmazni a dependency property –ket. Tegyük meg az el készületeket. látható.Data. Nevezzük el mondjuk Form –nak.Controls. System. hogy tovább bontsuk és most élünk is vele. szükségünk van egy statikus konstruktorra ahol regisztráljuk a tulajdonságokat. static public DependencyProperty PhoneProperty.Windows. megadva nevüket.Forrás: http://www.hu 296 Tegyük fel. Tulajdonképpen ez a tervez nézet a legnagyobb el nye UserControl –ok használatának. static public DependencyProperty AddressProperty. System.Media.Input. mint eddig.Imaging.Collections. ez három darab dependency property –t kíván. hogy mindössze három adatra vagyunk kíváncsiak: név. System. Kiindulásként nézzen ki valahogy így: using using using using using using using using using using using using using System. azon belül pedig a User Control –t. Adjunk hozzá egy osztályt is a projecthez FormPart.Windows. System. System.Windows.Linq. típusukat valamint az osztályt amelyhez tartoznak: . Ezt az átláthatóság miatt tesszük.Windows.Navigation. System.doksi.Windows.: static public DependencyProperty UserNameProperty. Még korántsem vagyunk készen. System.Windows. Ezután létrejön a vezérl . mert azt a FrameworkElement már lefoglalta magának. cím és telefonszám.Text. azzal a különbséggel. a projecten jobb egérgommbal kattintva válasszuk ki az Add menüpontot.

Register ( "Address". } Itt álljunk meg egy kicsit! A fenti esetben a legegyszer bb módot használtuk a regisztrációhoz. PhoneProperty = DependencyProperty. typeof(Form) ). Az els paraméter az alapértelmezett értéke a tulajdonságnak. typeof(string). hamarosan ket is megnézzük. amelyek a dependency property –k értékének lekérdezésére illetve beállítására szolgál. typeof(Form) ). } A FrameworkPropertyMetadata osztály segítségével a dependency property m ködéséhez adhatunk meg adatokat. A harmadik paraméter tulajdonképpen egy eseménykezel . PositiveIntProperty = DependencyProperty. typeof(string). A Register metódus rendelkezik még további paraméterekkel is. mint pl. typeof(Form) ). amely szintén egy eseménykezel re mutat. AddressProperty = DependencyProperty. az adatkötések engedélyezése. typeof(Form). typeof(string). Tegyük fel. nézzünk meg néhány bonyolultabb esetet. static public bool IsPositive(object value) { return (int)value >= 0. amely nem lehet kisebb nullánál: static public DependencyProperty PositiveIntProperty.Register ( "PositiveInt". ez szolgál a kapott érték ellen rzésére. . ez pedig az.MaxValue. Egyetlen dologgal tartozom még. hamarosan megnézzük ezt is. hogy a renderelés melyik fázisában vegye figyelembe a tulajdonság értékét a WPF...Register ( "Phone". null).Forrás: http://www. new ValidateValueCallback(IsPositive) ). hogy a DP egy egész számot vár. Nekünk most a Register metódus utolsó paramétere fontos. new FrameworkPropertyMetadata(int.doksi. stb. A választ a DependecyObject –t l örökölt GetValue és SetValue metódusok jelentik.hu 297 static Form() { UserNameProperty = DependencyProperty. typeof(int). hogy hogyan lesz mindebb l „igazi” tulajdonság. a második paraméter amely egy FrameworkPropertyMetadataOptions enum egy értékét veszi fel olyan dolgok beállítsára szolgál. amely a tulajdonság megváltozásakor fut le. null.Register ( "UserName".

most nem jelenítjük meg: Form f = new Form(). Egyúttal elkészítettük az érték változásakor lefutó eseménykezel t is. new FrameworkPropertyMetadata( int. hogy ebben a „burkoló” tulajdonságban ne nagyon használjunk semmilyen ellen rzést az adatok felé. hogy itt csakis a kapott értéket ellen rizhetjük. azon belül is a FrameworkPropertyMetadata konstruktorához. hogy milyen sorrendben érkeznek: Els ként a ValidateValueCallback fut le.PositiveInt = -1. Jó lenne tudni. Ami nagyon fontos. mivel a -1 nem megfelel érték. value). typeof(Form).doksi. } } Érdemes figyelni arra. mivel a SetValue önmagában is gond nélkül hívható. FrameworkPropertyMetadataOptions. illetve figyelembe vehetünk más dependency property –ket . Térjünk vissza a Register metódushoz.hu 298 public int PositiveInt { get { return (int)GetValue(PositiveIntProperty).Forrás: http://www.Register ( "PositiveInt". Fordulni lefordul. amelyek a tulajdonság értékének változásakor futnak le.MaxValue. new PropertyChangedCallback(PositivePropertyChanged). Ennek utolsó paramétere szintén egy eseménykezel re mutat. Teszteljük is le az új tulajdonságot. Van tehát három metósusunk. new CoerceValueCallback(CoercePositiveValue)). Következ a sorban a CoerceValueCallback. } set { SetValue(PositiveIntProperty. new ValidateValueCallback(IsPositive) ). typeof(int). az az. majd valahol a kódban példányosísunk egy Form objektumot. amely a kapott érték korrigálására szolgál: PositiveIntProperty = DependencyProperty.PositiveInt = 10. Ebben a fázisban finomíthatunk a kapott értéken. f. magához a fogadó objektumhoz nem férünk hozzá. ehhez adjuk hozzá mondjuk az új vezérl nkhöz. de kapunk egy ArgumentException –t.None. f.

CoerceValue: meghívja a paraméterként megadott DP CoerceValueCallback –jához rendelt eseménykezel t. Itt elindíthatunk egy eseményt. object value) { return value. hogy nem –e lehetne ezekkel a metódusokkal kikerülni a felhasználó által megadott helytelen adatokat. A CoerceValueCallBack használatára jó példa a Slider osztály: rendelkezik Minimum és Maximum értékekkel. Három tulajdonsága van: OldValue: a régi érték NewValue: az új érték Property: a megváltoztatott dependency property Most már mindent tudunk. készítsük el a vezérl nk „igazi” tulajdonságait: public string UserName { get { return (string)GetValue(UserNameProperty).hu 299 is. azért sok lehet ségünk van: ha az érték valamiért nem megfelel a DependencyObject metódusait használhatjuk: ClearValue: törli a paraméterként megadott dependency property helyi értékét (ld.Forrás: http://www. hogy kijavítjuk a felhasználó hibáit. value). amellyel értesítjük a rendszert. de nem ajánlott.doksi. } Ugyan most nem csináltunk semmi különöset. mivel hozzáférünk a küld objektumhoz. InvalidateProperty: újraszámítja az adott DP –t. static public void PositivePropertyChanged(DependencyObject d. korábban). Most nézzük a metódusokat: static private object CoercePositiveValue(DependencyObject d. } . ReadLocalValue: visszadja a megadott DP helyi értékét. Végül a PopertyChangedCallback jön. Egész egyszer en nem lehet arra építeni. amelyeket az aktuális érték nem léphet át. az ajánlott módszer továbbra is egy érthet hibaüzenet legyen. Lehetne. } set { SetValue(UserNameProperty. DependencyPropertyChangedEventArgs e) { } Itt a DependencyPropertyEventArgs fontos nekünk. hogy valami megváltozott. - Felmerülhet a kérdés (méghozzá jogosan).

de ezt hagyjuk akkorra. Routed event készítése nagyon hasonlít a dependency property –éhoz. } set { SetValue(AddressProperty. az eseménykezel típusa végül pedig a tartalmazó osztály. Tehát. A feldolgozás nem a vezérl feladata lesz. az csak tárolja az információkat. stratégiája. szólni kell a rendszernek. Legyen az eseményünk neve SubmitEvent: static public readonly RoutedEvent SubmitEvent.Forrás: http://www. value).Bubble. RoutingStrategy. Az adatokat felvettük. amikor elkészítettük a felhasználói felületet. hogy készítünk erre a célra egy eseményt. typeof(Form) ).hu 300 } public string Address { get { return (string)GetValue(AddressProperty). szintén a statikus konstruktorban: SubmitEvent = EventManager. A paraméterek sorrendben: az esemény neve. typeof(RoutedEventHandler).doksi. hogy más osztályok is feliratkozzanak az eseményre. Az eseményt „természetesen” gombnyomásra küldjük el. } set { SetValue(PhoneProperty. Lehet séget kell adnunk arra. } } public string Phone { get { return (string)GetValue(PhoneProperty). ezt az olvasó majd megteszi az ismertetett eszközökkel. Ezt is regisztrálnunk kell. Ezt legegyszer bben úgy tehetjük meg. most el is kellene küldeni ket. innent l a „hagyományos” eseménydefiníciót használjuk: . hogy dolgozza fel azokat.RegisterRoutedEvent ( "Submit". value). } } Az egyszer ség kedvéért nem alkalmaztunk semmilyen ellen rzést. amikor a felhasználó megadta az adatait. mégpedig egy routed event –et.

hu 301 public event RoutedEventHandler Submit { add { base. RaiseEvent(args).microsoft. Borzasztóan puritánok leszünk.com/winfx/2006/xaml" Height="110" Width="180" BorderThickness="2" BorderBrush="Blue"> <Grid> <Grid.RowDefinitions> <UniformGrid Width="150" Height="70" Rows="3" Columns="2" Grid. hogy megvalósítsuk a felhasználói felületet.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. } Eljött az id .SubmitEvent).RowDefinitions> <RowDefinition Height="80" /> <RowDefinition Height="*" /> </Grid.Form" xmlns="http://schemas. ez most igazából nem lényeges: <UserControl x:Class="JegyzetWPF.Row="1" Click="submitbutton_Click" /> </Grid> </UserControl> A submitbutton Click eseménye fogja elindítani az általunk definiált eseményt: .AddHandler(Form.RemoveHandler(Form. } } Az eseményt el is kell indítanunk.SubmitEvent. ezt a WPF világban az UIElement osztálytól örökölt RaiseEvent metódussal tesszük meg: protected void RaiseSubmitEvent() { RoutedEventArgs args = new RoutedEventArgs(Form.doksi.Row="0"> <TextBlock Text="Név:" /> <TextBox Name="nametextbox" /> <TextBlock Text="Cím:" /> <TextBox Name="addresstextbox" /> <TextBlock Text="Telefonszám:" /> <TextBox Name="phonetextbox" /> </UniformGrid> <Button Name="submitbutton" Content="OK" Width="70" Height="20" Grid.SubmitEvent.Forrás: http://www.microsoft. } remove { base. value). value).

Clear().Text == "" || addresstextbox. az új vezérl felhasználását. } Rendben vagyunk.microsoft. vagy pedig újat csinálunk. Korábban azt mondtuk. elmentjük az rlap tartalmát. Itt két választásunk van.Show("Töltse ki az adatokat!").hu 302 private void submitbutton_Click(object sender. amivel majd a névtérre hivatkozunk. de ezt most nem fogjuk megírni.Window1" xmlns="http://schemas. } } Tettünk bele egy kis felhasználóbarátságot is. Ez a parancs az ApplicationCommands. hogy megjelenik a Submit esemény is. vagy egy már létez parancsot használunk fel. hogy a . a ClearFields metódus pedig kitakarít: public void ClearFields() { nametextbox. A mentésért felel s kód ezért mostantól a vezérl része lesz. hogy nem a form dolga lesz az adatok mentése.Text == "") { MessageBox.Save lesz. Ahhoz. phonetextbox.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. ezért gyorsan kiválaszthatjuk a nekünk kell névteret.Forrás: http://www. a legfontosabb dolgot hagytuk a végére.Clear(). most ezen változtatunk egy kicsit. ClearFields(). vagyis azt amikor létez parancsot használunk. Látható. Szerencsére itt van IntelliSense támogatás. ami viszont azzal jár.doksi. hogy használni tudjuk a vezérl t regisztrálnunk kell a tartalmazó névteret.Text == "" || phonetextbox. El ször az egyszer bbet nézzük meg. Parancsot kétféleképpen adhatunk hozzá egy vezérl höz: vagy közvetlenül a CommandBindings listához adjuk hozzá. Ezt a kód „tetején” tehetjük meg (vastagbet vel kiemelve): <Window x:Class="JegyzetWPF. RoutedEventArgs e) { if (nametextbox.Clear().com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft. } else { RaiseSubmitEvent(). Térjünk vissza a „f ablakunkhoz” és nyissuk meg a hozzá tartozó XAML t. A következ napirendi pont a commanding támogatás bevezetése. addresstextbox. mivel nem kapcsolódik szorosan a témához (legyen házi feladat az olvasó számára). ehhez tetsz leges esménkezel t rendelhetünk.

hogy lehet –e mentenünk.CanExecute = nametextbox..Text != "" && phonetextbox.. Kezdjük az egyszer bb esettel! A form konstruktorába helyezzük a következ kódrészletet: CommandBinding binding = new CommandBinding ( ApplicationCommands. } A bizotnságosabb változat nem sokban különbözik az el z t l. SaveCommand_CanExecute)).Forrás: http://www. A két metódus az osztály tagja lesznek. this..CommandBindings.Add(binding). CanExecuteRoutedEventArgs e) { e.Save. } static private void SaveCommand_CanExecute(object sender. CanExecuteRoutedEventArgs e) { var form = (Form)sender..Text != "". vagy pedig az „er sebb” megoldást választjuk és a RegisterClassCommandBinding metódussal kapcsoljuk hozzá az osztályhoz a parancsot. Most azt a feltételt adtuk meg. Így néznek ki: private void SaveCommand_Execute(object sender.RegisterClassCommandBinding( typeof(Form). hogy hozzáférjünk az eredeti példány adataihoz: static private void SaveCommand_Execute(object sender. míg utóbbi azt vizsgálja. Most a statikus Ebben az esetben a kezel metódusok statikusak lesznek és mivel már nem részei az osztálynak ezért a sender paramétert konvertálnunk kell.Save. . } private void SaveCommand_CanExecute(object sender.doksi.hu 303 végfelhasználó tetszés szerint emódosíthatja azt. elöbbibe fogjuk helyezni az adatok mentéséért felel s kódot. ExecutedRoutedEventArgs e) { //mentünk. hogy minden mez legyen kitöltve. konstruktort fogjuk használni: CommandManager. SaveCommand_Execute.Text != "" && addresstextbox. A SaveCommand_Execute és a SaveCommand_CanExecute a parancs két eseményének kezel i lesznek. SaveCommand_Execute. new CommandBinding( ApplicationCommands. SaveCommand_CanExecute ). ExecutedRoutedEventArgs e) { //mentünk.

Forrás: http://www.doksi.hu

304
e.CanExecute = form.nametextbox.Text != "" && form.addresstextbox.Text != "" && form.phonetextbox.Text != ""; }

Már csak egyvalami hiányzik a fegyvertárunkból, ez pedig az új parancsok létrehozásának képessége. Korábban már volt róla szó, hogy a WPF a RoutedComand vagy a RoutedUICommand osztályt használja parancsok létrehozásához, mi az elöbbit fogjuk választani. Adjunk hozzá az osztályhoz egy statikus tagot:
static public RoutedCommand SaveCommand;

A statikus konstruktorban fogjuk beállítani:
InputGestureCollection igc = new InputGestureCollection(); igc.Add(new KeyGesture(Key.S, ModifierKeys.Shift)); FormSaveCommand = new RoutedCommand("Save", typeof(Form), igc);

Az

IputGestureCollection

segítségével

a

gyorsbillenty ket

állítottuk

be.

Forrás: http://www.doksi.hu

305

51. ADO.NET
Az ADO (ActiveX Data Objects) a COM (Component Object Model – a .NET el tti f fejleszt i platform) adatelérési rétege volt. Hídat képezett a programozási nyelv és az adatforrás között, vagyis anélkül írhattunk programot, hogy ismertük volna az adatbázist. A .NET Framework sok más mellett ezt a területet is jelent sen felújította, olyannyira, hogy az ADO.NET és az ADO közt a név jelenti a legnagyobb hasonlóságot. Az évek során az ADO.NET a Framework szinte minden verzióváltásakor b vült egy kicsit, így napjainkban már igen széleskör választék áll rendelkezésünkre. A következ néhány fejezetben nagyjából id rendi sorrendben fogunk megismerkedni ezekkel az eszközökkel. Els ként a „hagyományos” móddal kerülünk közelebbi kapcsolatba, ezek az osztályok lényegében már a .NET els verzióiban is megvoltak. Az adatbázist ún. Data Provider –eken keresztül érhetjük el, a lekérdezett adatokat pedig DataSet objektumokban tároljuk, amelyek tulajdonképpen megfeleltek egy „in – memory” relációs adatbázisnak, vagyis az adatok a memóriában vannak XML formátumban. Emiatt ezt a metódust „Disconnected – model” –nek nevezzük, hiszen nincs szükség folyamatos kapcsolatra az adatbázissal. A következ lépést a LINQ család jelentette, amely több részre oszlik és ezek nem mindegyike tartozik az ADO.NET alá (de mindannyiukkal megismerkedünk majd). A LINQ a Language Integrated Query kifejezés rövidítése, amely annyit tesz, hogy közvetlenül a forráskódból a nyelvben ténylegesen szerepl eszközökkel SQL szer lekérdezéseket írhatunk. A LINQ jelent sége az, hogy nem csak relációs adatbázisból kérdezhetünk le, hanem bármilyen forrásból, amelyre implementálva van – így hagyományos objektum vagy listák esetében is használhatjuk. Hivatalosan öten alkotják a LINQ családot: LINQ To Objects: nem az ADO.NET része. A memóriában lév gy jteményeket kérdezhetünk le vele. LINQ To XML: XML forrásokon hajthatunk végre lekérdezéseket. LINQ To SQL: A Microsoft els ORM (Object Relational Mapping) eszköze. Az adatbázis tábláit hagyományos objektumként kezelhetjük. LINQ To DataSets: Mivel a LINQ To SQL csakis Microsoft SQL adatbázissal m ködik ezért szükség volt valamire, ami a többivel is elbír. Miután az adatok bekerülnek egy DataSet –be írhatunk rá lekérdezést.

Ebb l a felsorolásból egyvalaki kimaradt, méghozzá a LINQ To Entities. Ez a .NET Framework 3.5 SP1 –ben debütált Entity Framework –höz készült. Az EF a LINQ To SQL továbbgondolása, jóval rugalmasabb és sokrét bb annál. Az EF egy másik lehet séget is biztosít a lekérdezésekhez, az ún. Entity SQL –t. Hogy a kett közt mi a különbség majd a megfelel fejezetekben kiderül.

51.1 MS SQL Server 2005/2008 Express
A következ fejezetek megértéséhez, illetve a tanultak alkalmazásához szükségünk lesz valamilyen relációs adatbázisra. A .NET –hez legjobban nyílván valamilyen Microsoft termék illik. Szerencsére Redmondban gondoltak azokra is, akik még tanulják a mesterséget, így a „nagy” MS SQL Server –ek mellett ott vannak a kistestvérek is, az Express család. Ezek a szoftverek teljes mértékben ingyenesek, akár céges keretek közt is használhatjuk ket. Természetesen nem nyújtják az

Forrás: http://www.doksi.hu

306 összes szolgáltatást, mint bátyjaik, de tanuláshoz (és kisebb kereskedelmi programokhoz) tökéletesek. A letölt 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 nem–Express változatai feltelepítik az SQL Server Express -t, ha a telepítésnél ezt kérjük. Az adatbázisok kezeléséhez érdemes telepíteni a megfelel verzióhoz tartozó SQL Server Management Studio Express –t is, amely grafikus felületet nyújt lekérdezések írásához, adatbázisok kezeléséhez.

A képen a Management Studio 2005 Express látható.

Forrás: http://www.doksi.hu

307

52. SQL alapok
Az SQL (Structured Query Language) egy adatbázisokhoz kifejlesztett nyelv. Tulajdonképpen ez csak egy gy jt név, mivel az összes RDBMS (Relation Database Management System) saját dialektust „beszél”. Ugyanakkor az eltérések általában nem nagyok, így viszonylag könny váltani. A következ fejezetekben az MS SQL nyelvével ismerkedünk meg, ennek neve Transact – SQL vagy TSQL. A következ webhelyen egy kiváló TSQL leírást talál a kedves olvasó, angol nyelven: http://www.functionx.com/sqlserver/index.htm

SQL lekérdezések írásához indítsuk el a Management Studio –t, a belépésnél válasszuk a Windows Authentication módot, majd kattintsunk a Connect felíratú gombra:

Természetesen a Server name sor változhat.

52.1 Adatbázis létrehozása
Mivel még nincs adatbázisunk, ezért kattintsunk jobb egérgombbal a bal oldalon látható Object Explorer –ben a legfels bb szintre:

Válasszuk a New Query menüpontot, ekkor megjelenik a szerkeszt ablak.

Forrás: http://www.doksi.hu

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 utasításokat. A végrehajtáshoz nyomjuk meg az F5 billenty t, ekkor – ha nem rontottunk el semmit - megjelenik az adatbázisunk a Databases fül alatt (ha nem, akkor jobb egérgomb rajta és Refresh):

52.2 Táblák létrehozása
Eljött az ideje, hogy feltöltsük adatokkal az adatbázisunkat. Egy adatbázisban egy vagy több tábla tárolja az adatokat. Egy tábla sorokból (Row) és oszlopokból (Column) áll. Minden egyes oszlopnak van típusa, illetve azonosítója, amellyel hivatkozhatunk rá. Az egyes sorok ún. rekordok, amelyek egy-egy bejegyzései a táblának. Vegyük a következ egyszer példát: egy táblában eltároljuk emberek nevét és címét. Ekkor az oszlopok neve legyen pl. Name és Address, típusuk pedig valamilyen szöveges formátum (err l b vebbet hamarosan). Egy sor a táblában pl. így néz majd ki: Dr. A. Kula Erdély, Baljós Kastély Fasor. 13/b. Ezt a táblát a következ képpen tudjuk létrehozni: a Databases fül alatt keressük ki az adatbázisunkat, majd jobb egérgombbal kattintsunk rajta és New Query:
create table Persons (Name varchar(50), Address varchar(100));

A tábla neve után az oszlopok, és típusaik következnek. A varchar nagyjából megfelel egy string típusnak, zárójelben a maximális hossz szerepel. Ezután, ha kinyitjuk a Databases –t, akkor a Tables alatt megtaláljuk az új adattáblánkat (ha nem látszik, akkor Refresh). Egy táblának lehetnek speciális oszlopai. Gyakran kerülünk olyan helyzetbe, amikor egy tábla egy sorát egyértelm en meg akarjuk különböztetni a többit l, pl. legyen egy tábla a dolgozóknak, és egy a munkahelyeknek. A táblák felépítése legyen olyan, hogy a dolgozók táblája tartalmaz egy „Munkahely” oszlopot, amely a munkahely nevét tartalmazza. Ezzel a megoldással egyetlen nagy probléma van, mégpedig az, hogy feltételezi, hogy nincs két azonos nev cég. Ha viszont van, akkor egy munkahelyre vonatkozó lekérdezésnél bajban leszünk. Erre a problémára megoldás az ún. els dleges kulcs (primary key v. PK). Egy tábla több els dleges kulcsot jelképez oszlopot is tartalmazhat, és ezek értékének egyedinek kell lennie (értsd.: nem lehet egy táblában két azonos érték , azonos oszlopban lév PK). Els dleges kulcsot a következ képpen hozunk létre:
create table varchar(100)) Persons (ID int primary key, Name varchar(50), Address

Els dleges kulcsnak legegyszer bb szimpla numerikus értéket adni (más megoldáshoz kulcsszó: GUID), ezt tettük a fenti példában is, az int egy egész számot

Forrás: http://www.doksi.hu

309 jelöl. Egy kis kényelmetlenség azonban még van, ugyanis minden esetben kézzel kell megadnunk a kulcsot, ez pedig nem túl biztonságos. Szerencsére van megoldás, automatizálhatjuk a folyamatot, megadva a kezd értéket és a kulcs növelésének mértékét:
create table Persons (ID int identity(1,10)primary key, Name varchar(50), Address varchar(100))

Most az els PK 1 lesz és tízesével fog növekedni, tehát a másodikként beillesztett elem kulcsa 11, a másodiknak 21, stb… lesz. Egy oszlopnak azt is megengedhetjük, hogy nullértéket tartalmazzon. Módosítsuk a fenti kifejezést, 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 nullértéket, ilyen táblát nem is hozhatunk létre.

52.3 Adatok beszúrása táblába
Az INSERT parancsot fogjuk használni:
insert into Persons (Name, Address) values ('Dr. A. Kula', 'Erdély, Baljós Kastély Fasor 13/b')

Megadtuk a tábla nevét, majd azokat az oszlopokat, amelyeknek értéket adunk. Most az utoljára létrehozott táblát használtuk, amelyik automatikusan állítja be az els dleges kulcsokat, ezért azt nem kell megadnunk. Ha ezt nem kértük, akkor a PK –t is meg kell adnunk. Azokat az oszlopokat sem kell megadnunk, amelyeknek megengedtük, hogy elfogadjanak nullértéket.

52.4 Oszlop törlése táblából
Ha el akarunk távolítani egy oszlopot, akkor azt a következ képpen tehetjük meg:
alter table Persons drop column Name

Els ként meg kell adnunk a módosítani kívánt táblát, majd a törlend oszlop nevét.

52.5 Kiválasztás
Egy lekérdezés legelemibb tagja, amelyet minden egyes lekérdezés tartalmaz, az a select utasítás:
select * from Persons

Ezzel az utasítással a Persons táblából kiválasztottuk az összes oszlopot. Természetesen megadhatunk oszlopnevet is:

Forrás: http://www.doksi.hu

310

select Name from Persons

A wildcard karaktert (*) éles alkalmazásoknál ritkán, de leginkább soha nem használjuk, mivel szinte soha nincs szükség minden oszlopra.

52.6 Sz rés
A where a lekérdezett adatok sz résére szolgál, megadhatunk vele egy feltételt, és csak azokat a sorokat kapjuk vissza, amelyek ennek megfelelnek:
select Name from Persons where Name='Judit'

Ez a lekérdezés az összes olyan sort fogja megjeleníteni a Persons táblából, amelyeknek a Name oszlopa megegyezik a megadottal. A where számos operátort használhat, néhány példa:
/*Azokat a sorokat választuk ki, ahol a kor nem kissebb mint 18*/

select * from Persons where Age !< 18
/*Azokat a sorokat választjuk ki, ahol a kor 18 és 65 között van*/

select * from Persons where Age between 18 and 65
/*Azokat választjuk ki akiket nem István -nak hívnak*/

select * from Persons where Name != 'István'
/*Ugyanaz mint elöbb*/

select * from Persons where Name <> 'István'

Összetett feltételt is megadhatunk:
/*A kor nagyobb mint 18 és kissebb mint 65*/

select * from Persons where Age > 18 and Age < 65
/*A Jen vagy József nev eket választjuk ki*/

select * from Persons where Name = 'Jen ' or Name = 'József'

Azt is ellen rizhetjük vele, hogy egy adott mez nullértéket tartalmaz:
select * from Persons where Age is null

A like operátorral tovább finoimíthatjuk a sz rést:
select * from Persons where Name like ’re%’

Itt a ’%’ azt jelenti, hogy bármelyik és bármennyi karakter, tehát azokat a neveket keressük, amelyek a ’re’ „szócskával” kezd dnek. Egy másik ilyen sz r a ’_’ karakter, ez a ’%’ –kal ellentétben csak egyetlen karaktert jelent:
select * from Persons where Name like ’_stvan’

amely alapján majd rendezi a végeredményt. ahogy a táblában szerepelnek. select Names. Address from Persons order by Address A lekérdezés elején megadtuk a megnézni kívánt oszlopokat. akiknek neve A vagy B bet vel kezd dik. Egy másik kulcsszó amely a kapott eredmény finomítására szolgál a distinct. ezért tudnunk kell rendezni a sorokat. akiknek egy valamilyen bet vel kezd dik a nevük és utána a „stvan” karaktersor áll. módosításnál.… Budaörs. Ez persze nem mindig jó.… A rendezés után az eredmény ez lesz: . legyenek a címek a következ k(a nevek most lényegtelenek): Budapest.doksi.Forrás: http://www. kiválasztásnál. A where-t nagyon gyakran fogjuk használni törlésnél. Ekkor a lekérdezés így alakul: select distinct City from Customers Az eredeti tábla egy részlete: Kis Miska Baja Kovács János Budapest Nagy Ede Budapest És az eredmény: Baja Budapest 52. A rendezés abc szerint történik. amely a többszöri el fordulást javítja.hu 311 Ebben a némileg kicsavart példában az összes olyan személyt keressük. eddig semmi szokatlan nem történt. hogy melyik városokból érkeznek megrendelések. A végén viszont megjelenik az order by.… Baja. mögötte pedig az az oszlop. amelyek valamelyike illeszkedik: select * from Persons where Name like ’[AB]%’ Most azokat keresük. Erre a feladatra szolgál az order by. pl. A szögletes zárójelekkel több karaktert is megadhatunk. A vásárlók táblája (Customers) tartalmaz is egy városra vonatkozó oszlopot (City). Képzeljük el.7 Rendezés A lekérdezett adatokat abban a sorrendben kapjuk vissza. hogy egy webáruházat m ködtetünk és szeretnénk megtudni.

Ezútán jön a feltétel. amely egy másik táblára mutat idegen kulcsnak (foreign key) nevezzük. amivel kiválasztjuk a kívánt sort. Tehát vannak személyek és hozzájuk tartozó házak. léteznek ezenkív l egy-egy illetve több-több relációk is.9 Relációk A relációk megértéséhez képzeljünk el egy egyszer példát. … 21 2 Kecskemét. Felmerülhet a kérdés.… Budapest. hogy néz ki a két tábla (a példa nagyon egyszer ): Persons: ID Name 1 Kovács János 2 Nagy Sándor Houses: HID PID Address 11 1 Budapest. amely az t birtokló személyre mutat (a Persons tábla megfelel sorának els dleges kulcsára). ugyanis egy személy több házzal is rendelkezhet.hu 312 Baja. 52.Forrás: http://www. vagyis az egyes házak tartalmaznak egy azonosítót. A táblákat létrehozó utasítások a következ ek lesznek: .… Budaörs. 52.… Itt az ötödik pozicíóban lesz eltér karakter (ö – p) és ez alapján már tud rendezni. Rendben. hogy hogyan rendeljük össze a két táblát.doksi. amelyeket módosítani akarunk az új értékekkel együtt. van még egy probléma. mi most egy foreig key constraint –et fogunk bevezetni. mégpedig az. Nézzük meg. constraint –et használunk. Ezt a relációt egy–több (onee-to-many) relációnak nevezzük. Erre fogjuk használni az els dleges kulcsokat. Ezt az azonosítót. majd azokat a mez ket. Több típusa is van. Vegyük a már létez Persons adatbázist és készítsünk egy új adattáblát Houses névvel. hogy miért nem használjuk a már meglév táblát és egészítjük ki a házakra vonatkozó információkkal? Az ok nagyon egyszer . … A táblák létehozásánál ún. amely meghatározza egy oszlop tulajdonságait.8 Adatok módosítása Az UPDATE parancsot fogjuk használni: update Persons set Name='Béla' where Name='Attila' El ször megadjuk a táblát.

52. Vegyük az el z fejezet két tábláját és írjunk egy lekérdezést. PID int foreign key references Persons(ID).Forrás: http://www. Address from Persons inner join Houses on Houses.PID = Persons.ID . hogy melyik tábla melyik oszlopára hivakozunk.doksi.hu 313 create table Persons ( ID int not null primary key. Address ntext ) Az idegen kulcs létehozásakor meg kell adnunk.10 Join Több táblából kérdezhetünk le a join utasítással. Name varchar(50) ) create table Houses ( HID int not null primary key. amely visszaadja a házakhoz tartozó személyeket: select Name.

System. A szükséges osztályok a System.Integrated Security=True" ). adatbázis neve.Data. ehhez szükségünk lesz egy ún. A DbConnection utódja lesz minden más kapcsolódásért felel s osztály. ami tulajdonképpen egy tolmács szerepét fogja betölteni köztünk és az adatforrás közt. } } } Ebben a programban a már elkészített adatbázishoz csatlakoztunk. System. using using using using using System. hogy milyen adatbázishoz kapcsolódunk.Initial Catalog=testDataBase. vagy kézzel kapcsolódunk. Az SqlConnection a DbConnection absztrakt osztály leszármazottja. Az SqlConnection konstruktora a connectionstring –et kapja paraméterül (be lehet ezt állítani utólag is.State).doksi. Az egyszer ség kedvéért a jegyzet az MSSQL adatbáziskezel t preferálja. Console. most a lehet legegyszer bbet alkalmaztuk. mint az MSSQL -lel.Open(). Els ként ki kell választanunk.Collections.WriteLine(connection. connection. a ConnectionString tulajdonságon keresztül).Data. Els ként a . amely a kapcsolódáshoz szükséges információkat (szerver neve. stb…) tartalmazza. A kapcsolatot kétféleképpen tudjuk elkészíteni.SqlClient névterekben rejt znek.hu 314 53. Egy connectionstring sokkal bonyolultabb is lehet. A kapcsolatot az SqlConnection osztállyal fogjuk megteremteni. connection object lesz. connection. ezért a kézi módszert fogjuk el ször átnézni. System. amely segítségével parancsokat tudunk küldeni és adatokat visszakapni.Forrás: http://www. connectionstring –re. Mivel el bbihez szükség van plusz ismeretekre. de lesz példa másra is. vagy a beépített varázslókat használjuk. így gyakorlatilag ugyanúgy dolgozhatunk egy MySQL szerverrel.ReadKey(). Ez egy ún.Text.Generic. Kapcsolódás az adatbázishoz Miel tt nekilátunk adatokkal dolgozni létre kell hoznunk egy olyan objektumot. kapcsolódás módja.Data. System.SqlClient. namespace JegyzetTestDB { class Program { static void Main(string[] args) { SqlConnection connection = new SqlConnection ( @"Data Source=computer381\SQLEXPRESS. Console.Close().Data illetve a System.

így létezik OracleConnectionStringBuilder.OleDb névtérre van szükségünk. Végül bezárjuk a kapcsolatot. } Egy connectionstring elég bonyolult is lehet. A következ a sorban az adatbázis – és nem a tábla – neve.Data. Console. SqlConnection connection = new SqlConnection(). csBuilder. Szerencsére az SqlConnection. csBuilder. stb… Mi most az SqlConnectionStringBuilder –t fogjuk használni: SqlConnectionStringBuilder csBuilder = new SqlConnectionStringBuilder(). hogy eltároljuk a konfigurációs file –ban. egyszer en betehetjük az egészet egy using blokkba: using(SqlConnection connection = new SqlConnection(. csBuilder.State).hu 315 szerver nevét adtuk meg. connection.1 Kapcsolódás Access adatbázishoz A fejezet címében szerepl cselekvést a Microsoft Jet adatázis „vezérl n” kereszt l fogjuk megtenni. OleDbConnectionStringBuilder. Ezután megnyitjuk a kapcsolatot. 53. ahol az adatbázis megtalálható. amely egy újabb DbConnection leszármazott.NET tartalmaz egy DbConnectionStringBuilder nev osztályt. Mi sem fogjuk másra használni.IntegratedSecurity = true. Ha viszont elrontottuk (pl.ConnectionString = csBuilder. Az SQLEXPRESS az SQL Server Express példányának a neve.DataSource = @"(local)\SQLEXPRESS".NET osztálykönyvtár. ez alapértelmezett telepítésnél mindig ugyanaz lesz. kezdetnek lekérjük a kapcsolat állapotát. pontosabban a DbConnection megvalósítja az IDisposable interfészt. így nem muszáj mindig kézzel lezárni a kapcsolatot.doksi. A .lekérdezhet a parancssorba beírt hostname paranccsal. A connectionstring –ek könnyebb kezeléséhez egy másik lehet ség. . akkor a képerny n meg kell jelennie az Open szónak. ha mindent jól csináltunk.ConnectionString. connection.)) { connection. connection. Ez – ha a szerver a saját gépünk . Használhatjuk helyette az univerzálisabb (local) –t is.Open(). ami a connectionstring felépítését könnyíti meg. Most már dolgozhatunk az adatbázissal. így megadásakor könnyen hibázhatunk.WriteLine(connection. Használatához a System. Valójában a Jet jóval több szolgáltatást rejt magában. akkor egy SqlException kivételt fogunk kapni. de az évek során használata egybeforrt az Access –szel.Forrás: http://www. mint gondolnánk. elírtuk az adatbázis nevét).. hiszen a többi adatbáziskezel höz van specializált .. ezen belül is az OleDbConnection osztályra. erre hamarosan lesz péda.Close().InitialCatalog = "testDataBase".Open(). Ezt az osztályt az összes provider specializálja.

inkább helyett) Oracle adatbázisokkal való fejlesztéshez érdemes telepíteni az Oracle Data Provider for . majd Add Reference. 53.OracleClient. A letölt oldalak: http://www. Oracle adatbázishoz OLE protokoll segítségével is csatlakozhatunk.Forrás: http://www.hu 316 using (OleDbConnection connection = new OleDbConnection("Provider=Microsoft.Jet.NET osztály. amikor az adatforráshoz nincs más specifikus .4. Az assembly neve System.NET) könyvtárakat illetve az Oracle Developer Tools for Visual Studio –t. és a dokumentáció is széleskör .com/technology/tech/dotnet/tools/index.oracle.OLEDB. amely támogatja az OLE protokollt (pl.mdf'.2 Kapcsolódás Oracle adatbázishoz A többivel ellentétben az ehhez szükséges névtér nem szerepel az alapbeállítások között.oracle.NET (ODP.Open(). } Az OleDb névtér osztályainak segítségével minden olyan adatforráshoz hozzáférünk. A kapcsolatot – nem meglep módon – az OracleConnection osztállyal hozzuk létre. A fentieken kív l (s t.com/technology/tech/windows/odpnet/index. Ezt a névteret leginkább akkor használjuk.Persist Security Info=False")) { connection. így nekünk kell hozzáadnunk az assembly –t.html http://www.html .Data Source='test. ehhez jobb egérgomb a References fülön. A letöltés mindkét esetben ingyenes.doksi. az Access ilyen).Data.0.

Gyakorlatilag megfelel egy adatbázis egy adattáblájának.1 DataTable A DataTable osztály lesz a kapcsolat nélküli réteg alapköve. dc.NET kapcsolat néküli rétegét azok az osztályok alkotják. Egy DataTable oszlopokból és sorokból áll. A DataColumn konstruktorában els helyen az oszlop neve. //egyedi érték az összes sora vonatkoztatva Egy DataTable. A DataTable az oszlopait egy DataColumnCollection típusú gy jteményben tárolja.PrimaryKey = new DataColumn[] { new DataColumn("ID". typeof(int)) }.doksi.Columns. //nullérték engedélyezett dc. megadva a nevét és típusát. Vagy helyben intézzük. Ezután kétféle módja következik oszlop hozzáadásának. Amit fontos tudnunk. Ez az egész folyamat els re bonyolultul hangzik. hasonlóan kivitelezve. //a sorokban az oszlop alapértelmezett értéke dc. typeof(string))). Kapcsolat nélküli réteg Az ADO.Add("Name". Utóbbinál egy Type típust vár és a typeof operátor pont ezt adja vissza.DefaultValue = "semmi".Forrás: http://www. table. Egy oszlopnak számos más tulajdonsága is lehet: DataColumn dc = new DataColumn("TestColumn"). dc. amelyek önállóan. Hozzunk létre egy egyszer DataTable –t: DataTable table = new DataTable("PersonTable"). Vagyis lekérdezzük az adatbázisból a szükséges dolgokat és azokat a kliens oldalon eltároljuk. majd a típusa áll.AllowDBNull = true. 54.Data névtérben vannak. hogy egy DataTable –hoz csakis akkor adhatunk hozzá adatokat (sorokat). //maximális szöveghossz dc.höz els dleges kulcso(ka)t is beállíthatunk: DataTable table = new DataTable("PersonTable").MaxLength = 30. table. Ezután módosításokat végzünk és ha kész vagyunk.Add(new DataColumn("Address".DataType = typeof(string). kapcsolat nélkül tárolják magukban az adatokat.Columns. ha tartalmaz legalább egy oszlopot. . illetve az ezeknek megfelel DataColumn és DataRow osztályok példányaiból. akkor ismét kapcsolódunk a kiszolgálóhoz és végrehajtjuk a változtatásokat. A kapcsolat nélküli réteg osztályai a System. de a munka nagy részét nem nekünk kell elvégeznünk.hu 317 54. így gyakorlatilag tömbként (is) kezelhet . amely az InternalDataCollection osztályból származik. amely megvalósítja az ICollection és IEnumerable interfészeket. A tábal konstruktorában megadtuk a tábla nevét (ez nem kötelez ). typeof(string)). vagy pedig egy már létez DataColumn példányt adunk hozzá. table. illetve használható foreach konstrukcióban.Unique = true.

dt.Add("Name". A DataTable osztálynak van is egy NewRow nev metódusa.DataType = typeof(int).Columns. primKey. //kezd érték dt. "Toalmas").doksi. A teljes példa most ez lesz: DataTable table = new DataTable("PersonTable"). primKey. vagyis olyat ami ismeri a tábla felépítését és automatikusan beállítja a megfelel értékeket. . A hagyományos módszer így nézne ki: table.Columns. mivel nem adtunk meg els dleges kulcsot. DataColumn primKey = new DataColumn("ID"). table. Ezt a DataTable Rows tulajdonságán keresztül tehetjük meg.AutoIncrement = true. Ebben az esetben azonban ez a sor kivételt (ArgumentException) okoz. Nézzük mi okozza a bajt. primKey.AutoIncrement = true. amely értelemszer en DataRow példányokat vár illetve ad vissza. Ez els re meglep lehet hiszen beállítottuk az els dleges kulcs tulajdonságait.AutoIncrementSeed = 0. //amennyivel növeli table. amely megfelel a tábla sémájának. mert azt nem szeretnénk minden alkalommal kézzel megadni. dt. dt.Add("Reiter Istvan". table. table.hu 318 A PrimaryKey tulajdonság egy DataColumn – tömböt vár. amelyben a sor adatai vannak. hiszen egy hagyományos SQL adatbázis is rendelkezhet több els dleges kulccsal. Nyílván azért állítottuk be az els dleges kulcs automatikus növelését. Egy els dleges kulcshoz (de tulajdonképpen bármelyik oszlophoz) automatikusan is hozzárendelhetünk értékeket (hasonlóan mint az „igazi” adatbázisnál): DataTable table = new DataTable("PersonTable").PrimaryKey = new DataColumn[] { primKey }. primKey. typeof(string))). Van azonban egy kis probléma.Columns.AutoIncrementStep = 1.Rows. vagyis egy olyan DataRow példányt ad vissza. A hiba oka az.AutoIncrementStep = 1. typeof(string)).PrimaryKey = new DataColumn[] { dt }. Ekkor a Rows tulajdonság Add metódusa egy paramétertömböt vár. Miután létrehoztuk a tábla sémáját feltölthetjük adatokkal.AutoIncrementSeed = 0. ami pont erre való. DataColumn dt = new DataColumn("TestColumn"). vagy pedig megadjuk az összes oszlopot és így az els dleges kulcsot is.Add(primKey).DataType = typeof(int).Add(new DataColumn("Address". table.Forrás: http://www. hogy ilyenkor csakis a megfelel sémával rendelkez sorokat adhatunk meg.

NewRow().Add(new DataColumn(("Name"). typeof(int))). A.NewRow(). ezeket a módokat a megfelel fejezet részletezi. drOne["Name"] = "Reiter Istvan".Add(drOne). table. akkor azt látnák. typeof(string))).hu 319 DataRow drOne = table. XML file) is rendelhetünk adatokat. Baljos Fasor 13/b.Add(new DataColumn(("Address"). Egy tábla sorait szinte mindig érdemes a NewRow metódussal létrehozni. DataRow drTwo = table. Ha most kiíratnánk a tábla adatait. Természetesen egy DataTable elemeit elérhetjük a programunkban. dr["Name"] = "Reiter Istvan". így sokkal kevesebb a hibalehet ség. A DataTable LoadDataRow metódusával felül tudunk írni létez adatokat: DataTable table = new DataTable("PersonTable").. dr["Age"] = 22. row["Name"]..LoadDataRow(new object[] { "Reiter Istvan".Rows.Rows.. table.".". table. dr["Address"] = "Toalmas. typeof(string))). DataRow dr = table.. a következ példában az el z példa tábláját íratjuk ki: foreach (DataRow row in table. Kula". table.Add(drTwo). .NewRow(). drTwo["Name"] = "Dr. egyébként létehoz egy új sort. row["Address"]). .Columns. 44. drOne["Address"] = "Toalmas .Columns. table. table. LoadOption.WriteLine("Name: {0}.Rows. a módosított adatokkal. hogy egyetlen bejegyzést tartalmaz.". table. A metódus az els kulcs (vagy az els dleges kulcs) alapján „szétnéz” az adatbázisban és ha talál egyezést.Forrás: http://www. drTwo["Address"] = "Erdely. A DataRow indexel ivel az oszlopok nevére hivatkozhatunk. Egy DataTable –hez küls adatforrásból (SQL adatbázis. "World" }. } Az biztosan felt nt már.doksi.Add(new DataColumn(("Age"). Ez a „hagyományos” típustalan adattáblák legnagyobb hátránya.Rows) { Console.OverwriteChanges).Columns. Address: {1}". hogy az oszlopok neveihez nem kapunk semmilyen támogatást a fejleszt eszközt l.Add(dr). s t a fordító sem tud mit csinálni. akkor azt a bejegyzést módosítja. erre a problémára (is) megoldást jelenthet a típusos DataSet –ek használata.

//Added dr["Age"] = 56. Console.doksi.RowState). table. stb… de a változások csakis akkor lesznek véglegesek. Console.WriteLine(dr. //Detached dr["Name"] = "Reiter Istvan".Added: a sort hozzáadtuk egy táblához .Add(dr). amelyeket a RowState tulajdonsággal kérdezhetünk le (vigyázat. //Modified Látható.Columns.NewRow().Unchanged: a legutolsó AcceptChanges hívás óta nem változott a sor értéke .. typeof(int))). table.hu 320 A DataRow –nak különböz állapotai lehetnek.WriteLine(dr.. amikor meghívjuk. módosíthatunk. dr["Age"] = 22. . ha meghívtuk az AcceptChanges metódust. Egy sor állapota a következ k lehetnek: . table.Detached: a DataRow példány már elkészült. //Added dr. Egy sort törölhetünk. Ezzel a metódussal szintén rendelkezik a DataTable osztály is.Add(new DataColumn(("Address"). dr["Age"] = 22. Módosítsuk az el z programot.Deleted: a sort töröltük a Delete metódussal Néhány példa: DataTable table = new DataTable("PersonTable").AcceptChanges(). de nincs hozzáadva egy táblához sem. ezt a tulajdonságot csakis olvashatjuk). typeof(string))). Console.WriteLine(dr. Console. hogy az Unchanged és a Modified állapotokat csakis az AcceptChanged hívása utá veheti fel. table. . akkor az összes sorára is meghívódik. typeof(string))).".WriteLine(dr.RowState).BeginEdit().RowState).Add(new DataColumn(("Age"). A BeginEdit és EndEdit metódusokkal több módosítás is elvégezhet egy soron.Columns.RowState). Console. hogy az utolsó módosítás így nézzen ki: dr.Modified: a sor változott az utolsó AcceptChanges óta .Rows.RowState). DataRow dr = table.Columns. dr["Address"] = "Toalmas. //Unchanged dr["Age"] = 22.Forrás: http://www. .WriteLine(dr.Add(new DataColumn(("Name").

EventArgs e) { DataTable table = new DataTable("Persons"). table.Forrás: http://www.Add(new DataColumn(("Age"). typeof(int))).doksi. typeof(string))).1.0) DataGrid –et leváltva. typeof(string))).1 DataGridView Korábban már láttunk példát adatkötésekre. amikor a ListBox vezérl vel és társaival ismerkedtünk. //Unchanged dr. most nem lesz rá szükség.EndEdit().hu 321 Console. Készítsünk egy új Windows Forms Application projectet! A ToolBox –ban keressük ki a DataGridView –ot (a Data fül alatt lesz) és húzzuk a formra.Add(new DataColumn(("Name"). Ezt ebben az esetben is megtehetjük. . 54. Ezután hozzuk létre a form Load eseménykezel jét és írjuk bele a következ t: private void Form1_Load(object sender. table.WriteLine(dr. A vezérl höz kapcsolódó beállítások ablakot bezárhatjuk a vezérl tetején megjelen ki nyilacskával.RowState).NET Framework 2.Columns.0 verziójában jelent meg el ször. A DataGridView AllowUserToAddRows és AllowUserToDeleteRows tulajdonságainak értékét állítsuk false –ra.Columns. A fenti példákban a DataRow példányokat az eredeti azonosítójukkal kezeltük. amelyek alkalmassá teszik erre a feladatra. a DataGridView.NewRow(). Ez a vezérl a . Adatok megjelenítésére van egy speciális vezérl nk.NET 1.Add(new DataColumn(("Address"). hiszen a DataTable rendelkezik mindazon tulajdonságokkal (interfészekkel). table. a régi (. DataRow dr = table. de ezt megtehettük volna a DataTable Rows tulajdonságával amely DataRowsCollectionnel tér vissza és ennek van indexel je.Columns.

kiválaszthatjuk egy sor vagy oszlop összes celláját is: DataGridViewBand rowBand = dataGridView1.Columns[1].aspx Egy DataGridView –hoz bármely olyan osztály köthet .DataSource = table.Add(dr).NewRow(). dr2["Name"] = "Kovács János". dr2["Age"] = 55. table..http://msdn. IBindingList illetve IBindingListView interfészek valamelyikét. dataGridView1. Az tisztán látszik.AcceptChanges(). } Az eredmény: Bár a DataGridView alapértelmezett megjelenése kissé csúnyácska.Rows. .doksi. Az egyes cellák pedig DataGridViewCell típusúak. a jegyzet ezt a témát nem részletezi. dr["Age"] = 22.". ezeket is lehet indexelni.com/en-us/library/ms171598.microsoft. table. felsorolni.Forrás: http://www.Rows. de érdemes ellátogatni a következ oldalra: .. ezeket DataGridViewRow és DataGridViewColumn típusokkal írhatjuk le. így pl. IListSource. DataGrdiViewBand columnBand = dataGridView1. sok hasonlóval találkoztunk már.hu 322 dr["Name"] = "Reiter Istvan". DataRow dr2 = table. hogy egy DataGridView sorokból és oszlopokból áll. dr["Address"] = "Toalmas.Rows[0]. illetve ennek az osztálynak a leszármazottai. amely megvalósítja az IList. A Rows és Columns tulajdonságok DataGridViewRowCollection illetve DataGridViewColumnCollection típusú gy jteményekkel térnek vissza.Add(dr2). table. err l kés bb még lesz szó. de részletesen konfigurálható. Több cella kombinációját a DataGridViewBand osztállyal csoportosíthatjuk. stb… . dr2["Address"] = "Budapest".

. Address = address. string address) { Name = name.. A táblázatban az adatok a tulajdonságok definiciójának sorrendjében jelennek meg „felülr l lefelé” haladva. } set { age = value.Forrás: http://www. Age = age. table. "Baja. Egyetlen dolognak kell eleget tennünk. mivel ezeket kötjük majd hozzá. el kell készíteni a megfelel tulajdonságokat. EventArgs e) { List<Person> personList = new List<Person>().doksi.").Add(p2). 22.DataSource = personList."). personList. } set { address = value. } } } Ennek az osztálynak a példányaival fogjuk feltölteni a DataGridView –ot. . Person p3 = new Person("Kis Balázs".hu 323 Egy DataGridView –hoz nem csak DataTable objektumokat köthetünk.. public int Age { get { return age. } } private string address..AcceptChanges(). personList. Person p2 = new Person("Kovács János".. Person p1 = new Person("Reiter István".Add(p3). . dataGridView1. hanem hagyományos listákat is.. } private string name. Most hozzuk létre a listánkat: private void Form1_Load(object sender. "Tóalmás. "Budapest. } } private int age. } . Hozzunk létre egy egyszer osztályt: public class Person { public Person(string name. public string Name { get { return name. 66.").. 10. } set { name = value. personList. int age.Add(p1). public string Address { get { return address.

A következ programunk erre is alkalmas lesz. table.DataSource = table..Columns. Három feladatunk lesz: els ként ellen rizzük. dr1["Age"] = 55. azáltal. Ez – valljuk be – nem túl szép megoldás. ezenkív l új sorok beszúrását is meg fogjuk oldani.Add(new DataColumn("Name". Ha megpróbáljuk elhagyni azt a cellát. hogy egy barátságosabb .. hogy módosítsuk az adatainkat a DataGridView –ból.Add(dr2).. Húzzunk egy gombot is a formra. . másodszor a már létez adatok módosítása.Rows. A konstruktorban beállítjuk az oszlopokat: table.Columns. string). table. dataGridView1.doksi.Forrás: http://www.. hogy meghívjuk a DataTable AcceptChanges eseményét.NewRow(). dr2["Address"] = "Kecskemét. ezért módosítjuk a programot. . DataRow dr2 = table. Természetesen felmerülhet az igény.".Add(new DataColumn("Age". Ezután hozzuk létre az adattáblát. table.Rows. akkor egy szép nagy MessageBox –ot kapunk a képünkbe. ezzel fogjuk véglegesíteni a változtatásokat.". hogy a felhasználó megfelel adatot adjon meg (a kornál ne szerepeljen pl. dr1["Name"] = "Kovács János". Haladjunk sorban: bármilyen módosítás nélkül indítsuk el az alkalmazást és valamelyik kor oszlopba írjunk néhány bet t. hiszen a DataTable illetve a lista lokális objektumok voltak. mivel csak egyszeri adatokat jelenítettünk meg. typeof(int))).Add(dr1). typeof(string))).NewRow(). dr1["Address"] = "Budapest. dr2["Name"] = "Kis Balázs".hu 324 Az eredmény: Az eddigi példáink nem voltak túlzottan rugalmasak. A form Load eseményében pedig feltöltjük a táblát és hozzákötjük a DataGridView – hoz: DataRow dr1 = table. dr2["Age"] = 8. végül pedig új sor hozzáadása az adatforráshoz.Columns.Add(new DataColumn("Address". table. A DataGridView AllowUserToAddRows tulajdonságát állítsuk true értékre. de ezúttal a form osztály tagjaként. typeof(string))).

az is megoldható. ha helytelen adatot adtunk meg egy cellában és megpróbálunk továbblépni. A példánknál maradva kezeljük a FormatException –t. DataGridViewDataErrorEventArgs e) { if (e.Exception is FormatException) { MessageBox. amíg jó értéket nem adtunk meg. amely DataGridViewDataErrorEventArgs típusú paramétere hordozza az információt. amely a helytelen formátumú.doksi. egy felsorolt típus (DataGridViewDataErrorContexts) formájában. az értékek kombinálásával: private void dataGridView1_DataError(object sender. de ha mégis. Ha csak bizonyos kivételek érdekelnek minket. Hozzuk létre az eseménykezel t.Commit | DataGridViewDataErrorContexts.CurrentCellChange)) { MessageBox.Forrás: http://www. Például.Show("Kérem adjon meg helyes adatokat!"). DataGridViewDataErrorEventArgs e) { if (e. hogy továbbadjuk a kivételt. Ezt a logikai vagy operátorral tudjuk ellen rizni. akkor a tulajdonság értéke egyszerre lesz Parsing. A Context tulajdonsággal a hiba körülményeir l kapunk információt.Show("Kérem adjon meg helyes adatokat!"). A DataGridView bármely hiba esetén a DataError eseményt aktiválja.Show("Kérem adjon meg helyes adatokat!").hu 325 hibaüzenettel rukkoljon el . vagy típusú adatok esetében váltódik ki: private void dataGridView1_DataError(object sender. ha nem volt kivétel. ha a ThrowException tulajdonság értékét igazra állítjuk: e. DataGridViewDataErrorEventArgs e) { if (e.Context == (DataGridViewDataErrorContexts.Exception != null) { MessageBox. Commit és CurrentCellChanged. } } A paraméter Exception tulajdonsága null értéket vesz fel. akkor az alkalmazás nem szól semmit. amivel már azonosítani tudjuk a hiba forrását. } } Ezenkívül az is megoldható. } } . Finomítsunk kicsit a dolgon: private void dataGridView1_DataError(object sender. de nem léphetünk tovább a cellából.Parsing | DataGridViewDataErrorContexts. de ne írjunk bele semmit. akkor kirakunk egy MessageBox –ot és tájékoztatjuk a felhasználót a hibájáról.ThrowException = true. Ha most elindítjuk a programot és el idézünk valamilyen hibát (mondjuk a kor oszlopba nem numerikus karaktert írunk).

" + e. illetve. amelyek a DataGridView soraira és oszlopaira hivatkoznak: private void dataGridView1_DataError(object sender. DataGridViewDataErrorEventArgs e) { if (e.ColumnIndex. Ezeket a DataGridView már „beépítve” tartalmazza. hogy maga a DataGridView is DataGridViewCell típusú objektumot ad vissza. hogy nem megfelel érték kerüljön egy cellába. } } Látható. A CellValidating akkor lép életbe.RowIndex]. a ColumnIndex és RowIndex tulajdonságokkal. e.Exception != null) { dataGridView1. hogy a rákötött adatforrásban ne módosuljanak az adatok.ColumnIndex. amely közvetlenül a CellValidating után jön.Commit | DataGridViewDataErrorContexts.Show("A " + e.Forrás: http://www. DataGridViewDataErrorEventArgs e) { if (e. mégpedig ErrorProvider –ekkel.Parsing | DataGridViewDataErrorContexts.RowIndex]. amikor a cella elveszíti a fókuszt. Amennyiben a megadott adatok megfelel ek.Value.CurrentCellChange)) { MessageBox.ToString() + ".ToString() + " cella helytelen adatot tartalmaz: " + dataGridView1[e. az indexel je egy Máshogy is jelezhetjük a felhasználóknak a hibás adatokat.ErrorText = "Hibás adatok".doksi. Ezzel az eseménnyel megakadályozhatjuk. akkor a DataGridView mögött lév adatforrás is módosul (ez persze csakis adatkötött környezetben igaz). } } A cellák kezeléséhez van még néhány fontos esemény.RowIndex.ToString()).hu 326 A hibát okozó cellát is egyértelm en azonosíthatjuk. ez az esemény utólagos formázások kivitelezésére hasznos.Rows[e. csak be kell állítanunk a sor vagy cella ErrorText tulajdonságát: private void dataGridView1_DataError(object sender.Context == (DataGridViewDataErrorContexts. indexelhet . Párja a CellValidated. .

az EditMode tulajdonság értéke legye EditOnKeyStrokeOrF2 illetve a SelectionMode legyen FullRowSelect vagy RowHeaderSelect. hogy kijelöljük és nyomjuk le a Del billenty t. Ekkor egyszer en kattintsunk valamelyik sorra. amikor az új sorra kerül a fókusz.Row. ezt a korábban létrehozott gombra drótozzuk rá: private void button1_Click(object sender.Row.Cells[1]. A sor alapértelmezett értékekkel inicializálódik (ezeket a DataColumn DefaultValue tulajdonságával tudjuk beállítani). amely akkor fut le. amely az eltvolítandó sor indexét várja paraméterként. e. Ezt az eseményt a felhasználó irányítására használhatjuk. illetve ha az nincs beállítva akkor nullértékkel (ha engedélyezett: AllowDBNull tulajdonság. Ha valamelyik cellájára rákerül a fókusz.Value = "<Személy neve>". alapértelmezetten igaz érték ).Row. ha nem írunk be semmit és visszalépünk egy másik sorba. amíg meg nem hívtuk az AcceptChanges metódust. .Cells[2]. illetve. e. } Sorok hozzáadása rendkív l egyszer en zajlik.Value = "<Személy lakhelye>". private void dataGridView1_DefaultValuesNeeded(object sender. Ha az AllowUserToAddRows tulajdonság igaz értéket kapott.Value = 0. Sorok törléséhez engedélyeznünk kell az AllowUserToDeleteRows tulajdonságot.doksi. akkor az új sor automatikusan létrejön és ez megtörténik a mögötte lév adatforrásban. Ezenkív l használhatjuk még a DataGridView Rows tulajdonságának RemoveAt metódusát is. DataGridViewRowEventArgs e) { e. akkor ezek az értékek elt nnek.Cells[0]. EventArgs e) { table.AcceptChanges(). Az alapértelmezett sorértékeket futás közben is megváltoztathatjuk a DataGridView DefaultValuesNeeded eseményében.Forrás: http://www.hu 327 Természetesen egy DataTable sorainak állapota addig nem végleges. jelen esetben a DataTable –ben. akkor a DataGridView végén megjelenik egy csillaggal jelzett sor. } Ekkor az adatforrás még nem módosul.

ahonnan adatkötéskor a megjelenített adatok származnak. másodszor pedig hozzákötjük a DataTable oszlopához is. ha nem adjuk meg a HeaderText tulajdonság értékét. hogy kiválaszthassa a felhasználó a személyek nemét. tbc. typeof(string))). hogy egy oszlopot – a megjelenített érték típusától függ en – módosítsunk.DisplayIndex = 2.Name = "Address". "N " }. hanem specializáltabb változatokat is.DataPropertyName = "Age". tbc.AutoGenerateColumns = false. DataGridViewTextBoxColumn tbc = new DataGridViewTextBoxColumn(). Most jön a DataGridViewComboBoxColumn: . hogy a vezérl az adatkötéskor elkészítse a megfelel oszlopokat.Name = "Age". DataGridViewTextBoxColumn tbc1 = new DataGridViewTextBoxColumn().doksi. így a a táblabeli adatainkon nem kell változtatni. tbc2.Add(new DataColumn(("Sex"). Ebben az esetben tulajdonképpen két kötés is lesz. mint pl. DataGridViewTextBoxColumn tbc2 = new DataGridViewTextBoxColumn(). tbc2.Columns. Ezt fogjuk rákötni a ComboBox –ra. tbc1.Name = "Name". tbc. míg a Name az oszlop nevét adja meg. hiszen egyszer megadjuk a vezérl értékkészeltét. amelynek tagjai értelemszer en DataGridViewTextBoxCell típusúak lesznek. Az oszlopokat kézzel fogjuk létrehozni. tbc2.DisplayIndex = 0.DataPropertyName = "Name". és ez fog megjelenni a fejlécben is. A DataPropertyName tulajdonság azt a tulajdonságot jelöli. A specializált oszlopok specializált cellákból állnak. Egészítsük ki a programunkat.DisplayIndex = 1. Miel tt továbbmegyünk készítsünk egy stringtömböt: string[] sexList = new string[] { "Férfi". ehhez állítsuk az AutoGenerateColumn tulajdonságot false értékre. tehát egy DataGridViewTextBoxColumn típusú oszlop cellákra vonatkozó tulajdonsága DataGridViewTextBoxCellCollection típusú gy jteményt fog visszaadni. Ezzel más dolgunk nincsen. A DataGridView ennél problémásabb lesz. egy kép vagy egy ComboBox. Most hozzáadjuk a már ismert adatok oszlopait (az egész a form Load eseményében zajlik): dataGridView1. tbc1. Akár arra is van lehet ség. hiszen alapértelmezés szerint engedélyezett nullértéket megadni erre az oszlopra. A DisplayIndex a megjelenítés sorrendjét határozza meg.DataPropertyName = "Address".hu 328 Egy DataGridView nem csak hagyományos cellákat tartalmazhat. tbc1.Forrás: http://www. Els ként egészítsük ki ezzel az oszloppal a DataTable –t: table. így megakadályozzuk.

cbt.DataPropertyName = "Sex".Sort = "Name". dataGridView1. } A RowFilter tulajdonság egy kifejezést vár. A Sort tulajdonsággal rendezhetjük is az adatokat: private void Form1_Load(object sender. hogy milyen feltétel alapján válogassa ki az oszlopokat. sz rhetjük (hasonló a funkcionalitása mint az SQL where és order by parancsainak). cbt. cbt.DisplayIndex = 3.hu 329 DataGridViewComboBoxColumn cbt = new DataGridViewComboBoxColumn().Forrás: http://www. hogy a DataView köthet . ha kiíratjuk az adott sor értékeit (pl. semmi mást nem kell módosítanunk. Egy DataView jellemz en egy DataTable példányra „ül rá”. 54. Vegyük a következ példát: private void Form1_Load(object sender. EventArgs e) { DataView view = new DataView(table).Name = "Sex".doksi. EventArgs e) { DataView view = new DataView(table). cbt.2 DataView A DataView osztállyal egy táblát több szemszögb l nézhetjük. . A kész program így néz ki: Mentéskor az adattábla is módosulni fog. így egy táblát más – más néz pontból köthetünk rá vezérl kre. view.RowFilter = "Name = 'Reiter Istvan'". view. amely megadja. Látható. illetve rendezhetjük. err l könnyen meggy zödhetünk. A RowStateFilter pedig a sorok állapotának megfelel en keres. Gyakorlatilag készen vagyunk. egy MessageBox –xal).DataSource = view.DataSource = sexList.

primKeyOrd. orders. DataRow custRow2 = customers. orders.DataSource = view. typeof(int)). } Ebben a példában a rendezés a Name oszlop alapján valósul meg. typeof(int)). customers. Egy DataSet tulajdonképpen megfelel egy adatbázisnak. primKeyCust. typeof(string))).Columns. typeof(int))). customers. primKeyCust.AutoIncrement = true.PrimaryKey = new DataColumn[] { primKeyCust }.Columns.AllowDBNull = false. amely a memóriában van.AutoIncrementStep = 1.Add(new DataColumn("OrderedThing".AutoIncrementStep = 1. 54. custRow2["Name"] = "Kis Balázs".Forrás: http://www.Add(new DataColumn("Address". orders.AllowDBNull = false.NewRow(). typeof(string))). primKeyCust. Hozzunk létre egy DataSet –et két táblával: DataTable customers = new DataTable("Customers").AutoIncrementSeed = 0. customers. orders. custRow1["Name"] = "Nagy Sándor". primKeyOrd.Add(new DataColumn("CID".Add(primKeyOrd).Add(primKeyCust). .".AutoIncrementSeed = 0. custRow1["Address"] = "Budapest. DataTable orders = new DataTable("Orders"). DataRow custRow1 = customers. customers. typeof(string))).PrimaryKey = new DataColumn[] { primKeyOrd }. vagyis táblákból és a köztük lév relációkból. A példaprogramban egy egyszer Készítsük el az oszlopokat: ügyfél – rendelés szituációt fogunk modellezni.NewRow().doksi. ..AutoIncrement = true. DataSet data = new DataSet("ShopDataSet").Columns.3 DataSet Egy DataSet példány DataTable és DataRelation objektumokból áll. primKeyCust.hu 330 dataGridView1.Columns.. DataColumn primKeyOrd = new DataColumn("OID". primKeyOrd.Columns.Columns. primKeyOrd.Add(new DataColumn("Name". DataColumn primKeyCust = new DataColumn("CID".

Erre a feladatra a DataRelation osztály szolgál: DataRelation relation = new DataRelation("CustAndOrd". hogy az adatainkat a relációk alapján ábrázoljuk.Rows. amelyre majd a harmadik paraméter a gyermekoszlop mutat (ez gyakorlatilag megfelel egy idegenkulcs relációnak). customers. Kössük hozzá az egyik táblát egy DataGridView –hoz: dataGridView1.Rows. A DataRelation itt bemutatott konstruktora (van még öt) három paramétert vár: a reláció nevét.AcceptChanges().Tables["Orders"]. ordRow1["CID"] = 0. amelyek majd a tábla soraira is meghívják.Add(custRow2). shopData.Rows. shopData. shopData. Maga a program elég egyértelm semmi olyan nincs benne.Tables. akkor a táblák közti relációk (amik az adatbázisban léteztek) elvesznek. a szül oszlopot.Columns["CID"]). 54.Forrás: http://www.Add(customers).1 Relációk táblák között Amikor egy valamilyen adatbázisból beolvasunk adatokat egy DataSet –be. shopData. shopData.Relations. customers. hogy ki mit rendelt. . A DataSet –en meghívott AcceptChanges meghívja az egyes táblák metódusát.Add(ordRow2).".. Ebb l kifolyólag képesnek kell lennünk reprodukálni ezeket a kapcsolatokat.Tables.Add(orders).Tables[1].Columns["CID"].NewRow(). shopData. ordRow1["OrderedThing"] = "Elektromos gyomorkaparó". A relációs objektumot hozzá is kell adnunk a DataSet –hez.Add(relation). DataRow ordRow1 = orders. ordRow2["OrderedThing"] = "Napelemes szemfenéktörl ". DataRow ordRow2 = orders. Természetes az igény.Add(ordRow1). tehát pl.NewRow()..Add(custRow1).Rows.doksi. . ennek tulajdonképpen ez az egyetlen el nye az új vezérl vel szemben. Mi most az egyes vásárlók rendeléseire vagyunk kíváncsiak. amivel már ne találkoztunk volna. orders.hu 331 custRow2["Address"] = "Eger. orders. Erre a feladatra két megoldás is van: az els a „régi” DataGrid (a DataGridView el dje) vezérl használata. ordRow2["CID"] = 1.3. meg tudjuk nézni.DataSource = shopData.Tables["Customers"].

Forrás: http://www.DataSource = shopData.doksi. } A DataMember tulajdonság azt mondja meg. A form Load eseményében fogjuk rákötni az adatforrásokat: private void Form1_Load(object sender. akkor jobb klikk valamelyik szimpatikus fülön és Choose Items.hu 332 Ajunk hozzá a formhoz egy DataGrid –et (ha nincs ott a ToolBox –ban. majd keressük ki a vezérl t). dataGrid1. EventArgs e) { dataGrid1. Ezután az eredmény a következ lesz: A sor elején lév jelre kattintva megjelennek a hozzárendelt relációk: Erre kattintva pedig megnézhetjük a másik tábla adatait: . hogy melyik táblát használjuk.DataMember = "Customers".

Tables["Persons"].com/en-us/library/y8c0cxey. ehelyett egy ún. Készítsük el ket is masterBinding és detailBinding néven.windows. Két DataGridView lesz a formon. Térjünk vissza a Persons táblához és helyezzük el azt egy DataSet –ben.aspx 54. vagyis fordítási id ben semmilyen ellen rzés nem történik a típusok ellen rzésére.hu 333 Ez volt az egyszer bb megoldás.com/en-us/library/system. ezután pedig írjuk a következ t: personsData. EventArgs e) { masterBinding. masterBinding. Ezenkív l szükségünk lesz két BindingSource objektumra is.DataSource = masterBinding. így amikor az els griden a kiválasztott sor módosul. detailBinding. master-detail megoldást fogunk létrehozni.forms. a másodikhoz pedig a táblák közti relációt.microsoft.DataSource = detailBinding.aspx http://msdn.DataSource = masterBinding.bindingsource.DataMember = "Customers". ez az osztály az adatkötések finomrahangolásában segít minket. akkor a másikon megjelenik a hozzá rendelt adat: private void Form1_Load(object sender. a master –ben navigálva (ez a fenti példában a vásárlók táblája lesz) láthatjuk a detail –ben a hozzá tartozó adatokat (a rendelést). masterGrid. Az els BindingSource –höz a Customers táblát fogjuk kötni. A DataGridView nem képes relációk megjelenítésére.doksi.microsoft. detailBinding.Rows[0]["Age"] = "ezegystring".3. Húzzuk rá a két vezérl t a formra és nevezzük el ket masterGrid –nek és detailGrid –nek.DataSource = shopData. detailGrid. } Az eredmény: Részletesebb információkat talál a master-detail kapcsolatokról és a BindingSource osztályról az olvasó a következ oldalakon: http://msdn.2 Típusos DataSet –ek Eddig „normális” típustalan DataSet –eket használtunk.Forrás: http://www. .DataMember = "CustAndOrd".

akkor használhatunk típusos DataSet –eket.Forrás: http://www. de ha nem akarunk ezzel bajlódni.Tables[0]. personsDataSet.doksi. majd válasszuk az Add/New Item menüpontokat és az ablakban válasszuk ki a DataSet –et. ezután pedig grafikus felületen tervezhetjük meg a DataSet elemeit. dataGridView1.Name = "Béla". hogy a táblák a DataSet példányosításakor azonnal létejönnek. az az. így már fordításkor kiderülnek a rossz típus használatából fakadó hibák.hu 334 Ezzel a sorral a program ugyan lefordul.DataSource = personsDataSet. Készítsük is el a Persons DataSet –et egy táblával. Persze odafigyeléssel meg lehet el zni a bajt.Rows. Persons. nem kell kézzel elkészíteni illetve hozzáadni azokat. A példában a neve Persons.PersonsTableRow row = personsDataSet.PersonsTable.AcceptChanges(). . A ToolBox –ból hozzáadhatunk táblákat és relációkat is (meg mást is. amelyeket fordításkor ellen rizhetünk. de err l a következ fejezet szól majd). majd hozzuk létre a DataSet példányt: Persons personsDataSet = new Persons().xsd lesz. A Visual Studio létrehozza a megfelel állományokat. Az egyes oszlopok (amik most fizikailag sorok) tulajdonságait a Properties ablakban állíthatjuk. row.Add(row). row. hiszen stringet nem lehet implicit numerikus értékké alakítani (fordítva viszont m ködne). Ami még fontos. Húzzunk most a formra egy DataGridView –ot. amelynek legyen a neve PersonsTable. personsDataSet. A vélemények megoszlanak a típustalan és típusos megoldások közt. hogy a tábla adatait tulajdonságokon kereszt l adhatjuk meg.Age = 20.NewPersonsTableRow(). A Solution Explorerben kattintsunk jobb egérgombbal a projectre. de futás közben kivétel generálódik (ArgumentException). Látható. row. igazából egyik használatából sem származik különösebb el ny vagy hátrány.PersonsTable.Address = "Budapest".

reader.doksi. amelyekre a lekérdezés hatással volt (ahogy a nevében is benne van. ami a feltételnek megfelel . amellyel ezeket le tudjuk kérdezni: SqlCommand cmd = new SqlCommand(). Ami hiányzik. cmd.ConnectionString = @"Data Source=(local)\SQLEXPRESS. Most küldjük is el a szerver felé a lekérdezést: SqlDataReader reader = cmd.hu 335 55.Forrás: http://www. stb. Name from Persons". ezt nem lekérdezésre.GetInt32(0). az az ún.Connection = connection.Open().Integrated Security=True". ehhez egy ciklust használunk. Egy adatbázison kétféleképpen tudunk lekérdezést végrehajtani. } Az adatbázisunkban legyen egy Persons nev tábla.Initial Catalog=Test. vagy pedig a szerveren hívunk meg egy tárolt eljárást. Name: {1}". mindkét esetben a DbCommand absztrakt osztály egy leszármazottját fogjuk használni. Az SqlDataReader Get*** metódusai a megfelel típusú adatokat adja vissza a paramétereként megadott oszlopból. amíg van mit olvasni. ami tulajdonképpen az adatok kezeléséért felel s. hogy a lekérdezett adatokkal dolgozzunk. while (reader. Írjunk egy lekérdezést. Az ExecuteScalar csakis az els sort míg az ExecuteNonQuery csakis azoknak az oszlopoknak a számát.GetString(1)). Adatbázis adatainak lekérdezése és módosítása Azt már tudjuk. amelyeket az ExecuteReader metódus visszaad. cmd. használjuk). reader. . hanem törlésre. amelynek egy ID és egy Name oszlopa van. connected layer. a CommandText pedig a lekérdezést fogja tartalmazni. méghozzá azokat. Most meg is kell jelenítenünk az adatokat. } Az SqlDataReader sorokat olvas ki az adatbázisból. Akárhogy is döntünk. vagy a klienst l küldjük a lekérdezést. módosításra. A Connection tulajdonság az SqlConnection –re mutat.ExecuteReader().WriteLine("ID: {0}. ez MSSQL szerver esetén az SqlCommand lesz. hogyan kapcsolódjunk egy adatbázis szerverhez és az sem okozhat gondot. Az SqlCommand utasításait háromféle módon hathatjuk végre: az ExecuteReader visszaadja az összes sort. connection.CommandText = "select ID. ami addig megy.Read()) { Console. Készítsünk egy Console Application –t (az egyszer ség kedvéért) és nyissunk egy kapcsolatot: using (SqlConnection connection = new SqlConnection()) { connection.

stb.1 Connected és Disconnected rétegek közti kapcsolat Az el z részben már megismerkedtünk a kapcsolat nélküli réteggel. selectCmd. . utána a típusos társának használata is bemutatásra kerül. Name from Persons". a keresett személy azonosítóját: SqlCommand cmd = new SqlCommand(). adapter.doksi.Fill(personsDataSet). mivel a DataGridView vezérl t fogjuk használni az adatok megjelenítésére.Forrás: http://www. mint az el z fejezetben. Mi most értelemszer en az SqlDataAdapert osztály példányait fogjuk használni. cmd. akkor pedig létehozza: adapter. hogy közvetítsen. Ezután az Update metódussal egyszer en meghívjuk ezeket a lekérdezéseket: adapter. adapter. ha még nem. hogy a kett t össze tudjuk kapcsolni egy olyan osztályra van szükségünk.SelectCommand = selectCmd. hogy a szerveren már van is egy tárolt eljárás GetPersonById néven.). Az adatbázis legyen ugyanaz. Meg kell írnunk a szükséges törlés. A DataSet mellett a tábla nevét is megadhatjuk. selectCmd. akkor az adatokat hozzáf zi.Parameters. cmd.CommandText = "GetPersonById".Fill(personsDataSet. A Fill metódusa meghívja a SelectCommand utasítását és feltölti a paramétereként megadott DataSet –et (a háttérben tulajdonképpen az el z fejezetben bemutatott DataReader osztályok m ködnek). Ez – ahogy azt már megszokhattuk – egy absztrakt osztály.Value = 1. DeleteCommand. egyetlen dolga van. Most a personsDataSet Persons táblájának módosításait írtuk vissza. ha tárolt eljárást akarunk használni. Erre a DataAdapter osztályt fogjuk használni.Connection = connection.Int).CommandText = "select ID. cmd. SqlDataAdapter adapter = new SqlDataAdapter(). A SelectCommand tulajdonsága egy SqlCommand példányt vár. hogy mit kell tennünk. amelynek leszármazottai az egyes adatbázis szerverek sajátosságaihoz specializálódnak. 55. amely egy paramétert kap. SqlDbType. amely „hídat” képez köztük.Update(personsDataSet.hu 336 Most nézzük meg.CommandType = CommandType. ez fog az adatok lekérdezésére szolgálni (ugyanígy van UpdateCommand. "Persons"). Az adatok visszaírása az adatbázisba is nagyon egyszer . módosítás SQL lekérdezéseket és ezeket SqlCommand formájában megadni az adapternek. SqlCommand selectCmd = new SqlCommand().StoredProcedure. Tegyük fel. cmd. El ször egy típustalan DataSet –et használunk.Add("@PersonID".Connection = connection. de most hozzunk létre egy Windows Application –t. Ahhoz. Maga az adapter gyakorlatilag semmir l nem tud semmit. ha az már létezik. "Persons").

hiszen grafikus felületen varázslók segítségével konfigurálhatjuk be az egészet. ekkor megjelenik az adatforrást kiválasztó ablak. Szintén ez jelenik meg akkor. akkor a View menüben megtaláljuk (vagy használjuk a Ctrl+Alt+S billenty kombinációt). amely egyel re teljesen üres. ha még nem dolgoztunk adatbázissal: Válasszuk ki a Microsoft SQL Server –t. ezt s szokásos jobb klikk a projecten Add/New Item m veletekkel tudjuk megtenni.hu 337 Típusos DataSet –ek esetében még egyszer bb lesz a dolgunk.doksi. akkor kattintsunk a Change gombra. A projecthez adjunk hozzá egy DataSet –et. ha a Data Source sorban nem a következ áll: Microsoft SQL Server (SqlClient). Ezután megjelenik a tervez nézet. Kattintsunk jobb egérgombbal a Data Connections elemen és válasszuk ki. akkor rögtön az Add Connection ablak jelenik meg. Most szükségünk lesz a Server Explorer ablakra. azon belül pedig a „.NET Framework Data Provider for SQL Server” –t. ha alapértelmezetten nem látható. Ha korábban már dolgoztunk valamilyen adatbázisal. OK után visszatérünk az el z ablakhoz: .Forrás: http://www. hogy Add Connection.

ha a parancssorba (Start/Futtatás cmd) beírjuk a hostname parancsot. Könnyebben is hozzá tudunk jutni ezekhez az információkhoz. Az SQL Server Express neve alapértelmezetten SQLEXPRESS lesz. ekkor a kapcsolódás ablakban megjelennek a szükséges információk: .hu 338 A Server Name sorba kell írnunk a számítógép azonosítóját és az SQL Server példányának nevét. Elöbbit megtudhatjuk.doksi. akkor alapértelmezés szerint ment). ha megnyitjuk az SQL Server Management Studio –t. ezt meg kellett adnunk telepítéskor (kivéve ha a Visual Studio telepítette.Forrás: http://www.

automatikusan csatlakozik majd (ha van futó SQL Server).hu 339 Térjünk vissza a Visual Studio –hoz. Írjuk be a megszerzett adatokat. Ha hibaüzenetet kapunk. fölül zöld ikonnal és futó státusszal egy m köd SQL Express 2005 látható. hogy Start. Ismét teszteljük a kapcsolatot a Visual Studio –ban. Ha nincs elindított szerver (az ikon piros a példányok neve mellett). A képen a Configuration Manager látható. Ezután létrejönnek a táblák és automatikusan a hozzájuk tartozó relációk is: . akkor jobb egérgombbal kattintsunk a szerverpéldányon és válasszuk ki. Tehát nyissuk le a fület és keressük ki a táblák közül azokat amelyeket használni akarunk. akkor ellen rizzük. Itt találjuk az SQL Server Configuration Manager –t. A Start menü programjai közül keressük ki az SQL Servert. Húzzuk át ezeket a DataSet tervez jébe. Nyomjuk meg az Ok gombot.Forrás: http://www. akkor csak nyissuk le a fülét. azon belül pedig a Configuration Tools mappát.doksi. alul ínaktív 2008 –as társa. akkor ki tudjuk választani a használni kívánt adatbázist. majd kattintsunk az alul lév Test Connection gombra. ekkor az adatbázis megjelenik a Server Explorer –ben. Ha m ködik. hogy elindítottuk –e az SQL Servert. Ezután ha használni akarjuk.

hogy minden oszlopot lekérdezünk. Ekkor a megjelen ablakban megnézhetjük az adott tábla tartalmát. de ezt b víthetjük is. Ebben az ablakban megtekinthetjük a táblák tartalmát az adapterek segítségével.doksi. Egyel re csak annyit tudunk tenni. illetve az adapteren létrehozott lekérdezések alapján mást is csinálhatunk: . Válasszuk ki valamelyik adaptert.hu 340 Minden egyes táblához elkészül egy TableAdapter objektum is. A táblákat tesztelhetjük is. kattintsunk rajta jobb egérgombbal és válasszuk ki a Preview Data menüpontot.Forrás: http://www. kattintsunk jobb egérgombbal a tervez n és válasszuk a Preview Data menüpontot.

A ToolBox –ból húzzunk egy TableAdapter –t a tervez be. A táblák generálása után a típusos dataset –ekkel való munka nagyon hasonló a hagyományosokéhoz. illetve. amelynek megadhatjuk az új táblák helyét. hogy adatbázistól függetlenül DataTable objektumokat adjunk a dataset –hez. mivel a generált osztályok mind parciális osztályok. Ehhez kattintsunk jobb gombbal valamelyik táblára és válasszuk. ekkor megjelenik egy varázsló. . hogy milyen m veleteket generáljon. azzal a különbséggel.doksi. hogy View code. Az egyes táblákat tesz leges kóddal is b víthetjük.hu 341 Egy dataset nem csak egy adatbázisból tartalmazhat adatokat.Forrás: http://www. hogy mostantól IntelliSense támogatás és fordítási idej típusellen rzés is jár a csomaghoz. vagyis attól függetlenül kiegészíthet ek. Ugyanígy arra is van lehet ség.

A megnyitáshoz az XmlReader egy statikus metódusát a Create –et fogjuk használni. ezekr l is lesz szó). case XmlNodeType. internal elérés leszármazottja (tehát közvetlenül nem tudnánk példányosítani).AppendText(reader.Element: richTextBox1.Value).Xml névtérben vannak. break. break. a típusos DataSet –ek. } Az XmlReader NodeType tulajdonsága egy XmlNodeType felsorolás egy tagját adja vissza.Forrás: http://www.doksi. hogyan tudunk beolvasni egy XML filet. Függetlenül attól. A következ példában használni fogjuk az XmlWriter osztály egy leszármazottját. a példában a legalapvet bb típusokat vizsgáltuk és ezek függbényében írtuk ki a file tartalmát.Text: richTextBox1. amelyek segítségével villámgyorsan hatjhatóak végre a m veletek (persze van nekik számos specializált változatuk is. Els ként nézzük meg.AppendText("</" + reader.AppendText("<" + reader. végig tudunk iterálni rajta (a példában egy RichTextBox vezérl t használunk a megjelenítésre): while (reader.Read()) { switch (reader.1 XML file –ok kezelése A szükséges osztályok a System. Ennek oka az. hogy az XmlReader egy absztrakt osztály tudjuk példányosítani. stb… is ezt a formátumot használja az adatsémák felépítésre. break. XML A . hogy ilyen esetekben valójában egy XmlTextReaderImpl típusú objektum jön létre. Ezek absztrakt osztályok. amely az XmlReader egy bels . A két legalapvet bb ilyen osztály az XmlReader és az XmlWriter.EndElement: richTextBox1.NodeType) { case XmlNodeType. default: break.xml").NET Framework er sen épít az XML –re.Name + ">"). }.hu 342 56.Create("test. ez egy stream –t l kezdve egy szimpla filenévig mindent elfogad: XmlReader reader = XmlReader. a LINQ To SQL. az XmlTextWriter –t. Miután tehát megnyitottuk a filet.Name + ">"). case XmlNodeType. ugyanis a filet kódból fogjuk létrehozni: . 56.

writer.WriteAttributeString("Note".17.WriteComment(DateTime. 20:00:59--> A file személyek adatait fogja tartalmazni: writer. "List of persons").0" encoding="utf-8"?> <!--2008. writer. writer.0" encoding="utf-8"?> <!--2008. a WriteElementString pedig feltölti azt értékekkel. A file tartalma most a következ : <?xml version="1. 20:05:24--> <PersonsList Note="List of persons"> <Person> <Name>Reiter Istvan</Name> <Age>22</Age> </Person> . 20:02:05--> <PersonsList> <Person> <Name>Reiter Istvan</Name> <Age>22</Age> </Person> </PersonsList> Az egyes tagekhez attribútumokat is rendelhetünk: writer.17.Formatting = Formatting. Encoding. az Xml file -októl megszokott hierachikus szerkezetet fogja megteremteni (ennek az értékét is beállíthatjuk). Elkezdjük az adatok feltöltését.WriteStartDocument(). Els paraméter az attribútum neve. Ekkor a file így alakul: <?xml version="1.WriteStartElement("PersonsList").WriteEndElement().UTF8).0" encoding="utf-8"?> <!--2008. és beszúrunk egy kommentet is.Now. kulcs érték párokkal. A file: <?xml version="1.17.Forrás: http://www.WriteElementString("Age".ToString()). writer. "Reiter Istvan"). utána pedig a hozzá rendelt érték jön.10.doksi.10. "22").hu 343 XmlTextWriter writer = new XmlTextWriter("newxml. A Formatting tulajdonság.10.WriteStartElement("Person"). writer.xml".Indented. writer.WriteElementString("Name". A StartElement egy új „tagcsoportot” kezd a paramétereként megadott névvel. writer.

xml"). ++i) { //reader.MoveToContent(). amely azt az attribútumot tartalmazza.AttributeCount. Említést érdemelnek még a MoveToFirstAttribute és a MovetoNextAttribute metódusok. while (reader. richTextBox1.MoveToElement(). Ez a metódus a pramétereként megadott attribútumra állítja a reader –t.AttributeCount. .Value).MoveToAttribute(i).Create("newxml.Name + " " + reader. Miután végeztünk visszatérünk a kiindulási pontra (hogy a Read metódus tudja végezni a dolgát). Módosítsuk egy kicsit az el z példát: for (int i = 0. richTextBox1. ezekkel a file egy olyan pontjára tudunk navigálni. i < reader. Ez a metódus a legközelebbi olyan csomópontra ugrik.AppendText(reader.MoveToAttribute(i). ha létezik az attribútum.Value). Néhány példa: bool l = reader. reader. amelyen a reder áll (értelemszer en következik. Ellenkez esetben a pozíció nem változik. hogy van –e a csomóponton attribútum. i < reader.Forrás: http://www.doksi.hu 344 </PersonsList> Az XmlReader rendelkezik néhány MoveTo el taggal rendelkez metódussal.Read()) { if (reader.MoveToAttribute("Note"). és a visszatérési érték hamis lesz. reader. és igaz értékkel tér vissza.MoveToNextAttribute(). A MoveToElement metódus visszalép arra a csomópontra. hogy az el z kett metódust gyakran használjuk együtt): XmlReader reader = XmlReader. } reader. ++i) { reader. amely tartalmaz adatot is. amely megfelel egy feltételnek.AppendText(reader. utána pedig az AttributeCount tulajdonság segítségével (amely azoknak a számát adja vissza) végigiterálunk rajtuk.Name + " " + reader. } } A HasAttribute metódussal megtudakoljuk. ekkor az XmlReader (vagy leszármazottainak) Value tulajdonsága ezt az értéket fogja tartalmazni. amelyek nevükhöz méltóan az els illetve a „következ ” attributumra pozícionálnak.HasAttributes) { for (int i = 0.

hogy léteznek gyermekei. Az osztály. A példában az el z leg létrehozott file –t fogjuk használni. } Az XmlDocument a ChildNodes tulajdonságát az XmlNode –tól örökli (ugyanígy a HasChildNodes –t is). Most ismerkedjünk meg az XmlReader egy leszármazottjával az XmlTextReader – rel. amelyb l egyébként maga az XmlDocument is származik. Egy XML file egyes csomópontjait egy-egy XmlNode objektum fogja jelképezni. Egy XmlDocument bejárható foreach ciklussal a ChildNodes tulajdonságon kereszt l. A következ példában egy XmlDocument els szinten lév gyermekeit járjuk be: foreach (XmlNode node in xdoc.Forrás: http://www. Persze nem biztos. mint amit az XmlReader esetében már láttunk. Egy másik fontos osztály. amely egy XmlNodeList objektumot ad vissza. .doksi. A forrás betöltését az XmlDocument Load illetve LoadXml metódusaival végezzük: XmlDocument xdoc = new XmlDocument(). Ahogy azt már megszokhattuk az XmlDocument számos forrásból táplálkozhat.xml"). Az XML DOM (Document Object Model) a file –okat a hierarchikus felépítésük szerint kezeli. hogyan tudjuk manipulálni a file –t.ChildNodes) { richTextBox1. xdoc. amelyen kereszt l elérjük a DOM –ot az az XmlDocument lesz. amelyet az XmlReader példányosításánál létrehoztunk. stream vagy egyszer filenév. 56.Load("test.xml"). Az objektum létrehozása a hagyományos úton zajlik (neki is megadhatunk pl. így az egyes XmlNode –okat bejárva a teljes „fát’ megkaphatjuk. Hozzunk létre kódból egy teljesen új dokumentumot: XmlDocument xdoc = new XmlDocument(). pl.2 XML DOM Eddig az XML állományokat hagyományos file –ként kezeltük.Name). de nem utolsósorban a Skip metódus maradt. Most nézzük meg. ennél azonban van egy némileg kényelmesebb lehet ségünk is. amely átugorja egy csomópont gyermekeit és a következ azonos szinetn lév csomópontra ugrik. az az XmlNode. egy filestream –et): XmlTextReader reader = new XmlTextReader("newxml. ezt a HasChildNodes tulajdonsággal tudjuk ellen rizni.AppendText(node. Gyakorlatilag az XmlTextReader tulajdonságai megegyeznek annak az osztályéval.hu 345 } Végül. Használata teljesen ugyanolyan.

"test". és a játékosok pontszámát szeretnénk eltárolni. handler = (sender. a másik pedig a ReplaceChild.AppendChild(text). A Save metódussal pedig el tudjuk menteni a dokumentumot.CreateElement("Test"). 56. amely jelzi. XmlNode destination = source. amely egy létez csúcsot távolít el a csúcsok listájából. ha a kiírt adatokat meg tudnánk feleltetni egy osztálynak? Ez megtehetjük. Ez els re semmi különös. akár filenevet. formátumára. Az XmlText és XmlElement osztályok is az XmlNode leszármazottai. e) => { MessageBox. Tulajdonképpen a szérializálás annyit tesz. Kezeljük le például azt az eseményt. Természetesen a szérializálás jelent sége ennél sokkal nagyobb és nemcsak XML segítségével tehetjük meg. amikor egy új csúcsot adunk hozzá: XmlNodeChangedEventHandler handler = null. majd visszaolvashatjuk onnan.Save("domtest.Forrás: http://www. hogy a .NodeInserting += handler. Egy hátulüt mégis van.3 XML szerializáció Mi is az a szérializálás? A legegyszer bben egy példán kereszt l lehet megérteni a fogalmat.CloneNode(false). képzeljük el. hogy írtunk egy játékot.AppendChild(element). Az elért pontszám mellé jön a játékos neve és a teljesítés ideje is.Value). Két másik metódusról is megemlékezünk. els a RemoveChild.Node. amely felülír egy csúcsot. akár egy stream –et megadva. "test"). }. A metódus egyetlen paramétert vár.hu 346 XmlElement element = xdoc. hogy ez egy elég bonyolult feladat: figyelni kell a beolvasott adatok típusára. XmlNode node = xdoc. xdoc. node.CreateTextNode("Hello XML DOM!"). mégpedig az.Element. amelyek segítségével teljes kör felügyeletet nyerhetünk. xdoc.xml"). A fenti kód eredménye a következ lesz: <Test>Hello XML DOM!</Test> A CloneNode metódussal már létez csúcsokat „klónozhatunk”: XmlNode source = xdoc. stb… Nem lenne egyszer bb. Az XmlDocument eseményeket is szolgáltat.doksi. hiszen simán kiírhatjuk az adatokat egy file –ba. ha készítünk egy megfelel osztályt.CreateNode(XmlNodeType. amelyet szérializálva kiírhatunk XML formátumba és onnan vissza is olvashatjuk (ezt deszérializálásnak hívják).Show(e. hogy a csúcs gyermekeit is átmásoljuk –e. XmlText text = xdoc.

Egy nagyon fontos dolgot kell megjegyeznünk.Open). ezek közül az XML a legáltalánosabb.0"?> <dateTime>2008-10-23T16:19:44. [XmlElement("PlayerName")] public string PlayerName { get { return playername.hu 347 memóriabeli objektumainkat egy olyan szekvenciális formátumba konvertáljuk. Ezeken kív l még szükség lesz egy alapértelmezett konstruktorra is (a deszérializáláshoz. DateTime deser = (DateTime)ser. Kezdjük egy egyszer példával (hamarosan megvalósítjuk a pontszámos példát is). Már említetük. } } private int score. DateTime. az osztálynak csakis a publikus tagjai szérializálhatóak a private vagy protected elérés ek automatikusan kimaradnak (emellett az osztálynak magának is publikus elérésünek kell lennie). Most már eleget tudunk ahhoz.xml".Serialization névtérben található. Ezután létrejön egy XML file. hiszen ott még nem tudja. ser. hogy milyen objektumról van szó). hogy több lehet ségünk is van a szerializációra. hiszen ezt más környezetben is felhasználhatjuk.53125+02:00</dateTime> Az XmlSerializer a System. XmlSerializer ser = new XmlSerializer(typeof(DateTime)). FileMode.Forrás: http://www. benne a szérializált objektummal: <?xml version="1. XmlSerializer ser = new XmlSerializer(typeof(DateTime)). hogy készítsünk egy szérializálható osztályt. szérializáljunk egy hagyományos beépített objektumot: FileStream fstream = new FileStream("serxml. A deszérializálás hasonlóképpen m ködik: FileStream fstream = new FileStream("serxml. amelyb l vissza tudjuk alakítani az adatainkat.Deserialize(fstream).doksi.xml".Serialize(fstream.Now). [XmlElement("Score")] public int Score { . } set { playername = value. FileMode.Xml.Create). public class ScoreObject { public ScoreObject() { } private string playername.

so). de így jobban kezelhet . ser.ColumnName = "ID".Serialize(fstream.Now.org/2001/XMLSchema-instance" xmlns:xsd="http://www.xml". [XmlElement("Date")] public DateTime Date { get { return date.DataType = typeof(int).Create). } } } Az egyes tulajdonságoknál beállítottuk. Készítsünk egy egyszer táblát: DataTable table = new DataTable("Persons").Forrás: http://www. hogy eltároljuk az objektumainkat. XmlSerializer ser = new XmlSerializer(typeof(ScoreObject)).Date = DateTime. } } private DateTime date.org/2001/XMLSchema"> <PlayerName>Player1</PlayerName> <Score>1000</Score> <Date>2008-10-23T16:34:32. hogy az miként jelenjen meg a file –ban (sima element helyett lehet pl. so. Rendben. Mi van akkor.AutoIncrement = true. so. FileMode. ha az objektum jóval bonyolultabb. hogyan tudunk egy DataTable/DataSet objektumot XML formátumba menteni.hu 348 get { return score.4 XML és a kapcsolat nélküli réteg Az el z fejezetben szérializációval értük el.w3.AutoIncrementStep = 1. FileStream fstream = new FileStream("scores. Enélkül is lefordulna és m ködne. primcol. DataColumn primcol = new DataColumn(). primcol. . primcol. primcol. most szérializáljuk: ScoreObject so = new ScoreObject().AutoIncrementSeed = 0. so. } set { score = value.734375+02:00</Date> </ScoreObject> 56.0"?> <ScoreObject xmlns:xsi="http://www.doksi.PlayerName = "Player1". attribútum is).w3.Score = 1000. primcol. } set { date = value. esetleg nem is egy objektumról van szó? Ebben a fejezetben megtanuljuk. És az eredmény: <?xml version="1.

row2["Name"] = "Nagy Ede". table. Az eredmény sokkal összetettebb lesz.Add(primcol).0" standalone="yes"?> <NewDataSet> <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.Add(row2).doksi. a sémáját is el kell tárolnunk: table. hiszen az egyes oszlopok felépítését is kiírjuk: <?xml version="1... Ezután a WriteXml metódus segítségével kiírhatjuk a tábla adatait: table..".hu 349 table.Rows. DataRow row1 = table. A file: <?xml version="1. Ha szeretnénk visszaolvasni a táblát.Forrás: http://www. typeof(string))).. XmlWriteMode.</Address> </Persons> <Persons> <ID>1</ID> <Name>Nagy Ede</Name> <Address>Baja.</Address> </Persons> </DocumentElement> Ahogy azt látjuk. ez a megoldás csak a tábla adatait írja ki..NewRow(). .org/2001/XMLSchema" xmlns:msdata="urn:schemasmicrosoft-com:xml-msdata"> <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="Persons" msdata:UseCurrentLocale="true"> .Columns. row1["Address"] = "Budapest. .PrimaryKey = new DataColumn[] { primcol }.WriteXml("persons.Add(row1). row2["Address"] = "Baja. .Columns.NewRow().0" standalone="yes"?> <DocumentElement> <Persons> <ID>0</ID> <Name>Kovács János</Name> <Address>Budapest.WriteXml("persons. table.Rows.xml". row1["Name"] = "Kovács János". . DataRow row2 = table.Columns. table.Add(new DataColumn("Name"..w3.".WriteSchema).xml"). typeof(string))).Add(new DataColumn("Address". table.. akkor annak a szerkezetét.. table.

Figyelem! Még nincs vége: a következ frissítés 2009. . –én várható.ReadXml("persons. Április 12. ..Forrás: http://www.xml"). ahogy azt a táblák esetében már láttuk..</Address> </Persons> <Persons> <ID>1</ID> <Name>Nagy Ede</Name> <Address>Baja. DataSet –ek esetében gyakorlatilag pontosan ugyanúgy járunk el. Amennyiben a tábla adataira nincs szükség. .</Address> </Persons> </NewDataSet> Ezt a file –t már tudjuk használni: DataTable backupTable = new DataTable(). csak a sémára..//Persons" /> <xs:field xpath="ID" /> </xs:unique> </xs:element> </xs:schema> <Persons> <ID>0</ID> <Name>Kovács János</Name> <Address>Budapest.doksi. akkor használjuk a WriteXmlSchema/ReadXmlSchema metódusokat. backupTable.hu 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="..

Sign up to vote on this title
UsefulNot useful