Grafikus C# – Windows Forms tanfolyam › 3.4 lecke Windows Forms · 3.4 lecke · adatok felvétele és listázása Eddig az adatbázis felhasználóit a DB Browserben vettük fel. Most a program maga is tud új felhasználót regisztrálni (INSERT), és az adatbázis tartalmát táblázatban mutatja meg (DataGridView). Közben két fontos új dolgot tanulunk: az Regisztráció SQLite-tal és DataGridView
ExecuteNonQuery módosító parancsot és a DataTable használatát.
A lecke végére
- paraméterezett
INSERTutasítással új sort veszel fel az adatbázisba (ExecuteNonQuery); - megakadályozod az azonos nevű felhasználók felvételét;
- a
DataGridViewvezérlőben megjeleníted a felhasználók listáját; - érted a
DataTableés azSQLiteDataAdapterszerepét (Fill,DataSource); - a listában nem mutatod a jelszót;
- a lista frissítését külön metódusba teszed, és minden változtatás után meghívod.
Mielőtt elkezded
Ehhez a leckéhez kell a 3.3 lecke (DB.cs, paraméterezett lekérdezés, bool visszatérési érték) és az 1.9–1.10 leckék (jelszómező, visszajelző felirat).
A felület
Bővítsd az előző leckék projektjét, vagy hozz létre újat (belepes.db, NuGet csomag, DB.cs a 3.2 és 3.3 szerint). Az ablakra kerül:
UseSystemPasswordChar: True).Regisztráció felirattal.DataGridView (Toolbox, Data csoport): a felhasználók táblázata. Az előtag dgv.A DataGridView néhány hasznos tulajdonságát állítsd a Properties ablakban:
Mit tegyél ide: a tervezőnézet a két beviteli mezővel, a Regisztráció gombbal és az üres DataGridView-val, mellette a Properties ablak a beállított tulajdonságokkal.Javasolt fájlnév: winforms-3-4-felulet.webp · Alt szöveg: A regisztrációs ablak felülete DataGridView-val
Új felhasználó felvétele: INSERT
Az INSERT új sort vesz fel a táblába (3.1 lecke). A C# programban az SQLiteCommand ExecuteNonQuery() metódusával hajtjuk végre: ez olyan parancsokhoz való, amelyek módosítanak (INSERT, UPDATE, DELETE), és az érintett sorok számát adja vissza. A felhasználó beírt szövegét paraméterként adjuk át (3.3 lecke):
public static bool Regisztral(string nev, string jelszo)
{
using (SQLiteConnection kapcsolat = UjKapcsolat())
{
kapcsolat.Open();
// 1. Létezik már ez a név?
using (SQLiteCommand ellenor = new SQLiteCommand("SELECT COUNT(*) FROM belepesi_adatok WHERE felhnev = @nev", kapcsolat))
{
ellenor.Parameters.AddWithValue("@nev", nev);
if (Convert.ToInt32(ellenor.ExecuteScalar()) > 0)
{
return false;
}
}
// 2. Felvétel
using (SQLiteCommand beszur = new SQLiteCommand("INSERT INTO belepesi_adatok (felhnev, jelszo) VALUES (@nev, @jelszo)", kapcsolat))
{
beszur.Parameters.AddWithValue("@nev", nev);
beszur.Parameters.AddWithValue("@jelszo", jelszo);
beszur.ExecuteNonQuery();
}
return true;
}
}false értékkel tér vissza, és nem vesz fel új sort. A felhasználó érthető üzenetet kap.felhnev oszlopa egyedi (3.1 lecke), ezért ha mégis két azonos nevet próbálna felvenni a program (például egyszerre két kattintás), az adatbázis hibát jelez, és a gomb try–catch blokkja elkapja.A gomb eseménykezelője a bevitelt ellenőrzi, a DB.Regisztral hívja, és a választól függően jelez:
private void Uzenet(string szoveg, bool hiba)
{
lblUzenet.Text = szoveg;
lblUzenet.ForeColor = hiba ? Color.Firebrick : Color.SeaGreen;
}
private void btnRegisztral_Click(object sender, EventArgs e)
{
string nev = txtFelhasznalo.Text.Trim();
string jelszo = txtJelszo.Text;
if (nev.Length == 0)
{
Uzenet("Add meg a felhasználónevet!", true);
return;
}
if (jelszo.Length < 6)
{
Uzenet("A jelszó legalább 6 karakter legyen!", true);
return;
}
try
{
if (DB.Regisztral(nev, jelszo))
{
Uzenet("Sikeres regisztráció.", false);
txtFelhasznalo.Clear();
txtJelszo.Clear();
Frissit();
}
else
{
Uzenet("Ez a felhasználónév már foglalt.", true);
}
}
catch (Exception ex)
{
MessageBox.Show("Adatbázis-hiba: " + ex.Message);
}
}Jósolj!
Kétszer egymás után regisztrálod ugyanazt a nevet. Mit ír ki a program másodszor? Hány sor lesz a táblában ebből a névből?
Jóslat ellenőrzése
Másodszor a Ez a felhasználónév már foglalt. üzenetet. A névből csak egy sor lesz a táblában, mert a DB.Regisztral az ellenőrzés után false értékkel tért vissza, az INSERT pedig nem futott le.
Az adatok táblázatban: DataTable és DataGridView
A DataGridView táblázatos nézetben mutat adatokat. Nem közvetlenül az adatbázisból olvas, hanem egy DataTable objektumból, amely egy memóriában lévő táblázat (sorok és oszlopok). Az SQLiteDataAdapter a „közvetítő”: lefuttatja a SELECT lekérdezést, és az eredményt a DataTable-be tölti (Fill). A DataGridView.DataSource tulajdonságának ezt a táblát megadva a táblázat megjelenik:
public static DataTable Felhasznalok()
{
DataTable tabla = new DataTable();
using (SQLiteConnection kapcsolat = UjKapcsolat())
{
kapcsolat.Open();
using (SQLiteDataAdapter adapter = new SQLiteDataAdapter("SELECT id, felhnev FROM belepesi_adatok ORDER BY felhnev", kapcsolat))
{
adapter.Fill(tabla);
}
}
return tabla;
}using System.Data; sor kell a használatához.tabla objektumba tölti.void és nem bool, hanem DataTable: a hívó közvetlenül a táblázat adatforrásává teheti.using blokk a Fill után lezárja a kapcsolatot, a DataTable az adatokat megőrzi.A jelszó nem tartozik a listába
Ha egy lekérdezés SELECT * formában minden oszlopot lekérne, a jelszavak is megjelennének a táblázatban. Mindig csak azokat az oszlopokat kérd le, amelyekre a felületnek szüksége van. Valódi programban a jelszavak hash-ként vannak tárolva, de akkor sem mutatnánk meg őket.
A lista frissítése
Az INSERT után az adatbázis megváltozik, de a táblázat nem frissül magától: a DataTable egy pillanatfelvétel, amelyet a lekérdezés pillanatában készítettünk. Ezért a lista kitöltését egy külön Frissit() metódusba tesszük, és meghívjuk az ablak betöltésekor és minden sikeres felvétel után:
private void Frissit()
{
try
{
dgvFelhasznalok.DataSource = DB.Felhasznalok();
dgvFelhasznalok.Columns["id"].HeaderText = "Azonosító";
dgvFelhasznalok.Columns["felhnev"].HeaderText = "Felhasználónév";
}
catch (Exception ex)
{
MessageBox.Show("Nem sikerült betölteni a listát: " + ex.Message);
}
}
private void Form1_Load(object sender, EventArgs e)
{
Frissit();
}DataGridView automatikusan létrehozza az oszlopokat és a sorokat.felhnev), ezt itt szebbre cseréljük.Form1_Load eseményben (induláskor) és a regisztráció után. Így a lista mindig friss.Jósolj!
A Frissit() hívás kimarad a sikeres regisztráció után. A felvétel megtörténik, de mit lát a felhasználó a táblázatban? És az adatbázisban?
Jóslat ellenőrzése
A táblázat a régi állapotot mutatja (az új felhasználó nem látszik), az adatbázisban viszont már ott van a sor. A program újraindítása után, amikor a Form1_Load újra lefut, megjelenik. A hiba a frissítés kimaradása, az adatok nem vesztek el.
Mit tegyél ide: a futó program a DataGridView-ban három felhasználóval (Azonosító és Felhasználónév oszlop, jelszó nélkül), és a zöld „Sikeres regisztráció.” üzenettel.Javasolt fájlnév: winforms-3-4-datagridview.webp · Alt szöveg: A felhasználók táblázata a regisztráció után
Hibakeresés: te vagy a hibadetektív
Mi a baj, és hogyan javítod?
Gondold végig az okot, és csak utána nyisd le a megoldást.
1. A táblázat nem frissül
Beszúrtad az új felhasználót, de a táblázat változatlan marad. A Ok és javítás
DataGridView nem frissül magától az INSERT után. A sikeres felvétel után hívd meg a Frissit() metódust, amely újra lekérdezi az adatokat, és az új DataTable-t adja a DataSource-nak.
2. Ismeretlen DataTable
// CS0246: The type or namespace name 'DataTable' could not be foundHiba és javítás
Fordítási hiba (CS0246): hiányzik a using System.Data; sor a DB.cs fájl elejéről.
3. Üres táblázat
DataTable tabla = new DataTable();
// ... az adapter létrejön, de a Fill hívás kimarad ...
return tabla;Hiba és javítás
Rossz működés: az adapter.Fill(tabla) hívás nélkül a DataTable üres marad, és a táblázat is üres lesz. A Fill hozza át az adatokat az adatbázisból.
4. Összefűzött INSERT
string sql = "INSERT INTO belepesi_adatok (felhnev, jelszo) VALUES ('" + nev + "', '" + jelszo + "')";Hiba és javítás
Biztonsági hiba (SQL-befecskendezés, 3.3 lecke): az aposztrófos név hibát okoz, és a felhasználó az SQL jelentését is megváltoztathatja. Használj paramétereket: VALUES (@nev, @jelszo).
5. UNIQUE hiba a felhasználónál
// SQLiteException: constraint failed
// UNIQUE constraint failed: belepesi_adatok.felhnevHiba és javítás
Az adatbázis védelme jelzett: már van ilyen nevű felhasználó. A DB.Regisztral előzetes ellenőrzése ezt általában megelőzi, de a try–catch a váratlan esetre is megvéd. A felhasználónak érthető üzenetet kell adni: Ez a felhasználónév már foglalt.
6. Látszanak a jelszavak
new SQLiteDataAdapter("SELECT * FROM belepesi_adatok", kapcsolat)Hiba és javítás
A SELECT * minden oszlopot lekér, a jelszót is. A listához csak azokat az oszlopokat kérd, amelyeket a felhasználónak látnia kell: SELECT id, felhnev FROM belepesi_adatok.
Mini projekt: regisztráció és felhasználólista
Állítsd össze a teljes programot:
- Ablak:
txtFelhasznalo,txtJelszo,btnRegisztral,dgvFelhasznalok(ReadOnly, nincs új sor, a teljes szélességet kitölti),lblUzenet. - A
DB.csosztályba:RegisztralésFelhasznalokmetódus a leckében látott módon. - A
Frissit()metódus és aForm1_Loada lista betöltéséhez. - A gomb ellenőrzi a bevitelt, regisztrál, és jelez; sikernél törli a mezőket és frissíti a listát.
- Próbáld ki: új név, ugyanaz a név még egyszer, rövid jelszó, aposztrófos név (például O'Hara), üres név.
- Nézd meg a DB Browserben, hogy a sorok ott vannak-e (előbb zárd be a DB Browsert, vagy írd ki a változtatásait:
Ctrl+S).
Mit tegyél ide: a kész program a felhasználólistával és az „Ez a felhasználónév már foglalt.” piros üzenettel.Javasolt fájlnév: winforms-3-4-kesz.webp · Alt szöveg: A kész regisztrációs program felhasználólistával
Három önálló gyakorlófeladat
Egyszerre csak egyet csinálj: tervezd meg, írd meg, futtasd, ellenőrizd, és csak utána nyisd ki a megoldást.
1. Hány felhasználó van?
Egy A lblDarab felirat mutassa a táblázat soraiban lévő felhasználók számát: Felhasználók száma: 5.Egy lehetséges megoldás
// a Frissit() metódusban, a DataSource beállítása után:
lblDarab.Text = $"Felhasználók száma: {dgvFelhasznalok.Rows.Count}";Rows.Count a táblázat sorainak száma. Mivel az új sor ki van kapcsolva (AllowUserToAddRows: False), nincs üres plusz sor.
2. Szűrés névrészletre
Tegyél az ablakra egy A txtKeres mezőt. Gépelés közben a táblázat csak azokat a felhasználókat mutassa, amelyek neve tartalmazza a beírt szöveget.Egy lehetséges megoldás
// DB.cs:
public static DataTable Kereses(string resz)
{
DataTable tabla = new DataTable();
using (SQLiteConnection kapcsolat = UjKapcsolat())
{
kapcsolat.Open();
using (SQLiteCommand parancs = new SQLiteCommand("SELECT id, felhnev FROM belepesi_adatok WHERE felhnev LIKE @minta ORDER BY felhnev", kapcsolat))
{
parancs.Parameters.AddWithValue("@minta", "%" + resz + "%");
using (SQLiteDataAdapter adapter = new SQLiteDataAdapter(parancs))
{
adapter.Fill(tabla);
}
}
}
return tabla;
}
// Form1.cs:
private void txtKeres_TextChanged(object sender, EventArgs e)
{
dgvFelhasznalok.DataSource = DB.Kereses(txtKeres.Text.Trim());
}% jeleket (bármennyi karakter) a C# kód fűzi a szöveghez, és az egész minta megy paraméterként az SQL-be. A felhasználó szövege itt sem kerül az SQL szövegébe, ezért a megoldás biztonságos. A SQLiteDataAdapter(parancs) változat egy kész parancsot kap, így a paramétere is érvényes.
3. Jelszó megerősítése
Tegyél az ablakra egy második jelszómezőt ( A txtJelszo2), és csak akkor engedd a regisztrációt, ha a két jelszó egyezik.Egy lehetséges megoldás
if (jelszo != txtJelszo2.Text)
{
Uzenet("A két jelszó nem egyezik!", true);
return;
}!= jel azt jelenti: nem egyenlő. Az ellenőrzést a jelszó hosszának vizsgálata mellé tedd, még az adatbázis-hívás előtt.
Gyakorlótár: 6 további feladat
Három szinten gyakorolhatsz: alap, közepes és kihívás. A feladatokhoz a 3.4 lecke projektje kell (DB.cs a Regisztral és Felhasznalok metódussal, dgvFelhasznalok, lblUzenet, Frissit()). Csak a próbálkozás után nyisd le a megoldást.
Alap szint
1. Fordított rendezés
Egy A két SQL szöveg rögzített, a jelölőnégyzet csak azt választja ki, melyiket használjuk. Így biztonságos marad: a felhasználó nem szövegrészt ad meg, csak egy igaz/hamis döntést.chkCsokkeno jelölőnégyzet váltson a felhasználók növekvő és csökkenő név szerinti sorrendje között. Az SQL-t ne a felhasználó szövegéből állítsd össze.Megoldás megnyitása
public static DataTable Felhasznalok(bool csokkeno)
{
string sql = csokkeno
? "SELECT id, felhnev FROM belepesi_adatok ORDER BY felhnev DESC"
: "SELECT id, felhnev FROM belepesi_adatok ORDER BY felhnev";
DataTable tabla = new DataTable();
using (SQLiteConnection kapcsolat = UjKapcsolat())
{
kapcsolat.Open();
using (SQLiteDataAdapter adapter = new SQLiteDataAdapter(sql, kapcsolat))
{
adapter.Fill(tabla);
}
}
return tabla;
}private void chkCsokkeno_CheckedChanged(object sender, EventArgs e)
{
dgvFelhasznalok.DataSource = DB.Felhasznalok(chkCsokkeno.Checked);
}
2. A kijelölt felhasználó neve
Ha a felhasználó egy sorra kattint a táblázatban, a A lblKijelolt feliraton jelenjen meg a kijelölt név.Megoldás megnyitása
SelectionChanged esemény a kijelölés megváltozásakor fut. A CurrentRow az aktuális sor (lehet null), a Cells["felhnev"] a név cellája.private void dgvFelhasznalok_SelectionChanged(object sender, EventArgs e)
{
if (dgvFelhasznalok.CurrentRow != null)
{
lblKijelolt.Text = "Kijelölt: " + dgvFelhasznalok.CurrentRow.Cells["felhnev"].Value;
}
}
Közepes szint
3. Jelszószabály függvénnyel
Írj egy A számjegy keresése a bool JelszoJo(string jelszo) metódust: legalább 6 karakter, és legalább egy számjegy. A regisztráció csak jó jelszóval menjen tovább.Megoldás megnyitása
foreach ciklussal, a char.IsDigit metódussal történik. A szabályt külön metódusba tesszük, hogy a regisztráció kódja rövid maradjon, és a szabály egy helyen módosítható legyen.private bool JelszoJo(string jelszo)
{
if (jelszo.Length < 6)
{
return false;
}
foreach (char betu in jelszo)
{
if (char.IsDigit(betu))
{
return true;
}
}
return false;
}
// a gombban:
if (!JelszoJo(jelszo))
{
Uzenet("A jelszó legalább 6 karakter legyen, és tartalmazzon számjegyet!", true);
return;
}
4. Az új sor kijelölése
Sikeres regisztráció után a táblázatban jelölődjön ki az új felhasználó sora. A frissítés után végigmegyünk a sorokon, megkeressük a nevet, és kijelöljük. A Megoldás megnyitása
CurrentCell beállítása görgeti is a táblázatot, ha a sor nem látszana.private void SorKijelolese(string nev)
{
foreach (DataGridViewRow sor in dgvFelhasznalok.Rows)
{
if (sor.Cells["felhnev"].Value.ToString() == nev)
{
dgvFelhasznalok.CurrentCell = sor.Cells["felhnev"];
sor.Selected = true;
return;
}
}
}
// a regisztráció sikeres ága:
Frissit();
SorKijelolese(nev);
Kihívás szint
5. Az adatbázis hibájának felismerése
A regisztráció Az catch blokkja ismerje fel, ha az adatbázis a UNIQUE szabály megsértése miatt utasította el a felvételt (például két egyszerre indított regisztráció miatt), és érthető üzenetet adjon.Megoldás megnyitása
SQLiteException ResultCode tulajdonsága megmondja a hiba fajtáját, a SQLiteErrorCode.Constraint a szabálysértést jelöli. A pontos kódérték a csomag verziójától függhet, ezért érdemes kipróbálni, és szükség esetén az ex.Message szövegét is megvizsgálni (az UNIQUE constraint failed szöveg szerepel benne).catch (SQLiteException ex)
{
if (ex.ResultCode == SQLiteErrorCode.Constraint || ex.Message.Contains("UNIQUE"))
{
Uzenet("Ez a felhasználónév már foglalt.", true);
}
else
{
MessageBox.Show("Adatbázis-hiba: " + ex.Message);
}
}
6. A lista exportálása CSV fájlba
Egy gomb mentse a felhasználók azonosítóját és nevét egy pontosvesszővel tagolt szöveges fájlba (1;kata). A fájlnevet egy A sfdExport SaveFileDialog kérdezze meg. A jelszó ne kerüljön bele.Megoldás megnyitása
DataTable sorait a foreach járja végig, a StreamWriter soronként írja a fájlba. A using blokk a végén lezárja a fájlt. A DB.Felhasznalok() eleve csak az id és a felhnev oszlopot kéri le, így a jelszó véletlenül sem kerülhet az exportba.private void btnExport_Click(object sender, EventArgs e)
{
sfdExport.Filter = "CSV fájl (*.csv)|*.csv";
sfdExport.DefaultExt = "csv";
if (sfdExport.ShowDialog() != DialogResult.OK)
{
return;
}
try
{
DataTable tabla = DB.Felhasznalok();
using (System.IO.StreamWriter iro = new System.IO.StreamWriter(sfdExport.FileName))
{
foreach (DataRow sor in tabla.Rows)
{
iro.WriteLine(sor["id"] + ";" + sor["felhnev"]);
}
}
Uzenet("Az exportálás sikeres.", false);
}
catch (Exception ex)
{
MessageBox.Show("Nem sikerült exportálni: " + ex.Message);
}
}
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. Az A Megmondja a DataGridView-nak, honnan jöjjenek az adatok. Ha egy DataTable-t kap, megjeleníti a sorait és oszlopait. Mert a DataTable a lekérdezés pillanatában készült pillanatfelvétel. Az új adat megjelenítéséhez újra le kell kérdezni. Mert a lekérdezés csak az Az előzetes ellenőrzés (1. Mi a különbség az ExecuteScalar és az ExecuteNonQuery között?
ExecuteScalar egyetlen értéket kérdez le (például COUNT), az ExecuteNonQuery módosító parancsot hajt végre (INSERT, UPDATE, DELETE), és az érintett sorok számát adja vissza.2. Mire való a DataTable és az adapter.Fill?
DataTable egy memóriában lévő táblázat. A Fill az SQL lekérdezés eredményét tölti bele.3. Mire való a DataSource?
4. Miért nem frissül magától a táblázat egy INSERT után?
5. Miért nem szerepel a jelszó a listában?
id és a felhnev oszlopot kéri. A jelszót a felületen soha nem mutatjuk meg.6. Mi védi meg az azonos nevű felhasználók felvételét?
COUNT(*)), és az adatbázis UNIQUE beállítása védőhálóként.
Mit tudsz már?
- Paraméterezett INSERT-tel új sort veszel fel.
- Megakadályozod az azonos nevű felhasználókat.
- DataGridView-ban megjeleníted az adatokat DataTable segítségével.
- A jelszót nem mutatod a listában.
- A lista frissítését külön metódusba teszed.
Következő témák: SELECT, szűrés és módosítás
A felvétel és a listázás megvan. A következő leckében a lekérdezéseket (WHERE, ORDER BY), utána a módosítást és a törlést (UPDATE, DELETE) tanuljuk meg.




