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

hu

Reiter István

C#
2009, 0.91 verzió

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

2

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

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

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

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

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

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

5 34.2. Windows Presentation Foundation - Bevezet .………………………....153 35. Windows Forms…………………………………………………………………...156 36. Windows Forms – Vezérl k.……………………….........................................160 36.1. Form……………………………………………….........................................160 36.2. Button……………………………………………….......................................161 36.3. Label……………………………………………….........................................162 36.4. TextBox……………………….………………………………………………...162 36.5. ListBox……………………….………………………………………………….164 36.6. CheckBox……………………….………………………………………………167 36.7. CheckedListBox……………………….………………………………………168 36.8. RadioButton……………………….…………………………………………...169 36.9. ComboBox……………………….……………………………………………..171 36.10. TreeView……………………….……………………………………………...172 36.11. DomainUpDown……………………….……………………………………..175 36.12. NumericUpDown……………………….…………………………………….176 36.13. ProgressBar…………………………………………………………………..176 36.14. TrackBar……………………….………………………………………………177 36.15. PictureBox……………………….……………………………………………177 36.16. RichTextBox……………………….…………………………………………178 36.17. DateTimePicker……………………….……………………………………...181 36.18. MenuStrip……………………………………………………………………..183 36.19. Általános párbeszédablakok………………………………………………186 36.20. TabControl…………………………………………………………………….192 36.21. ContextMenuStrip……………………………………………………………192 36.22. Gyakorló feladatok…………………………………………………………..192 37. Windows Forms – Komponensek.……………………………………………...193 37.1. Timer……………………………………………………………………………..193 37.2. ErrorProvider…………………………………………………………………...193 37.3. BackGroundWorker…………………………………………………………...194 37.4. Process………………………………………………………………………….196 37.5. ImageList………………………………………………………………………..196 37.6. Gyakorló feladatok…………………………………………………………….197 38. Windows Forms - Új vezérl k létrehozása…………………………………....198 38.1. Származtatás…………………………………………………………………...198 38.2. UserControl -ok………………………………………………………………..200 39. Rajzolás: GDI+……………………………………………………………………..203 39.1. Képek kezelése………………………………………………………………...205 40. Drag and Drop……………………………………………………………………...206 41. Windows Presentation Foundation…………………………………………….208 41.1. A WPF Architektúra…………………………………………………………...209 41.2. A WPF osztályhierarchiája…………………………………………………...209 41.3. XAML…………………………………………………………………………….210 41.3.1. Fordítás……………………………………………………………………211 41.3.2. Logikai- és Vizuális fa…………………………………………………..211 41.3.3. Felépítés…………………………………………………………………...212 42. WPF – Események és tulajdonságok……………………………………………215 43. WPF – Vezérl k elrendezése………………………………………………………221 43.1. StackPanel……………………………………………………………………...221 43.2. WrapPanel………………………………………………………………………223 43.3. DockPanel………………………………………………………………………224

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

6 43.4. Canvas…………………………………………………………………………..225 43.5. UniformGrid…………………………………………………………………….226 43.6. Grid……………………………………………………………………………….227 44. WPF – Vezérl k………………………………………………………………………231 44.1. Megjelenés és szövegformázás…………………………………………….231 44.2. Vezérl k………………………………………………………………………....236 44.2.1. TextBlock és Label……………………………….……………………236 44.2.2. CheckBox és Radiobutton…………………………………………...238 44.2.3. TextBox, PasswordBox és RichTextBox………………………….240 44.2.4. ProgressBar, ScrollBar és Slider…………………………………...243 44.2.5. ComboBox és ListBox………………………………………………..247 44.2.6. TreeView……………………………….………………………………..250 44.2.7. Menu……………………………….…………………………………….251 44.2.8. Expander és TabControl……………………………………………..252 44.2.9. Image és MediaElement……………………………….……………..254 45. Er források…………………………………………………………………………...256 45.1. Assembly resource……………………………………………………………256 45.2. Object resource………………………………………………………………..256 46. Stílusok………………………………………………………………………………..260 46.1. Triggerek………………………………………………………………………262 46.1.1. Trigger…………...………………………………………………………262 46.1.2. MultiTrigger……………………………………………………………..263 46.1.3. EventTrigger……………………………………………………………263 47. Sablonok………………………………………………………………………………265 48. Commanding………………………………………………………………………....267 49. Animációk és transzformációk……………………………………………………272 49.1. Transzformációk……………………………………………………………….272 49.1.1. MatrixTransform……………………………………………………….273 49.1.2. RotateTransform……………………………………………………….277 49.1.3. TranslateTransform…………………………………………………...279 49.1.4. ScaleTransform………………………………………………………..280 49.1.5. SkewTransform………………………………………………………...282 49.1.6. TransformGroup……………………………………………………….283 49.2. Animációk……………………………………………………………………….285 49.2.1. From-to-by animációk………………………………………………...286 49.2.2. Key Frame animációk…………………………………………………290 49.2.3. Spline animációk………………………………………………………291 49.2.4. Animációk és transzformációk……………………………………...292 50. Vezérl k készítése………………………………………………………………….294 50.1. UserControl…………………………………………………………………….295 51. ADO.NET…………………………………………………………………………….305 51.1. MS SQL Server 2005/2008 Express………………………………………...305 52. SQL Alapok…………………………………………………………………………...307 52.1. Adatbázis létrehozása………………………………………………………...307 52.2. Táblák létrehozása…………………………………………………………….308 52.3. Adatok beszúrása táblába…………………………………………………...309 52.4. Oszlop törlése táblából……………………………………………………….309 52.5. Kiválasztás……………………………………………………………………...309 52.6. Sz rés…………………………………………………………………………...310 52.7. Rendezés………………………………………………………………………..311

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

nem élnek a „lusta” kiértékeléssel. vagyis a feltétel „k” értékét l függetlenül mindenképpen teljesül. A logikai „vagy” m velet: if(l == true | k == true) { Console.hu 29 példában a „k” soha nem fog kiértékel dni. Ez az egyetlen háromoperandusu operátor. } } Ennek az operátornak egy operandusa van. feltételes operátor. a következ képpen m ködik: .WriteLine("Igaz"). public class RelOp { static public void Main() { int x = 10.ReadKey(). } A logikai „és”: if(l == true & k == true) { Console. közölük az „és” és „vagy” operátoroknak létezik a „csonkolt” logikai párja is. A különbség annyi. } A logikai operátorok családjához tartozik (ha nem is szorosan) a feltételes operátor. ha az operandusban megfogalmazott feltétel hamis vagy egyenl nullával. hogy a logikai operátorok az eredményt l függetlenül kiértékelik a teljes kifejezést. akkor ad vissza igaz értéket. A „tagadás” (negáció) igazságtáblája: A Eredmény hamis igaz igaz hamis Ez a három operátor ún. } Console.doksi.WriteLine("Igaz"). if(!(x == 11)) { Console.Forrás: http://www.WriteLine("X nem egyenlo 11 -el!"). A harmadik a „tagadás” (!()): using System. mivel „l” van az els helyen (balról jobbra haladunk) és „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. A számítógép az adatokat kettes számrendszer –beli alakban tárolja.WriteLine(x & 2). } } . így pl.ReadKey().WriteLine( (x == y) ? "Egyenlo" : "Nem egyenlo").Forrás: http://www. ha van egy byte típusú változónk (ami egy byte azaz 8 bit hosszú) aminek a „2” értéket adjuk. //1010 & 0010 = 0010 = 2 Console. Az eddig megismert kett mellé még jön négy másik operátor is. class Program { static public void Main() { int x = 10.doksi.ReadKey(). Console. 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. using System. } } 6. egyébként 0: 01101101 00010001 AND 00000001 Példa: using System. public class RelOp { static public void Main() { int x = 10.hu 30 feltétel ? igaz-ág : hamis-ág. akkor az a következ képpen jelenik meg a memóriában: 2 00000010 A bit operátorok ezzel a formával dolgoznak. Console. int y = 10.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

doksi. void Method(object i) { } MyDelegate md = Method().Write("Method Two…"). Most d hívásakor mindkét metódus meghívódik. d(). d += MethodTwo. } MyDelegate d = MethodOne. Egy delegate –nek átadott metódus paraméterei lehetnek olyan típusok. 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(). kontravariáns (contravariant) viselkedés. int Square(int _in) { return (_in * _in). Ez az ún. 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().hu 116 void MethodTwo() { Console. Ennek a fordítottja igaz a visszatérési értékre. 25. } GenericDelegate<int> gd = Square.Forrás: http://www.2 Paraméter és visszatérési érték Egy delegate használhat generikus paramétereket is: delegate T GenericDelegate<T>(T param). .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

132

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

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

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

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

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

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

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

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

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

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

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

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

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

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

135

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

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

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

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

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

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

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

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

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

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

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

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

31.6 Szinkronizáció

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

137

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

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

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

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

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

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

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

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

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

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

} } }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

DataSource = t. 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: .doksi. listBox1. new MyString("Judit"). Módosítsuk a programot. ami pont a fent látható csúfságot adja vissza. new MyString("Viktória"). A megoldás tehát az. } public override string ToString() { return data. public MyString(string s) { data = s.hu 166 { MyString[] t = new MyString[] { new MyString("István"). mi viszont nem terheltük túl ezt a metódust.Forrás: http://www. hogy elkészítjük a metódust a saját osztályunkhoz is: class MyString { public string data. ezért az eredeti változat hívódik meg. ha megváltozik a kiválasztott elem. new MyString("Béla"). } 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") }. amelyet az vált ki.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

aktuális

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

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

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

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

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

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

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

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

181

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

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

tartalmaz

információkat,

LinkText

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

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

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

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

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

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

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

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

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

Az eredmény pedig ez lesz:

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

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

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

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

184

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

rendelhetünk hozzá pl. 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. de nem fog látszani. Ugyanígy létrehozhatjuk a többi esemény stílusát is. 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. . A gombunk egyel re nem csinál túl sokat. Click eseménykezel t.Triggers> <Trigger Property="IsMouseOver" Value="true"> <Setter TargetName="back" Property="Fill" Value="Black" /> <Setter TargetName="main" Property="TextElement. hogy rákattintottunk. akkor azt látni is fogjuk.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. fogja tárolni a „tartalmazott” adatokat. 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.doksi. amely rendesen le is fut majd. hogy ez a sablon gombok számára készült. legalábbis vizuálisan nem.hu 266 HorizontalAlignment="Center" Content="{TemplateBinding Content}" /> </Grid> </ControlTemplate> <Button Content="Hello" Template="{StaticResource circlebuttontemplate}" /> Az eredmény: A sablon definiciójának elején rögtön megmondtuk. Ez utóbbihoz szükséges a ContentPresenter osztály is amely a sablonunk alján helyezkedik el.Forrás: http://www. mint megoldani ezt a problémát. Egyébként pont ugyanúgy m ködik mint egy mezei gomb. mivel a ContentControl leszármazottak – amilyen a Button is – enélkül nem jelenítenék meg a tartalmukat.

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

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

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

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

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

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

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

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

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

M21 = double. m. RoutedEventArgs e) { Matrix m = new Matrix().M12 = double.Text). m. .50" Stroke="Black" StrokeThickness="2"> <Polygon.Fill> <Polygon.Text).Text).Text). m.doksi. m.RenderTransform> <MatrixTransform x:Name="polytransform" /> </Polygon.Fill> <SolidColorBrush Color="Red"/> </Polygon.hu 276 </WrapPanel> <WrapPanel> <TextBlock Text="M12: " /> <TextBox Name="m12" Width="30">1</TextBox> </WrapPanel> <WrapPanel> <TextBlock Text="M21: " /> <TextBox Name="m21" Width="30">0</TextBox> </WrapPanel> <WrapPanel> <TextBlock Text="M22: " /> <TextBox Name="m22" Width="30">1</TextBox> </WrapPanel> <WrapPanel> <TextBlock Text="OffsetX: " /> <TextBox Name="offsetx" Width="30">0</TextBox> </WrapPanel> <WrapPanel> <TextBlock Text="OffsetY: " /> <TextBox Name="offsety" Width="30">0</TextBox> </WrapPanel> <Button Name="applybutton" Content="Alkalmaz" Margin="5" Width="50" Click="applybutton_Click" /> </StackPanel> <Canvas Grid. m.Parse(m11.Text).OffsetY = double.95 100.OffsetX = double.50 50.Parse(m22.M22 = double.Parse(m12. m.Text).Column="1"> <Polygon Points="100.Parse(offsety.Parse(offsetx.RenderTransform> </Polygon> </Canvas> </Grid> </Window> Végül a gombhoz tartozó eseménykezel : private void applybutton_Click(object sender.Parse(m21.M11 = double.Forrás: http://www.75 50.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Az

IputGestureCollection

segítségével

a

gyorsbillenty ket

állítottuk

be.

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

305

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

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

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

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

306 összes szolgáltatást, mint bátyjaik, de tanuláshoz (és kisebb kereskedelmi programokhoz) tökéletesek. A letölt oldalak: - http://www.microsoft.com/downloads/details.aspx?familyid=220549b5-0b074448-8848-dcc397514b41&displaylang=en http://www.microsoft.com/downloads/details.aspx?FamilyID=58ce885d-508b45c8-9fd3-118edd8e6fff&DisplayLang=en

A Visual Studio 2005/2008 nem–Express változatai feltelepítik az SQL Server Express -t, ha a telepítésnél ezt kérjük. Az adatbázisok kezeléséhez érdemes telepíteni a megfelel verzióhoz tartozó SQL Server Management Studio Express –t is, amely grafikus felületet nyújt lekérdezések írásához, adatbázisok kezeléséhez.

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

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

307

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

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

Természetesen a Server name sor változhat.

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

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

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

308

A CREATE DATABASE paranccsal fogunk dolgozni:
create database TestDataBase

Az SQL nem case-sensitive, azaz teljesen mindegy, hogy kis- vagy nagybet vel írjuk az utasításokat. A végrehajtáshoz nyomjuk meg az F5 billenty t, ekkor – ha nem rontottunk el semmit - megjelenik az adatbázisunk a Databases fül alatt (ha nem, akkor jobb egérgomb rajta és Refresh):

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

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

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

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

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

Most az els PK 1 lesz és tízesével fog növekedni, tehát a másodikként beillesztett elem kulcsa 11, a másodiknak 21, stb… lesz. Egy oszlopnak azt is megengedhetjük, hogy nullértéket tartalmazzon. Módosítsuk a fenti kifejezést, hogy a Name sor lehessen „semmi”, az Address pedig nem:
create table Persons (ID int identity(1,10)primary key, Name varchar(50) null, Address varchar(100) not null)

Els dleges kulcs nem tartalmazhat nullértéket, ilyen táblát nem is hozhatunk létre.

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

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

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

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

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

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

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

310

select Name from Persons

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful