Haladó programozás (C#) 2007 2008, I. félév BMF NIK Saját vezérlők készítése Osztályok származtatása a System.Windows.Forms névtér vezérlőiből Származtatott vezérlők felhasználása és tesztelése Saját vezérlők származtatása a System.Windows.Forms.UserControl osztályból Vezérlőelemek hozzáadása, tulajdonságok bevezetése és módosítása Vezérlők fontosabb speciális attribútumai Saját összetett vezérlőkre épülő alkalmazások fejlesztése Komplex funkcionalitású vezérlő fejlesztése és integrálása a Visual Studio környezetbe Saját vezérlők elhelyezése az Eszköztárban miklos.arpad@nik.bmf.hu Hallgatói tájékoztató A jelen bemutatóban található adatok, tudnivalók és információk a számonkérendő anyag vázlatát képezik. Ismeretük szükséges, de nem elégséges feltétele a sikeres zárthelyinek, illetve vizsgának. Sikeres zárthelyihez, illetve vizsgához a jelen bemutató tartalmán felül a kötelező irodalomként megjelölt anyag, a gyakorlatokon szóban, illetve a táblán átadott tudnivalók ismerete, valamint a gyakorlatokon megoldott példák és az otthoni feldolgozás céljából kiadott feladatok önálló megoldásának képessége is szükséges. 2 1
Windows Forms vezérlők osztályhierarchiája Saját összetett vezérlők javasolt őse A nem vizuális komponensek őse Saját egyedi vezérlők javasolt őse 3 Új vezérlők származtatási lehetőségei System.Object Az új vezérlőt teljes egészében el kell készíteni, fejlesztése rendkívül időigényes, de működése teljes mértékben meghatározható System.ComponentModel.Component Elsősorban nem látható(felhasználói felületet nem igénylő) komponensek esetén ajánlott (pl. időzítő, eseménynapló, soros/usb port stb.) System.Windows.Forms.Control Az új vezérlőautomatikusan képes ablakkezelésre, számos tulajdonsága és eseménye van, ugyanakkor minden rajzolási funkcióját el kell készíteni Ez a megoldás a programok teljesítménye szempontjából optimális System.Windows.Forms.UserControl Az új vezérlőt már meglévő vezérlők kompozíciójaként állíthatjuk elő tetszőleges saját tulajdonságokkal és eseményekkel Ez a megoldás a fejlesztési időszükséglet szempontjából optimális System.Windows.Forms.* Meglévő vezérlőkre nagymértékben hasonlító vezérlők készítéséhez ajánlott 4 2
Feladat (1) Készítsünk olyan szövegdobozt, amely sárga háttérszínnel jelzi, ha a benne lévő szöveg nem módosítható! Ötletek: Célszerű már meglévő vezérlőből származtatni A System.Windows.Forms.TextBox vezérlő ideális választás erre a célra. Ha az ősvezérlőnek már van a kívánthoz hasonló funkciót megvalósító metódusa vagy tulajdonsága, használjuk fel ReadOnly tulajdonság Az elkészült vezérlő példányait kézzel kell létrehozni a teszteléshez A Visual Studio eszköztárában ( Toolbox ) nem jelennek meg automatikusan az új vezérlők. Az elkészült vezérlőknek az eszköztáron történő elhelyezésére később látunk példát. 5 Megoldás (1) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 using System; using System.Drawing; using System.Windows.Forms; public class TextBoxEx: TextBox { public new bool ReadOnly { get { return base.readonly; } set { if (value) this.backcolor = Color.Yellow; else this.backcolor = Color.FromKnownColor(KnownColor.Window); base.readonly = value; } } } MainForm.cs 6 3
Kompozícióra épülő vezérlők Ősük a System.Windows.Forms.UserControl osztály Elsődleges céljuk bővített funkcionalitású, egyszerűen újrafelhasználható összetett vezérlők előállítása Képességeik nem korlátozódnak az őket alkotóvezérlők képességeinek összességére, akár teljesen önállófunkcionalitással is rendelkezhetnek (például saját, alkotóelemeiktől független eseményekkel). Lehetőséget adnak emellett gyakran használt alapfunkciók beépítésére (például listák elemeinek előre feltöltése szokványos adatokkal vagy formázási és adatellenőrzési funkciók beépítése a vezérlőbe), és igen egyszerűen integrálhatók a Visual Studio fejlesztőkörnyezetbe. Legnagyobb előnyük az újrafelhasználhatóság, ami az egységes megjelenés és funkcionalitás mellett különösen nagyobb fejlesztéseknél fontos. Sokszor használjuk őket arra is, hogy tipikusan együtt használt vezérlőket egyetlen komponenssé egyesítsünk (például címke + szövegmező). 7 Feladat (2) Készítsünk újrafelhasználhatócímszerkesztőkomponenst, amely lehetőséget ad ügyfeleink címadatainak (város, irányítószám, utca, házszám) bevitelére! Ötletek: A vezérlőt a System.Windows.Forms.UserControl osztályból származtassuk Célszerűa Visual Studio segítségével osztálykönyvtárat ( Class Library ) létrehozni, majd az Add User Control... funkciósegítségével létrehozni a komponens alapjait. Ezt követően egy másik projektet is hozzunk létre a teszteléshez, ez azonban már Windows alkalmazás legyen. Az új vezérlő saját tulajdonságai és eseményei itt lesznek láthatók. Ha az új vezérlőbelsőelemeinek tulajdonságait vagy eseményeit kívülről láthatóvá szeretnénk tenni, ágyazzuk saját tulajdonságba vagy eseménybe A vezérlő belső alvezérlőit (pl. TextBox) semmiképpen sem ajánlatos nyilvánossá tenni. Az új vezérlőtervezési idejűviselkedését attribútumok segítségével elegánsan integrálhatjuk a fejlesztőkörnyezetbe (lásd később) Ezzel segítjük a komponens későbbi felhasználóit (sőt, gyakran saját magunkat is). 8 4
Megoldás (2) Új tulajdonság megadása és kiegészítése attribútumokkal AddressEditor.cs 9 Vezérlők attribútumai (kivonatos referencia) Attribútum neve BindableAttribute BrowsableAttribute CategoryAttribute DefaultEventAttribute DefaultPropertyAttribute DefaultValueAttribute DescriptionAttribute DesignOnlyAttribute Attribútum leírása Tervezési idejűattribútum, amely megadja, hogy a hozzárendelt tulajdonság támogat-e automatikus adatkötést Megadja, hogy a hozzárendelt tulajdonság látható-e a Visual Studio tulajdonságszerkesztőjében ( Properties Window ) A tulajdonságszerkesztőaz itt megadott kategóriába fogja sorolni a hozzárendelt tulajdonságot A hozzárendelt osztály alapértelmezett eseményének beállítására szolgál A hozzárendelt osztály alapértelmezett tulajdonságának beállítására szolgál A hozzárendelt tulajdonság alapértelmezett értékét közli a Visual Studio tulajdonságszerkesztőjével A tulajdonság leírása (a tulajdonságszerkesztőalsópanelén jelenik meg) Megadja, hogy a tulajdonság kizárólag tervezési időben módosítható 10 5
Feladat (3) Készítsünk vezérlőt, amely lehetővé teszi hierarchikus jellegű mappastruktúrák bejárását és a bennük lévőmappák és fájlok adatainak megjelenítését! A vezérlőemellett tegye lehetővéa kiindulómappa (gyökérelem) megadását és a vezérlőfelületén kiválasztott elem (fájl vagy mappa) adatainak kiolvasását is! Ötletek: A vezérlőt a System.Windows.Forms.TreeView osztályból származtassuk Célszerűa Visual Studio segítségével osztálykönyvtárat ( Class Library ) létrehozni, ezután az Add User Control... funkciósegítségével létrehozni egy új összetett vezérlőt, majd végül a vezérlőősét a kódban kézzel átírni. Ezt követően egy másik projektet is hozzunk létre a teszteléshez, ez azonban már Windows alkalmazás legyen. Az új vezérlősaját tulajdonságai és eseményei itt lesznek láthatók. A mappák bejárását célszerű rekurzív algoritmussal megvalósítani A mappák és a fájlok adatait érdemes magukban a megjelenített csomópontokban tárolni Ennek megvalósításához származtassunk megfelelőtárolóosztályokat a System.Windows.Forms.TreeNode osztályból. 11 Megoldás (3) FolderTree.cs 12 6
Várható buktatók és nehézségek Rekurzív mappabejárás Gyakori hibalehetőség a végtelen rekurzió, amit az ilyenkor keletkező StackOverflowException kivétel jelez A fa gyökéreleméhez tartozóelérési út beállítása A kiindulómappához (pl. C:\Windows\system32 ) érdemes saját tulajdonságot készíteni (pl. RootFolder néven) Probléma: mikor és hogyan olvassa be a vezérlőa kért mappa tartalmát? A vezérlők tervezési időben, a Visual Studio grafikus felületén is élnek és futtatják az általunk írt kód megfelelőrészeit, ezért rendkívül átgondoltan kell őket megtervezni. Amikor egy vezérlőt felhelyezünk egy készülőalkalmazás ablakába, a Visual Studio meghívja a vezérlőparaméter nélküli konstruktorát, ezért ebben nem szabad olyan kódot elhelyezni, ami bizonyos hardver, külsőszoftverelem vagy mappák, illetve fájlok meglétét feltételezi. A fentiek következtében nagyon fontos a kivételek megfelelőkezelése is (pl. a gyökérelem helytelen megadása, fájlelérési hibák...). Sok almappát és fájlt tartalmazómappastruktúra esetén nagyon sokáig tarthat a betöltés 13 Saját vezérlők elhelyezése az Eszköztárban A Visual Studio eszköztárában tetszőleges.net vagy.com (más néven ActiveX) komponens elhelyezhető A kívánt komponenst tartalmazószerelvényt a Tools menü Choose Toolbox Items menüpontjának kiválasztása után adhatjuk meg (a listán nem szereplő komponenseket a Browse gombbal kereshetjük meg). A komponensekhez a ToolboxBitmapAttribute attribútum használatával saját ikon is megadható. 14 7
Irodalomjegyzék (alapismeretek) Nagel, Evjen, Glynn, Skinner, Watson, Jones: Professional C# 2005 Kiadó: Wiley Publishing, Inc., 2006 ISBN: 978-0-7645-7534-1 Web: http://www.wiley.com/, http://www.wrox.com/ Nyelv: angol Terjedelem: 1540 oldal Windows Forms vezérlők: 751 786. o. Saját komponensek és vezérlők készítése: 787 800. o. Microsoft Corp., Visual Studio Developer Center Windows Forms vezérlők: http://msdn2.microsoft.com/en-us/library/ettb6e2a(vs.80).aspx Saját komponensek és vezérlők készítése: http://msdn2.microsoft.com/en-us/library/ms171766(vs.80).aspx 15 Kiegészítés: rekurzív algoritmusok A rekurzív algoritmusok olyan lépéssorozatokat jelentenek, amelyek egyik lépése szintén maga az algoritmus Az algoritmus rekurzív ismétléseinek végrehajtására általában más-más paraméterekkel kerül sor. Ha a rekurzív ismétlés az algoritmus utolsólépése, akkor végrekurzióról ( tail recursion ), ha utána további feldolgozási lépések történnek, akkor bővítő rekurzióról ( augmenting recursion ) beszélünk. A rekurzív algoritmusoknál kulcsfontosságú, hogy gondoskodjunk garantált kilépési feltételről, ellenkezőesetben végtelen rekurziós ciklusba kerülne a program, és az operációs rendszer (vagy a keretrendszer) leállítja. Példa egyszerű bővítő rekurzív algoritmus C# nyelvű megvalósítására: 1 2 3 4 5 6 7 int Faktoriális(int x) { if (x <= 1) return 1; else return x * Faktoriális(x-1); } 18 8
Kiegészítés: az ISupportInitialize interfész Az interfészek segítségével az osztályhierachiától függetlenül írhatóelőfunkcionalitás más (az interfészt támogató ) osztályok számára A témáról további információk az AAO OOP előadásanyagban találhatók. Az ISupportInitialize interfészt támogatókomponensek számára a Visual Studio garantálja, hogy az automatikusan előállított InitializeComponent() metódus belsejében a komponens első tulajdonságának beállítása előtt meghívja a BeginInit() metódust, az utolsó tulajdonság beállítása után pedig az EndInit() metódust Így elérhető, hogy a komponens értesüljön arról, amikor létrehozását követően tulajdonságait a Visual Studio beállítja a tervezéskor megadott értékekre. Ez két szempontból is hasznos: egyrészt nem fordulhat elő, hogy (pl. összefüggőtulajdonságok esetén) menet közben a komponens időlegesen érvénytelen állapotba kerül, másrészt elkerülhetőaz esetleg időigényes adat-vagy képernyőfrissítés többszöri megismétlése minden egyes tulajdonság beállítása után. 19 9