Grafikus C# – Windows Forms tanfolyam › 1.12 lecke

Windows Forms · 1.12 lecke

PictureBox, ImageList és képek megjelenítése

A képek megjelenítése az ablakos programok egyik legkedveltebb része. Ebben a leckében megtanulod megjeleníteni a képeket a PictureBox vezérlővel, kiválasztani őket a fájlválasztó ablakból, és egy ImageList segítségével lapozható képnézegetőt építesz.

  • kb. 75 perc
  • kezdő–közepes szint
  • képek
Diáklány C# programot készít laptopon

A lecke végére

  • képet jelenítesz meg a PictureBox vezérlőben, és beállítod a SizeMode tulajdonságot;
  • a felhasználó által kiválasztott képet megnyitod az OpenFileDialog segítségével, hibakezeléssel;
  • képeket töltesz egy ImageList komponensbe, és sorszám alapján előveszed őket;
  • érted, mire való az osztályszintű változó (mező), és hogyan jegyzi meg a program az aktuális sorszámot;
  • lapozható képnézegetőt készítesz Előző és Következő gombbal;
  • felismered a képek kezelésének tipikus hibáit.

Mielőtt elkezded

Ehhez a leckéhez kell az 1.11 lecke, az 1.6 lecke (ComboBox), az 1.8 lecke (Increment) és a 2.2 lecke gondolata (fájlválasztó), de az utóbbit itt is elmagyarázzuk. Készíts elő 3–4 saját képet (JPG vagy PNG) egy mappában.

1.11 ErrorProvider és ToolTip →

A PictureBox és a SizeMode

A PictureBox egy keret, amelyben képet jeleníthetsz meg. Tegyél az ablakra egyet pbKep néven (előtag: pb), majd a Properties ablakban az Image tulajdonságnál kattints a három pontra, és importálj egy saját képet.

Ha a kép nem fér bele a keretbe, vagy kisebb nála, a SizeMode tulajdonság dönti el, mi történjen:

NormalA kép eredeti méretben, a bal felső saroktól látszik. A keretből kilógó rész levágódik.
StretchImageA kép a keretre nyúlik, torzulhat.
ZoomA kép arányosan kicsinyedik vagy nagyobbodik, hogy beleférjen. Ez a leggyakrabban használt, jó választás.
CenterImageA kép eredeti méretben, középre igazítva látszik.
AutoSizeA PictureBox mérete igazodik a képhez.
Form1.csC#
pbKep.SizeMode = PictureBoxSizeMode.Zoom;

Jósolj!

A kép 1200 × 800 képpont, a PictureBox 300 × 200. Mit látsz, ha a SizeMode értéke Normal? És Zoom?

Jóslat ellenőrzése

Normal esetén a kép bal felső sarka látszik, kb. a negyede, a többi levágódik. Zoom esetén a teljes kép látszik, arányosan lekicsinyítve (300 × 200 képpontosra).

📷 Képernyőkép helye

Mit tegyél ide: a PictureBox ugyanazzal a képpel három beállításban egymás mellett: Normal (levágva), StretchImage (torz) és Zoom (arányos).Javasolt fájlnév: winforms-1-12-sizemode.webp · Alt szöveg: Ugyanaz a kép a PictureBox három SizeMode beállításával

Kép megnyitása a fájlválasztóval

A felhasználó saját képét az OpenFileDialog komponenssel választathatod ki (Toolbox, Dialogs csoport, neve: ofdKep). A képfájlok szűrőjében több kiterjesztés is megadható, pontosvesszővel elválasztva:

