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

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

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

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

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

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

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

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

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

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

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

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

vagy az azonosító elé írt teljes eléréssel hivatkozhatunk: using MyNameSpace.Data). 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. //vagy MyNameSpace. Egy névtér tulajdonképpen egy virtuális doboz. ha valamilyen kifejez nev névtértben vannak (System. ahol más is kell ott jelezni fogom. Ennek a problémának a kiküszöbölésére hozták létre a névterek fogalmát.19 megzavarodhat. . Névteret magunk is definiálhatunk. metódusok.Valami A jegyzet els felében f leg a System névteret fogjuk használni. Nyílván könyebb megtalálni az adatbáziskezeléshez szükséges osztályokat. stb… vannak.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

*/ } /*Egyéb tagok*/ } 15. vagyis bármennyi taggal elvégezhet ek legyenek a m veletek. metódusok) kapják meg.75 static public double Pi() { return pi.6 Gyakorló feladatok 1. 2. 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). 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. } static public double Cos(double x) { /*. amelyek meghatározatlan számú paramétert várnak ciklussal olvassuk be a tagokat! ...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

IList és IDictionary interfészeken alapulnak. 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. IEnumerator<T> { } Ekkor a megvalósítás teljesen ugyanúgy m ködik mint a hagyományos esetben.7 Generikus interfészek A letöbb hagyományos interfésznek létezik generikus változata is.113 24. A generikus adatszerkezetek a generikus ICollection. így ezeket megvalósítva akár mi magunk is létrehozhatunk ilyet. .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. Az Orientation tulajdonság megváltoztatásával vízszintes elrendezést kapunk: <StackPanel Orientation="Horizontal"> Az látható.microsoft.com/winfx/2006/xaml" .: <Window x:Class="JegyzetWPF. térköz nélkül helyezi el. Amennyiben megadjuk a Horizontal/VerticalAlignment tulajdonságot.Window1" x:Name="window1" xmlns="http://schemas. A StackPanel és WrapPanel sajátja.222 <Button>Button2</Button> <Button>Button3</Button> </StackPanel> </Window> Az eredmény: Az elemek függ legesen követik egymást. hogy az elemeit ömlesztve. 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.microsoft. 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.

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

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

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

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

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

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

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

.230 Most a gomb két oszlopot kapott.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

RowDefinitions> <RowDefinition Height="100" /> <RowDefinition Height="*" /> </Grid.Window1" x:Name="window1" xmlns="http://schemas. amelyek értelemszer en a régi és az új értéket hordozzák magukban.com/winfx/2006/xaml" Title="Window1" Height="200" Width="300" Background="Black"> <Grid> <Grid. A paraméter tulajdonságai közt megtaláljuk a NewValue és az OldValue tagokat.RowDefinitions> <ProgressBar x:Name="progressbar1" Minimum="0" Maximum="100" Width="150" Height="30" Grid.microsoft.245 paramétertípus egy generikus osztály. a fenti esetben például double értékekre specializálódott.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.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: .microsoft. Az XAML így néz majd ki: <Window x:Class="JegyzetWPF.

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

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

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

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

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

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

.Click="menu1_Click"> <MenuItem Header="Menu1"> <MenuItem Header="SubMenu1.252 </Menu> </Grid> </Window> Egy menüpont kiválasztását és így egy parancs végrehajtását pl..microsoft. ezért tartalmazhat más elemeket is.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. 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.Window1" x:Name="window1" xmlns="http://schemas.microsoft. RoutedEventArgs e) { MessageBox.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.1" /> </MenuItem> <MenuItem Header="Menu2" /> </Menu> Az eseménykezel : private void menu1_Click(object sender.Header.2. hogy a tartalmazott elemeket képes elrejteni/megjeleníteni: <Window x:Class="JegyzetWPF. 44. egy almenü megnyitása. mint pl.OriginalSource).Content> <TextBlock> Ezt a szöveget elrejthetjük.8 Expander és TabControl Az Expander a HeaderedContentContorl osztályból származik.Content> .ToString()). </TextBlock> </Expander.Show(((MenuItem)e. Ezt a tulajdonságát kiegészti azzal.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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. hogy a gomb a középpontja körül forogjon.293 </Button> A RenderTransformOrigin tulajdonságot azért állítottuk be. .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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