Grafikus C# – Windows Forms tanfolyam › 9.11 lecke Windows Forms · 9.11 lecke · grafikon adatokból A grafikonos leckében beégetett számokból rajzoltál. Most az adatbázis összesítéséből készítesz grafikont: egy SQL-lekérdezés eredményét sorról sorra átteszed a Grafikon adatbázisból: oszlopok, színek, tengelycímek
Chart vezérlőbe, kiemelsz egy oszlopot színnel, és feliratokat adsz a tengelyeknek.
A lecke végére
- egy
GROUP BYlekérdezés eredményétChartoszlopokká alakítod; - egy kiválasztott oszlopot külön színnel kiemelsz (
Points[index].Color); - címet és tengelyfeliratokat adsz a grafikonnak;
- a grafikont újrarajzoltatod, ha a felhasználó másik elemet választ;
- felismered a tipikus hibákat (kétszer rajzolt sorozat, üres tábla).
Mielőtt elkezded
Ehhez a leckéhez kell a 9.9 lecke (Chart, Series, AddXY, Titles), a 3.12 lecke (SUM, GROUP BY), a 3.8 lecke (DataTable, SQLiteDataAdapter) és az 1.6 lecke (ComboBox).
Előkészítés
Ugyanazt a gyakorlo.db adatbázist használjuk, amelyet a 3.12 leckében hoztál létre (Dolgozok tábla). Ha nincs meg, ott megtalálod a létrehozó SQL-t.
- Hozz létre új Windows Forms (.NET Framework) projektet
GrafikonDbnéven, telepítsd aSystem.Data.SQLitecsomagot (3.2), és add hozzá aDB.csosztályt"gyakorlo.db"fájlnévvel. - Másold a
gyakorlo.dbfájlt abin\Debugmappába. - A Formra tegyél egy
Chartvezérlőt (chart1,Dock = Fill) és fölé egyComboBox-ot (cmbReszleg,DropDownStyle = DropDownList). A ComboBox elemei:Iroda,Raktár,Műhely. - A fájl tetejére írd be:
using System.Data;
using System.Data.SQLite;
using System.Drawing;
using System.Windows.Forms.DataVisualization.Charting;Egy lekérdezés eredménye egy sorozat lesz
A terv három lépés: (1) az SQL kiszámolja részlegenként a fizetések összegét, (2) az eredmény egy DataTable-ben érkezik, (3) a tábla sorain végigmenve minden sor egy oszlop lesz a grafikonon.
private void Rajzol()
{
string sql = "SELECT reszleg, SUM(fizetes) AS osszeg FROM Dolgozok GROUP BY reszleg ORDER BY reszleg";
try
{
DataTable tabla = new DataTable();
using (SQLiteConnection kapcsolat = DB.UjKapcsolat())
using (SQLiteDataAdapter adapter = new SQLiteDataAdapter(sql, kapcsolat))
{
adapter.Fill(tabla);
}
chart1.Series.Clear();
Series sorozat = chart1.Series.Add("Fizetések összege");
sorozat.ChartType = SeriesChartType.Column;
sorozat.IsValueShownAsLabel = true;
foreach (DataRow sor in tabla.Rows)
{
string reszleg = sor["reszleg"].ToString();
int osszeg = Convert.ToInt32(sor["osszeg"]);
sorozat.Points.AddXY(reszleg, osszeg);
}
}
catch (Exception ex)
{
MessageBox.Show("Hiba: " + ex.Message);
}
}
private void Form1_Load(object sender, EventArgs e)
{
Rajzol();
}AS osszeg) az oszlop neve a DataTable-ben. Az érték object típusú, ezért alakítjuk át a Convert.ToInt32-vel.Jósolj!
Hány oszlop lesz a grafikonon, milyen sorrendben, és milyen magasak? (Az SQL ORDER BY reszleg betűrendbe rendezi a sorokat.)
Jóslat ellenőrzése
Három oszlop lesz, betűrendben: Iroda 1 165 000, Műhely 860 000, Raktár 935 000. A számok az oszlopok fölött is látszanak, mert az IsValueShownAsLabel igaz.
Egy oszlop kiemelése színnel
Eddig minden oszlop azonos színű volt. A grafikon akkor segít, ha a lényeges oszlop kiemelkedik. Egy sorozat minden pontjának külön színe lehet: a Points.AddXY az új pont indexét adja vissza, azzal elérhető a pont, és állítható a Color tulajdonsága.
private void Rajzol(string kivalasztott)
{
// ... lekérdezés és DataTable feltöltése, mint fent ...
foreach (DataRow sor in tabla.Rows)
{
string reszleg = sor["reszleg"].ToString();
int osszeg = Convert.ToInt32(sor["osszeg"]);
int index = sorozat.Points.AddXY(reszleg, osszeg);
if (reszleg == kivalasztott)
{
sorozat.Points[index].Color = Color.OrangeRed;
}
else
{
sorozat.Points[index].Color = Color.SlateBlue;
}
}
}AddXY visszatérési értéke a most hozzáadott pont sorszáma a sorozatban.Color.OrangeRed és a Color.SlateBlue a 9.4 leckéből ismert színnevek.Rövidebben, feltételes operátorral
Az if / else helyett írhatod egyetlen sorban: sorozat.Points[index].Color = reszleg == kivalasztott ? Color.OrangeRed : Color.SlateBlue;. Ugyanazt csinálja, csak tömörebb.
Cím és tengelyfeliratok
Egy grafikon csak akkor érthető, ha kiderül belőle, mit mér és mi van a tengelyeken. A címet a Titles gyűjteménnyel adtad meg a 9.9 leckében, a tengelyek feliratát pedig a ChartAreas[0] tengelyeinek Title tulajdonsága tartalmazza.
chart1.Titles.Clear();
chart1.Titles.Add("Fizetések összege részlegenként");
chart1.ChartAreas[0].AxisX.Title = "Részleg";
chart1.ChartAreas[0].AxisY.Title = "Összeg (Ft)";Próbáld ki!
Írd be a négy sort, futtasd, és nézd meg, hol jelennek meg a feliratok. Utána írd át a címet úgy, hogy a kiválasztott részleg neve is benne legyen ("Fizetések összege – kiemelve: " + kivalasztott).
Frissítés, amikor a felhasználó választ
Eddig a grafikon csak az ablak megnyitásakor rajzolódott. Most kössük a ComboBox-hoz: ha másik részleget választasz, a kiemelés átkerül a másik oszlopra. Ehhez a cmbReszleg SelectedIndexChanged eseményét használjuk (dupla kattintás a ComboBoxon a tervezőben).
private void Rajzol(string kivalasztott)
{
string sql = "SELECT reszleg, SUM(fizetes) AS osszeg FROM Dolgozok GROUP BY reszleg ORDER BY reszleg";
try
{
DataTable tabla = new DataTable();
using (SQLiteConnection kapcsolat = DB.UjKapcsolat())
using (SQLiteDataAdapter adapter = new SQLiteDataAdapter(sql, kapcsolat))
{
adapter.Fill(tabla);
}
chart1.Series.Clear();
chart1.Titles.Clear();
chart1.Titles.Add("Fizetések összege részlegenként");
chart1.ChartAreas[0].AxisX.Title = "Részleg";
chart1.ChartAreas[0].AxisY.Title = "Összeg (Ft)";
Series sorozat = chart1.Series.Add("Fizetések összege");
sorozat.ChartType = SeriesChartType.Column;
sorozat.IsValueShownAsLabel = true;
foreach (DataRow sor in tabla.Rows)
{
string reszleg = sor["reszleg"].ToString();
int osszeg = Convert.ToInt32(sor["osszeg"]);
int index = sorozat.Points.AddXY(reszleg, osszeg);
if (reszleg == kivalasztott)
{
sorozat.Points[index].Color = Color.OrangeRed;
}
else
{
sorozat.Points[index].Color = Color.SlateBlue;
}
}
}
catch (Exception ex)
{
MessageBox.Show("Hiba: " + ex.Message);
}
}
private void cmbReszleg_SelectedIndexChanged(object sender, EventArgs e)
{
Rajzol(cmbReszleg.Text);
}
private void Form1_Load(object sender, EventArgs e)
{
cmbReszleg.SelectedIndex = 0;
}Jósolj!
Az indításkor a Form1_Load csak a SelectedIndex = 0 sort futtatja. Rajzolódik-e ettől grafikon, vagy a Rajzol-t is meg kell hívni?
Jóslat ellenőrzése
Rajzolódik. A SelectedIndex átállítása kiváltja a SelectedIndexChanged eseményt, az pedig meghívja a Rajzol-t az Iroda névvel, így az első oszlop lesz kiemelve. Ha a Rajzol-t is beírnád a Form1_Load-ba, a grafikon kétszer készülne el.
Gyakori hibák
1. Hiányzó Series.Clear()
Ha a Rajzol elejéről kihagyod a chart1.Series.Clear() sort, a második hívásnál (másik elem választásakor) a régi sorozat a grafikonon marad, és mellé kerül egy új, vagy ugyanazon a néven felvett sorozatnál hibaüzenettel leáll a program. A Titles.Clear() hiánya ugyanígy halmozza a címeket.
Megoldás: újrarajzolás előtt mindig töröld a sorozatokat és a címeket.
2. A tábla nem ismeri az oszlopnevet
Column 'osszeg' does not belong to table .Az SQL-ben nem adtál aliast (SUM(fizetes) AS osszeg nélkül), így az oszlop neve SUM(fizetes) lett, a C# kódban pedig sor["osszeg"] szerepel.
Megoldás: az SQL-ben adott alias és a C# kódban használt név legyen pontosan ugyanaz.
3. NULL az összegben
Object cannot be cast from DBNull to other types.Ha egy csoportban minden fizetés hiányzik (NULL), a SUM eredménye is NULL, a Convert.ToInt32 pedig nem tud mit kezdeni vele (lásd 3.12).
Megoldás: COALESCE(SUM(fizetes), 0) AS osszeg az SQL-ben.
Alkoss
Dolgozz ugyanabban a projektben. Először próbáld ki magad, a megoldást csak utána nyisd le.
1. feladat – Létszám grafikon
Készíts új gombot (btnLetszam), amely részlegenként a dolgozók számát rajzolja oszlopdiagramra. A cím legyen Létszám részlegenként, az Y tengely felirata Fő.Megoldás
private void btnLetszam_Click(object sender, EventArgs e)
{
string sql = "SELECT reszleg, COUNT(*) AS letszam FROM Dolgozok GROUP BY reszleg ORDER BY reszleg";
try
{
DataTable tabla = new DataTable();
using (SQLiteConnection kapcsolat = DB.UjKapcsolat())
using (SQLiteDataAdapter adapter = new SQLiteDataAdapter(sql, kapcsolat))
{
adapter.Fill(tabla);
}
chart1.Series.Clear();
chart1.Titles.Clear();
chart1.Titles.Add("Létszám részlegenként");
chart1.ChartAreas[0].AxisX.Title = "Részleg";
chart1.ChartAreas[0].AxisY.Title = "Fő";
Series sorozat = chart1.Series.Add("Létszám");
sorozat.ChartType = SeriesChartType.Column;
sorozat.IsValueShownAsLabel = true;
foreach (DataRow sor in tabla.Rows)
{
sorozat.Points.AddXY(sor["reszleg"].ToString(), Convert.ToInt32(sor["letszam"]));
}
}
catch (Exception ex)
{
MessageBox.Show("Hiba: " + ex.Message);
}
}
2. feladat – A legnagyobb oszlop kiemelése
Módosítsd az összeg-grafikont úgy, hogy ne a ComboBox-ban kiválasztott, hanem mindig a legnagyobb összegű részleg oszlopa legyen piros-narancs. Tipp: rendezd az SQL eredményét csökkenő sorrendbe, és figyeld a ciklus első körét. Az SQL-ben Megoldás
ORDER BY osszeg DESC a legnagyobbat teszi az első helyre. A ciklusban az első sor indexe 0, azt színezzük ki.// SQL: ... GROUP BY reszleg ORDER BY osszeg DESC
foreach (DataRow sor in tabla.Rows)
{
string reszleg = sor["reszleg"].ToString();
int osszeg = Convert.ToInt32(sor["osszeg"]);
int index = sorozat.Points.AddXY(reszleg, osszeg);
if (index == 0)
{
sorozat.Points[index].Color = Color.OrangeRed;
}
else
{
sorozat.Points[index].Color = Color.SlateBlue;
}
}
3. feladat – Küszöb számlálóval
Tegyél a Formra egy A szűrést a ciklusban végezzük: csak akkor adunk pontot a sorozathoz, ha az összeg eléri a küszöböt. Az érték változásakor újra kell rajzolni (NumericUpDown-t (nudKuszob, Maximum = 5000000, Increment = 100000). Az összeg-grafikonon csak azok a részlegek jelenjenek meg, amelyek összege legalább a küszöb.Megoldás
ValueChanged esemény).// a Rajzol ciklusában:
int kuszob = (int)nudKuszob.Value;
foreach (DataRow sor in tabla.Rows)
{
string reszleg = sor["reszleg"].ToString();
int osszeg = Convert.ToInt32(sor["osszeg"]);
if (osszeg >= kuszob)
{
int index = sorozat.Points.AddXY(reszleg, osszeg);
sorozat.Points[index].Color = Color.SlateBlue;
}
}
private void nudKuszob_ValueChanged(object sender, EventArgs e)
{
Rajzol(cmbReszleg.Text);
}
4. feladat – Átlagfizetés
Rajzolj grafikont a részlegek átlagfizetéséről ( Az AVG). Figyelj arra, hogy az átlag nem biztos, hogy egész szám.Megoldás
AVG tizedestörtet adhat, ezért Convert.ToDouble-t használunk, a szám megjelenését pedig a LabelFormat tulajdonsággal rendezzük, hogy ne legyen hosszú tizedestört a felirat.string sql = "SELECT reszleg, AVG(fizetes) AS atlag FROM Dolgozok GROUP BY reszleg ORDER BY reszleg";
// ... DataTable feltöltése, Series.Clear() ...
Series sorozat = chart1.Series.Add("Átlagfizetés");
sorozat.ChartType = SeriesChartType.Column;
sorozat.IsValueShownAsLabel = true;
sorozat.LabelFormat = "N0";
foreach (DataRow sor in tabla.Rows)
{
double atlag = Convert.ToDouble(sor["atlag"]);
sorozat.Points.AddXY(sor["reszleg"].ToString(), atlag);
}
Ellenőrizd magad
- Tudom, hogyan lesz egy
DataTablesorábólChartpont. - Tudom, hogy az
AddXYvisszaadja az új pont indexét, és azzal színezhetem a pontot. - Be tudom állítani a címet és a két tengely feliratát.
- Újrarajzolás előtt törlöm a sorozatokat és a címeket.
- Tudom, miért kell alias az SQL-ben, és mit kezd a C# kód vele.
Visszatérés a tanmenethez
A grafikon adatbázisból elkészült. A tanmenetben megtalálod a folytatást és az összetett gyakorlófeladatokat.