Form1.csC#
private void btnMegnyit_Click(object sender, EventArgs e)
{
ofdKep.Filter = "Képfájlok (*.png;*.jpg;*.jpeg;*.bmp)|*.png;*.jpg;*.jpeg;*.bmp|Minden fájl (*.*)|*.*";

if (ofdKep.ShowDialog() == DialogResult.OK)
{
try
{
pbKep.Image = Image.FromFile(ofdKep.FileName);
pbKep.SizeMode = PictureBoxSizeMode.Zoom;
}
catch (Exception ex)
{
MessageBox.Show("Nem sikerült megnyitni a képet: " + ex.Message);
}
}
}
FilterA szűrő elemei párokban állnak (felirat és minta). Több minta egy szűrőben pontosvesszővel kapcsolható össze.
Image.FromFile(út)Betölti a képet a megadott fájlból. Ezt kapja a PictureBox.Image tulajdonság.
try–catchHa a fájl nem kép, vagy sérült, a program nem áll le, hanem üzenetet mutat (2.2 lecke).
DialogResult.OKCsak akkor nyitunk meg képet, ha a felhasználó a Megnyitás gombot választotta.

A betöltött fájl zárolva marad

Az Image.FromFile a kép fájlját zárolja, amíg a kép használatban van. Ilyenkor nem tudod törölni vagy felülírni a fájlt. Egyszerű programnál ez nem baj, a későbbi, 9. fejezetben megtanuljuk a megfelelő kezelését.

Jósolj!

A felhasználó a szűrő „Minden fájl” beállításával egy szövegfájlt (jegyzet.txt) választ ki. Mi történik?

Jóslat ellenőrzése

Az Image.FromFile hibát dob (általában OutOfMemoryException, ami félrevezető üzenet, de azt jelenti, hogy a fájl nem képformátum). A try–catch miatt a program nem omlik össze, hanem hibaüzenetet mutat.

Az ImageList: képek gyűjteménye

Az ImageList egy komponens, amely több képet tárol, és sorszám alapján előhívhatók belőle. Így a program képei egy helyen vannak, és nem kell külön fájlokat kezelni. Tegyél az ablakra egyet (Toolbox, Components csoport, neve: ilKepek), majd:

  1. Állítsd az ImageSize tulajdonságot 256, 256 értékre (a legnagyobb megengedett méret). A ColorDepth legyen Depth32Bit.
  2. Az Images tulajdonságnál kattints a (Collection) gombra, és az Add gombbal add hozzá 3–4 képedet.

Az ImageSize az alapból apró!

Az ImageList minden képet az ImageSize méretére alakít, és az alapérték 16 × 16 képpont. Ha ezt nem állítod át a képek hozzáadása előtt, minden kép apró lesz és elmosódik. Előbb állítsd be a méretet, utána add hozzá a képeket.

📷 Képernyőkép helye

Mit tegyél ide: az Image Collection Editor ablak három hozzáadott képpel, mellette a Properties ablak az ImageSize 256, 256 értékkel.Javasolt fájlnév: winforms-1-12-imagelist.webp · Alt szöveg: Az ImageList képeinek szerkesztése a Visual Studióban

Az ImageList képeit a Images gyűjteményből kérheted le sorszámmal (0-tól), a darabszámot pedig az Images.Count adja:

Form1.csC#
pbKep.Image = ilKepek.Images[0];      // az első kép
int db = ilKepek.Images.Count; // ennyi kép van a gyűjteményben

Jósolj!

Az ilKepek három képet tartalmaz. Mi történik, ha ezt írod: ilKepek.Images[3]?

Jóslat ellenőrzése

Futás közbeni hiba (ArgumentOutOfRangeException): három kép esetén a sorszámok 0, 1 és 2. A 3-as sorszám már nem létezik.

Új fogalom: az eseménykezelőn kívüli változó

A lapozható képnézegetőnek emlékeznie kell arra, hányadik képnél tart. Egy eseménykezelőben létrehozott változó azonban a metódus végén megszűnik: a következő kattintáskor már nem létezik. Ha meg akarjuk jegyezni az értékét, a változót a metódusokon kívül, az osztály elején kell létrehozni. Az ilyen változó neve mező, és az osztály összes metódusa eléri:

Form1.csC#
public partial class Form1 : Form
{
private int aktualis = 0; // mező: az aktuális kép sorszáma

public Form1()
{
InitializeComponent();
}

// ... itt következnek a metódusok ...
}
A metódusokon kívül állKözvetlenül az osztály kapcsos zárójelén belül, a metódusok előtt vagy között.
Megőrzi az értékétAmíg az ablak él, a mező értéke megmarad, a metódusok pedig módosítják.
Helyi változóA metóduson belüli változó (például a hely a keresésnél) a metódus végén megszűnik.

Kapcsolódik a későbbi anyaghoz

A mezőkről, a láthatóságról és az osztályok felépítéséről a 12. fejezetben tanulunk részletesen. Most elég annyi, hogy a metóduson kívüli változó megjegyzi az értékét.

Most már megírhatjuk a lapozást: a Mutat() metódus megjeleníti az aktuális képet, az Előző és a Következő gomb pedig módosítja a sorszámot. Ha az elejére vagy a végére érünk, körbe fordulunk:

Form1.csC#
private int aktualis = 0;

private void Mutat()
{
if (ilKepek.Images.Count > 0)
{
pbKep.Image = ilKepek.Images[aktualis];
lblSorszam.Text = $"{aktualis + 1} / {ilKepek.Images.Count}";
}
}

private void Form1_Load(object sender, EventArgs e)
{
pbKep.SizeMode = PictureBoxSizeMode.Zoom;
Mutat();
}

private void btnKovetkezo_Click(object sender, EventArgs e)
{
aktualis = aktualis + 1;

if (aktualis >= ilKepek.Images.Count)
{
aktualis = 0;
}

Mutat();
}

private void btnElozo_Click(object sender, EventArgs e)
{
aktualis = aktualis - 1;

if (aktualis < 0)
{
aktualis = ilKepek.Images.Count - 1;
}

Mutat();
}
Mutat()Megjeleníti az aktuális képet és a sorszámot. Minden lapozás után ezt hívjuk.
aktualis + 1A sorszám az embereknek 1-től számozott, a program 0-tól: ezért kell az + 1 a kiírásnál.
aktualis >= CountHa túlléptünk az utolsó képen, visszaugrunk az elsőre (a sorszám 0).
aktualis < 0Ha az első kép előtt vagyunk, az utolsóra ugrunk (a sorszám Count - 1).

Jósolj!

Három kép van, az aktualis értéke 2 (a harmadik kép). Megnyomod a Következő gombot. Melyik képet látod, és mi lesz a felirat?

Jóslat ellenőrzése

Az aktualis 3-ra nő, ami túl nagy, ezért 0-ra áll vissza: az első képet látod, és a felirat 1 / 3.

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. Csak egy darab látszik a képből

A képnek csak a bal felső sarka látszik a PictureBoxban.

Ok és javítás

A SizeMode értéke Normal, ezért a kép eredeti méretben látszik, és a keretből kilógó rész levágódik. Állítsd Zoom értékre.

2. Apró, elmosódott kép

Az ImageListből előhívott képek nagyon kicsik és homályosak.

Ok és javítás

Az ImageSize értéke az alapértelmezett 16 × 16 maradt, amikor a képeket hozzáadtad. Állítsd 256 × 256 értékre, töröld a képeket, és add hozzá újra.

3. Kilép a sorszám a tartományból

aktualis = aktualis + 1;
pbKep.Image = ilKepek.Images[aktualis];
Hiba és javítás

Futás közbeni hiba (ArgumentOutOfRangeException) az utolsó képnél: a sorszám túllépi a darabszámot. Ellenőrizd a határt, és ugorj vissza 0-ra (if (aktualis >= ilKepek.Images.Count)).

4. A Következő mindig az első képre ugrik

private void btnKovetkezo_Click(object sender, EventArgs e)
{
int aktualis = 0;
aktualis = aktualis + 1;
Mutat();
}
Hiba és javítás

Rossz működés: az aktualis változót a metóduson belül hoztad létre, ezért minden kattintásnál újra 0-ról indul, és a metódus végén eltűnik. A változót az osztály elején kell létrehozni (mező), a metóduson kívül.

5. Rossz fájl a képnéző számára

A program összeomlik, amikor egy nem kép fájlt választanak.

Ok és javítás

Az Image.FromFile hibát dob nem képformátumú fájlnál. Tedd try–catch blokkba, és mutass a felhasználónak érthető üzenetet.

Mini projekt: képnéző

Készíts képnézegetőt, amely a saját képeidet mutatja lapozva és a megnyitott képet is megjeleníti:

  1. Felület: pbKep (SizeMode: Zoom, BorderStyle: FixedSingle), btnElozo, btnKovetkezo, lblSorszam, btnMegnyit és ofdKep, valamint ilKepek (ImageSize: 256, 256, benne 3–4 saját képpel).
  2. A lapozó gombok és a Mutat() a 4. szakasz kódja alapján működnek.
  3. A Megnyitás gomb a 2. szakasz kódjával egy külső képet jelenít meg (ilyenkor a lapozás nem használható, de a következő lapozásnál az ImageList képei jönnek vissza).
  4. A cmbMeret legördülő listából (Zoom, Stretch, Center) a felhasználó választhassa a megjelenítés módját.
Form1.csC#
private void cmbMeret_SelectedIndexChanged(object sender, EventArgs e)
{
if (cmbMeret.Text == "Zoom")
{
pbKep.SizeMode = PictureBoxSizeMode.Zoom;
}
else if (cmbMeret.Text == "Stretch")
{
pbKep.SizeMode = PictureBoxSizeMode.StretchImage;
}
else if (cmbMeret.Text == "Center")
{
pbKep.SizeMode = PictureBoxSizeMode.CenterImage;
}
}
📷 Képernyőkép helye

Mit tegyél ide: a kész képnéző egy képpel, az Előző és Következő gombbal, a „2 / 3” felirattal, a Megnyitás gombbal és a megjelenítési mód legördülő listájával.Javasolt fájlnév: winforms-1-12-kepnezo.webp · Alt szöveg: A kész képnéző lapozógombokkal és megjelenítési móddal

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. A kép mérete

Írd ki egy lblMeret feliratra a megjelenített kép méretét képpontban, például: 1200 × 800.

Egy lehetséges megoldás
if (pbKep.Image != null)
{
lblMeret.Text = $"{pbKep.Image.Width} × {pbKep.Image.Height}";
}

Az Image.Width és az Image.Height a kép szélessége és magassága. A null ellenőrzés kell, mert még lehet, hogy nincs betöltött kép.

2. Automatikus diavetítés

Készíts egy tmrVetites Timert (Interval: 2000), amely kétmásodpercenként a következő képre lép. A chkVetites jelölőnégyzet kapcsolja be és ki.

Egy lehetséges megoldás
private void chkVetites_CheckedChanged(object sender, EventArgs e)
{
tmrVetites.Enabled = chkVetites.Checked;
}

private void tmrVetites_Tick(object sender, EventArgs e)
{
btnKovetkezo.PerformClick();
}

A PerformClick() úgy viselkedik, mintha a gombra kattintottak volna, így nem kell a lapozó kódot megismételni.

3. Az Előző gomb az első képen

Az Előző gomb legyen letiltva, amíg az első képnél vagy, és a Következő gomb legyen letiltva az utolsó képen (körbefordulás nélkül).

Egy lehetséges megoldás
// a Mutat() metódus végén:
btnElozo.Enabled = aktualis > 0;
btnKovetkezo.Enabled = aktualis < ilKepek.Images.Count - 1;

Az összehasonlítás eredménye igaz vagy hamis, ezt kapja az Enabled tulajdonság. A lapozó gombokban ilyenkor a körbefordulást el kell hagyni.

Gyakorlótár: 6 további feladat

Három szinten gyakorolhatsz: alap, közepes és kihívás. A feladatokhoz használj egy pbKep PictureBoxot (SizeMode: Zoom) és az imlKepek ImageList komponenst. A képeket a projekt mappájába teheted, vagy az ImageList Images gyűjteményébe tölthetsz be. Csak a próbálkozás után nyisd le a megoldást.

Alap szint

1. Kép betöltése fájlból

A btnKep gomb nyisson meg egy képválasztó ablakot (ofdKep), és a kiválasztott képet jelenítse meg a pbKep PictureBoxban. Hibás fájlnál ne omoljon össze.

Megoldás megnyitása

Az Image.FromFile betölti a képet. Ha a fájl nem kép, kivétel keletkezik, ezért try–catch blokkba tesszük. A Filter tulajdonság csak a képfájlokat mutatja.

Form1.csC#
private void btnKep_Click(object sender, EventArgs e)
{
ofdKep.Filter = "Képek (*.png;*.jpg;*.jpeg;*.bmp)|*.png;*.jpg;*.jpeg;*.bmp";

if (ofdKep.ShowDialog() != DialogResult.OK)
{
return;
}

try
{
pbKep.Image = Image.FromFile(ofdKep.FileName);
}
catch (Exception ex)
{
MessageBox.Show("Nem sikerült megnyitni a képet: " + ex.Message);
}
}

2. Két kép váltogatása

Az imlKepek két képet tartalmaz. Egy gomb minden megnyomásra a másik képet mutassa a PictureBoxban.

Megoldás megnyitása

Az imlKepek.Images[0] az első kép, az Images[1] a második. A váltáshoz egy bool mezőben jegyezzük meg, melyik látszik éppen.

Form1.csC#
private bool elsoKepLatszik = true;

private void btnValt_Click(object sender, EventArgs e)
{
elsoKepLatszik = !elsoKepLatszik;

if (elsoKepLatszik)
{
pbKep.Image = imlKepek.Images[0];
}
else
{
pbKep.Image = imlKepek.Images[1];
}
}

Közepes szint

3. Képnézegető

Az imlKepek több képet tartalmaz. A btnElore és a btnVissza gomb léptessen előre és hátra, az utolsó után az első következzen, és fordítva. A lblSorszam mutassa: 2 / 5.

Megoldás megnyitása

Az aktuális képsorszámot mezőben tároljuk. A körbeforgatást ellenőrzéssel oldjuk meg: ha túlmennénk az utolsón, visszaugrunk az elsőre, és fordítva. A megjelenítést külön metódusba tesszük.

Form1.csC#
private int sorszam = 0;

private void Megjelenit()
{
pbKep.Image = imlKepek.Images[sorszam];
lblSorszam.Text = $"{sorszam + 1} / {imlKepek.Images.Count}";
}

private void btnElore_Click(object sender, EventArgs e)
{
sorszam = sorszam + 1;

if (sorszam >= imlKepek.Images.Count)
{
sorszam = 0;
}

Megjelenit();
}

private void btnVissza_Click(object sender, EventArgs e)
{
sorszam = sorszam - 1;

if (sorszam < 0)
{
sorszam = imlKepek.Images.Count - 1;
}

Megjelenit();
}

private void Form1_Load(object sender, EventArgs e)
{
Megjelenit();
}

4. Dobókocka

Az imlKepek hat képet tartalmaz (a kocka 1–6 oldala, ebben a sorrendben). A btnDob gomb véletlenszerűen mutassa az egyiket, és a lblDobas írja ki a dobott számot.

Megoldás megnyitása

A Random objektumot mezőben hozzuk létre, hogy mindig ugyanazt használjuk, mert gyors egymásutánban új Random-ok ugyanazt a számot adhatnák. A Next(1, 7) 1 és 6 közötti számot ad (a felső határ nem tartozik bele).

Form1.csC#
private Random veletlen = new Random();

private void btnDob_Click(object sender, EventArgs e)
{
int dobas = veletlen.Next(1, 7);

pbKep.Image = imlKepek.Images[dobas - 1];
lblDobas.Text = "Dobás: " + dobas;
}

Kihívás szint

5. Diavetítés

A Start gomb indítson automatikus diavetítést: a tmrDia Timer (Interval: 2000) kétmásodpercenként váltson a következő képre. A Stop gomb állítsa le.

Megoldás megnyitása

A Timer Tick eseménye ugyanazt teszi, mint az előre gomb. A közös lépést külön metódusba tesszük, így nem kell kétszer megírni.

Form1.csC#
private int sorszam = 0;

private void KovetkezoKep()
{
sorszam = sorszam + 1;

if (sorszam >= imlKepek.Images.Count)
{
sorszam = 0;
}

pbKep.Image = imlKepek.Images[sorszam];
}

private void tmrDia_Tick(object sender, EventArgs e)
{
KovetkezoKep();
}

private void btnStart_Click(object sender, EventArgs e)
{
tmrDia.Start();
}

private void btnStop_Click(object sender, EventArgs e)
{
tmrDia.Stop();
}

6. Két kocka és statisztika

Két PictureBox (pbKocka1, pbKocka2) két kockát mutat. A gomb mindkettővel dob, a lblStat pedig kiírja: Dobások: 7, összeg átlaga: 7,3. A számláló és az összeg a program futása alatt folyamatosan gyűljön.

Megoldás megnyitása

A dobások számát és az összegek összegét két mezőben tároljuk, mert a gombnyomások között is meg kell maradniuk. Az átlag az összeg osztva a dobások számával. A (double) átalakítás azért kell, hogy az osztás ne egész számként csonkoljon.

Form1.csC#
private Random veletlen = new Random();
private int dobasokSzama = 0;
private int osszesen = 0;

private void btnDob_Click(object sender, EventArgs e)
{
int elso = veletlen.Next(1, 7);
int masodik = veletlen.Next(1, 7);

pbKocka1.Image = imlKepek.Images[elso - 1];
pbKocka2.Image = imlKepek.Images[masodik - 1];

dobasokSzama = dobasokSzama + 1;
osszesen = osszesen + elso + masodik;

double atlag = (double)osszesen / dobasokSzama;
lblStat.Text = $"Dobások: {dobasokSzama}, összeg átlaga: {Math.Round(atlag, 1)}";
}

Gyors önellenőrzés

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

1. Melyik SizeMode tartja meg a kép arányát, és mutatja a teljes képet?

A Zoom.

2. Mit ad vissza az Image.FromFile, és mikor hibázik?

Betölti a képet a fájlból. Hibát dob, ha a fájl nem létezik vagy nem képformátumú.

3. Mire való az ImageList?

Több képet tárol egy helyen, amelyek sorszámmal elérhetők.

4. Milyen sorszámot kap az ImageList első képe?

A 0-t. Három kép sorszáma: 0, 1, 2.

5. Miért kell a lapozásnál az aktuális sorszámot az osztály elején létrehozni?

Mert a metóduson belüli változó a metódus végén megszűnik. A metóduson kívüli változó (mező) megőrzi az értékét a kattintások között.

6. Miért kell az ImageSize értékét a képek hozzáadása előtt beállítani?

Mert az ImageList a képeket a beállított méretre alakítja át hozzáadáskor. Az alapérték 16 × 16, ami apró és elmosódott képeket ad.

Mit tudsz már?

  • Képet jelenítesz meg a PictureBoxban, és beállítod a SizeMode-ot.
  • A felhasználó képét megnyitod hibakezeléssel.
  • Képeket tárolsz az ImageListben.
  • Érted, mire való a mező (metóduson kívüli változó).
  • Lapozható képnézegetőt készítettél.

Következő lecke: záró projekt, adatbekérő és kalkulátor

Az 1. fejezet vezérlőit már mind ismered. A következő leckében egy záró projektben használjuk őket együtt.