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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

class Program { static public void Main() { int x = 10. //1010 | 0010 = 1010 = 10 Console. 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. } } Biteltolás balra: a kettes számrendszerbeli alak „fels ” bitjét eltoljuk és a jobb oldalon keletkez üres bitet nullára állítjuk.hu 31 Bitenkénti „vagy”: hasonlóan m ködik mint az „és”. class Program { static public void Main() { int x = 143.doksi.WriteLine(x | 2).ReadKey(). Az operátor: <<: 10001111 LEFT SHIFT 100011110 using System.ReadKey(). } } .WriteLine(x << 1). Console. //10001111 (=143) << 1 = 100011110 = 286 Console.Forrás: http://www. Console.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

doksi. ami a 97 decimális számnak felel meg.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. .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

")."). } } class Dobermann : Dog { public Dobermann() { } public override void Eat() //Ez nem fog lefordulni { //valamit csinálunk } } Nem jelölhetünk virtuálisnak statikus.WriteLine("Egy allat eszik. } } class Dog : Animal { public Dog() { } public sealed override void Eat() { Console.. azaz megtilthatjuk.WriteLine("Egy kutya eszik.4 Lezárt osztályok Egy osztályt lezárhatunk.doksi. absztrakt és override –al jelölt tagokat (az utolsó kett egyébként virtuális is lesz... 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..Forrás: http://www. ekkor a leszármazottak már nem definiálhatják át a m ködését: class Animal { public Animal() { } public virtual void Eat() { Console. 19. 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. .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

A generikus adatszerkezetek a generikus ICollection. IEnumerator<T> { } Ekkor a megvalósítás teljesen ugyanúgy m ködik mint a hagyományos esetben. csak épp használnunk kell a generikus paraméter(eke)t. . Például az IEnumerable és IEnumerator is ilyen: class MyClass<T> : IEnumerable<T>. IList és IDictionary interfészeken alapulnak.Forrás: http://www.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.doksi.hu 113 24.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

31.2 Szálak
Elérkeztünk a fejezet eredeti tárgyához, már eleget tudunk ahhoz, hogy megértsük a többszálú alkalmazások elvét. Els programunkban lekérjük az adott programrész szálának az azonosítóját:
using System; using System.Threading; class Program { static public void Main() { Console.WriteLine("Hello from thread {0}", Thread.CurrentThread.ManagedThreadId); Console.ReadKey(); } }

A kimenet minden esetben a következ lesz:
Hello from thread 1

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

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

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

132

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

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

Egyel re ne foglalkozzunk az ismeretlen dolgokkal, nézzük meg azt amit ismerünk. A BeginInvoke –kal fogjuk meghívni a delegate –et, ennek els paramétere megegyezik a delegate paraméterével (vagy paramétereivel). Az EndInvoke fogja majd az eredményt szolgáltatni, ennek visszatérési értéke megegyezik a delegate –éval. Az IasyncResult amit a BeginInvoke visszatérít segít elérni az eredményt és az EndInvoke is ezt kapja majd paraméteréül. A BeginInvoke másik két paraméterével most nem foglalkozunk, készítsünk egy egyszer delegate –et és hívjuk meg aszinkron:
using System; using System.Threading; class Program { public delegate int MyDelegate(int x); static int Square(int x) { Console.WriteLine("Hello from thread {0}", Thread.CurrentThread.ManagedThreadId); return (x * x); } static public void Main() { MyDelegate d = Square; Console.WriteLine("Hello from thread {0}", Thread.CurrentThread.ManagedThreadId); IAsyncResult iar = d.BeginInvoke(12, null, null); Console.WriteLine("BlaBla..."); int result = d.EndInvoke(iar); Console.WriteLine(result); Console.ReadKey(); } }

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

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

Látható, hogy egy új szál jött létre. Amit fontos megértenünk, hogy a BeginInvoke azonnal megkezdi a feladata végrehajtását, de az eredményhez csak az EndInvoke hívásakor jutunk hozzá, tehát küls szemlél ként úgy látjuk, hogy csak akkor fut le a metódus. A háttérben futó szál üzenete is látszólag csak az eredmény kiértékelésénél jelenik meg, az igazság azonban az, hogy a Main üzenete el bb ért a processzorhoz, ezt hamarosan látni fogjuk. Többszálú program írásánál össze kell tudnunk hangolni a szálak munkavégzését, pl. ha az egyik szálban kiszámolt eredményre van szüksége egy másik, kés bb indult szálnak. Az ilyen eseteket szinronizálásnak nevezzük. Szinkronizáljuk az eredeti programunkat, vagyis várjuk meg amíg a delegate befejezi a futását (természetesen a szinkronizálás ennél jóval bonyolultabb, err l a következ fejezetekben olvashatunk):
IAsyncResult iar = d.BeginInvoke(12, null, null); while(!iar.IsCompleted) { Console.WriteLine("BlaBla..."); Thread.Sleep(1000); } int result = d.EndInvoke(iar); Console.WriteLine(result);

Ezt a feladatot az IAsyncResult interfész IsCompleted tulajdonságával oldottuk meg. A kimenet:
Hello from thread 1 BlaBla... Hello from thread 3 BlaBla... BlaBla... BlaBla... BlaBla... 144

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

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

134 valamint egy darab IAsyncResult típusú paraméterrel rendelkezik. Ez a metódus azonnal le fog futni, ha a mellékszál elvégezte a feladatát:
using System; using System.Threading; using System.Runtime.Remoting.Messaging;

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

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

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

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

135

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

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

31.4 Szálak létrehozása
Ahhoz, hogy másodlagos szálakat hozzunk létre nem feltétlenül kell delegate –eket használnunk, mi magunk is elkészíthetjük. Vegyük a következ programot:
using System; using System.Threading; class TestClass { public void PrintThreadInfo() { Console.WriteLine("I'm in thread: {0}", Thread.CurrentThread.Name); } } class Program { static public void Main() { Thread first = Thread.CurrentThread; tmp.Name = "FirstThread"; TestClass tc = new TestClass(); tc.PrintThreadInfo(); Console.ReadKey(); } }

Els ként elneveztük az els dleges szálat, hogy kés bb azonosítani tudjuk, melyikmelyik. Most készítsünk egy másik objektumot, de ezúttal küldjük el a háttérbe:
Thread first = Thread.CurrentThread; tmp.Name = "FirstThread"; TestClass tc = new TestClass(); tc.PrintThreadInfo();

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

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

A kimenet pedig ez lesz:
I'm in thread: FirstThread I'm in thread: SecondThread

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

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

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

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

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

31.6 Szinkronizáció

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

137

A szálak szinkronizációjának egy primitívebb formáját már láttuk a delegate –ek esetében, most egy kicsit komolyabban közelítünk a témához. Négyféleképpen szinkronizálhatjuk a szálainkat, ezek közül az els a blokkolás. Ennek már ismerjük egy módját, ez a Thread.Sleep metódus:
using System; using System.Threading; class Program { static public void Main() { Console.WriteLine("Start..."); Thread.Sleep(5000); Console.WriteLine("Stop..."); Console.ReadKey(); } }

Ez a statikus metódus a paramétereként ezredmásodpercben megadott ideig várakoztatja azt a szálat, amelyben meghívták. Amikor egy szálat leblokkolunk az azonnal „elereszti” a processzort és addig inaktív marad, amíg a blokkolás feltételének a környezet eleget nem tesz, vagy a folyamat valamilyen módon megszakad. Egy viszonylag ritkán használt metódusa a Thread osztálynak a SpinWait. Ez hasonlóan m ködik mint a Sleep, de benne marad a processzorban, csak éppen nem csinál semmit, folyamatosan üres utasításokat hajt végre. Látszólag nem sok értelme van egy ilyen metódus használatának, de olyankor hasznos, amikor csak nagyon rövid (néhány ezredmásodperc) id t kell várni, mondjuk egy er forrásra, ekkor nagy költség lenne cserélgetni a szálakat, mint azt a Sleep teszi. A Join metódus addig várakoztatja a hívó szálat, amíg az a szál amin meghívták nem végezte el a feladatát:
using System; using System.Threading; class Program { static public void Main() { Thread t = new Thread( delegate(){ Thread.Sleep(3000); }); t.Start(); t.Join(); Console.WriteLine("The End"); Console.ReadKey(); } }

A Join –nak megadhatunk egy „timeout” paramétert (ezredmásodpercben), amely id lejárta után – ha a szál nem végzett feladatával – hamis értékkel tér vissza. A következ példa ezt mutatja meg:

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

138
using System; using System.Threading; class Program { static public void Main() { Thread t = new Thread( delegate() { Thread.Sleep(3000); Console.WriteLine("Hello from Thread"); }); t.Start(); if(t.Join(1000) == false) { Console.WriteLine("Timed out..."); t.Abort(); //megszakítjuk a szál futását } Console.ReadKey(); } }

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

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

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

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

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

} } }

A lock –hoz hasonlóan m ködik a Mutex is, a legnagyobb különbség az a kett közt, hogy utóbbi processz szinten zárol, azaz a számítógépen futó összes folyamat elöl elzárja a használat lehet ségét. Az er forrás/metódus/stb. használata el tt meg kell hívnunk a WaitOne metódust, a használat után pedig el kell engednünk az er forrást a ReleaseMutex metódussal (ha ezt nem tesszük meg a kódból, akkor az alkalmazás futásának végén a CLR automatikusan megteszi helyettünk). A következ példában létrehozunk több szálat és versenyeztetjük ket egy metódus használatáért:
using System; using System.Threading; class Program { private static Mutex m = new Mutex(); static void ResourceMethod() { m.WaitOne(); Console.WriteLine("Thread {0} use this resource...", Thread.CurrentThread.Name); Thread.Sleep(1000); Console.WriteLine("Thread {0} release this resource", Thread.CurrentThread.Name); m.ReleaseMutex(); }

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

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

A Semaphore hasonlít a lock –ra és a Mutex –re, azzal a különbséggel, hogy megadhatunk egy számot, amely meghatározza, hogy egy er forráshoz maximum hány szál férhet hozzá egy id ben. A következ program az el z átirata, egy id ben maximum három szál férhet hozzá a metódushoz:
using System; using System.Threading; class Program { private static Semaphore s = new Semaphore(3, 3); static void ResourceMethod() { s.WaitOne(); Console.WriteLine("Thread {0} use this resource...", Thread.CurrentThread.Name); Thread.Sleep(500); Console.WriteLine("Thread {0} release this resource", Thread.CurrentThread.Name); s.Release(); } static void ThreadProc() { for(int i = 0;i < 10;++i) { ResourceMethod(); }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

aktuális

A RichTextBox rengeteg metódust állít rendelkezésünkre a szöveg formázásához. Ehhez el ször ki kell jelölni a szöveget, ezt vagy kézzel tesszük meg, vagy a Find metódussal. Húzzunk a formra három CheckBox –ot, ezekkel fogjuk beállítani, hogy félkövér, d lt vagy aláhúzott legyen a szöveg. A három vezérl használja ugyanazt az eseményt:
private void checkBox3_CheckedChanged(object sender, EventArgs e) { FontStyle style = FontStyle.Regular; switch(((CheckBox)sender).Name) { case "checkBox1": style = FontStyle.Bold; break; case "checkBox2": style = FontStyle.Italic; break; case "checkBox3": style = FontStyle.Underline;

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

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

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

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

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

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

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

181

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

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

tartalmaz

információkat,

LinkText

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

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

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

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

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

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

A FirstDayOfWeek tulajodnságával a hetek kezd napját adhatjuk meg, ez egyes országokban eltér lehet, alapértelmezetten az operációs rendszer nyelve alapján kap kezd értéket. Kijelölhetünk napokat, amelyek a többit l kiemelkednek, ezt a BodledDates, AnnuallyBoldedDates illetve MonthlyBoldedDates tulajdonságokkal érhetjük el, mindegyikük egy DateTime típusú tömböt vár:
private void Form1_Load(object sender, EventArgs e) { monthCalendar1.BoldedDates = new DateTime[] { new DateTime(2008, 9, 2) }; monthCalendar1.AnnuallyBoldedDates = new DateTime[] { new DateTime(2008, 9, 6), new DateTime(2008, 9, 1),

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

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

Az eredmény pedig ez lesz:

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

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

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

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

184

Kétféleképpen tudjuk feltölteni a menüpontokat, a legegyszer bb a Visual Studio tervez nézetét használni, ekkor csak szimplán be kell írni a szöveget. A másik lehet ség, hogy kódból hozzuk létre, ez a következ képpen néz ki:
private void Form1_Load(object sender, EventArgs e) { for (int i = 0; i < 3; ++i) { ToolStripMenuItem item = new ToolStripMenuItem("Menu" + i.ToString()); item.DropDownItems.Add("Menu" + i.ToString() + ".1"); menuStrip1.Items.Add(item); } }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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’

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

majd keressük ki a vezérl t).Forrás: http://www. akkor jobb klikk valamelyik szimpatikus fülön és Choose Items. 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. } A DataMember tulajdonság azt mondja meg. dataGrid1.DataMember = "Customers". 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: .doksi. EventArgs e) { dataGrid1.DataSource = shopData.

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

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

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

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

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

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

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

doksi. Válasszuk ki valamelyik adaptert. A táblákat tesztelhetjük is. Egyel re csak annyit tudunk tenni. illetve az adapteren létrehozott lekérdezések alapján mást is csinálhatunk: .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.Forrás: http://www. de ezt b víthetjük is. kattintsunk jobb egérgombbal a tervez n és válasszuk a Preview Data menüpontot. hogy minden oszlopot lekérdezünk. Ekkor a megjelen ablakban megnézhetjük az adott tábla tartalmát.

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

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

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful