Reiter István

C#
2009, 0.91 verzió

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ezért könny elkerülni a fenti hibát.1 Kulcsszavak Szinte minden programnyelv definiál kulcsszavakat. blokkokkal jelöljük ki. ellenkez esetben a fordító hibát jelez.writeline(…) –t írnánk a program nem fordulna le.A kis. 4.és nagybet k különböz jelent séggel bírnak. Az “int” egy beépített típus neve is. azaz kulcsszó. metódusok. mert az egyes fordítóprogramok az ezekkel a szabályokkal létrehozott kódot tudják értelmezni. .1. A C# C-stílusú szintaxissal rendelkezik (azaz a C programozási nyelv szintaxisához hasonlít). }) segítségével. ezért nem fog lefordulni a program: int int.17 Ebben a bekezdésben szerepel néhány (sok) kifejezés ami ismeretlen lehet. Ezeket az azonosítókat a saját meghatározott jelentésükön kív l nem lehet másra használni. amelyek speciális jelent séggel bírnak a fordító számára. akkor azokra a szabályokra gondolunk. amelyek megszabják a forráskód felépítését. Ez azért fontos.) ún. aminek az “int” nevet akarjuk adni. ez három fontos kitételt von maga után: . a kapcsos zárójelek ({.Az egyes utasítások végén pontosvessz (.WriteLine(…) helyett console. azaz a “program” és “Program” azonosítók különböznek. 4.A program egységeit (osztályok.) áll . A C# 77 darab kulcsszót ismer: abstract as base bool break byte case catch char checked class const continue decimal default delegate do double else enum event explicit extern false finally fixed float for foreach goto if implicit in int interface internal is lock long namespace new null object operator out override params private protected public readonly ref return sbyte sealed short sizeof stackalloc static string struct switch this throw true try typeof uint ulong unchecked unsafe ushort using virtual volatile void while . //hiba A legtöbb fejleszt eszköz megszínezi a kulcsszavakat (is). a jegyzet kés bbi fejezeteiben mindenre fény derül majd. stb. Vegyünk például egy változót. Ha a fenti kódban Console.1 A C# szintaktikája Amikor egy programozási nyelv szintaktikájáról beszélünk.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

10. } Console. byte y = x as y.52 int x = 300. byte y = checked((byte)x). nulla 10. Az ellen rzés kikapcsolását is megtehetjük az unchecked használatával: int x = 300.3 Karakterkonverziók A char típust implicit módon tudjuk numerikus típusra konvertálni. } Ennek az operátornak a legnagyobb haszna az. if(x is int) //ha x egy int { Console. hogy egy adott osztály megvalósít –e egy bizonyos interfészt (err l kés bb). referenciatípusokhoz null). } } . Console.WriteLine(x).ch <= 'z'.++ch) { int x = ch.2 Is és as Az is operátort típusok futásidej lekérdezésére használjuk: int x = 10. mivel némi teljesítményvesztéssel jár. hogy le tudjuk kérdezni. 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'. byte y = unchecked((byte)x).WriteLine("X tipusa int"). Párja az as az ellen rzés mellett egy explicit típuskonverziót is végrehajt: int x = 10. ekkor a karakter Unicode értékét kapjuk vissza: using System.ReadKey(). Az ajánlás szerint ellen rzött konverziókat csak a fejlesztés ideje alatt használjunk.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

6 Gyakorló feladatok 1.. Az összeadás és szorzás metódusok a paramétereiket a params tömbön (ld. Készítsük el a Math statikus osztályt! Az osztály tartalmazzon metódusokat a négy alapm veletre (illetve tetsz leges matematikai m veletre).. } static public double Cos(double x) { /*. amelyek meghatározatlan számú paramétert várnak ciklussal olvassuk be a tagokat! . 2. 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.75 static public double Pi() { return pi. vagyis bármennyi taggal elvégezhet ek legyenek a m veletek.*/ } /*Egyéb tagok*/ } 15. metódusok) kapják meg.

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

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

//Ez viszont igen } } .78 name = n. //Ez nem m ködik Name = n.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

