Reiter István - C# (2009, 350 oldal)

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

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

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

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

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

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

Ha nem az lenne megnyitva.Forrás: http://www.doksi.NET fejlesztéshez. valamint a Visual Basic. 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. kiválaszthatjuk.NET. Ez utóbbi automatikusan lefordítja a projektet. IntelliSense rendszert. Új projekt létrehozásához Kattintsunk a File menüre. amely automatikusan felajánlja az elkezdett metódusok/változók/osztályok/stb. 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.hu 13 A Visual Studio tartalmazza az ún. Lefordítani a programot a Build menüpont Build Solution parancsával (vagy az F6 gyorsbillenty vel) tudjuk. ez már támogatja a C# 3. nevének kiegészítését. hogy Debug vagy Release módban fordítsunk. Boo. 3. . IronPython és F# programozási nyelveket is. ha a legutolsó fordítás után megváltozott valamelyik file. Miel tt lefordítjuk a programunkat. Grafikus felület alkalmazás készítéséhez válasszuk ki a Windows Application vagy a WPF Application sablont.0 verzióját. akkor kattintsunk a bal oldali listában a C# elemre. el bbi tartalmaz néhány plusz információt a hibakeresés el segítéséhez (így egy kicsit lassabb).2 SharpDevelop A SharpDevelop egy nyílt forráskódú ingyenes alternatíva Windows operációs rendszer alatti . utóbbi a már kész programot jelenti. A legfrisebb verziót idén februárban adták ki. 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).

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

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

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

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

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

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

Forrás: http://www.NET megfelel jük. Ezeket a tárolókat változóknak nevezzük.Char 1 System.Int16 System.Uint64 System. a többi karakter szám is. 2147483647). értéke igaz(1) vagy hamis(0) El jeles 8 bites egész szám (-128.. ahová az adatainkat ideiglenesen eltároljuk. El jel nélüli 32 bites egész szám (0. 5.32767 El jel nélküli 16 bites egész szám (0. ami azt jelenti.hu 20 5. Lehet leg kerüljük az ékezetes karakterek használatát.. hogy a “rendes” vagy a .Decimal 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...Uint16 System.Byte 1 System. hogy minden egyes változó típusának ismertnek kell lennie fordítási id ben.SByte System.Uint32 System. akkor szükség lehet tárolókra.65535) El jeles 32 bites egész szám (– 2147483647.255) Egy Unicode karakter Logikai típus.Int64 System.Boolean 1 System. . A típus határozza meg..127) El jeles 16 bites egész szám (32768. Egy változót a következ módon hozhatunk létre C# nyelven: Típus változónév.. hogy egy változó milyen értékeket tartalmazhat illetve mekkora helyet foglal a memóriában. A következ táblázat a C# beépített típusait tartalmazza. 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 . A változónév els karaktere csak bet vagy alulvonás jel (_) lehet.Int32 System.NET néven hivatkozunk egy típusra.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. mellettük ott a .1 Típusok A C# er sen (statikusan) típusos nyelv.Single System.String System. A változók a memória egy(vagy több) cellájára hivatkozó leírók. Változók Amikor programot írunk.Double System.NET típus Méret (byte) System.doksi.

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

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

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

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. még az inicializálás el tt is. Tiger. hogy egy értéktípus még nem inicializált egy speciális típust a nullable típust kell használnunk.Wolf.Forrás: http://www. //x == 0 a = Animal. int? x = y. 5. Így a a fenti példában Tiger értéke négy lesz. x = (int)a. 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. amit a “rendes” típus után írt kérd jellel (?) jelzünk: int? i = null. int x = (int)a. míg az értéktípusok memóriában elfoglalt helye a deklaráció pillanatában automatikusan feltölt dik nulla értékekkel. Ugyanez az értéktípusoknál már nem m ködik: int vt = null.7 Null típusok A referenciatípusok az inicializálás el tt nullértéket vesznek fel.hu 24 enum Animal { Cat.doksi. //ez le sem fordul Ez azért van. hogy meg tudjuk állapítani. illetve mi magunk is jelölhetjük ket “beállítatlannak”: class RefType { } RefType rt = null. //x == 3 Magunk is megadhatjuk az értékeket: enum Animal { Cat = 1. mert a referenciatípusok rengeteg plusz információt tartalmaznak. //implicit konverzió y = (int)x.Cat. //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. Wolf } Animal a = Animal. Tiger. Dog = 3. Dog. //explicit konverzió . Ahhoz.

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

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

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

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

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

Forrás: http://www. Console. } } . egyébként 0: 01101101 00010001 AND 00000001 Példa: using System.doksi.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. 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. class Program { static public void Main() { int x = 10.WriteLine(x & 2). public class RelOp { static public void Main() { int x = 10. akkor az a következ képpen jelenik meg a memóriában: 2 00000010 A bit operátorok ezzel a formával dolgoznak.ReadKey(). ha van egy byte típusú változónk (ami egy byte azaz 8 bit hosszú) aminek a „2” értéket adjuk.ReadKey().WriteLine( (x == y) ? "Egyenlo" : "Nem egyenlo").hu 30 feltétel ? igaz-ág : hamis-ág. így pl. using System. Az eddig megismert kett mellé még jön négy másik operátor is. Console. } } 6. int y = 10. //1010 & 0010 = 0010 = 2 Console. A számítógép az adatokat kettes számrendszer –beli alakban tárolja. Console.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ami a 97 decimális számnak felel meg.doksi.Forrás: http://www.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.

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

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

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

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

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

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

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

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

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

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

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

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

Amikor viszont lenyomunk egy gombot a Console.hu 66 class Program { static public void Main() { Derived d = new Derived(). 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. A destruktorokra vonatkozik néhány szabály. 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. Console.doksi. ahogy az várható is volt.ReadKey(). struktúrának nem . } } Amikor elindítjuk a programot nem történik semmi. majd az sobjektum semmisül meg.Forrás: http://www.

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

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

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

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

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

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

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

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

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).6 Gyakorló feladatok 1.hu 75 static public double Pi() { return pi. 2. amelyek meghatározatlan számú paramétert várnak ciklussal olvassuk be a tagokat! .doksi. 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.. vagyis bármennyi taggal elvégezhet ek legyenek a m veletek.*/ } /*Egyéb tagok*/ } 15.Forrás: http://www. } static public double Cos(double x) { /*.. Az összeadás és szorzás metódusok a paramétereiket a params tömbön (ld. metódusok) kapják meg.

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

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

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

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

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

ami mutatja. 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)."). B vítsük ki az sosztályt: class Animal { public Animal() { } public void Eat() { Console. 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. hogy az egyes metódushívásoknál melyik metódust kell meghívni. metódustáblával. hívjuk is meg valamelyik sosztályon: Dog d = new Dog().hu 81 19. rendelkeznek többek közt egy ún. Az eredmény az lesz.WriteLine("Az allat eszik. Persze ezt is meg kell határozni valahogy. ez nagy vonalakban úgy történik.. ezért az eggyel feljebbi st kell . Honnan tudja vajon a fordító. d..doksi. hogy a fordító a fordítás pillanatában megkapja a metódus nevét és elindul „visszafelé” az osztályhierarchia mentén. ugyanakkor megengedi több interfész impementálását (interfészekr l hamarosan).Eat().Forrás: http://www. } } sosztály (egyel re Ezt az új metódust az útódosztályok is örökölni fogják. Ö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. hogy kiírjuk a képerny re a megadott üzenetet. A C# csakis egyszeres örökl dést engedélyez. 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. Készítsük el az elméleti rész példáját (Állat-Kutya-Krokodil) C# nyelven.

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

WriteLine("A kutya eszik. hogy készítettünk egy Animal típusú elemekb l álló tömböt és. Virtuális metódust a virtual kulcsszó segítségével deklarálhatunk: class Animal { public Animal() { } public virtual void Eat() { Console. Egy override –al jelölt metódus automatikusan virtuális is lesz.."). aniArray [0]..").. } } class Dog { Public Dog() { } public override void Eat() { Console. így az leszármazottai is átdefiniálhatják a m ködését.Forrás: http://www. 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.. } } A leszármazott osztályban az override kulcsszóval mondjuk meg a fordítónak. Amit a fordító lát. Csakhogy az Eat() egy virtuális .Eat(). az az.WriteLine("Az allat eszik.doksi. d. aniArray [1] = new Dog(). aniArray [1].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.hu 83 19. aniArray [0] = new Animal().Eat(). A kimenet: A kutya eszik… Mi történik vajon a következ esetben: Animal[] aniArray = new Animal[2].Eat(). hogy az elemein meghívtuk az Eat() metódust. Próbáljuk ki az új metódust: Dog d = new Dog().

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

