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ő