hogy valahogy kijavíthassuk ezt a hibát. kivételt fog dobni. 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). elkapni azt és végül kezeljük a hibát: using System. class Program { static public void Main() { int[] x = new int[2]. A fenti programra a következ lesz a kimenet: A hiba: Index was outside the bounds of the array. Console.IndexOutOfRangeException –t. hogy ezek is blokkok. try { x[2] = 10.WriteLine("A hiba: {0}". amikor túlindexeltünk: using System. akkor egy ún.103 23. class Program { static public void Main() { int[] x = new int[2]. Kivételkezelés Nyílván vannak olyan esetek.ReadKey(). Kivételt a throw utasítással dobhatunk: . amikor az alkalmazásunk nem úgy fog m ködni. Ezután a program leáll. így kivételt kapunk.Message). a catch pedig elkapja a megfelel kivételt (arra figyeljünk. Amikor az alkalmazásunk „rossz” állapotba kerül. mégpedig egy System. } } A try blokk jelöli ki a lehetséges hibaforrást. } } Itt az utolsó érvényes index az 1 lenne.IndexOutOfRangeException e) { Console.ReadKey(). e. x[2] = 10. Az ilyen „abnormális” m ködés kezelésére találták ki a kivételkezelést. } catch(System. } Console. ilyennel már találkoztunk a tömböknél. ezért el fogjuk kapni a kivételt. ahogy elképzeltük. 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..")).104 using System.Message). ekkor minden kivételt elkap: try { throw(new System.WriteLine("A hiba: {0}".Exception e) { Console. } catch(System.WriteLine("A hiba: {0}".")). akkor a vezérlést az els alkalmas catch blokk veszi át. így ha ezt adjuk meg a catch –nél. } catch { Console. } .1 Kivétel hierarchia Amikor kivétel dobódik.Exception("Exception. e.Message). class Program { static public void Main() { try { throw(new System. A catch –nek nem kötelez megadni a kivétel típusát. akkor az összes lehetséges kivételt el fogjuk kapni vele: catch(System. e.Exception e) { Console.IndexOutOfRangeException e) { Console. e.Message). } catch(System.Message).Exception e) { Console.. hiszen az mindent elkapna.WriteLine("Kivétel történt…"). } } } A C++ -tól eltér en itt példányosítanunk kell a kivételt. ha az az utolsó helyen áll.WriteLine("A hiba: {0}". catch(System.. de ha van olyan amelyik az s kivételt kapja el.Exception osztályból származik. } Egyszerre több catch is állhat egymás után. akkor a program csak akkor fog lefordulni. e. } 23.Exception("Exception.WriteLine("A hiba: {0}".

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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,

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:

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

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ó

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:

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

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

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)).Name = i. } Console.Start(). threads[i]. for(int i = 0.i <threads. } } .ReadKey().ToString().141 } static public void Main() { Thread[] threads = new Thread[10]. threads[i].Length.

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

. hogy mit és hogyan fogunk hívni (a fenti példában egy metódust).ArgumentNullException). Az InvokeMember els paramétere annak a konstruktornak/metódusnak/tulajdonságnak/… a neve amelyet meghívunk. A második paraméterrel jelezzük. 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.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. Következ a sorban a binder paraméter. Ezután maga a típus amin a hívást elvégezzük. Megjegyzend . ezzel beállíthatjuk. hogy fordítási id ben semmilyen ellen rzés sem történik a példányosítandó osztályra nézve.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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:

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;

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:

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)

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),

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:

184

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. A StackPanel és WrapPanel sajátja.microsoft. Amennyiben megadjuk a Horizontal/VerticalAlignment tulajdonságot.com/winfx/2006/xaml" .microsoft. Ezen a problémán segíthetünk „margók” beállításával. hogy a vezérl k minden esetben teljes mértékben kitöltik a saját helyüket. Az Orientation tulajdonság megváltoztatásával vízszintes elrendezést kapunk: <StackPanel Orientation="Horizontal"> Az látható.: <Window x:Class="JegyzetWPF. hogy az elemeit ömlesztve.Window1" x:Name="window1" xmlns="http://schemas.222 <Button>Button2</Button> <Button>Button3</Button> </StackPanel> </Window> Az eredmény: Az elemek függ legesen követik egymást. térköz nélkül helyezi el.

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

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

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

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

Amit az el z ekkel meg tudunk csinálni. de nagyobb mint kellene: <UniformGrid> <Button Width="170" Height="20">Button1</Button> <Button>Button2</Button> <Button>Button3</Button> <Button>Button4</Button> </UniformGrid> 43. .227 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="100" Width="300"> <UniformGrid> <Button>Button1</Button> <Button>Button2</Button> <Button>Button3</Button> <Button>Button4</Button> </UniformGrid> </Window> Explicit méretmegadás.6 Grid A Grid a WPF leger teljesebb (és leggyakrabban használt) container osztálya. de kissebb: <UniformGrid> <Button Width="100" Height="20">Button1</Button> <Button>Button2</Button> <Button>Button3</Button> <Button>Button4</Button> </UniformGrid> Explicit megadás. azt vele is reprodukálni tudjuk.

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

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

230 Most a gomb két oszlopot kapott. .

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

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

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

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

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

mint amit már ismerünk. Funkciójuk szinte semmit nem változott a WinForms –hoz tapasztaltakhoz képest. 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. a TextBlock kakukktojás ebben a fejezetben.2 Vezérl k Ebben a fejezetben megismerkedünk az alapvet vezérl kkel. A TextBlock –ot . de a Label –hez való hasonlósága miatt itt a helye. hiszen nem vezérl . S t. de tulajdonságaik illetve deklarációjuk igen.1 TextBlock és Label Ez a két vezérl els ránézésre ugyanazt a célt szolgálja. 44. 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. hiszen a vezérl k képességei nagyjából ugyanazok.236 Egyes vezérl k rendelkeznek saját kerettel is.2.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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.

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

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ó.

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.

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

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:

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’

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

..ReadXml("persons. . Amennyiben a tábla adataira nincs szükség.xml"). ahogy azt a táblák esetében már láttuk..350 <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="Persons"> <xs:complexType> <xs:sequence> <xs:element name="ID" msdata:AutoIncrement="true" type="xs:int" /> <xs:element name="Name" type="xs:string" minOccurs="0" /> <xs:element name="Address" type="xs:string" minOccurs="0" /> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> <xs:unique name="Constraint1" msdata:PrimaryKey="true"> <xs:selector xpath=".//Persons" /> <xs:field xpath="ID" /> </xs:unique> </xs:element> </xs:schema> <Persons> <ID>0</ID> <Name>Kovács János</Name> <Address>Budapest. csak a sémára. DataSet –ek esetében gyakorlatilag pontosan ugyanúgy járunk el. backupTable..</Address> </Persons> </NewDataSet> Ezt a file –t már tudjuk használni: DataTable backupTable = new DataTable(). –én várható. . Április 12. Figyelem! Még nincs vége: a következ frissítés 2009.</Address> </Persons> <Persons> <ID>1</ID> <Name>Nagy Ede</Name> <Address>Baja. . akkor használjuk a WriteXmlSchema/ReadXmlSchema metódusokat.

Sign up to vote on this title
UsefulNot useful