WriteLine("Egy allat eszik....4 Lezárt osztályok Egy osztályt lezárhatunk.WriteLine("Egy kutya eszik.doksi. 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. } } class Dog : Animal { public Dog() { } public sealed override void Eat() { Console.. absztrakt és override –al jelölt tagokat (az utolsó kett egyébként virtuális is lesz."). azaz megtilthatjuk. de ezt nem kell külön jelölni). .hu 85 A következ fejezet lezárt osztályaihoz hasonlóan egy metódust is deklarálhatunk lezártként. 19. 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 Dobermann : Dog { public Dobermann() { } public override void Eat() //Ez nem fog lefordulni { //valamit csinálunk } } Nem jelölhetünk virtuálisnak statikus.").Forrás: http://www.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

. Plusz feladatként indexellen rzést is lehet végezni az indexel nél. Ezt ellen riztük is az operátor megvalósításánál.doksi. Most csak az összeadás m veletet valósítottuk meg. 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.Forrás: http://www. a többi a kedves olvasóra vár.

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

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

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

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

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

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

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

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

c.i < 10. //ez nem m ködik 24.++i) { Console.hu 110 Stack<int> s = new Stack<int>(10).++i) { s. s. for(int i = 0. new() {} .i < 10.doksi.Pop().WriteLine("{0}.Push(i).Pop()). IFace. {1}".3 Generikus megszorítások Alapértelmezetten egy generikus paraméter bármely típust jelképezheti. Stack<Base> b = s. 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.Forrás: http://www. A deklarációnál azonban kiköthetünk megszorításokat a paraméterre.ToString()). } A generikus típusok nem kovariánsak. c. Stack<string> c = new Stack<string>(10). } for(int i = 0. amelyre normális esetben igen: class Base { } class Derived : Base { } Stack<Derived> s = new Stack<Derived>(10). azaz egy generikus példányt nem kasztolhatunk olyan típusra.Push(i.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ezt a „címe operátorral” (&) és a „*” operátorral végeztük el. string* messagePointer = &message. 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. 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. } } } Els ként létrehoztunk egy változót az üzenettel. vagy meg kell valósítanunk egy bonyolult és id igényes algoritmust).Forrás: http://www.doksi. Ahhoz.: amikor együtt kell m ködnünk az operációs rendszerrel. Console. . utóbbi az adott memóriacím értékét adja visza.ReadKey(). igaz ritkán fordul el olyan probléma amikor ezt ki kell használnunk (pl.hu 126 30. Unsafe kód A C# lehet vé teszi a memória direkt elérését mutatókon keresztül. hogy használhassunk mutatókat (pointereket) az adott metódust. Console. osztályt.WriteLine(messagePointer). class Program { static public void Main() { unsafe { string message = "Hello Pointer!".

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

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

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

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

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(); }

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

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

doksi. . Következ a sorban a binder paraméter. ezzel beállíthatjuk. A második paraméterrel jelezzük. Ezután maga a típus amin a hívást elvégezzük. végül a hívás paraméterei (ha vannak)).Forrás: http://www.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. így ha elgépeltünk valamit az bizony kivételt fog dobni futáskor (System. hogy fordítási id ben semmilyen ellen rzés sem történik a példányosítandó osztályra nézve. Megjegyzend . 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. hogy mit és hogyan fogunk hívni (a fenti példában egy metódust).ArgumentNullException).

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

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

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

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

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

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

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

Most még parancssorból fogunk fordítani és a Visual Studio beépített tervez jét sem fogjuk használni.button.Bevezet A Windows Forms a .Controls. utána pedig a WPF következik. ezért ez az egyszer program is bonyolultnak fog t nni els re.Windows.Size = new System.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. de a késöbbiekben már jobb lesz a helyzet. Készítsük hát el els ablakos programunkat.1 Windows Forms . így arra tudtunk koncentrálni amire kell. this.Text = "Click Me!". EventArgs e) { MessageBox. a következ fejezetekben ez a párhuzamosság már nem fog helyet kapni.Drawing.Text = "FirstWindow" this. public FirstWindow() { this.0 és a 3. 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.button.Drawing.Drawing. class FirstWindow : Form { private Button button. this. Entity Framework) eszközeivel. using System. this. 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. 30). 34. A WF az eredeti Windows API metódusati hívja meg menedzselt környezetben.Point(50.NET 2. this.Size(100.doksi.button.Forrás: http://www. 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). vagy is a nyelv elemeinek megértésére.5 (LINQ.Show("Hello WinForms!").button = new Button(). Grafikus felület alkalmazások fejlesztése Az eddigi fejezetekben konzolalkalmazásokat készítettünk. megtanulunk adatokat és adatbázisokat kezelni a .Location = new System. A jegyzet hátralév részében megismerkedünk a Windows Forms és a Windows Presentation Foundation alapelemeivel. } protected void Button_Click(object sender.Click += new EventHandler(Button_Click). Azonban a C# (és a .Add(button).Forms.button.hu 151 34. } public static void Main() . 200). this.Size(200.Size = new System.NET els grafikus interfész API (Application Programming Interface) –jának neve. 100).

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

EventArgs e) { comboBox1.Forrás: http://www. stb…) is használhatóak. A vezérl t a form Load eseményében töltöttük fel adatokkal: .Text). egy filerendszer elemei). } 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. A fa egyes elemei TreeNode típusuak.FindString(textBox1.doksi. 36.SelectedIndex = radioButton1.Checked == true ? comboBox1.Text) : comboBox1.FindStringExact(textBox1.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Show("A letöltés kész!"). hanem az ImageIndex tulajdonságon keresztül egyesével is beállíthatjuk azt: treeView1. Egy ImageList –et pl. megadva az egyes Node –okhoz rendelt ikont.5 ImageList Ennek a komponesnek a segítségével több képet tudunk kezelni.Forrás: http://www. A komponensnek el re megadhatjuk az elemeit.doksi. 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. RunWorkerCompletedEventArgs e) { MessageBox. } 37. Adjunk néhány elemet a vezérl höz. a képek méreteit és színmélységét is. 37.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. hasonlóan mint egy gy jteménnyel. felhasználhatunk egy TreeView vezérl höz. A többszálúságról szóló fejezetben már talákoztunk vele.ImageIndex = 0. .Nodes[0]. ezután az eredmény: Természetesen nem csak egy képet rendelhetünk az összes Node –hoz. A komponensben beállíthatjuk. Az ImageList „tetején” megjelenik egy kis nyilacska. ekkor azok „belefordulnak” a futtatható állományba. Húzzunk a formra egy ImageList komponenenst és egy TreeView vezérl t.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

térköz nélkül helyezi el.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. hogy az elemeit ömlesztve.microsoft. A StackPanel és WrapPanel sajátja. 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. hogy a vezérl k minden esetben teljes mértékben kitöltik a saját helyüket.doksi. Az Orientation tulajdonság megváltoztatásával vízszintes elrendezést kapunk: <StackPanel Orientation="Horizontal"> Az látható. 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.: <Window x:Class="JegyzetWPF. Amennyiben megadjuk a Horizontal/VerticalAlignment tulajdonságot.Window1" x:Name="window1" xmlns="http://schemas.Forrás: http://www.com/winfx/2006/xaml" .microsoft.

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

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

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

és csakis azon kereszt l érhetjük el. vagy pedig nagyobb mint egy cella. de ekkor két lehet ség van: vagy kisebb mint amekkora egy cella.microsoft.microsoft. automatikusan is elrendezi ket).com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. Minden egyes vezérl ugyanannyi helyet foglal el.doksi. Nézzünk ezekre néhány pédát: A „normális” eset: <Window x:Class="JegyzetWPF. pontosabban minden cella mérete egyenl .ZIndex="1" Width="170" Height="70">Button2</Button> </Canvas> </Window> A ZIndex –et a Panel –tól örökli.com/winfx/2006/xaml/presentation" . akkor a ZIndex tulajdonságát is beállíthatjuk.Window1" x:Name="window1" xmlns="http://schemas.Left="100" Width="170" Height="70">Button1</Button> <Button Panel. ekkor a cella közepén foglal helyet és körülette megfelel méret térköz lesz. é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. Ez alapértelmezetten minden elemnek nulla.Forrás: http://www.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <Canvas> <Button Canvas.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. 43.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. akkor az érvényesülni is fog.Window1" x:Name="window1" xmlns="http://schemas. ekkor a ZIndex –e módosul és a „kilógó” része a többi vezérl t alatt lesz. Ha valamelyik elemnek explicit megadjuk a szélességét vagy magasságát.

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.doksi. de nagyobb mint kellene: <UniformGrid> <Button Width="170" Height="20">Button1</Button> <Button>Button2</Button> <Button>Button3</Button> <Button>Button4</Button> </UniformGrid> 43. .hu 227 xmlns:x="http://schemas.Forrás: http://www. Amit az el z ekkel meg tudunk csinálni.6 Grid A Grid a WPF leger teljesebb (és leggyakrabban használt) container osztálya. de kissebb: <UniformGrid> <Button Width="100" Height="20">Button1</Button> <Button>Button2</Button> <Button>Button3</Button> <Button>Button4</Button> </UniformGrid> Explicit megadás. azt vele is reprodukálni tudjuk.microsoft.

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

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

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

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

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

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

0" /> <GradientStop Color="Blue" Offset="0.ImageSource = new BitmapImage(new Uri("test.Fill> <RadialGradientBrush> <GradientStop Color="Red" Offset="0. XAML: <Rectangle. ib.doksi. közvetlenül a GradientStops tulajdonságot használva (eddig a konstruktorban adtuk meg ket).Fill = ib. 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: . Ugyanez XAML – ben: <Rectangle. brushRectangle.Forrás: http://www.Relative)).hu 234 Most egy kicsit másként adtuk hozzá színeket.5" /> </RadialGradientBrush> </Rectangle.jpg". UriKind.Fill> <ImageBrush ImageSource="test.Fill> Az ImageBrush –sal képeket tudunk kezelni: ImageBrush ib = new ImageBrush(). A vezérl knek egyszer bben állíthatjuk be a színezését.jpg" /> </Rectangle. mivel ezek a Control osztálytól öröklik a Background és Foreground tulajdonságukat.Fill> Ha rosszul adtuk meg a kép nevét XamlParseException –t kapunk.

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

Forrás: http://www.2 Vezérl k Ebben a fejezetben megismerkedünk az alapvet vezérl kkel. mint amit már ismerünk. A TextBlock –ot . Funkciójuk szinte semmit nem változott a WinForms –hoz tapasztaltakhoz képest.hu 236 Egyes vezérl k rendelkeznek saját kerettel is. hiszen a vezérl k képességei nagyjából ugyanazok. a TextBlock kakukktojás ebben a fejezetben.2. A példákban jobbára a küls megjelenéssel foglalkozunk. 44. de valójában nagy különbség van közöttük. de a Label –hez való hasonlósága miatt itt a helye. de tulajdonságaik illetve deklarációjuk igen. 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. S t.1 TextBlock és Label Ez a két vezérl els ránézésre ugyanazt a célt szolgálja.doksi. hiszen nem vezérl .

a példában az ALT+t kombináció azt eredményezi.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.</Bold><LineBreak /> Ez pedig <Italic>d lt bet s. amelyre a megfelel billenty kombinációval a fókusz kerül.doksi.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. . Az adatkötésekr l (amelyet a Binding osztály vezérel majd) kés bb beszélünk majd.microsoft. A gyorsbillenty t a kiválasztott karakter elé írt alulvonással tudjuk kijelölni. hogy a fókusz a TextBox –ra kerül.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.Window1" x:Name="window1" xmlns="http://schemas.Window1" x:Name="window1" xmlns="http://schemas. hogy kis mennyiség szöveges (akár formázott) tartalmat jelenítsen meg: <Window x:Class="JegyzetWPF.hu 237 arra találták ki.microsoft.Forrás: http://www.</Italic> </TextBlock> </Grid> </Window> A szövegformázást a Label is támogatja. hanem mert <Window x:Class="JegyzetWPF.microsoft. de támogatja a gyorsbillenty ket: t nem ezért szeretjük.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 CheckBox –ot nem lehet .com/winfx/2006/xaml" Title="Window1" Height="150" Width="200"> <Grid> <Grid.Header> Milyen napszak van? </GroupBox.2 CheckBox és RadioButton Vegyük a következ XAML kódot: <Window x:Class="JegyzetWPF.RowDefinitions> <Label x:Name="label1" Content="Napszak: " Grid.microsoft.doksi.Row="1"> <GroupBox. Ezenkív l a Label egy ContentControl leszármazott is.microsoft. így mindig csak egyet tudtunk megjelelölni.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.RowDefinitions> <RowDefinition Height="30" /> <RowDefinition Height="*" /> </Grid. akkor megszokhattuk.2. így a Binding –et sem fogja felajánlani. hogy egy GroupBox –ban elhelyezett CheckBox –oknak megadhattunk csoportot.Forrás: http://www.Window1" x:Name="window1" xmlns="http://schemas.Row="0"/> <GroupBox Grid. nekünk kell beírni. 44. err l a következ fejezetben olvashatunk többet.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. A WPF máshogy m ködik.

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

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

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

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

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

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

Window1" x:Name="window1" xmlns="http://schemas.hu 245 paramétertípus egy generikus osztály.Forrás: http://www. 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. amelyek értelemszer en a régi és az új értéket hordozzák magukban.Row="0" /> <Button x:Name="button1" Content="Start" Width="100" Height="30" Grid.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. A paraméter tulajdonságai közt megtaláljuk a NewValue és az OldValue tagokat.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.RowDefinitions> <RowDefinition Height="100" /> <RowDefinition Height="*" /> </Grid.Row="1" Click="button1_Click" /> </Grid> </Window> Az eredmény pedig: .microsoft. a fenti esetben például double értékekre specializálódott.doksi.

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

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

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

TextPath tulajdonság. akár olyan objektumot is tartalmazhat.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. amivel az elemek egy tulajdonságának értékét tehetjük kereshet vé: <ComboBox x:Name="combobox1" IsEditable="True" TextSearch.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. Ekkor is rákereshetünk.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.Forrás: http://www. Ha látni is szeretnénk. A ComboBoxItem. akkor az IsEditable tulajdonságát kell true értékre állítanunk.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> . ha beállítottuk a TextSearch. amit írunk. amely nem szöveges tíusú.doksi. de keresni tud az alapján.Text attached property –t: <ComboBox x:Name="combobox1" IsEditable="True" Width="100" Height="30" > <ComboBoxItem> <Button TextSearch.

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

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

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

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

Minden olyan típust támogat. 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. <Window x:Class="JegyzetWPF.microsoft.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).Window1" x:Name="window1" . megállítás.com/winfx/2006/xaml" Title="Window1" Height="130" Width="150"> <Grid> <Image x:Name="image1" Width="100" Height="100" Source="image.Forrás: http://www.2. rendelkezik az alapvet m veletek (lejátszás.microsoft.com/winfx/2006/xaml" Title="Window1" Height="130" Width="150"> <Grid> <TabControl x:Name="tabcontrol1" Width="120" Height="100" > <TabItem Header="TAB1"> <TextBlock Text="TAB1" /> </TabItem> <TabItem Header="TAB2"> <TextBlock Text="TAB2" /> </TabItem> </TabControl> </Grid> </Window> 44.9 Image és MediaElement Az Image vezérl képek megjelenítésére szolgál (gyakorlatilag megfelel a WinForms –os PictureBox –nak).microsoft.doksi.hu 254 xmlns:x="http://schemas. A megjelenítend képet a Source tulajdonságán kereszt l tudjuk megadni: <Window x:Class="JegyzetWPF.Window1" x:Name="window1" xmlns="http://schemas. stb…) elvégzéséhez szükséges metódusokkal.

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

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

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

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

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

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

a TargetType tulajdonság segítségével viszont egy adott vezérl típusra érévnyesíthetjük a stílust: . így lehet vé téve több stílus összeolvasztását: <Style x:Key="style1"> <Setter Property="Control.0" /> <GradientStop Color="Black" Offset="0. amelynek értékeit az új stílus is használja.FontSize" Value="42" /> </Style> <Style x:Key="style2" BasedOn="{StaticResource style1}"> <Setter Property="Control. de eseményeket is hozzárendelhetünk egy vezérl höz az EventSetter objektumokkal: <Style x:Key="eventstyle"> <EventSetter Event="ButtonBase. hogy egy tulajdonság értékét nem tudjuk egy egyszer értékkel megadni.FontFamily" Value="Arial Black" /> <Setter Property="Control.Value> </Setter> </Style> Nem csak tulajdonságokat.Click" Handler="Button_Click" /> </Style> A BasedOn tulajdonság beállításával megadhatunk egy másik stílust. mivel beállítottuk a bet méretet.Forrás: http://www.Background"> <Setter.Value> <RadialGradientBrush> <GradientStop Color="Red" Offset="0. ekkor a következ t tehetjük: <Style x:Key="backgroundstyle"> <Setter Property="Control.75" /> </RadialGradientBrush> </Setter. amely felülbírálja a stílust. Gyakran el fordul.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.doksi.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.

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

Ezek között a feltételek közt ÉS kapcsolat van. akkor a szöveg pirosra vált.IsFocused" Value="true" /> </MultiTrigger. de ha lekerül róla a fókusz visszatér a rendszerértékhez. Nem marad viszont úgy. 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. Természetesen egy stílus több trigger –t és egy trigger több Setter –t is tartalmazhat. azaz minden feltételnek teljesülnie kell ahhoz. mondjuk <TextBox Style="{StaticResource multitriggerstyle}" Width="100" Height="20" /> Amikor gépelni kezdünk a TextBox –ba.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. 46.hu 263 <Trigger Property="Control. 46.Triggers> <EventTrigger RoutedEvent="MouseEnter"> . a Trigger által definiált viselkedés csakis addig tart.doksi. most csak egy egyszer példa következik): <Style TargetType="{x:Type Button}"> <Style. amíg az aktiválását kiváltó tulajdonság a megfelel értéken áll.IsMouseOver" Value="true"> <Setter Property="Control.Setters> <Setter Property="Control.1.Forrás: http://www. akkor a bet méret megn .IsEnabled" Value="true" /> <Condition Property="Control.Foreground" Value="Red" /> </MultiTrigger.3 EventTrigger Az EventTrigger eseményekre reagál.2 MultiTrigger A MultiTrigger segítségével összetettebb feltételeket is felállíthatunk.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.Setters> </MultiTrigger> </Style.Triggers> <MultiTrigger> <MultiTrigger.1.Conditions> <Condition Property="Control.Conditions> <MultiTrigger. hogy a trigger aktiválódjon. <Style x:Key="multitriggerstyle"> <Style.FontSize" Value="42" /> </Trigger> </Style.

Triggers> </Style> A példában egy egyszer animációt definiáltunk.(SolidColorBrush.Background).doksi.Color)" From="White" To="Black" Duration="0:0:5" /> </Storyboard> </BeginStoryboard> </EventTrigger.TargetProperty="(Button.hu 264 <EventTrigger.Forrás: http://www.Actions> <BeginStoryboard> <Storyboard> <ColorAnimation Storyboard. 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. . amely akkor kezd dik.

Egy példán kereszt l világosabb lesz a dolog egy kissé. Amikor Windows Forms –szal dolgoztunk.és vizuális fák intézményével. Egy korábbi fejezetben már megismerkedtünk a logikai. 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" . egy Grid –et egy Rectangle –t.hu 265 47. Segítségünkre lesz az XamlPad nev program.Forrás: http://www. róluk egy másik fejezetben esik szó majd. 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. A WPF – felépítéséb l adódóan – változtat ezen. hogy miként tudunk új sablont készíteni: ezt a fát kell átdefiniálni. most ezt a tudásunkat fogjuk egy kicsit b víteni. hogy az semmit sem veszítsen funkcionalitásából (s t. Indítsuk el és készítsünk egy egyszer ListBox vezérl t egy ListBoxItem –mel. úgy. Sablonok Ez a fejezet a vezérl k sablonjaival foglalkozik. a WPF még két másik HierarchicalDataTemplate) rendelkezik. amely megmutatja a vizuális fát. Kapcsoljuk be és nézzük meg az eredményt: Amit látunk az nem más.doksi. mint a ListBox vezérl alapértelmezett sablonja. 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. Fölül találunk egy ikont. stb… Most már könny kitalálni. Tartalmaz pl. amely a Windows SDK –val érkezik (általában beépül a Start menübe). akár egy teljesen eltér elemet is készíthetünk).

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

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

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

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

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

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

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 : . transzformációs mátrixxal: M11. hogy olyan tulajdonságokkal vértezhetjük fel az alkalmazásainkat. hogy a WPF mer en új grafikus rendszerrel rendelkezik. 49.1 Transzformációk Minden transzformációs osztály a System. 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. 1 A harmadik oszlop – mivel a WPF csak lineáris transzformációkat használ – állandó lesz. az OffsetX és OffsetY tagok pedig az X illetve Y tengelyen való eltolás mértékét jelzik. M12. 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. 0 M22. ennek egyik hatalmas el nye. 0] OffsetY. Ennek gyakorlatilag csak a MatrixTransform esetén lesz szerepe. [ M21. El bbi a vezérl k tulajdonságainak (pl. szélesség.Windows. Ebben a részben csakis 2D –s transzformációkat és animációkat nézünk meg. amelyek megvalósítása eddig embertpróbáló feladat vagy éppen lehetetlen volt.: helyzet. ebb l négy el re definiált viselkedést hordoz.Media. egy lehet vé teszi tetsz leges transzformáció létrehozását míg az utolsó több transzformációból állhat. A következ fejezetekben mindegyikükkel megismerkedünk. animációk és transzformációk képében. stb…) manipulálásával dolgozik. A maradék négy változó a szándékunktól függ en kap szerepet.Transform osztályból származik.Forrás: http://www.hu 272 49. Ennek az osztálynak hat leszármazottja van. A WPF rögtön két módszert is felkínál. Ilyen lehet ség a vezérl ink „életrekeltése” is.doksi. OffsetX. Animációk és transzformációk Már megtanultuk. A transzformációs mátrix kiindulási helyzete a következ : 100 [010] 001 Tehát az egységmátrix.

Gyakorlatilag az esetek kilencvenkilenc százalékában nem lesz szükségünk erre a transzformációra. Az új koordináták ábrázolva (zölddel): 49.3).3) és (2.1). A transzformációs mátrix igen egyszer lesz.doksi. mivel ez az egyetlen olyan WPF transzformáció.1). a többiek ezt a részletet elrejtik el lünk.hu 273 A pirossal jelölt háromszög csúcsainak a koordinátái: (1. Természetesen ugyanezt megtehettük volna sokkal egyszer bben is az OffsetY változó állításával.Forrás: http://www. Vagyis egy egységgel eltoltuk a háromszöget az Y tengelyen pozitív irányba.1. 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).2) és (2. (1. (1. Vegyük a következ példát: .1 MatrixTransform Az új ismereteinknek megfelel en az els vizsgált alany a MatrixTransform lesz. amikor szükségünk van a transzformációs mátrixra.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

hu 289 To="220" Duration="0:0:5" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle. két stílus)). 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. azt jeleznünk kell a fordítónak is. 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. By csakis From –mal vagy önmagában állhat.DesiredFrameRate="30"> Egyetlen dolog van amir l még nem beszéltünk ez pedig a By tulajdonság. 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. hogy nem a Rectangle tulajdonságai közt kell keresgélnie. egészen addig amíg az animált elemet a GC el nem takarítja. A megoldást a BeginStoryBoard HandoffBehavior tulajdonsága jelenti. el is indul. Ennek egyetlen hátulüt je a memóriaigénye. . A fenti példát nagyon egyszer en kijavíthatjuk: <DoubleAnimation Storyboard. hogy nem használhatjuk egyszerre mindhárom tulajdonságot. Az emberi szem nagyjából 25-30 frame/sec –et lát folytonos mozgásnak. mivel a második animáció megérkezésekor az els azonnal leáll. de érdemes tudni róla. Ha a To mellett a By is jelen van. A WPF alapértelmezés szerint 60 frame/sec értékkel dolgozik. ugyanakkor az eredeti animáció(k) is a memóiában marad(nak). ezt pedig a CLR nem tudta feloldani. 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. 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. Lefordul. ugyanis ekkor egy teljesen új animáció jön létre. hanem arra amikor több forrásunk van (pl. Van azonban egy kis probléma. akkor a tulajdonság nevét zárójelek közé kell írni. hiszen nem tudja róla. <Storyboard Timeline.Forrás: http://www.doksi. akkor utóbbi semmilyen hatással nem lesz.Left)" To="220" Duration="0:0:5" /> Tehát a szabály: ha attached property –t akarunk animálni. el bbi esetben az animált tulajdonság a From értékét l a From és a By összegéig tart. míg utóbbi esetben az alapértékt l a By –ig. vagy amíg egy új animációt alkalmazunk az objektumon. Amikor ilyen tulajdonságot használunk. Ezt viszonylag ritkán használjuk. Az els és legfontosabb információ róla az az.TargetProperty="(Canvas. Ha ennek értékét „Compose” –ra állítjuk akkor a két animáció egyesül. hogy tudja mit hol kell keresnie. de amikor el akarjuk indítani az animációt egy InvalidOperationException –t kapunk.

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

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

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

Forrás: http://www. 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. míg az animációban a FillBehavior tulajdonságot Stop – ra állítottuk.doksi.hu 293 </Button> A RenderTransformOrigin tulajdonságot azért állítottuk be. hogy a gomb a középpontja körül forogjon. .

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

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

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

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

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

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

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

SubmitEvent.com/winfx/2006/xaml" Height="110" Width="180" BorderThickness="2" BorderBrush="Blue"> <Grid> <Grid.Forrás: http://www.Form" xmlns="http://schemas.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. hogy megvalósítsuk a felhasználói felületet.microsoft. } } Az eseményt el is kell indítanunk.AddHandler(Form.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. Borzasztóan puritánok leszünk. } Eljött az id .doksi.SubmitEvent. ez most igazából nem lényeges: <UserControl x:Class="JegyzetWPF.SubmitEvent).Row="1" Click="submitbutton_Click" /> </Grid> </UserControl> A submitbutton Click eseménye fogja elindítani az általunk definiált eseményt: . value).hu 301 public event RoutedEventHandler Submit { add { base.RemoveHandler(Form. } remove { base. 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. RaiseEvent(args).RowDefinitions> <RowDefinition Height="80" /> <RowDefinition Height="*" /> </Grid.microsoft. value).RowDefinitions> <UniformGrid Width="150" Height="70" Rows="3" Columns="2" Grid.

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

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

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’

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

… 21 2 Kecskemét. ugyanis egy személy több házzal is rendelkezhet. Ezútán jön a feltétel.hu 312 Baja. mégpedig az. … A táblák létehozásánál ún. vagyis az egyes házak tartalmaznak egy azonosítót.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. hogy hogyan rendeljük össze a két táblát.… Budapest. amely egy másik táblára mutat idegen kulcsnak (foreign key) nevezzük. amely meghatározza egy oszlop tulajdonságait. majd azokat a mez ket. 52. amivel kiválasztjuk a kívánt sort. amelyeket módosítani akarunk az új értékekkel együtt. Rendben. 52. Ezt a relációt egy–több (onee-to-many) relációnak nevezzük.… Budaörs. Ezt az azonosítót.doksi.Forrás: http://www.… Itt az ötödik pozicíóban lesz eltér karakter (ö – p) és ez alapján már tud rendezni. Több típusa is van. 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 .9 Relációk A relációk megértéséhez képzeljünk el egy egyszer példát. léteznek ezenkív l egy-egy illetve több-több relációk is. Felmerülhet a kérdés. constraint –et használunk. van még egy probléma. Nézzük meg. Vegyük a már létez Persons adatbázist és készítsünk egy új adattáblát Houses névvel. 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. A táblákat létrehozó utasítások a következ ek lesznek: . Erre fogjuk használni az els dleges kulcsokat. mi most egy foreig key constraint –et fogunk bevezetni. amely az t birtokló személyre mutat (a Persons tábla megfelel sorának els dleges kulcsára). Tehát vannak személyek és hozzájuk tartozó házak.

Address from Persons inner join Houses on Houses. hogy melyik tábla melyik oszlopára hivakozunk. amely visszaadja a házakhoz tartozó személyeket: select Name. 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.ID . Name varchar(50) ) create table Houses ( HID int not null primary key.doksi. 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.PID = Persons. 52.hu 313 create table Persons ( ID int not null primary key.

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

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

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

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

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

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

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

doksi.0 verziójában jelent meg el ször.Add(new DataColumn(("Age"). .0) DataGrid –et leváltva. EventArgs e) { DataTable table = new DataTable("Persons"). A vezérl höz kapcsolódó beállítások ablakot bezárhatjuk a vezérl tetején megjelen ki nyilacskával. A fenti példákban a DataRow példányokat az eredeti azonosítójukkal kezeltük. 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.RowState). most nem lesz rá szükség. Ez a vezérl a . 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.hu 321 Console.1 DataGridView Korábban már láttunk példát adatkötésekre. table.1. table. amelyek alkalmassá teszik erre a feladatra.Add(new DataColumn(("Address"). typeof(string))). A DataGridView AllowUserToAddRows és AllowUserToDeleteRows tulajdonságainak értékét állítsuk false –ra.NewRow(). hiszen a DataTable rendelkezik mindazon tulajdonságokkal (interfészekkel).EndEdit(). typeof(string))). 54.Columns. Adatok megjelenítésére van egy speciális vezérl nk. //Unchanged dr.NET Framework 2. de ezt megtehettük volna a DataTable Rows tulajdonságával amely DataRowsCollectionnel tér vissza és ennek van indexel je.Add(new DataColumn(("Name").WriteLine(dr. typeof(int))).Columns.Columns. amikor a ListBox vezérl vel és társaival ismerkedtünk. Ezt ebben az esetben is megtehetjük. DataRow dr = table. table. a régi (.NET 1. a DataGridView.Forrás: http://www.

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

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

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

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

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

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

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

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

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

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

akkor jobb klikk valamelyik szimpatikus fülön és Choose Items. EventArgs e) { dataGrid1.DataSource = shopData. 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: . dataGrid1. A form Load eseményében fogjuk rákötni az adatforrásokat: private void Form1_Load(object sender. hogy melyik táblát használjuk.hu 332 Ajunk hozzá a formhoz egy DataGrid –et (ha nincs ott a ToolBox –ban.doksi.Forrás: http://www.DataMember = "Customers". majd keressük ki a vezérl t). } A DataMember tulajdonság azt mondja meg.

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

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

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

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

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

ha megnyitjuk az SQL Server Management Studio –t. ezt meg kellett adnunk telepítéskor (kivéve ha a Visual Studio telepítette. Az SQL Server Express neve alapértelmezetten SQLEXPRESS lesz. Könnyebben is hozzá tudunk jutni ezekhez az információkhoz. akkor alapértelmezés szerint ment). 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. ha a parancssorba (Start/Futtatás cmd) beírjuk a hostname parancsot.Forrás: http://www.doksi.

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

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

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

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

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful