Grafikus C# – Windows Forms tanfolyam › 9.3 lecke Windows Forms · 9.3 lecke · saját rajzolás Eddig kész vezérlőket (Paint esemény és Graphics objektum
PictureBox, Button) használtál. Ebben a leckében magad rajzolsz a képernyőre: vonalakat, téglalapokat, köröket, szöveget. A Windows Formsban a rajzolás szabálya más, mint amire elsőre gondolnál: a rajz nem „marad ott” magától, a felület tetszőleges pillanatban újrarajzolódhat (ablak elmozdítása, kicsinyítés), ezért a rajzolást a Paint eseményben végezzük, mindig a tárolt adatokból. Megtanulod a Graphics objektum használatát, az Invalidate és a Refresh különbségét, a villogást megszüntető kettős pufferelést, és a Bitmapra rajzolás módját.
A lecke végére
- érted, miért kell a
Painteseményben rajzolni, és miért nem aCreateGraphics()hívásban; - használod a
PaintEventArgs(Graphics,ClipRectangle) és aGraphicsalapműveleteit; - megkülönbözteted az
Invalidate, aRefreshés azUpdatehívást; - bekapcsolod a kettős pufferelést (
DoubleBuffered), és megszünteted a villogást; - a
Graphics.FromImagesegítségévelBitmapra rajzolsz; - tudod, mikor kell felszabadítani a
Graphicsobjektumot, és mikor nem.
Mielőtt elkezded
Ehhez a leckéhez az 5.1 lecke (események), az 1.8 lecke (Timer) és a 9.1 lecke (Bitmap) szükséges. Az 5.9 leckében a kígyójátékban már használtál egyszerű rajzolást: itt az elvek rendszerét tanuljuk meg.
A Paint esemény: a rajz „elfelejthető”
Képzeld el, hogy egy Panel felületére egyszer vonalat húzol (a CreateGraphics() segítségével). Ha az ablakot egy másik ablak eltakarja, majd újra előkerül, vagy kicsinyíted és visszaállítod, a vonal eltűnik: a Windows nem őrzi a vezérlő felületét. Ehelyett azt kéri a vezérlőtől, hogy rajzolja újra magát: ez a Paint esemény. Ezért a rajz szabálya: az adatot tárold, a Paint esemény rajzolja ki belőle:
// az adat (a modell): hol van a kör és mekkora
private Point kozep = new Point(120, 90);
private int sugar = 50;
// a Paint eseményt a Panel kéri minden újrarajzoláskor: itt rajzolunk
private void pnlRajz_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics; // a rajzolófelület (nem kell felszabadítani!)
g.Clear(Color.White);
using (Pen toll = new Pen(Color.SeaGreen, 3))
{
g.DrawEllipse(toll, kozep.X - sugar, kozep.Y - sugar, 2 * sugar, 2 * sugar);
}
}
// az adat módosítása után kérjük az újrarajzolást
private void btnNagyit_Click(object sender, EventArgs e)
{
sugar += 10;
pnlRajz.Invalidate(); // a Windows hamarosan meghívja a Paint eseményt
}Graphics (az a felület, amelyre rajzolsz) és a ClipRectangle (az a terület, amely ténylegesen újrarajzolandó; egyszerű programban figyelmen kívül hagyható).kozep és a sugar a modell, a Paint metódus a nézet. Ha az adat változik, csak az adatot módosítod és az újrarajzolást kéred: a rajzolás kódja egy helyen marad.Paint eseménye. Rajzfelületnek a Panel vagy a PictureBox (a Paint a kép fölé rajzol) a szokásos, a Form saját felületére is rajzolhatsz.Paint gyakran fut (átméretezés, takarás, animáció). Ne tegyél bele lassú műveletet (fájlbeolvasás, hálózat, hosszú számítás), és ne módosíts benne vezérlőket.Jósolj!
A programban a gombnyomás a pnlRajz.CreateGraphics().DrawLine(...) hívással húz egy vonalat a panelre. A felhasználó megnyomja a gombot (a vonal megjelenik), majd kicsinyíti és visszaállítja az ablakot. Mi látszik?
Jóslat ellenőrzése
A vonal eltűnik: a CreateGraphics csak a pillanatnyi képre rajzol, az újrarajzolás (Paint) ezt letörli. Ezért a vonalat az adatok között kell tárolni, és a Paint eseményben kell kirajzolni.
A Graphics objektum alapjai
A Graphics a „rajzvászon és rajzeszközök” egyben. A koordináta-rendszer origója a bal felső sarok, az X jobbra, az Y lefelé nő, a mértékegység a képpont (pixel). Az alakzatokat a megfelelő metódus rajzolja (Draw… a körvonalat tollal, Fill… a kitöltést ecsettel; a tollakat és ecseteket a 9.4 lecke részletezi):
| Metódus | Mit rajzol? |
|---|---|
Clear(szín) | Az egész felületet a megadott színre festi (háttér). |
DrawLine(toll, x1, y1, x2, y2) | Vonal két pont között. |
DrawRectangle, FillRectangle | Téglalap körvonala, illetve kitöltve (x, y, szélesség, magasság). |
DrawEllipse, FillEllipse | Ellipszis (kör): a befoglaló téglalap adja meg. |
DrawArc, DrawPie | Körív, kimetszés (kezdőszög és ív fokban). |
DrawPolygon, FillPolygon | Sokszög pontok tömbjéből (Point[]). |
DrawString(szöveg, betű, ecset, x, y) | Szöveg rajzolása. |
DrawImage(kép, x, y) | Kép kirajzolása (9.1–9.2 lecke). |
private void pnlRajz_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; // éldzsimítás: szebb körök, ferde vonalak
g.Clear(Color.White);
using (Pen toll = new Pen(Color.Black, 2))
using (Brush sarga = new SolidBrush(Color.Gold))
using (Font betu = new Font("Segoe UI", 14, FontStyle.Bold))
{
g.FillEllipse(sarga, 20, 20, 120, 120); // a fej: a befoglaló téglalap bal felső sarka és mérete
g.DrawEllipse(toll, 20, 20, 120, 120);
g.FillEllipse(Brushes.Black, 55, 55, 14, 14); // szemek
g.FillEllipse(Brushes.Black, 95, 55, 14, 14);
g.DrawArc(toll, 45, 60, 70, 60, 20, 140); // száj: kezdőszög 20°, ív 140°
g.DrawString("Szia!", betu, Brushes.DarkSlateBlue, 160, 70);
}
}DrawEllipse(toll, x, y, w, h) a kört a befoglaló téglalap bal felső sarkával adja meg (nem a középpontjával). A középpont (cx, cy) és a sugár (r) esetén: cx - r, cy - r, 2 * r, 2 * r.DrawArc szöge fokban van: 0° a jobb oldal (3 óra), pozitív irányban az óramutató járásával megegyezően nő (mert az Y lefelé nő).Pen, Brush és Font objektumok rendszererőforrást használnak: mindegyiket felszabadítjuk (using). A Brushes.Black és a többi előre elkészített ecset közös, ezeket ne szabadítsd fel.Paint elején.Jósolj!
A g.FillEllipse(Brushes.Red, 100, 100, 50, 50) hívás hová rajzol kört: a (100, 100) pont a kör középpontja vagy a bal felső sarka? Mekkora a kör?
Jóslat ellenőrzése
A (100, 100) a befoglaló téglalap bal felső sarka, a kör átmérője 50 (sugara 25), középpontja a (125, 125). Ha középpontot adsz meg, a bal felső sarok: cx - sugar, cy - sugar.
Invalidate, Refresh és a villogás elleni pufferelés
| Hívás | Mit tesz? |
|---|---|
Invalidate() | Érvénytelennek jelöli a vezérlő felületét: a Windows a következő alkalommal (nem azonnal) újrarajzoltatja. A leggyakoribb választás, több hívás összevonódik. |
Invalidate(Rectangle) | Csak a megadott téglalapot jelöli érvénytelennek (kisebb rajz, kevesebb villogás). |
Update() | Az érvénytelen részt azonnal kirajzoltatja. |
Refresh() | Invalidate() és Update() együtt: azonnali, teljes újrarajzolás. Ritkábban kell (lassabb). |
// a Panel DoubleBuffered tulajdonsága védett: saját osztályban állítható
public class RajzPanel : Panel
{
public RajzPanel()
{
this.DoubleBuffered = true; // előbb a háttérpufferbe rajzol, utána egyszerre mutatja: nincs villogás
this.ResizeRedraw = true; // átméretezéskor teljes újrarajzolás
}
public bool Pufferelt
{
get { return this.DoubleBuffered; }
set { this.DoubleBuffered = value; }
}
}
// a Formon a Panel helyett a RajzPanel használható:
// Build után megjelenik a Toolbox tetején (vagy kódból: Controls.Add(new RajzPanel()))DoubleBuffered = true mellett a rajz előbb egy rejtett pufferbe készül el, és csak a kész kép kerül a képernyőre. A villogás megszűnik. Animációnál és gyakori újrarajzolásnál mindig kapcsold be.Form.DoubleBuffered közvetlenül beállítható (this.DoubleBuffered = true). A Panel, PictureBox tulajdonsága védett, ezért kell a saját leszármazott osztály.ResizeRedraw = true az átméretezéskor az egész felületet újrarajzolja. Nélküle a rajz egy része (pl. a jobb szélhez igazított) hibásan marad.Jósolj!
Egy időzítő 20 ms-onként mozgat egy labdát, és minden lépésben a pnlRajz.Refresh() metódust hívja, kettős pufferelés nélkül. Mit látsz?
Jóslat ellenőrzése
A labda mozgása közben a kép villog, és a program lassabb is, mert a Refresh minden lépésben azonnal, teljesen újrarajzol. A DoubleBuffered = true és az Invalidate() használata sima mozgást ad.
Rajzolás Bitmapra: két megközelítés
A. Rajz a Paint eseményben (vektoros)
Az alakzatokat adatként (List<Alakzat>) tárolod, és a Paint minden alkalommal újrarajzolja őket. Előny: bármikor módosíthatók, törölhetők, mozgathatók (a 9.7 lecke). Hátrány: sok alakzatnál lassabb.
B. Rajz Bitmapra (raszteres)
Egy Bitmapra rajzolsz (Graphics.FromImage), és a Paint csak a kész képet mutatja. Előny: a rajz „megmarad”, gyors, a kép menthető (festőprogram). Hátrány: a már lerajzolt dolog nem módosítható (csak ráfestéssel).
private Bitmap vaszon; // a rajz tárolója (festőprogram módszer)
private void Form1_Load(object sender, EventArgs e)
{
vaszon = new Bitmap(pnlRajz.Width, pnlRajz.Height);
using (Graphics g = Graphics.FromImage(vaszon)) // ez a Graphics a miénk: using kell
{
g.Clear(Color.White);
}
}
private void Rajzol(Point a, Point b)
{
using (Graphics g = Graphics.FromImage(vaszon))
using (Pen toll = new Pen(Color.Black, 3))
{
g.DrawLine(toll, a, b);
}
pnlRajz.Invalidate();
}
private void pnlRajz_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(vaszon, 0, 0); // a Paint csak kirajzolja a kész képet
}Bitmapra rajzoláshoz külön Graphics kell. Ezt te hozod létre, ezért te is szabadítod fel (using), ellentétben az e.Graphicsszel.Graphicset kell felszabadítani?Amit te hozol létre (FromImage, CreateGraphics, FromHwnd): igen. Az e.Graphics a Paint eseményé: nem, a keretrendszer szabadítja fel.vezerlo.CreateGraphics() a vezérlő felületére közvetlenül rajzol, de a rajz az első újrarajzoláskor elveszik: ne használd tartós rajzra. Csak ideiglenes, villanásnyi visszajelzéshez való, és using kell hozzá.Jósolj!
A Paint esemény kezelője a végén g.Dispose(); hívást tartalmaz, ahol g = e.Graphics. Mi történik?
Jóslat ellenőrzése
A keretrendszer még használná az e.Graphics objektumot (például a további vezérlők rajzolásához), ezért a felszabadított Graphics hibát (kivételt, vagy hiányzó rajzot) okozhat a következő használatnál. Az e.Graphics nem a te tulajdonod: ne szabadítsd fel.
Mit tegyél ide: a Paint-labor ablak: rács, pattogó labda, mosolygó arc, a „Paint hívások” számláló és a beállító jelölőnégyzetek.Javasolt fájlnév: winforms-9-3-paint-labor.webp · Alt szöveg: A Paint-labor program
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 rajz eltűnik az ablak mozgatásakor
private void btnRajz_Click(object sender, EventArgs e)
{
using (Graphics g = pnlRajz.CreateGraphics())
{
g.FillEllipse(Brushes.Red, 10, 10, 50, 50); // az újrarajzolás letörli
}
}Hiba és javítás
A rajz nincs tárolva. Tárold az adatot (a kör helyét), és a Paint eseményben rajzold ki; a gomb csak az adatot módosítsa és hívja az Invalidate() metódust.
2. Villog a rajz animációnál
A mozgó labda villog. Nincs kettős pufferelés. Használj Ok és javítás
DoubleBuffered = true beállítású panelt (saját osztály), és Invalidate() hívást a Refresh() helyett.
3. Végtelen újrarajzolás
private void pnlRajz_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.White);
pnlRajz.Invalidate(); // a Paint újabb Paint-et kér: a processzor 100%-on fut
}Hiba és javítás
A Paint kezelőben ne hívj Invalidate metódust (hurok). Az újrarajzolást az adat változásakor (esemény, időzítő) kérd.
4. ArgumentException vagy memória-szivárgás a Pen miatt
g.DrawLine(new Pen(Color.Black, 2), 0, 0, 100, 100); // minden Paint új Pen-t hoz létre, felszabadítás nélkülHiba és javítás
A Pen rendszererőforrás. Használj using blokkot (vagy egyszer létrehozott, a Form zárásakor felszabadított tollat). A gyakori Paint miatt a szivárgás gyorsan nő (9.8 lecke).
5. A rajz csak az ablak egy részén frissül
Átméretezéskor a rajz jobb széle hibás marad. A Ok és javítás
ResizeRedraw nincs bekapcsolva. Állítsd true értékre (a saját panelosztályban), vagy kezeld a Resize eseményt és hívj Invalidate() metódust.
6. A kör nem ott van, ahol vártam
A (100, 100) középpontú kör máshol jelenik meg. A Ok és javítás
DrawEllipse a befoglaló téglalap bal felső sarkát várja. Számold át: (cx - r, cy - r, 2r, 2r).
Mini projekt: Paint-labor
Készíts programot, amely a lecke összes fogalmát kísérletezhetővé teszi:
- RajzPanel osztály: a
PanelleszármazottjaDoubleBufferedésResizeRedrawbeállítással, aPufferelttulajdonsággal (ki- és bekapcsolható). A Formonrpnéven,Dock = Fillelhelyezéssel. - Modell: a labda (
labdaX,labdaY,vx,vy,R = 20), amely a panel széléről visszapattan; aztmrAnim20 ms-onként lépteti ésInvalidate()hívással kéri az újrarajzolást. - Paint: fehér háttér; 40 pixelenként halvány rács (
Penusingblokkban); a labda (FillEllipse,DrawEllipse); egy mosolygó arc a bal alsó sarokban (ellipszisek ésDrawArc); aDrawStringkiírja a Paint hívások számát és aze.ClipRectangleméretét. - Beállítók:
chkSima(Éldzsimítás,SmoothingMode),chkPuffer(Kettős pufferelés),chkRefresh(Refresh Invalidate helyett),tbSebesseg(TrackBar, a labda sebessége), Start/Szünet gomb. - Gombok: Új vonal a CreateGraphics-szal (a vonal eltűnik az első újrarajzoláskor: a hibás módszer bemutatása) és Új vonal a modellbe (
List<Point[]>: két pontból álló tömbök, amelyeket aPaintrajzol ki). - Bitmap-vászon: egy kis
pbVaszonpanel, amelyre az egér húzásával rajzolhatsz egyBitmapra (a 9.6 lecke előkészítése): most elég a gombra kattintás: Véletlen vonal a vászonra. - Teszteld: a két gomb viselkedése ablak kicsinyítése után, villogás pufferrel és anélkül (
Refreshmódban is), az éldzsimítás hatása, a Paint-számláló növekedése átméretezéskor, a program memóriahasználata hosszú futás után.
Megoldás megnyitása (próbáld ki előbb önállóan!)
A labda mozgását az időzítő végzi (a Paint csak rajzol). A mosolygó arc és a rács a Paint-ben készül, a tollak using blokkban.
public class RajzPanel : Panel
{
public RajzPanel()
{
this.DoubleBuffered = true;
this.ResizeRedraw = true;
}
public bool Pufferelt
{
get { return this.DoubleBuffered; }
set { this.DoubleBuffered = value; }
}
}
// Form1.cs
private float labdaX = 60, labdaY = 60, vx = 4, vy = 3;
private const int R = 20;
private int paintDb = 0;
private List<Point[]> vonalak = new List<Point[]>();
private Random veletlen = new Random();
private void Form1_Load(object sender, EventArgs e)
{
tmrAnim.Interval = 20;
tmrAnim.Start();
chkPuffer.Checked = true;
chkSima.Checked = true;
}
private void tmrAnim_Tick(object sender, EventArgs e)
{
float seb = tbSebesseg.Value / 4f;
labdaX += vx * seb;
labdaY += vy * seb;
if (labdaX - R < 0) { labdaX = R; vx = Math.Abs(vx); }
if (labdaX + R > rp.Width) { labdaX = rp.Width - R; vx = -Math.Abs(vx); }
if (labdaY - R < 0) { labdaY = R; vy = Math.Abs(vy); }
if (labdaY + R > rp.Height) { labdaY = rp.Height - R; vy = -Math.Abs(vy); }
if (chkRefresh.Checked) { rp.Refresh(); } else { rp.Invalidate(); }
}
private void rp_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
paintDb++;
g.SmoothingMode = chkSima.Checked ? SmoothingMode.AntiAlias : SmoothingMode.None;
g.Clear(Color.White);
using (Pen halvany = new Pen(Color.Gainsboro))
{
for (int x = 0; x < rp.Width; x += 40) { g.DrawLine(halvany, x, 0, x, rp.Height); }
for (int y = 0; y < rp.Height; y += 40) { g.DrawLine(halvany, 0, y, rp.Width, y); }
}
using (Pen vonal = new Pen(Color.SteelBlue, 2))
{
foreach (Point[] v in vonalak) { g.DrawLine(vonal, v[0], v[1]); } // a modellből
}
using (Brush labda = new SolidBrush(Color.FromArgb(104, 64, 165)))
using (Pen perem = new Pen(Color.Black, 2))
{
g.FillEllipse(labda, labdaX - R, labdaY - R, 2 * R, 2 * R);
g.DrawEllipse(perem, labdaX - R, labdaY - R, 2 * R, 2 * R);
}
// mosolygó arc a bal alsó sarokban
int ax = 20, ay = rp.Height - 100;
using (Brush sarga = new SolidBrush(Color.Gold))
using (Pen toll = new Pen(Color.Black, 2))
{
g.FillEllipse(sarga, ax, ay, 80, 80);
g.DrawEllipse(toll, ax, ay, 80, 80);
g.FillEllipse(Brushes.Black, ax + 22, ay + 24, 10, 10);
g.FillEllipse(Brushes.Black, ax + 48, ay + 24, 10, 10);
g.DrawArc(toll, ax + 18, ay + 28, 44, 36, 20, 140);
}
using (Font betu = new Font("Segoe UI", 11))
{
g.DrawString("Paint hívások: " + paintDb + " ClipRectangle: " + e.ClipRectangle.Width + " × " + e.ClipRectangle.Height, betu, Brushes.Black, 8, 8);
}
}
private void chkPuffer_CheckedChanged(object sender, EventArgs e)
{
rp.Pufferelt = chkPuffer.Checked;
}
private void btnCreateGraphics_Click(object sender, EventArgs e)
{
using (Graphics g = rp.CreateGraphics())
using (Pen toll = new Pen(Color.Red, 3))
{
g.DrawLine(toll, 0, 0, rp.Width, rp.Height); // az animáció következő képkockája letörli
}
}
private void btnModellVonal_Click(object sender, EventArgs e)
{
vonalak.Add(new Point[] { new Point(veletlen.Next(rp.Width), veletlen.Next(rp.Height)), new Point(veletlen.Next(rp.Width), veletlen.Next(rp.Height)) });
rp.Invalidate();
}
private void btnStart_Click(object sender, EventArgs e)
{
tmrAnim.Enabled = !tmrAnim.Enabled;
}Mit tegyél ide: a kész Paint-labor, a labdával, a rácsal és a számlálóval.Javasolt fájlnév: winforms-9-3-kesz.webp · Alt szöveg: A kész Paint-labor
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. Analóg óra
Rajzolj analóg órát: számlap, 12 jelölés, három mutató. Hol tárolod az adatot, és mikor rajzol újra? Az adat az idő (Egy lehetséges megoldás
DateTime.Now), amelyet másodpercenként egy Timer frissít (Invalidate()). A mutató végpontját szögfüggvényekkel számolod (9.5 lecke): a szög másodperc * 6° (a 12-től indulva).private PointF Vegpont(PointF kozep, double fok, float hossz)
{
double radian = (fok - 90) * Math.PI / 180; // 0°: a 12-es állás (felfelé)
return new PointF(kozep.X + (float)(hossz * Math.Cos(radian)), kozep.Y + (float)(hossz * Math.Sin(radian)));
}
// a Paintben: másodpercmutató
DateTime most = DateTime.Now;
PointF v = Vegpont(kozep, most.Second * 6, 90);
g.DrawLine(toll, kozep, v);
2. Rajz a Form felületére
A Form felületére (nem panelre) rajzolj háttérként átmenetes sávot. Melyik eseményt használod, és mit állítasz be? A Form Egy lehetséges megoldás
Paint eseményét, DoubleBuffered = true és ResizeRedraw = true beállítással. Az átmenetet a LinearGradientBrush adja (9.4 lecke).
3. Az újrarajzolás számlálója
Mérd meg, hány A Paint fut másodpercenként (FPS). Hogyan?Egy lehetséges megoldás
Paint minden hívása növel egy számlálót; egy másodperces Timer kiírja és nullázza.private int kepkocka = 0;private void rp_Paint(object sender, PaintEventArgs e) { kepkocka++; /* ... rajz ... */ }private void tmrFps_Tick(object sender, EventArgs e){
lblFps.Text = kepkocka + " FPS";
kepkocka = 0;
}
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. Hová rajzol?
A Bal felső sarok: (10, 20), szélesség 100, magasság 50; a jobb alsó sarok (110, 70). Az Y lefelé nő.g.DrawRectangle(Pens.Black, 10, 20, 100, 50): hol a bal felső sarok, mekkora a téglalap?Megoldás megnyitása
2. Invalidate vagy Refresh?
Melyiket használod: (a) egérmozgásra frissülő rajz; (b) hosszú számítás közben egyszeri azonnali kép? (a) Megoldás megnyitása
Invalidate(); (b) Refresh() (vagy Update()), mert a felület szála a számítás alatt nem rajzolna újra. (Hosszú műveletet a 11. fejezet szerint háttérszálon végezz.)
Közepes szint
3. Középre igazított szöveg
A panel közepére írj szöveget. Hogyan számolod a pozíciót? A Megoldás megnyitása
g.MeasureString a szöveg méretét adja; a pozíció: (panelSzeles - m.Width) / 2, (panelMagas - m.Height) / 2. Vagy a StringFormat Alignment = Center, LineAlignment = Center beállítása és a téglalapba rajzolás.using (StringFormat sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center })
using (Font betu = new Font("Segoe UI", 16))
{
g.DrawString("Középen", betu, Brushes.Black, new RectangleF(0, 0, rp.Width, rp.Height), sf);
}
4. Rajz a Paint eseményben vagy Bitmapra?
Döntsd el: (a) festőprogram szabadkézi vonallal; (b) tervezőprogram mozgatható alakzatokkal; (c) óra. (a) Bitmap (a vonalak megmaradnak); (b) a Megoldás megnyitása
Paint-ben, adatokból (módosíthatók); (c) a Paint-ben, az idő adatából.
Kihívás szint
5. Csak a változott rész újrarajzolása
A labda mozgásakor csak a régi és az új helyét érvénytelenítsd. Hogyan, és mire kell figyelned? Két Megoldás megnyitása
Invalidate(Rectangle) hívás (a régi és az új befoglaló téglalap, kis ráhagyással). A Paint-nek ilyenkor a ClipRectangle miatt a teljes jelenetet rajzolnia kell (a rendszer csak a kijelölt részt festi). Összetett jelenetnél hibás, a rács ilyenkor torzulhat: egyszerűbb a teljes Invalidate().
6. Saját vezérlő rajzzal
Készíts A tulajdonság Jelzolampa vezérlőt (Control leszármazott), amely egy színes kört rajzol az OnPaint metódusban, és a színe tulajdonsággal állítható. Mit kell tenned a szín módosításakor?Megoldás megnyitása
set ágában Invalidate(); a rajzolás az OnPaint(PaintEventArgs e) felülírásában, és a konstruktorban DoubleBuffered = true.public class Jelzolampa : Control
{
private Color szin = Color.Green;
public Jelzolampa()
{
this.DoubleBuffered = true;
this.Size = new Size(40, 40);
}
public Color LampaSzin
{
get { return szin; }
set { szin = value; this.Invalidate(); }
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
using (Brush b = new SolidBrush(szin))
{
e.Graphics.FillEllipse(b, 2, 2, this.Width - 5, this.Height - 5);
}
}
}
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. A vezérlő felülete elveszhet (takarás, kicsinyítés); a Windows a Az origó a bal felső sarok, az Y lefelé nő. Az Előbb a háttérpufferbe rajzol, és a kész képet mutatja: nincs villogás. Amit te hozol létre ( A befoglaló téglalap bal felső sarkával és méretével: Végtelen újrarajzolási hurkot okoz (a processzor tele lesz).1. Miért kell a Paint eseményben rajzolni?
Paint eseménnyel kéri az újrarajzolást, ezért a rajzot a tárolt adatokból ott kell elkészíteni.2. Hol az origó, és merre nő az Y?
3. Mi a különbség az Invalidate és a Refresh között?
Invalidate kéri az újrarajzolást (később, összevonva), a Refresh azonnal, teljesen újrarajzol.4. Mi a kettős pufferelés haszna?
5. Melyik Graphics-et kell felszabadítani?
FromImage, CreateGraphics); az e.Graphicset nem.6. Hogyan adod meg a kört a DrawEllipse-ben?
(cx - r, cy - r, 2r, 2r).7. Miért ne hívj Invalidate-et a Paint-ben?
Mit tudsz már?
- A
Painteseményben, tárolt adatokból rajzolsz. - Használod a
Graphicsalapműveleteit, és ismered a koordináta-rendszert. - Az
Invalidateés aDoubleBufferedsegítségével sima animációt készítesz. Bitmapra rajzolsz, és tudod, melyikGraphicset kell felszabadítani.- Felismered a tipikus rajzolási hibákat.
Következő lecke: Pen, Brush, Color, Rectangle
A rajzeszközök: tollak vonalstílussal, ecsetek (egyszínű, átmenetes, mintás), színek átlátszósággal, téglalap-műveletek.



