Grafikus C# – Windows Forms tanfolyam › 9.4 lecke

Windows Forms · 9.4 lecke · rajzeszközök

Pen, Brush, Color, Rectangle

A Graphics megmondja, mit rajzoljunk, a rajzeszközök pedig azt, hogyan: a toll (Pen) a vonalat és a körvonalat, az ecset (Brush) a kitöltést adja, a szín (Color) mindkettőnek az anyaga, a téglalap (Rectangle) pedig a helyet és a méretet fogalmazza meg. Ebben a leckében megtanulod az átlátszó színeket, a szaggatott és nyilas vonalakat, a sraffozott és átmenetes kitöltést, a téglalap-műveleteket (metszet, tartalmazás), és a DrawRectangle apró, de bosszantó csapdáját.

  • kb. 100 perc
  • közepes szint
  • toll, ecset, szín
Diáklány C# programot készít laptopon

A lecke végére

  • színeket adsz meg (név, FromArgb, átlátszóság) és keversz (átmenet két szín között);
  • a Pen tulajdonságait használod (vastagság, DashStyle, végződés, LineJoin);
  • egyszínű, sraffozott és átmenetes ecsetet készítesz (SolidBrush, HatchBrush, LinearGradientBrush);
  • a Rectangle tulajdonságait és műveleteit használod (Contains, IntersectsWith, Intersect, Union, Inflate);
  • tudod, melyik eszközt kell felszabadítani, és melyik a közös (Pens, Brushes);
  • elkerülöd a gyakori hibákat (DrawRectangle mérete, üres téglalapú átmenet, átlátszó szöveg).

Mielőtt elkezded

Ehhez a leckéhez a 9.3 lecke (Paint, Graphics, using) és a 7.9 lecke (Color, ColorDialog) szükséges.

9.3 Paint és Graphics →

Color: színek és átlátszóság

Form1.csC#
Color a = Color.Crimson;                          // névvel (140 elnevezett szín)
Color b = Color.FromArgb(104, 64, 165); // piros, zöld, kék (0–255), teljesen átlátszatlan
Color c = Color.FromArgb(128, 104, 64, 165); // az első szám az ALFA: 0 átlátszó ... 255 átlátszatlan
Color d = Color.FromArgb(100, Color.Red); // meglévő szín áttetszővé tétele

int piros = b.R, zold = b.G, kek = b.B, alfa = c.A; // a csatornák
float arnyalat = b.GetHue(); // 0–360 fok: a színkör szöge (HSB modell)
float telitettseg = b.GetSaturation(); // 0–1
float vilagossag = b.GetBrightness(); // 0–1 (HSL világosság)

// két szín keverése: t = 0 → a, t = 1 → b
private Color Kever(Color a, Color b, double t)
{
t = Math.Max(0, Math.Min(1, t));

return Color.FromArgb(
(int)(a.A + (b.A - a.A) * t),
(int)(a.R + (b.R - a.R) * t),
(int)(a.G + (b.G - a.G) * t),
(int)(a.B + (b.B - a.B) * t));
}
ARGBA szín négy csatornából áll: Alfa (átlátszóság), R (piros), G (zöld), B (kék); mind 0–255. A FromArgb paraméterei alfa, piros, zöld, kék sorrendben vannak (az alfa az első, ha négyet adsz meg).
ÁtlátszóságAz alfa 255 teljesen átlátszatlan, 0 teljesen átlátszó, a köztes értékek áttetszők: a mögötte lévő rajz átlátszik. Pénzszámos fedéshez, árnyékhoz, kijelölés-kerethez használható.
HSBA GetHue() a színkör szöge (piros 0°, zöld 120°, kék 240°): színek halványítására, színkör szerinti sorozatok készítésére hasznos.
Kevert színA Kever csatornánként lineárisan számol: színátmenetekhez, hőtérképhez, animált színváltáshoz.

Jósolj!

A Color.FromArgb(128, 255, 0, 0) színnel kitöltött téglalap fehér háttéren van. Milyen színűnek látod?

Jóslat ellenőrzése

Világos rózsaszín (kb. 255, 127, 127): a 50%-ban átlátszó piros a fehérrel keveredik. Fekete háttéren sötétpiros lenne. A látható szín az alfától és a háttértől függ.

Pen: a toll

Form1.cs (using System.Drawing.Drawing2D;)C#
private void pnlRajz_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;

// egyszerű toll: szín és vastagság (pixel)
using (Pen alap = new Pen(Color.Black, 3))
{
g.DrawLine(alap, 20, 20, 200, 20);
}

// szaggatott vonal
using (Pen szaggatott = new Pen(Color.SteelBlue, 2))
{
szaggatott.DashStyle = DashStyle.Dash; // Solid, Dash, Dot, DashDot, DashDotDot
g.DrawLine(szaggatott, 20, 50, 200, 50);

szaggatott.DashPattern = new float[] { 4, 2, 1, 2 }; // saját minta: vonal, hézag... (a vastagság többszöröse)
g.DrawLine(szaggatott, 20, 70, 200, 70);
}

// nyíl és lekerekített vonalvég
using (Pen nyil = new Pen(Color.Firebrick, 4))
{
nyil.StartCap = LineCap.Round; // lekerekített kezdet
nyil.EndCap = LineCap.ArrowAnchor; // nyílhegy a végén
g.DrawLine(nyil, 20, 100, 200, 100);
}

// vastag töröttvonal: a csúcsok összekötése (LineJoin)
using (Pen toro = new Pen(Color.SeaGreen, 10))
{
toro.LineJoin = LineJoin.Round; // Miter (hegyes), Bevel (levágott), Round (lekerekített)
g.DrawLines(toro, new Point[] { new Point(20, 160), new Point(80, 120), new Point(140, 160) });
}

// készen kapható tollak (1 pixel vastag): ezeket NEM szabadítjuk fel
g.DrawRectangle(Pens.DarkSlateGray, 220, 20, 100, 60);
}
VastagságA Pen második paramétere (float) a vastagság képpontban. A 0 vagy 1 vékony (egy képpontos) vonalat ad; törtszám is lehet.
DashStyle és DashPatternA DashStyle kész szaggatott mintákat ad; a DashPattern tömb a vonal és a hézag váltakozó hosszát adja (a toll vastagságának többszörösében).
VégződésekA StartCap, EndCap (LineCap: Flat, Round, Square, ArrowAnchor, DiamondAnchor…) a vonal végét formálja; a LineJoin a töröttvonal csúcsait.
Pens osztályA Pens.Black, Pens.Red stb. előre elkészített, 1 képpont vastag tollak: kényelmesek, de nem módosíthatók és nem felszabadítandók. A saját tollat (new Pen) mindig using blokkban használd.

Jósolj!

A Pens.Red.Width = 5; sort hajtod végre a Paint-ben. Mi történik?

Jóslat ellenőrzése

ArgumentException (a toll nem módosítható): a Pens osztály tollai közösek és védettek. Vastagabb tollhoz saját new Pen(Color.Red, 5) kell.

Brush: az ecset

EcsetMit ad?
SolidBrush(szín)Egyszínű kitöltés (áttetsző színnel áttetsző).
HatchBrush(stílus, előtér, háttér)Sraffozás: ismétlődő minta (vonalak, kockák, pöttyök).
LinearGradientBrush(téglalap, szín1, szín2, szög)Lineáris színátmenet az adott szögben.
TextureBrush(kép)Kép mint ismétlődő minta.
PathGradientBrushSugaras (alakzat közepétől kifelé) átmenet (haladó).
Form1.cs (using System.Drawing.Drawing2D;)C#
Rectangle hely = new Rectangle(20, 20, 200, 100);

// egyszínű, félig átlátszó
using (Brush egyszinu = new SolidBrush(Color.FromArgb(120, Color.DodgerBlue)))
{
g.FillRectangle(egyszinu, hely);
}

// sraffozott
using (Brush sraf = new HatchBrush(HatchStyle.DiagonalCross, Color.DarkGreen, Color.Honeydew))
{
g.FillRectangle(sraf, new Rectangle(240, 20, 200, 100));
}

// átmenetes: a téglalap a rajzolandó alakzat helye, a szög 45 fok
Rectangle atmenetHely = new Rectangle(20, 140, 200, 100);

using (LinearGradientBrush atmenet = new LinearGradientBrush(atmenetHely, Color.Gold, Color.OrangeRed, 45f))
{
g.FillEllipse(atmenet, atmenetHely);
}

// az áttekinthetőség: a hátsó rajz előbb, az elülső később (festő módszer)
g.FillRectangle(Brushes.LightGray, 240, 140, 120, 80);
g.FillRectangle(Brushes.White, 280, 170, 120, 80); // a később rajzolt takarja az előzőt
Átmenet és az alakzat helyeA LinearGradientBrush téglalapja azt a területet adja, amelyen a színátmenet lefut. Ha az alakzat máshol van, mint a téglalap, az átmenet nem az alakzaton fut végig (csak egyszínű sáv látszik): a téglalap egyezzen az alakzattal.
Üres téglalapA nulla szélességű vagy magasságú téglalap ArgumentException kivételt dob az átmenetes ecsetnél: ellenőrizd a méretet (például átméretezéskor).
Rajzolási sorrendA később rajzolt alakzat takarja a korábbit (festő algoritmus). A rajzolás sorrendje adja a „réteg” sorrendet; ezt a modellben listasorrenddel tárolod (9.7 lecke).
ErőforrásokMinden new …Brush felszabadítandó (using); a Brushes.Name közös, nem szabadítjuk. Gyakori Paint esetén a tollat, ecsetet előre elkészítve a Form zárásáig megtarthatod, és ott szabadíthatod (9.8 lecke).

Jósolj!

A LinearGradientBrush téglalapja (0, 0, 100, 100), de a kitöltött téglalap a (300, 300, 100, 100) helyen van. Milyen a kitöltés?

Jóslat ellenőrzése

A kitöltés egyszínű (az átmenet végén álló szín): az alakzat az átmenet területén kívül esik, és a színátmenet a terület szélén túl ismétlődik, vagy fixen az utolsó színt kapja. Az átmenetes ecset téglalapja mindig az alakzat helyén legyen.

Rectangle és a téglalap-műveletek

A Rectangle (egész koordináták) és a RectangleF (törtszám) a hely és a méret leírója (X, Y, Width, Height), ráadásul számos hasznos tulajdonsága és művelete van. Ezekkel az ütközésvizsgálat, a kijelölés és az elrendezés is egyszerű:

Form1.csC#
Rectangle a = new Rectangle(20, 20, 100, 60);
Rectangle b = new Rectangle(80, 50, 100, 60);

int bal = a.Left, felso = a.Top, jobb = a.Right, also = a.Bottom; // Right = X + Width, Bottom = Y + Height
Point hely = a.Location;
Size meret = a.Size;

bool bent = a.Contains(new Point(50, 40)); // a pont a téglalapon belül van? (kattintás vizsgálat)
bool utkozik = a.IntersectsWith(b); // átfedik egymást? (ütközés)

Rectangle kozos = Rectangle.Intersect(a, b); // a közös rész (üres, ha nincs: kozos.IsEmpty)
Rectangle egyesitett = Rectangle.Union(a, b); // mindkettőt befoglaló legkisebb téglalap

Rectangle nagyobb = a;
nagyobb.Inflate(10, 10); // minden oldalon 10 pixellel nagyobb (X, Y is mozog)
nagyobb.Offset(15, 0); // eltolás

// téglalap két pontból (az egérrel húzott terület, a pontok bármilyen sorrendben)
private Rectangle PontokbolRect(Point p, Point q)
{
return new Rectangle(Math.Min(p.X, q.X), Math.Min(p.Y, q.Y), Math.Abs(p.X - q.X), Math.Abs(p.Y - q.Y));
}

// a DrawRectangle csapdája: a körvonal 1 képponttal nagyobb a megadott méretnél
g.FillRectangle(Brushes.LightYellow, hely);
g.DrawRectangle(Pens.Black, hely.X, hely.Y, hely.Width - 1, hely.Height - 1); // -1: a kitöltéssel egy vonalban
Right és BottomA Right (X + Width) és a Bottom (Y + Height) a téglalap első képponton kívüli széle: nincs benne a téglalapban. A Contains ezt figyelembe veszi.
ÜtközésA játékok és a rajzolóprogramok ütközésvizsgálata a IntersectsWith hívás: két alakzat befoglaló téglalapjának átfedése. Körnél a pontos vizsgálat a középpontok távolsága.
InflateAz Inflate(dx, dy) minden oldalon bővít (a téglalap mérete 2·dx, 2·dy értékkel nő): kijelölő keret rajzolásához hasznos. Negatív érték zsugorít.
DrawRectangle −1A DrawRectangle a megadott méretnél 1 képponttal tovább rajzolja a körvonalat jobbra és lefelé. Ha kitöltött téglalapra rajzolsz kerettel, a szélesség és magasság -1 értéke illeszti a keretet.

Jósolj!

Az a = (20, 20, 100, 60) és a b = (80, 50, 100, 60) téglalapok metszete mekkora, és hol van?

Jóslat ellenőrzése

Az a jobb széle 120, alsó széle 80; a b bal széle 80, felső széle 50. A metszet X: 80–120, Y: 50–80, vagyis (80, 50, 40, 30). Az Union (20, 20, 160, 90).

📷 Képernyőkép helye

Mit tegyél ide: a stílus-labor: vonalstílusok, ecsetek és két téglalap metszetének kiemelése.Javasolt fájlnév: winforms-9-4-stilus-labor.webp · Alt szöveg: Toll- és ecsetstílusok a stílus-laborban

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. ArgumentException a Pens használatakor

Pens.Red.DashStyle = DashStyle.Dash;   // a közös toll nem módosítható
Hiba és javítás

A Pens tollai közösek és írásvédettek. Hozz létre saját tollat: using (Pen p = new Pen(Color.Red)) { p.DashStyle = ...; }.

2. Az átmenet nem látszik

using (LinearGradientBrush b = new LinearGradientBrush(new Rectangle(0, 0, 50, 50), Color.Red, Color.Blue, 0f))
{
g.FillRectangle(b, 200, 200, 100, 100); // az átmenet területén kívül
}
Hiba és javítás

Az ecset téglalapja nem egyezik az alakzattal. Használd ugyanazt a Rectangle változót az ecsetnél és a kitöltésnél.

3. ArgumentException az átmenetes ecsetnél

Az ablak kicsinyítése után kivétel keletkezik.

Ok és javítás

A téglalap szélessége vagy magassága nulla lett. Ellenőrizd (if (hely.Width < 1 || hely.Height < 1) return;) az ecset létrehozása előtt.

4. A keret kilóg a kitöltésből

A téglalap körvonala 1 képponttal nagyobb a kitöltésnél.

Ok és javítás

A DrawRectangle a megadott méret után is rajzol egy képpontot. Add meg a Width - 1, Height - 1 értéket.

5. A rajzolás lelassul sok alakzatnál

foreach (Alakzat a in alakzatok)
{
g.FillRectangle(new SolidBrush(a.Szin), a.Hely); // minden alakzathoz felszabadítatlan ecset
}
Hiba és javítás

Szivárog a rendszererőforrás (az ecset nincs felszabadítva). Használj using (Brush b = new SolidBrush(a.Szin)) blokkot, vagy cache-eld az ecseteket szín szerint.

6. A fekete szöveg olvashatatlan az átlátszó háttéren

A félig átlátszó színű téglalapon a szöveg nehezen olvasható.

Ok és javítás

Az áttetsző háttér színe a mögötte lévő rajztól függ, ezért a kontraszt változik. Szövegnél legyen átlátszatlan háttér, vagy számold a kontrasztot (6.9 lecke).

Mini projekt: stílus-labor

Készíts programot, amellyel kísérletezhetsz a tollakkal, ecsetekkel és a téglalap-műveletekkel:

  1. RajzPanel (a 9.3 lecke osztálya): rp, Dock = Fill, jobb oldalán a beállító panel.
  2. Toll beállítói: szín (btnTollSzin + ColorDialog), vastagság (nudVastag, 1–30), cmbVonal (Folytonos, Szaggatott, Pontozott, Szaggatott-pontozott), cmbVeg (Sima, Kerek, Nyíl), cmbCsucs (Hegyes, Levágott, Kerek).
  3. Ecset beállítói: cmbEcset (Egyszínű, Sraffozott, Átmenetes), két szín (btnSzin1, btnSzin2), tbAlfa (TrackBar 0–255: az átlátszóság), nudSzog (átmenet szöge).
  4. Előnézet (Paint): a háttér sakktábla minta (hogy az átlátszóság látszódjon); egy kitöltött téglalap és ellipszis az ecsettel; egy töröttvonal a tollal; egy nyíl; mindegyik a kiválasztott beállításokkal. Az using-os tollak, ecsetek.
  5. Kódmutató: a txtKod (csak olvasható, többsoros) megmutatja a beállításokhoz tartozó C# kódot (new Pen(Color.FromArgb(...), 3), DashStyle = ...), és a Másolás gomb a vágólapra teszi.
  6. Téglalap-labor: két téglalap (A: fix; B: nudBX, nudBY, nudBSz, nudBM mezőkkel állítható); a metszet (Intersect) sraffozott kitöltéssel, az Union szaggatott kerettel; a lblTeglalap kiírja: Metszet: (80, 50, 40, 30), átfedés: igen.
  7. Ellenőrzések: nincs nulla méretű átmenetes ecset (az előnézet kis ablaknál sem omlik össze); a közös tollakat (Pens) nem módosítja.
  8. Teszteld: minden vonalstílus és végződés, átlátszóság sakktábla fölött, az átmenet szöge, a keret illeszkedése (-1), kicsinyített ablak, a kódmutató egyezése a rajzzal, a téglalapok különböző helyzetei (nincs metszet, tartalmazás).
Megoldás megnyitása (próbáld ki előbb önállóan!)

A beállítók egy közös Valtozas kezelővel hívják az rp.Invalidate() metódust, a Paint pedig a vezérlők állapotából rajzol.

Form1.cs (using System.Drawing.Drawing2D;)C#
private Color tollSzin = Color.Black, szin1 = Color.DodgerBlue, szin2 = Color.Orange;

private void Form1_Load(object sender, EventArgs e)
{
cmbVonal.Items.AddRange(new object[] { "Folytonos", "Szaggatott", "Pontozott", "Szaggatott-pontozott" });
cmbVeg.Items.AddRange(new object[] { "Sima", "Kerek", "Nyíl" });
cmbCsucs.Items.AddRange(new object[] { "Hegyes", "Levágott", "Kerek" });
cmbEcset.Items.AddRange(new object[] { "Egyszínű", "Sraffozott", "Átmenetes" });

cmbVonal.SelectedIndex = cmbVeg.SelectedIndex = cmbCsucs.SelectedIndex = cmbEcset.SelectedIndex = 0;
tbAlfa.Maximum = 255;
tbAlfa.Value = 200;
}

private Pen UjToll()
{
Pen p = new Pen(tollSzin, (float)nudVastag.Value);

DashStyle[] vonalak = { DashStyle.Solid, DashStyle.Dash, DashStyle.Dot, DashStyle.DashDot };
LineCap[] vegek = { LineCap.Flat, LineCap.Round, LineCap.ArrowAnchor };
LineJoin[] csucsok = { LineJoin.Miter, LineJoin.Bevel, LineJoin.Round };

p.DashStyle = vonalak[cmbVonal.SelectedIndex];
p.StartCap = LineCap.Round;
p.EndCap = vegek[cmbVeg.SelectedIndex];
p.LineJoin = csucsok[cmbCsucs.SelectedIndex];

return p;
}

private Brush UjEcset(Rectangle hely)
{
Color c1 = Color.FromArgb(tbAlfa.Value, szin1);
Color c2 = Color.FromArgb(tbAlfa.Value, szin2);

if (cmbEcset.SelectedIndex == 1) { return new HatchBrush(HatchStyle.Percent50, c1, c2); }
if (cmbEcset.SelectedIndex == 2) { return new LinearGradientBrush(hely, c1, c2, (float)nudSzog.Value); }

return new SolidBrush(c1);
}

private void rp_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;

// sakktábla háttér az átlátszóság megmutatásához
for (int y = 0; y < rp.Height; y += 20)
{
for (int x = 0; x < rp.Width; x += 20)
{
g.FillRectangle(((x / 20 + y / 20) % 2 == 0) ? Brushes.White : Brushes.Gainsboro, x, y, 20, 20);
}
}

Rectangle hely = new Rectangle(30, 30, 220, 120);

if (hely.Width > 0 && hely.Height > 0)
{
using (Brush ecset = UjEcset(hely))
using (Pen toll = UjToll())
{
g.FillRectangle(ecset, hely);
g.DrawRectangle(toll, hely.X, hely.Y, hely.Width - 1, hely.Height - 1);

Rectangle kor = new Rectangle(280, 30, 120, 120);

using (Brush ecset2 = UjEcset(kor))
{
g.FillEllipse(ecset2, kor);
}

g.DrawEllipse(toll, kor);
g.DrawLines(toll, new Point[] { new Point(30, 240), new Point(110, 180), new Point(190, 240), new Point(270, 180) });
g.DrawLine(toll, 300, 220, 420, 220);
}
}

TeglalapLabor(g);
txtKod.Text = "new Pen(Color.FromArgb(" + tollSzin.R + ", " + tollSzin.G + ", " + tollSzin.B + "), " + nudVastag.Value + ") { DashStyle = DashStyle." + ((DashStyle)new DashStyle[] { DashStyle.Solid, DashStyle.Dash, DashStyle.Dot, DashStyle.DashDot }[cmbVonal.SelectedIndex]) + " }";
}

private void TeglalapLabor(Graphics g)
{
Rectangle a = new Rectangle(30, 300, 160, 100);
Rectangle b = new Rectangle((int)nudBX.Value, (int)nudBY.Value, (int)nudBSz.Value, (int)nudBM.Value);
Rectangle metszet = Rectangle.Intersect(a, b);
Rectangle egyesitett = Rectangle.Union(a, b);

g.DrawRectangle(Pens.DarkBlue, a.X, a.Y, a.Width - 1, a.Height - 1);
g.DrawRectangle(Pens.DarkRed, b.X, b.Y, b.Width - 1, b.Height - 1);

if (!metszet.IsEmpty)
{
using (Brush sraf = new HatchBrush(HatchStyle.DiagonalCross, Color.Black, Color.FromArgb(80, Color.Yellow)))
{
g.FillRectangle(sraf, metszet);
}
}

using (Pen szagg = new Pen(Color.Gray) { DashStyle = DashStyle.Dash })
{
g.DrawRectangle(szagg, egyesitett.X, egyesitett.Y, egyesitett.Width - 1, egyesitett.Height - 1);
}

lblTeglalap.Text = "Metszet: (" + metszet.X + ", " + metszet.Y + ", " + metszet.Width + ", " + metszet.Height + "), átfedés: " + (a.IntersectsWith(b) ? "igen" : "nem");
}

private void Valtozas(object sender, EventArgs e) { rp.Invalidate(); }

private void btnTollSzin_Click(object sender, EventArgs e)
{
using (ColorDialog cd = new ColorDialog { Color = tollSzin })
{
if (cd.ShowDialog(this) == DialogResult.OK) { tollSzin = cd.Color; rp.Invalidate(); }
}
}

private void btnSzin1_Click(object sender, EventArgs e)
{
using (ColorDialog cd = new ColorDialog { Color = szin1 })
{
if (cd.ShowDialog(this) == DialogResult.OK) { szin1 = cd.Color; rp.Invalidate(); }
}
}

private void btnSzin2_Click(object sender, EventArgs e)
{
using (ColorDialog cd = new ColorDialog { Color = szin2 })
{
if (cd.ShowDialog(this) == DialogResult.OK) { szin2 = cd.Color; rp.Invalidate(); }
}
}

private void btnMasol_Click(object sender, EventArgs e)
{
Clipboard.SetText(txtKod.Text);
}
📷 Képernyőkép helye

Mit tegyél ide: a kész stílus-labor az átlátszó alakzatokkal a sakktáblán, a vonalstílusokkal és a téglalap-metszettel.Javasolt fájlnév: winforms-9-4-kesz.webp · Alt szöveg: A kész stílus-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. Színátmenet-sáv

Rajzolj 256 egymás melletti 2 pixel széles függőleges vonalat, amelyek színe pirosról kékre vált (a Kever metódussal). Miért jobb ez az egyetlen átmenetes ecsetnél bizonyos esetekben?

Egy lehetséges megoldás

A saját keverés a színek közti átmenet minden lépését szabályozza (például három szín közötti, nem lineáris vagy HSB alapú átmenet); a LinearGradientBrush egyszerű két színre elég.

Form1.csC#
for (int i = 0; i < 256; i++)
{
using (Pen toll = new Pen(Kever(Color.Red, Color.Blue, i / 255.0), 2))
{
g.DrawLine(toll, i * 2, 0, i * 2, 40);
}
}

2. Kijelölő keret

A kijelölt alakzat körül szaggatott, 4 pixellel nagyobb keret látszik. Hogyan számolod a keret téglalapját?

Egy lehetséges megoldás

A téglalap másolatán Inflate(4, 4), és szaggatott toll (DashStyle.Dash).

Form1.csC#
Rectangle keret = alakzat.Hely;keret.Inflate(4, 4);using (Pen toll = new Pen(Color.RoyalBlue) { DashStyle = DashStyle.Dash }){    g.DrawRectangle(toll, keret.X, keret.Y, keret.Width - 1, keret.Height - 1);}

3. Ütköző labdák

Két mozgó kör ütközését vizsgáld. Elég-e a téglalapok IntersectsWith vizsgálata?

Egy lehetséges megoldás

Gyors előszűrésnek igen, de körnél a sarkokban téves találatot ad. Pontos vizsgálat: a középpontok távolsága kisebb a sugarak összegénél.

Form1.csC#
private bool Utkozik(PointF k1, float r1, PointF k2, float r2){    float dx = k1.X - k2.X;    float dy = k1.Y - k2.Y;    return dx * dx + dy * dy <= (r1 + r2) * (r1 + r2);   // a gyökvonás elkerülhető}

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. FromArgb

Írd meg a következő színt: 25%-ban átlátszatlan zöld (0, 128, 0).

Megoldás megnyitása

A 25% átlátszatlanság az alfa: 255 × 0,25 ≈ 64: Color.FromArgb(64, 0, 128, 0).

2. Melyiket kell felszabadítani?

Jelöld: (a) Pens.Red; (b) new Pen(Color.Red); (c) Brushes.Blue; (d) new SolidBrush(Color.Blue).

Megoldás megnyitása

(b) és (d): a saját példányokat. A (a) és (c) közös, készen kapott objektum: nem szabad felszabadítani.

Közepes szint

3. Csíkos háttér

Rajzolj váltakozó világos és sötétebb szürke vízszintes csíkokat a panelre, a csík magasságát egy számmező adja. Melyik a legegyszerűbb?

Megoldás megnyitása

Ciklus a panel magasságán, FillRectangle váltakozó Brushes ecsettel (a sorszám paritása szerint).

4. Pont a téglalapban

Egér kattintásra kiírja, melyik téglalapra kattintottál (több téglalap közül). Mit használsz, és mi a sorrend átfedés esetén?

Megoldás megnyitása

A Rectangle.Contains(point) hívást; átfedésnél a legfelül rajzoltat keresd (a lista végétől visszafelé), mert az látszik a képernyőn (9.7 lecke).

Kihívás szint

5. Hőtérkép színek

Egy érték (0–100) színe: 0 kék, 50 zöld, 100 piros. Hogyan készíted el a három szín közötti átmenetet?

Megoldás megnyitása

Két szakaszban: 0–50 között kékről zöldre, 50–100 között zöldről pirosra keversz (a Kever metódussal).

Form1.csC#
private Color HoSzin(double ertek){    ertek = Math.Max(0, Math.Min(100, ertek));    return ertek < 50 ? Kever(Color.Blue, Color.Green, ertek / 50) : Kever(Color.Green, Color.Red, (ertek - 50) / 50);}

6. Saját tollkészlet

A program sok vonalat rajzol ugyanazokkal a tollakkal. Készíts TollGyar osztályt, amely a tollakat színenként tárolja, és a Form zárásakor felszabadítja. Mi az előnye?

Megoldás megnyitása

Nem jön létre és szűnik meg ugyanaz a toll minden Paint-ben (gyorsabb, kevesebb szemét); a felszabadítás egy helyen történik (9.8 lecke).

TollGyar.csC#
public class TollGyar : IDisposable
{
private readonly Dictionary<string, Pen> tollak = new Dictionary<string, Pen>();

public Pen Toll(Color szin, float vastagsag)
{
string kulcs = szin.ToArgb() + ":" + vastagsag;
Pen p;

if (!tollak.TryGetValue(kulcs, out p))
{
p = new Pen(szin, vastagsag);
tollak[kulcs] = p;
}

return p;
}

public void Dispose()
{
foreach (Pen p in tollak.Values) { p.Dispose(); }

tollak.Clear();
}
}

Gyors önellenőrzés

Válaszolj fejben, és csak utána nyisd le a választ.

1. Mit jelent az alfa csatorna?

Az átlátszóságot: 0 teljesen átlátszó, 255 teljesen átlátszatlan.

2. Mire való a Pen és a Brush?

A Pen a vonalat/körvonalat (Draw…), a Brush a kitöltést (Fill…) adja.

3. Mi a DashPattern?

A vonal és a hézag hosszának váltakozó mintája (a vastagság többszörösében).

4. Hova kell az átmenetes ecset téglalapja?

Az alakzat helyére, különben az átmenet nem az alakzaton fut le.

5. Mi az Intersect és az Union?

Az Intersect a közös részt, az Union a két téglalapot befoglaló legkisebb téglalapot adja.

6. Miért kell a DrawRectangle-nél Width - 1?

A körvonal 1 képponttal nagyobb a megadott méretnél; így illeszkedik a kitöltéshez.

7. Melyik tollat tilos módosítani?

A Pens osztályét (közös): ArgumentException.

Mit tudsz már?

  • Színeket adsz meg, keversz, és átlátszóságot használsz.
  • Tollakat állítasz (vastagság, szaggatás, végződés, csúcsok).
  • Egyszínű, sraffozott és átmenetes ecsettel töltesz ki.
  • A Rectangle műveleteit (metszet, tartalmazás, bővítés) használod.
  • Tudod, mit kell felszabadítani, és elkerülöd a gyakori hibákat.

Következő lecke: Pontok, koordináták és méretezés

A koordináta-rendszer mélyebben: Point, PointF, átszámítás, a világ- és a képernyő-koordináta, a Graphics transzformációi.