Zenelejátszó program – WPF alapok
Ahogy a cikksorozat bevezetÅ‘jében emlÃtettem, az alkalmazásunk WPF-et fog használni. Meghazudtolnám magam, ha nem másznék egy kicsit is bele, hogy mi is ez a WPF? A cikkbÅ‘l remélhetÅ‘leg kiderül 🙂
A WPF mozaikszó a Windows Presentation Foundation mozaikszavak rövidÃtése. A UI keretrendszer technológia a .NET 3.0-ban mutatkozott be (nem keverendÅ‘ a Core 3.0-val), még 2006-ban.
Windows esetén a „tradicionális” UI keretrendszer a GDI, késÅ‘bb GDI+ (Graphics Device Interface) 2D-re kitalált rajzoló API-ra épült, aminek az alapjai egészen a Windows 1.0-ig nyúlnak vissza. A 80-as és 90-es évek elején a korabeli videóvezérlÅ‘k rendelkeztek hozzá hardveres gyorsÃtással, azonban a technológia fejlÅ‘désével egyre inkább okafogyottá vált a hardveres gyorsÃtás, mivel a CPU-k szoftverbÅ‘l és erÅ‘bÅ‘l megoldottak elég sok mindent.
A GDI alapú tradicionális Windows API-ra épülÅ‘ alkalmazásfejlesztés nem a legkényelmesebb. Éppen ezért a 90-es évek elején megjelentek a RAD (Rapid Application Development) rendszerek, amik ezt egyszerűsÃtették és elrejtették. Egyes programok saját UI keretrendszert alkalmaztak, ami egyszerűsÃtette a dolgokat (Ilyen volt anno a Winamp a saját Skin rendszerével, ami az 5.0-ra egész szépen kinÅ‘tte magát).
A .NET megjelenésekor a Windows Forms is a GDI-re épÃtett. Egyrészt azért, mert akkora ez már kiforrott volt, másrészt akkortájt (2001) ugyan már elérhetÅ‘ volt a DirectX és az OpenGL is, mint hardver gyorsÃtott rajzoló API, de a meghajtásukhoz szükséges hardver igencsak drága volt. EbbÅ‘l adódóan egy csomó gép (fÅ‘leg az irodai és egyéb üzleti gépek) nem rendelkeztek ilyen hardverrel.
Viszont 2006-ra ez jócskán megváltozott. A laptopokat is igyekeztek felszerelni 3D-képes videóvezérlÅ‘kkel, Ãgy lehetÅ‘ség volt egy modernebb UI keretrendszer megalkotására. Ez lett a WPF.
A WPF belsÅ‘leg a DirectX API-ra épÃt és hardver gyorsÃtott. Ha valami oknál kifolyólag a hardvergyorsÃtás nem érhetÅ‘ el, akkor szoftveres rajzolással is működik. Ebben az esetben értelemszerűen lassabban, de ezzel a mai gépek esetén nem igen kellene találkoznunk. A rajzolás belsÅ‘leg a DirectX 9.0 API-ra épül, amihez a képek betöltését a Windows Imaging API szolgáltatja. Ez a gyakorlatban azt jelenti, hogy nagyjából minden modern képformátumot alkalmazhatunk a programunkban.
A UI leÃrásához egy új nyelv is született, ami az XAML (eXtensible Application Markup Language) nevet kapta. Ez egy XML alapú leÃrónyelv, amit a HTML és a korai SVG szabvány inspirált. A UI leÃrása Ãgy vektorgrafikus, korlátok nélkül skálázható és valamilyen szinten reszponzÃv is (A HTML5-tÅ‘l azért messze áll a WPF).
Mára mondhatni „elavultnak” számÃt a WPF, mivel vannak újabb UI technológiák, mint a Windows 8-ban bemutatkozott UWP (Universal Windows Platform), ami szintén XAML-t használ, vagy a hamarosan megjelenÅ‘ MAUI, ami szintén XAML leszármazott. Tehát még ha elavult is, akkor is egy jó kiindulási pont.
Felmerülhet a kérdés, ha elavultnak titulálom, akkor miért ezt használjuk? Ennek az oka az, hogy az UWP szép és jó, de ugyanakkor számos megkötést hordoz magában, ami elsőre igencsak fura tud lenni. A WPF-nek is vannak megkötései és néha egy egészen furcsa kicsavart logikája. Illetve a WPF minden Windows verzión működik, bár ennek manapság, tekintve, hogy csak Windows 10 van, nem sok létjogosultsága van.
Architektúra és MVVM
Rambo módon belecsaphatnánk egyből az alkalmazásfejlesztésbe, de általában az ilyen alkalmazások nem szoktak hosszú életűek lenni. Nem azért, mert elérték a 100%-os funkcionalitást és világmegváltók lettek, hanem inkább azért, mert menet közben hányingert kapott minden fejlesztő a káosztól amit alkotott és szépen lassan (vagy gyorsan) nullára épült a motiváció és megáll az alkalmazás fejlesztése.
Éppen ezért minden komolyabb szoftver rendelkezik egy architektúrával, ami a szoftver vázát adja. Az architektúra azon döntések összessége, amik a kódban realizálódnak. Lényegében mondhatni kódolási szabályok, amelyeket a szoftver elején lefektetünk. Ezeket a szabályokat nagy valószÃnűséggel nem a nulláról kell kitalálnunk, hiszen architektúrára is vannak tervezési minták.
Egy ilyen tervezési vagy architekturális minta az MVVM, ami a Model–View–Viewmodel rövidÃtése. Ennek lényege, hogy a megjelenÃtést szétválasszuk az üzleti avagy alkalmazás logikától. Igazából hasonlÃt az MVC (Model-View-Controller) mintához, de az adatáramlási irány különbözik.
Az MVVM alkalmazható bármilyen grafikus felülettel rendelkezÅ‘ alkalmazásban, nem kell hozzá WPF. A WPF azonban ezt a mintát részesÃti elÅ‘nyben és ennek a használatát támogatja.
Ahogy a rövidÃtésbÅ‘l látszik, az MVVM három fÅ‘ részbÅ‘l áll:
- A modell a tartománymodellre (domain model) utal, ami a tartalom állapotát reprezentálja (objektumorientált módban), vagy jelentheti az adathozzáférési réteget is (adatközpontú megközelÃtés).
- A View (nézet) az elrendezés vagy megjelenÃtés, amit a felhasználó a képernyÅ‘n láthat.
- A Viewmodel a nézet absztrakciója, ami publikus tulajdonságokat és metódusokat tartalmaz. Az MVC modell vezérlÅ‘je vagy az MVP megjelenÃtÅ‘je helyett az MVVM adatkötést (binding) tartalmaz, ami a viewmodel-ben közvetÃt a view és az adatok között.
Az adatkötés, vagy binding az egész minta lényege. Ezt a WPF XAML leÃró nyelve biztosÃtja számunkra. SegÃtségével megoldható, hogy a View ne függjön közvetlenül a Viewmodel-tÅ‘l és a Viewmodel ne függjön az Å‘t használatba vevÅ‘ nézettÅ‘l. Ez az adatkötés dinamikusan épül fel a nézet betöltésekor. Az adatkötés a nézetben van definiálva.
A dinamikusan létrejövő kötések a minta használatának előnye és egyben hátránya is. Hátránya nagy méretű alkalmazások esetén a memóriahasználatban tud megmutatkozni, ezért WPF alkalmazások esetén előre kell tervezni a memóriaigénnyel.
Első WPF projektünk
A WPF használható .NET Framework alatt és .NET Core 3 vagy újabb .NET rendszereken is. Az alkalmazásunkat .NET 5 segÃtségével készÃtjük el, mivel itt érhetÅ‘ek el a C# legújabb újdonságai.
Projektet Visual Studio-ból is tudunk indÃtani, de én jobban szeretem a projekt struktúrát létrehozni parancssorból. Ennek az oka az, hogy jelenleg a Visual Studio még nem a .NET projekt sablonjait használja, Ãgy lehetnek eltérések, amik okozhatnak kellemetlen meglepetéseket.
WPF Alkalmazást a következÅ‘ parancs segÃtségével tudunk létrehozni egy mappán belül:
dotnet new wpf
Ennek hatására létre fog jönni a projekt és az összes szükséges fájl ami egy WPF alkalmazás működéséhez szükséges. De pontosan milyen fájlok jönnek létre és mi a feladatuk?
- App.xaml
- App.xaml.cs
- MainWindow.xaml
- MainWindow.xaml.cs
- AssemblyInfo.cs
Ami elsÅ‘re feltűnhet, hogy nincs Program.cs fájl a generált fájlok között, ami az alkalmazás belépési pontját, a Main metódust definiálná. Ennek az oka az, hogy ez az App osztály Å‘sében, az Application osztályban van definiálva, ezért nem kell. Természetesen készÃthetünk saját Main metódust, mert elÅ‘fordulhat, hogy szükségünk van rá. Ebben az esetben viszont definiálnunk kell, hogy a CLR melyik osztály Main metódusát tekintse belépési pontnak, valamint nekünk manuálisan kell beállÃtanunk elég sok mindent.
App.xaml.cs
Ez az alkalmazás fÅ‘ osztálya, ami az Application osztályból öröklÅ‘dik. Feladata a WPF alkalmazásunk futtatása és elindÃtása.
App.xaml
Az alkalmazás leÃró fájlja. Ez egy XAML dokumentum, ami az alkalmazásunk tulajdonságait állÃtja be. Ezek közül a legfontosabb a StartupUri tulajdonság, ami meghatározza a futtatáskor elsÅ‘nek elinduló ablakot.
A tulajdonságok beállÃtásán kÃvül másik fontos szerepe ennek a fájlnak a stÃlusok betöltése. A WPF rendelkezik egy stÃlusrendszerrel, ami a CSS-hez hasonlóan lehetÅ‘vé teszi, hogy alkalmazás szinten definiáljuk egyes vezérlÅ‘ elemek kinézetét. Ezeket a stÃlusokat vagy ebben a fájlban definiáljuk, vagy behivatkozzuk azokat a fájlokat, amelyek tartalmazzák a stÃlusokat.
MainWindow.xaml
Az alkalmazás fÅ‘ ablaka. Ez fog megjelenni a képernyÅ‘n a program elindÃtása után. Ha az alkalmazás MVVM-et használ, akkor mondhatjuk, hogy ez egy View.
MainWindow.xaml.cs
Ez a fő ablak code behind fájlja, ami az XAML dokumentumhoz tartozó C# osztály. Ebben a fájlban jön létre minden, az XAML-ben definiált eseménykezelő metódus, illetve ha logikát akarunk az ablakhoz rendelni, akkor azt itt tehetjük meg. MVVM esetén az esetek többségében nem szoktunk hozzányúlni a View mögé generálódó code behind fájlhoz, mivel a logika helye a Viewmodel-ben keresendő.
ElÅ‘fordulhat azonban, hogy egy olyan eseményre szeretnénk logikát épÃteni, aminek az elérése Viewmodel szintrÅ‘l nehezen elérhetÅ‘ (tipikus példa erre a Drag & Drop WPF esetén). Ebben az esetben a code behind is tartalmaz kódot, de ez egyfajta proxy vagy ragasztó szokott általában lenni, ami a Viewmodel szintjére viszi az adatot. Természetesen ettÅ‘l a megközelÃtéstÅ‘l el lehet térni, de nem ajánlatos, mert tapasztalatom szerint, ha keveredik a logika a code behind és Viewmodel fájlban, akkor ott tervezési hiba van.
Egy pár szó a Windows lelki világáról
Mivel Windows alkalmazás fejlesztéssel fogunk foglalkozni, nem árt, ha pár szót ejtünk a Windows belső lelki világáról. Nem kell azonban megijedni, nem merülünk el a felesleges részletekben.
A Windows alkalmazások egy Message Bus-on keresztül kommunikálnak az operációs rendszerrel. Lényegében minden művelet, ami a programmal történik, (Pl. billentyűlenyomás, átméretezés, pozÃció változás, elemre kattintás, stb…) a programunk számára egy üzenetként fog megérkezni.
Az App osztály őse lényegében erre az üzenetcsatornára iratkoztatja fel az ablakokat, illetve a háttérben elintézi, hogy az alkalmazásunk rajzoljon is valamit a képernyőre.
A kapott üzeneteket az alkalmazásnak fel kell dolgoznia, illetve a feldolgozásról válaszolnia is kell az operációs rendszer számára. Ha ez a válaszolás nem történik meg idÅ‘ben, akkor ebbÅ‘l tudja a rendszer, hogy a program meghalt, vagy hibás állapotban van (Magyar Windows esetén a ‘Program nem válaszol’ üzenet jelenik meg, ami utal a fel nem dolgozott üzenetekre a programhoz rendelt üzenetsorban).
Éppen ezért a hosszú ideig futó kódrészeket érdemes külön szálra kiszervezni, mert ha ez nem Ãgy történik, akkor a program működése akadozik és nem lesz egy kellemes élmény használni.
Kérdés persze az, hogy mi számÃt hosszú folyamatnak? Ez leginkább gép és alkalmazás függÅ‘. 100ms késlekedés már elég lehet, hogy akadozzon és frusztráló legyen a programunk használata.
Végszó
A programunk a késÅ‘bbiekben épÃteni fog a többszálú programozásra. A cikksorozat további részei elÅ‘tt érdemes lehet majd elolvasni a Helló világ, Helló C# idevágó fejezetét.
Folytatása következik. Addig is érdekességnek ajánlom a következő videót, ami ugyan nem C#, de cserébe jól elmagyarázza C++ kóddal, hogy mi minden történik a háttérben, mire egy ablakot kapunk.
2022.06.30. @ 14:51
Szia, az utolsó linkben van egy typeo
https://csharptutorial.hu/docs/hellovilag-hellocsharp/11-tobbszalu-programozas/ helyett
https://csharptutorial.hu/docs/hellovilag-hellocsharp/10-tobbszalu-programozas/ a működő