Grafikus C# – Windows Forms tanfolyam › 14.9 lecke Windows Forms · 14.9 lecke · záró projekt A 14. fejezet záró projektjében a programod elhagyja a fejlesztői gépet. Egy meglévő, kész programot (például a 13.10 diáknyilvántartót) kiadásra készítesz fel: arculat és verzió, Release fordítás, közzététel, telepítő, ZIP, ellenőrző összeg, README, változásnapló, licenc, majd tiszta gépes próba: telepítés, használat, frissítés egy újabb verzióra, és eltávolítás. A cél egy reprodukálható kiadási folyamat: egyetlen szkript, amely a forrásból újra és újra ugyanazt a csomagot állítja elő, és egy ellenőrzőlista, amely garantálja, hogy semmi nem marad ki. Ez az, amit egy valódi szoftverkiadás jelent.Mini projekt: kész alkalmazás kiadása

A projekt végére
- egy meglévő programból kiadható terméket készítesz (ikon, verzió, Névjegy, licenc);
- Release fordítást és közzétételt végzel, és egyetlen szkripttel előállítod a csomagokat;
- telepítőt, ZIP-et és ellenőrző összeget készítesz;
- tiszta gépen kipróbálod: telepítés, használat, frissítés, eltávolítás;
- megírod a README-t, a változásnaplót és a kiadási jegyzéket;
- önértékeled a kiadás minőségét.
Mielőtt elkezded
Ehhez a projekthez a 14.1–14.8 lecke szükséges, és egy kész, tesztelt program (például a 13.10 javított diáknyilvántartó).
Követelménytábla (100 pont)
| Követelmény | Pont |
|---|---|
Arculat és azonosítás: saját .ico ikon (exe és ablak), Product, Company, Copyright, Version a .csproj-ban; a verzió egy forrásból él; Névjegy ablak (verzió, mód, .NET, licenc, adatmappa, hibabejelentő gomb). | 15 |
Adatok helye: a program telepítés után helyesen működik (beállítások ApplicationData, adatok és napló LocalApplicationData); központi Utak osztály; adatbázis-migráció (user_version) régi adatokkal tesztelve; első indítás kezelve. | 15 |
Build és közzététel: tiszta Release build figyelmeztetés nélkül; keretrendszer-függő és önálló közzététel is elkészítve; DebugType=none; Release kimeneten lefuttatott regressziós teszt. | 15 |
Telepítő: Inno Setup szkript (állandó AppId, felhasználói telepítés, parancsikonok), a verzió paraméterként; telepítés, frissítés a régi fölé, eltávolítás kipróbálva. | 15 |
Frissítés-ellenőrző: biztonságos (HTTPS, időkorlát, csendes hiba, Version összehasonlítás), kikapcsolható; helyi próbafájllal tesztelve. | 10 |
Csomag és integritás: verziózott nevű ZIP-ek és telepítő, SHA-256 összeg, KIADAS.md; nincs PDB a csomagban; a PDB archiválva. | 10 |
Dokumentáció: felhasználói README (követelmény, telepítés, SmartScreen, adatok helye, eltávolítás), CHANGELOG.md, LICENSE, THIRD-PARTY-NOTICES.txt. | 10 |
| Tiszta gépes próba és önértékelés: másik gépen/virtuális gépen vagy új felhasználói fiókban kipróbálva, a tesztlista (18 pont) kitöltve, tanulságok összegezve. | 10 |
A kiadás terve
Diaknyilvantarto\ ← a solution mappa (forrás: verziókezelésben)
Diaknyilvantarto.csproj ← Version, Product, Company, Copyright, ApplicationIcon
Kepek\app.ico
telepito.iss ← Inno Setup szkript (Verzio paraméterrel)
kiadas.ps1 ← a teljes kiadási folyamat egy paranccsal
README.md CHANGELOG.md LICENSE THIRD-PARTY-NOTICES.txt
publish\fdd\ , publish\onallo\ ← (generált; nincs verziókezelésben)
kiadas\ ← (generált; ez a KIADÁSI CSOMAG)
Diaknyilvantarto-1.0.0-win-x64-fdd.zip
Diaknyilvantarto-1.0.0-win-x64-onallo.zip
Diaknyilvantarto-Setup-1.0.0.exe
KIADAS.md ← hash-ek, méretek, dátum, verzió
archivum\1.0.0\*.pdb ← a hibakereséshez (NEM kerül a felhasználóhoz)A kiadás szakaszai
- Előkészítés: arculat, verzió, adatok helye, Névjegy.
- Minőség: Release build, tesztek, figyelmeztetésmentesség.
- Csomagolás: közzététel, telepítő, ZIP, hash.
- Dokumentáció: README, változásnapló, licencek.
- Próba: tiszta gép, frissítés, eltávolítás.
- Archiválás: PDB, hash, kiadási jegyzék.
Az ellenőrzőlista elve
A kiadás hibája a legdrágább hiba: a felhasználók kezében van. Ezért nem emlékezetből dolgozunk, hanem ellenőrzőlistával, és szkripttel (a kézi lépések elfelejthetők). Minden kiadáshoz ugyanaz a lista fut, és a hibákból új pont kerül rá (mint a regressziós teszteknél, 13.8 lecke).
Mit tegyél ide: a kiadás mappa (három fájl és a KIADAS.md) és a program a tálcán az ikonjával.Javasolt fájlnév: winforms-14-9-kiadas.webp · Alt szöveg: A kész kiadási csomag
A 8 lépés támponttal
Haladj lépésről lépésre, és minden lépést pipálj ki a kiadási ellenőrzőlistán. A támpont segít elindulni, de a megoldást magad találd meg. Készítsd el az ikont (.ico, 16–256 px), állítsd be az Központi Névjegy ablak (verzió, mód, .NET, licenc, harmadik fél, adatmappa, hibabejelentő gomb); Rebuild Release módban; Az Inno Setup szkript a verziót paraméterként kapja ( Telepítsd az 1.0.0-t egy másik gépen/virtuális gépen/új felhasználói fiókban; használd (keletkezzen adat); készíts 1.0.1 patch kiadást (kis módosítás), telepítsd a régi fölé; ellenőrizd az adatok megmaradását, a verziót, a frissítés-ellenőrző viselkedését; távolítsd el. Támpont: 14.6 és 14.7 lecke. README (felhasználói), 1. lépés: arculat és verzió
ApplicationIcon-t és a Form.Icon-t; add meg a Product, Company, Copyright, Version=1.0.0 értékeket. A Verzio osztály egy helyen olvassa a verziót. Támpont: 14.8 lecke.2. lépés: adatok helye és migráció
Utak osztály; beállítások ApplicationData, adat/napló LocalApplicationData, export Dokumentumok; első indítás; adatbázis-migráció user_version-nel; Adatmappa megnyitása menü. Támpont: 14.7 lecke.3. lépés: Névjegy és licencek
LICENSE (például MIT) és THIRD-PARTY-NOTICES.txt a csomagok licenceivel (dotnet list package). Támpont: 14.4 és 14.8 lecke.4. lépés: frissítés-ellenőrző
FrissitesEllenorzo (HttpClient, időkorlát, Version), kikapcsolható beállítással; a próbához helyi legujabb.json. Támpont: 14.6 lecke.5. lépés: minőség (Release)
TreatWarningsAsErrors a Release-ben; a regressziós teszteket (13.8 lecke) a Release kimeneten futtasd (különösen az Assert-ekre és időzítésre épülő részeket). Támpont: 14.1 lecke.6. lépés: telepítő és kiadási szkript
/DVerzio=...); a kiadas.ps1 egy paranccsal: tiszta mappa, közzététel (fdd és önálló), telepítő fordítása, ZIP-ek, hash, KIADAS.md, PDB archiválás. Támpont: 14.5 és 14.6 lecke.7. lépés: tiszta gépes próba és frissítés
8. lépés: dokumentáció, archiválás, önértékelés
CHANGELOG.md, KIADAS.md (hash-ekkel); a PDB-k archiválása; töltsd ki a 18 pontos tesztlistát, és írd meg az önértékelést.
Mintamegoldás: kiadási szkript, telepítő, dokumentumok
Csak akkor nyisd meg, ha már megpróbáltad. A neveket ( Mit tegyél ide: a kiadás futása (a szkript lépései) és a kész Diaknyilvantarto) igazítsd a saját projektedhez.kiadas.ps1: a teljes kiadási folyamat egy paranccsal
param([Parameter(Mandatory)][string]$Verzio, [string]$Nev = "Diaknyilvantarto", [string]$Iscc = "C:\Program Files (x86)\Inno Setup 6\ISCC.exe")
$ErrorActionPreference = "Stop"
function Lepes($szoveg) { Write-Host "`n=== $szoveg ===" -ForegroundColor Cyan }
Lepes "0. Tiszta kiindulás"
Remove-Item -Recurse -Force publish, kiadas -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Path publish, kiadas, "archivum\$Verzio" -Force | Out-Null
Lepes "1. Release fordítás (a figyelmeztetés itt hiba)"
dotnet build -c Release -p:Version=$Verzio -p:TreatWarningsAsErrors=true
if ($LASTEXITCODE -ne 0) { throw "A fordítás nem sikerült." }
Lepes "2. Tesztek (ha van tesztprojekt)"
if (Test-Path "..\$Nev.Tests") { dotnet test "..\$Nev.Tests" -c Release; if ($LASTEXITCODE -ne 0) { throw "A tesztek elbuktak." } }
Lepes "3. Közzététel: keretrendszer-függő és önálló"
dotnet publish -c Release -r win-x64 --self-contained false -p:DebugType=none -p:Version=$Verzio -o publish\fdd
dotnet publish -c Release -r win-x64 --self-contained true -p:DebugType=none -p:Version=$Verzio -o publish\onallo
Lepes "4. PDB archiválás (a hibakereséshez; NEM a felhasználónak)"
dotnet build -c Release -p:Version=$Verzio -o "archivum\$Verzio\bin" | Out-Null
Get-ChildItem "archivum\$Verzio\bin" -Filter *.pdb | Move-Item -Destination "archivum\$Verzio" -Force
Remove-Item -Recurse -Force "archivum\$Verzio\bin"
Lepes "5. Telepítő (Inno Setup)"
& $Iscc "/DVerzio=$Verzio" "/DNev=$Nev" telepito.iss
if ($LASTEXITCODE -ne 0) { throw "A telepítő fordítása nem sikerült." }
Lepes "6. ZIP-ek"
foreach ($m in "fdd", "onallo")
{
Copy-Item README.md, CHANGELOG.md, LICENSE, THIRD-PARTY-NOTICES.txt "publish\$m" -ErrorAction SilentlyContinue
Compress-Archive -Path "publish\$m\*" -DestinationPath "kiadas\$Nev-$Verzio-win-x64-$m.zip" -Force
}
Lepes "7. Kiadási jegyzék (hash, méret)"
$sorok = @("# $Nev $Verzio", "", "Dátum: $(Get-Date -Format 'yyyy-MM-dd HH:mm')", "", "| Fájl | Méret (MB) | SHA-256 |", "|---|---|---|")
foreach ($f in Get-ChildItem kiadas -File | Where-Object { $_.Name -ne "KIADAS.md" })
{
$sorok += "| $($f.Name) | $([math]::Round($f.Length / 1MB, 1)) | $((Get-FileHash $f.FullName -Algorithm SHA256).Hash) |"
}
Set-Content kiadas\KIADAS.md $sorok -Encoding UTF8
Write-Host "`nKész: $((Get-ChildItem kiadas).Count) fájl a kiadas mappában." -ForegroundColor Greentelepito.iss: a verzió paraméterként
#ifndef Verzio
#define Verzio "1.0.0"
#endif
#ifndef Nev
#define Nev "Diaknyilvantarto"
#endif
[Setup]
; Az AppId ÁLLANDÓ: egyszer generáld, és ne változtasd (a frissítés ezen múlik)
AppId={{8C3F6B1A-0000-4D2E-9A11-123456789ABC}
AppName=Diáknyilvántartó
AppVersion={#Verzio}
AppPublisher=Példa Diák
DefaultDirName={autopf}\{#Nev}
DefaultGroupName=Diáknyilvántartó
PrivilegesRequired=lowest
PrivilegesRequiredOverridesAllowed=dialog
OutputDir=kiadas
OutputBaseFilename={#Nev}-Setup-{#Verzio}
Compression=lzma2
SolidCompression=yes
WizardStyle=modern
ArchitecturesAllowed=x64compatible
ArchitecturesInstallIn64BitMode=x64compatible
SetupIconFile=Kepek\app.ico
UninstallDisplayIcon={app}\{#Nev}.exe
LicenseFile=LICENSE
[Tasks]
Name: desktopicon; Description: "Parancsikon az Asztalon"; Flags: unchecked
[Files]
Source: "publish\onallo\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "README.md"; DestDir: "{app}"; Flags: ignoreversion
Source: "CHANGELOG.md"; DestDir: "{app}"; Flags: ignoreversion
Source: "THIRD-PARTY-NOTICES.txt"; DestDir: "{app}"; Flags: ignoreversion
[Icons]
Name: "{group}\Diáknyilvántartó"; Filename: "{app}\{#Nev}.exe"
Name: "{group}\Eltávolítás"; Filename: "{uninstallexe}"
Name: "{autodesktop}\Diáknyilvántartó"; Filename: "{app}\{#Nev}.exe"; Tasks: desktopicon
[Run]
Filename: "{app}\{#Nev}.exe"; Description: "A program indítása"; Flags: nowait postinstall skipifsilentREADME, CHANGELOG és ellenőrzőlista sablon
# Diáknyilvántartó 1.0.0
Egyszerű nyilvántartó iskolai használatra: diákok, jegyek, átlagok.
## Rendszerkövetelmény
- Windows 10 vagy újabb, 64 bites.
- Az önálló csomag és a telepítő nem igényel további telepítést.
- A keretrendszer-függő ZIP-hez: .NET 8 Desktop Runtime (x64): https://dotnet.microsoft.com/download
## Telepítés
1. Futtasd a `Diaknyilvantarto-Setup-1.0.0.exe` fájlt (nem kér rendszergazdai jelszót).
2. Ha a Windows (SmartScreen) figyelmeztet: *További információ → Futtatás mindenképpen*. Ez azért van, mert a program nincs digitálisan aláírva.
3. Ellenőrizd a letöltést: `Get-FileHash <fájl> -Algorithm SHA256`, és vesd össze a `KIADAS.md` értékével.
## Hová kerülnek az adataid?
`%LocalAppData%\Diaknyilvantarto` (adatok, napló) és `%AppData%\Diaknyilvantarto` (beállítások). A *Súgó → Adatmappa megnyitása* menü megnyitja. Eltávolításkor az adataid megmaradnak.
## Eltávolítás
Beállítások → Alkalmazások → Diáknyilvántartó → Eltávolítás.
## Hibabejelentés
*Súgó → Névjegy → Másolás hibabejelentéshez*, és küldd el a leírással: <kapcsolat>.
---
# CHANGELOG
## 1.0.1 – 2026-11-15
- Javítva: a CSV-betöltés sérült sornál nem állt le.
## 1.0.0 – 2026-11-01
- Első kiadás.
---
# KIADÁSI ELLENŐRZŐLISTA (minden kiadáshoz)
- [ ] Verzió növelve (`.csproj`), a CHANGELOG frissítve
- [ ] Release build figyelmeztetés nélkül; tesztek zöldek (Release kimeneten is)
- [ ] Közzététel (fdd + önálló) tiszta mappába; nincs .pdb a csomagban
- [ ] Telepítő lefordítva; az AppId nem változott
- [ ] ZIP-ek + SHA-256 + KIADAS.md
- [ ] README, LICENSE, THIRD-PARTY-NOTICES a csomagban
- [ ] Tiszta gépes próba: telepítés, használat, frissítés, eltávolítás
- [ ] PDB-k archiválva (archivum\<verzió>)KIADAS.md.Javasolt fájlnév: winforms-14-9-szkript.webp · Alt szöveg: A kiadási szkript és a kiadási jegyzék
Tesztlista (18 pont)
A kész kiadáson (a telepítőből telepített programon, tiszta környezetben) próbáld ki, és jegyezd fel az eredményt.
| # | Teszt | Elvárt eredmény |
|---|---|---|
| 1 | A kiadási szkript egyetlen paranccsal lefut tiszta mappából | A kiadas mappában: telepítő, két ZIP, KIADAS.md; hiba nélkül. |
| 2 | A Release build figyelmeztetés nélkül fordul | 0 hiba, 0 figyelmeztetés (TreatWarningsAsErrors). |
| 3 | A csomagban nincs .pdb és fejlesztői fájl | Csak a program, a függőségek, a dokumentumok. |
| 4 | Telepítés felhasználói módon (nem rendszergazda) | Nem kér jelszót; Start menü és (kérésre) Asztal parancsikon. |
| 5 | Az Alkalmazások és szolgáltatások bejegyzése | Név, verzió (1.0.0), kiadó helyes; egy bejegyzés. |
| 6 | Az ikon minden helyen | Fájlkezelő, Start menü, tálca, ablak címsora: saját ikon. |
| 7 | Az exe Tulajdonságok → Részletek füle | Terméknév, verzió, szerzői jog helyes. |
| 8 | Névjegy ablak | Verzió, mód (Release), .NET, licenc, adatmappa; a hibabejelentő gomb másol. |
| 9 | Első indítás (törölt adatmappákkal) | Létrejönnek a mappák és az adatbázis; üdvözlő üzenet. |
| 10 | Normál használat (felvétel, jegy, átlag, mentés, betöltés) | Az adatok az AppData mappákba kerülnek; nincs Access denied. |
| 11 | Újraindítás után az adatok megvannak | A beállítások (ablakméret) és az adatok megmaradtak. |
| 12 | Frissítés 1.0.1-re a régi fölé (azonos AppId) | Egy bejegyzés, verzió 1.0.1, az adat és a beállítás megmaradt. |
| 13 | Adatbázis-migráció régi (1-es verziójú) adatbázissal | Az új oszlop létrejön, az adat megmarad, .bak készül. |
| 14 | Frissítés-ellenőrző (helyi legujabb.json) | Az 1.0.0 jelzi az 1.0.1-et; az 1.0.1 nem; hibás JSON, nincs fájl: nincs összeomlás. |
| 15 | Csak olvasható adatmappa | Érthető hibaüzenet és kontrollált kilépés. |
| 16 | Önálló és keretrendszer-függő ZIP tiszta gépen | Az önálló fut; a fdd futtatókörnyezet nélkül a „You must install .NET” üzenetet adja (a README leírja). |
| 17 | Ellenőrző összeg | A Get-FileHash értéke egyezik a KIADAS.md bejegyzésével. |
| 18 | Eltávolítás | A program és a parancsikonok eltűnnek; a felhasználói adat megmarad (vagy a döntés szerint törlődik); újratelepítés a régi adatokkal működik. |
Gyakori hibák és önértékelés
Gyakori hibák a kiadásnál
- A
bin\Debugmappát adják át (lassú, PDB-vel, fejlesztői fájlokkal). - A verziót nem növelik: a frissítés-ellenőrző és a telepítő is „nem újabbat” lát.
- Új
AppIdminden verzióhoz: két program a listán. - A program a Program Files mappába ír: telepítés után Access denied.
- Nincs migráció: a frissített felhasználónál hiányzó oszlop.
- Csak a saját gépen próbálták ki: hiányzó futtatókörnyezet, DLL vagy fájl.
- Az eltávolítás törli a felhasználó adatait figyelmeztetés nélkül.
- Nincs README és SmartScreen-magyarázat: a felhasználó „vírusnak” hiszi a programot.
Önálló értékelés
Pontozd magad a követelménytábla szerint, majd válaszolj:
- Melyik tesztnél találtál hibát a tiszta gépen, amelyet a saját gépeden nem láttál?
- Mi történik, ha holnap kiadsz egy 1.0.2 javítást: mennyi idő és hány kézi lépés? Mit automatizálnál még?
- Mit jelent a felhasználó számára, hogy az adatai megmaradnak frissítéskor és eltávolításkor?
- Hogyan találnád meg egy felhasználó hibajelentéséből a pontos kiadást és a forráskódot?
- Mi marad ki a kiadásodból (aláírás, automatikus frissítés, tesztek)? Mit pótolnál?
Bővítési ötletek
- Automatikus build (CI): a kiadási szkript futtatása GitHub Actionsben, a csomagok feltöltése a Releases oldalra.
- Kódaláírás: aláírt telepítő (
signtool), a SmartScreen figyelmeztetés csökkentése. - winget-jegyzék: a program telepíthető legyen a
winget installparanccsal. - Biztonsági mentés/visszaállítás menü: az adatmappa ZIP-be mentése és visszaállítása verzióellenőrzéssel.
- Összeomlás-jelentés: a globális hibakezelő (13.4 lecke) felajánlja a napló és a környezeti adatok elküldését (a felhasználó jóváhagyásával, személyes adat nélkül).
- MSIX csomag: modern telepítés és tiszta eltávolítás.
Gyakorlótár: 6 további feladat
Három szinten gyakorolhatsz: alap, közepes és kihívás. Csak a próbálkozás után nyisd le a megoldást.
Alap szint
1. Mit adsz át?
Melyik mappa tartalma kerül a felhasználóhoz: A bin\Debug, publish vagy obj?Megoldás megnyitása
publish (vagy a belőle készült telepítő/ZIP); a bin\Debug és az obj fejlesztői kimenet.
2. Mi az ellenőrzőlista haszna?
Miért kell, ha van szkript? A szkript az automatizálható lépéseket végzi; a lista a kézi ellenőrzéseket (tiszta gépes próba, olvashatóság, dokumentáció) is lefedi, és biztosítja, hogy semmi nem marad ki.Megoldás megnyitása
Közepes szint
3. Verzió-növelés
Melyik számot növeled: hibajavításnál, új funkciónál? Hibajavításnál a patch (1.0.1), új, kompatibilis funkciónál a minor (1.1.0).Megoldás megnyitása
4. Archiválás
Mit őrzöl meg minden kiadásból, és miért? A telepítőt/ZIP-et, a hash-t és a Megoldás megnyitása
.pdb fájlokat, a forrás címkéjével (commit): a hibajelentések stack trace-ének értelmezéséhez és a pontos reprodukáláshoz.
Kihívás szint
5. Visszagörgetés
Egy kiadott verzióban súlyos hiba van. Mit teszel? Visszavonod/leveszed a hibás letöltést, kiadsz egy javított, magasabb verziót (nem a régi számmal), a README-ben és a CHANGELOG-ban jelzed; a régebbi, jó verziót (archívum) ideiglenesen elérhetővé teszed.Megoldás megnyitása
6. Automatizálás
Hogyan ellenőrizné egy automatikus rendszer, hogy a kiadás jó? CI: tiszta környezetben fordít, teszteket futtat, közzétesz, telepítőt készít, hash-t számol; egy „füstteszt” (a program elindul, és kilép hiba nélkül) a csomagolt kimeneten; csak sikeres esetben tölti fel a csomagot.Megoldás megnyitása
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. Reprodukálható: a forrásból mindig ugyanazt a csomagot állítja elő, kézi hibák nélkül. Ezen múlik, hogy az új telepítő frissítésként a régi helyére települ. Kipróbálás fejlesztői környezet nélküli gépen, hogy kiderüljön a hiányzó futtatókörnyezet, DLL vagy fájl. A csomagok nevét, méretét és SHA-256 összegét, a dátumot és a verziót. Az A felhasználói hibajelentések stack trace-ének értelmezésére. Minden kiadásnál, hogy a telepítő, a frissítés-ellenőrző és a hibajelentések megkülönböztessék.1. Mi a kiadási szkript haszna?
2. Miért állandó az AppId?
3. Mi a tiszta gépes próba?
4. Mit tartalmaz a KIADAS.md?
5. Hová kerülnek a felhasználói adatok?
AppData mappákba; az eltávolításkor megmaradnak (vagy rákérdez).6. Mire jó a PDB archiválása?
7. Mikor adsz ki új verziószámot?
A 14. fejezet összefoglalása: mit tudsz már?
- Megkülönbözteted a Debug és Release változatot, ismered a kimenet fájljait és a futtatókörnyezeteket.
- NuGet-függőségeket kezelsz, és közzéteszed a programot (keretrendszer-függő, önálló, egy fájl).
- Telepítőt készítesz, kezeled a frissítést és az eltávolítást.
- A program adatait a helyes helyre teszed, az adatbázist migrálod.
- Arculatot, verziót, Névjegyet és licencet adsz a programnak.
- Reprodukálható kiadási folyamatot építesz, és tiszta gépen ellenőrzöd.
Következő fejezet: Továbbtanulás és egyéb .NET grafikus technológiák
A 15. fejezetben a Windows Forms után merre léphetsz tovább: WPF, MAUI, webes alkalmazások, és hogyan folytasd a tanulást.

