Grafikus C# – Windows Forms tanfolyam › 2.15 lecke

Windows Forms · 2.15 lecke · haladó

PrintDialog, PrintDocument és nyomtatási előnézet

Egy szövegszerkesztőből a dokumentumot ki is kell tudni nyomtatni. A Windows Forms ehhez egy PrintDocument komponenst kínál, amelynek a lapokat mi rajzoljuk meg: a program szétosztja a szöveget oldalakra, a nyomtatási párbeszédablak és az előnézet pedig a Windows beépített ablakai. Ebben a leckében a MiniWord szövegét nyomtatjuk, és közben megértjük, hogyan működik egy többoldalas kiírás.

  • kb. 90 perc
  • haladó szint
  • nyomtatás
Diáklány C# programot készít laptopon

A lecke végére

  • a PrintDocument, a PrintDialog és a PrintPreviewDialog összekapcsolásával nyomtatási folyamatot építesz;
  • a BeginPrint és a PrintPage esemény szerepét érted;
  • többoldalas szöveget tördelsz oldalakra a MeasureString és a HasMorePages segítségével;
  • a nyomtatást PDF-nyomtatóval is kipróbálod, papír nélkül;
  • tudod, mit nem tud ez az egyszerű megoldás (a formázás nyomtatása);
  • felismered a nyomtatás tipikus hibáit.

Mielőtt elkezded

Ehhez a leckéhez kell a MiniWord a 2.14 lecke után, a mezők (1.12 lecke), a Font (2.4 lecke) és a try–catch (2.2). A nyomtatás haladó téma, ezért a kódot lépésenként magyarázzuk.

2.14 Mentetlen dokumentum →

Három komponens, egy dokumentum

A nyomtatás három komponensből áll, amelyeket a Toolbox Printing csoportjában találsz. Húzd mindhármat az ablakra (a komponenssávba kerülnek):

pdDokumentumA PrintDocument: a nyomtatandó dokumentum. Eseményeiben rajzoljuk meg a lapokat.
dlgNyomtatasA PrintDialog: a Windows Nyomtatás ablaka (nyomtató, példányszám kiválasztása).
ppdElonezetA PrintPreviewDialog: nyomtatási előnézet, a lapok képernyőn.
ÖsszekötésA dlgNyomtatas és a ppdElonezet Document tulajdonságát állítsd a pdDokumentum elemre (a Properties ablakban legördülő listából).

A fájl elejére add hozzá a using System.Drawing.Printing; sort, mert a nyomtatás típusai ebben a névtérben vannak.

📷 Képernyőkép helye

Mit tegyél ide: a komponenssáv a három nyomtatási komponenssel (pdDokumentum, dlgNyomtatas, ppdElonezet), mellette a Properties ablak, amelyben a dlgNyomtatas Document tulajdonsága a pdDokumentum értékre van állítva.Javasolt fájlnév: winforms-2-15-komponensek.webp · Alt szöveg: A nyomtatási komponensek és a Document tulajdonság beállítása

A PrintPage esemény: itt rajzoljuk a lapot

A PrintDocument a nyomtatás megkezdése előtt a BeginPrint eseményt váltja ki, majd minden egyes lapra lefuttatja a PrintPage eseményt. A PrintPage eseménykezelőjében a lapra rajzolunk, ugyanúgy, mint a képernyőre. A PrintPageEventArgs e paraméter adja az eszközöket:

e.GraphicsA rajzfelület: ide írjuk a szöveget a DrawString metódussal.
e.MarginBoundsA lap használható területe (a margókon belül). A szöveget ide tördeljük.
e.HasMorePagesHa igazra állítod, a nyomtató még egy lapot kér (a PrintPage újra lefut). Ha hamis, a nyomtatás véget ér.

Egyetlen lapra kiírva a szöveget így néz ki a legegyszerűbb változat:

Form1.csC#
private void pdDokumentum_PrintPage(object sender, PrintPageEventArgs e)
{
Font betu = new Font("Arial", 12);

e.Graphics.DrawString(rtbDokumentum.Text, betu, Brushes.Black, e.MarginBounds);

betu.Dispose();
e.HasMorePages = false;
}
DrawString(szöveg, betű, ecset, terület)A szöveget a megadott betűtípussal, színnel és téglalapba írja. A téglalapba nem férő rész levágódik.
Brushes.BlackFekete ecset: a szöveg színe.
betu.Dispose()Felszabadítja a betűtípus erőforrását. A grafikus erőforrásokat használat után le kell zárni (9. fejezet).

Jósolj!

A dokumentum három oldalnyi szöveget tartalmaz, de ez a kód egyetlen lapra rajzol, és a HasMorePages értéke hamis. Mi kerül a papírra?

Jóslat ellenőrzése

Csak az első lapnyi szöveg. A DrawString levágja azt, ami nem fér a téglalapba, és mivel a HasMorePages hamis, a többi lapra nincs nyomtatás. A hosszabb szöveg miatt kell a következő szakasz.

Többoldalas szöveg tördelése

Hosszabb szövegnél a programnak lapról lapra el kell döntenie, hány karakter fér egy lapra, azt kiírni, majd a maradékkal folytatni. Ehhez két mezőre van szükség: a nyomtatandó szövegre és arra, hogy hol tartunk benne (1.12 lecke: mező). A MeasureString metódus megmondja, hányadik karakterig fér a szöveg a területre:

Form1.csC#
private string nyomtatandoSzoveg = "";
private int pozicio = 0;

private void pdDokumentum_BeginPrint(object sender, PrintEventArgs e)
{
nyomtatandoSzoveg = rtbDokumentum.Text;
pozicio = 0;
}

private void pdDokumentum_PrintPage(object sender, PrintPageEventArgs e)
{
Font betu = new Font("Arial", 12);

string maradek = nyomtatandoSzoveg.Substring(pozicio);
int karakterek;
int sorok;

e.Graphics.MeasureString(maradek, betu, e.MarginBounds.Size, StringFormat.GenericTypographic, out karakterek, out sorok);

e.Graphics.DrawString(maradek.Substring(0, karakterek), betu, Brushes.Black, e.MarginBounds, StringFormat.GenericTypographic);

pozicio = pozicio + karakterek;
e.HasMorePages = pozicio < nyomtatandoSzoveg.Length;

betu.Dispose();
}
BeginPrintA nyomtatás elején egyszer fut le: elmenti a szöveget, és nullázza a pozíciót. Így az újabb nyomtatás is elölről indul.
pozicioMező: megjegyzi, a szöveg hányadik karakterénél tartunk. A lapok között is megmarad.
Substring(pozicio)A szöveg hátralévő része a pozíciótól a végéig.
MeasureString(..., out karakterek, out sorok)Megméri a szöveget a lap területén, és az out változókba tölti, hány karakter és sor fér el. (Az out működését az 1.3 lecke magyarázta.)
Substring(0, karakterek)Pontosan az a szövegrész, amely erre a lapra fér.
HasMorePages = pozicio < LengthHa még van kiíratlan szöveg, a nyomtató újabb lapot kér, és a PrintPage újra lefut.
BeginPrint: pozíció = 0→PrintPage: 1. lap→van még szöveg? igen→PrintPage: 2. lap→… nincs több: vége

Jósolj!

Egy lapra 1800 karakter fér, a szöveg 4000 karakter hosszú. Hányszor fut le a PrintPage? Mi a pozicio értéke az utolsó lap után?

Jóslat ellenőrzése

Háromszor: az első két lapra 1800–1800 karakter kerül (3600), a harmadikra a maradék 400. Az utolsó lap után a pozicio értéke 4000, a HasMorePages hamis lesz, és a nyomtatás véget ér.

Haladó téma: nem kell mindent megjegyezned

A MeasureString és a DrawString használata a grafikus programozás (9. fejezet) része. Itt elég megérteni a menetet: megmérjük, mi fér a lapra, kiírjuk, megjegyezzük, hol tartunk, és újabb lapot kérünk, amíg van szöveg.

Mit nem tud ez a megoldás?

A formázás nem jelenik meg a nyomtatáson

A fenti kód a dokumentum sima szövegét egyetlen betűtípussal (Arial, 12 pont) nyomtatja. A félkövér, dőlt, színes szövegek, az igazítás és a különböző betűméretek nem kerülnek a papírra. Ez a megoldás gyakorláshoz és jegyzetekhez jó.

A formázott szöveg nyomtatásához a Windows saját RichEdit komponensét kell megkérni arra, hogy a lapra rajzolja a szöveget (EM_FORMATRANGE üzenet), ugyanúgy, ahogy a 2.7 leckében a sorkizárt igazításhoz üzenetet küldtünk neki. Ez bonyolultabb, és külön segédosztályt igényel: a MiniWord alapfeladatához nem szükséges. Ha érdekel, a Microsoft dokumentációjában keresd a „RichTextBox printing EM_FORMATRANGE” kifejezést.

Jósolj!

A MiniWordben a címet középre igazítottad, nagy méretű, félkövér betűkkel. Hogyan jelenik meg a címsor az egyszerű nyomtatáson?

Jóslat ellenőrzése

Balra igazított, normál 12 pontos Arial szövegként, mint a többi szöveg. A formázás és az igazítás nem kerül a nyomtatásba.

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 az első oldal nyomtat

A hosszú dokumentumból csak egyetlen lap készül.

Ok és javítás

A HasMorePages értéke nincs igazra állítva. Be kell állítani: e.HasMorePages = pozicio < nyomtatandoSzoveg.Length;, vagyis ha van még kiíratlan szöveg, igaz.

2. Minden lapon ugyanaz a szöveg

private void pdDokumentum_PrintPage(object sender, PrintPageEventArgs e)
{
int pozicio = 0; // ...
}
Hiba és javítás

Rossz működés: a pozicio változót a metóduson belül hoztad létre, ezért minden lapon újra 0-ról indul. A változót mezőként, a metóduson kívül kell létrehozni (1.12 lecke), és a BeginPrint eseményben nullázni.

3. Ismeretlen típus

// CS0246: The type or namespace name 'PrintPageEventArgs' could not be found
Hiba és javítás

Fordítási hiba (CS0246): hiányzik a using System.Drawing.Printing; sor a fájl elejéről. A nyomtatás típusai ebben a névtérben vannak.

4. Második nyomtatásnál rossz szöveg

Másodszor nyomtatva a dokumentum egy része hiányzik.

Ok és javítás

A pozíció nincs nullázva a nyomtatás elején. A BeginPrint eseményben állítsd vissza pozicio = 0 értékre, és olvasd be újra a szöveget.

5. Nem jelenik meg a félkövér

A nyomtatott lapon nincs félkövér szöveg.

Ok és javítás

Ez nem hiba: az egyszerű megoldás csak sima szöveget nyomtat, egyetlen betűtípussal. A formázott nyomtatáshoz külön, haladó megoldás kell (lásd az 5. szakaszt).

Mini projekt: nyomtatás a MiniWordben

Építsd be a nyomtatást a MiniWordbe:

  1. Add hozzá a három komponenst (pdDokumentum, dlgNyomtatas, ppdElonezet), és kösd őket össze a Document tulajdonsággal. A fájl elejére tedd a using System.Drawing.Printing; sort.
  2. Írd meg a két mezőt (nyomtatandoSzoveg, pozicio), a BeginPrint és a PrintPage eseménykezelőt a 3. szakasz kódja szerint.
  3. Adj a File menühöz Nyomtatás és Nyomtatási kép menüpontot, a 4. szakasz kódjával.
  4. Próbáld ki egy hosszú, több oldalas szöveggel az előnézetben, majd a Microsoft Print to PDF nyomtatóval, és nézd meg a PDF-et.
  5. Ellenőrizd: üres dokumentumnál nem omlik össze a program, és a másodszori nyomtatás is jó.
📷 Képernyőkép helye

Mit tegyél ide: a MiniWord által készített, többoldalas PDF fájl első két lapja egymás mellett, az egyszerű szöveggel, a margókon belül tördelve.Javasolt fájlnév: winforms-2-15-pdf-kesz.webp · Alt szöveg: A MiniWordből nyomtatott többoldalas PDF első két lapja

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. Oldalszám a lap alján

Minden lap aljára kerüljön az oldalszám, például: 1. oldal.

Egy lehetséges megoldás
private int oldalSzam = 0;

// BeginPrint: oldalSzam = 0;

// PrintPage végén, az e.HasMorePages sor előtt:
oldalSzam = oldalSzam + 1;
Font kisBetu = new Font("Arial", 9);
e.Graphics.DrawString($"{oldalSzam}. oldal", kisBetu, Brushes.Gray, e.MarginBounds.Left, e.MarginBounds.Bottom + 10);
kisBetu.Dispose();

2. Fejléc a dokumentum nevével

Minden lap tetején jelenjen meg a dokumentum neve (például a címsor szövege).

Egy lehetséges megoldás
Font fejBetu = new Font("Arial", 9);
e.Graphics.DrawString(this.Text, fejBetu, Brushes.Gray, e.MarginBounds.Left, e.MarginBounds.Top - 25);
fejBetu.Dispose();

A this.Text az ablak címe (2.14 lecke). A margó felső széle fölé, a lap tetejére ír.

3. Nagyobb margó

Állítsd be, hogy a margók mindenhol körülbelül 2 cm-esek legyenek. (Segítség: a Margins osztály értékei századhüvelykben vannak, 1 hüvelyk = 2,54 cm.)

Egy lehetséges megoldás
// a Form1_Load eseményben:
pdDokumentum.DefaultPageSettings.Margins = new Margins(79, 79, 79, 79);

A 2 cm körülbelül 0,79 hüvelyk, vagyis 79 század. A négy érték sorban: bal, jobb, felső és alsó margó. A Margins is a System.Drawing.Printing névtérben van.

Gyakorlótár: 6 további feladat

Három szinten gyakorolhatsz: alap, közepes és kihívás. A feladatok a MiniWord nyomtatását gyakoroltatják (a pdDokumentum, dlgNyomtatas és ppdElonezet komponens a 2.15 lecke szerint). A fájl elejére tedd a using System.Drawing.Printing; sort. Csak a próbálkozás után nyisd le a megoldást.

Alap szint

1. Melyik komponens mire való?

Párosítsd: PrintDocument, PrintDialog, PrintPreviewDialog. Szerepek: (a) a Windows Nyomtatás ablaka, (b) a nyomtatandó dokumentum, amelynek lapjait mi rajzoljuk, (c) a nyomtatási előnézet.

Megoldás megnyitása

PrintDocument → (b), PrintDialog → (a), PrintPreviewDialog → (c). A két ablak a Document tulajdonságon keresztül kapcsolódik a PrintDocument-hez.

2. Előnézet gomb

A btnElonezet gomb nyissa meg a nyomtatási előnézetet.

Megoldás megnyitása

Az előnézet ablaknak meg kell adni a dokumentumot, majd a ShowDialog() megnyitja.

Form1.csC#
private void btnElonezet_Click(object sender, EventArgs e)
{
ppdElonezet.Document = pdDokumentum;
ppdElonezet.ShowDialog();
}

Közepes szint

3. A szövegdoboz betűtípusa

A nyomtatás ne fix Arial betűvel, hanem a szövegdoboz betűtípusával (rtbDokumentum.Font) történjen. Mire kell vigyázni a betűtípus felszabadításánál?

Megoldás megnyitása

A Dispose() csak azokat a betűtípusokat szabad felszabadítani, amelyeket mi hoztunk létre (new Font). A vezérlő Font tulajdonságát nem szabad felszabadítani, mert a vezérlő még használja: ez a program hibájához vezetne. Itt ezért elmarad a betu.Dispose() sor.

Form1.csC#
private void pdDokumentum_PrintPage(object sender, PrintPageEventArgs e)
{
Font betu = rtbDokumentum.Font; // nem mi hoztuk létre, nem szabad Dispose-olni

string maradek = nyomtatandoSzoveg.Substring(pozicio);
int karakterek;
int sorok;

e.Graphics.MeasureString(maradek, betu, e.MarginBounds.Size, StringFormat.GenericTypographic, out karakterek, out sorok);
e.Graphics.DrawString(maradek.Substring(0, karakterek), betu, Brushes.Black, e.MarginBounds, StringFormat.GenericTypographic);

pozicio = pozicio + karakterek;
e.HasMorePages = pozicio < nyomtatandoSzoveg.Length;
}

4. Üres dokumentum nyomtatása

Üres dokumentumnál a nyomtatás előtt figyelmeztessen a program (Nincs mit nyomtatni.), és ne jelenjen meg a nyomtatási ablak.

Megoldás megnyitása

Az ellenőrzés a nyomtatási ablak megnyitása előtt történik. A return kilép a metódusból.

Form1.csC#
private void miNyomtatas_Click(object sender, EventArgs e)
{
if (rtbDokumentum.TextLength == 0)
{
MessageBox.Show("Nincs mit nyomtatni.");
return;
}

dlgNyomtatas.Document = pdDokumentum;

if (dlgNyomtatas.ShowDialog() == DialogResult.OK)
{
pdDokumentum.Print();
}
}

Kihívás szint

5. Oldalbeállítás ablak

Egy Oldalbeállítás menüpont nyissa meg a Windows oldalbeállítás ablakát (PageSetupDialog), ahol a margók és a tájolás állítható, és a beállítás érvényesüljön a nyomtatáson.

Megoldás megnyitása

A PageSetupDialog komponenst a Toolbox Printing csoportjából húzod az ablakra (psdOldal). A Document tulajdonságával a nyomtatandó dokumentumhoz kötjük, így a beállított margók és a tájolás a PrintDocument beállításaiba kerülnek. A PrintPage eseményben használt e.MarginBounds már ezeket tükrözi.

Form1.csC#
private void miOldalbeallitas_Click(object sender, EventArgs e)
{
psdOldal.Document = pdDokumentum;
psdOldal.ShowDialog();
}

6. PDF-teszt tervezése

Tervezz tesztet a Microsoft Print to PDF nyomtatóval: milyen dokumentumokkal és mit ellenőriznél?

Megoldás megnyitása
TesztMit ellenőrzöl a PDF-ben?
Rövid szöveg (3 sor)Egy lap, a szöveg a margón belül.
Hosszú szöveg (több oldal)Több lap, a szöveg folytatódik, nem ismétlődik és nem hiányzik belőle.
Üres dokumentumA program figyelmeztet, nem készül PDF.
Sok üres sor és hosszú szóA tördelés nem szakad meg, nincs végtelen ciklus.
Második nyomtatás ugyanabbólA másodszor készült PDF azonos az elsővel (a pozíció újra nullázódik).
Oldalbeállítás módosításaA margók és a tájolás változnak.

Gyors önellenőrzés

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

1. Mi a PrintPage esemény szerepe?

Minden nyomtatandó lapra lefut, és ebben rajzoljuk meg a lap tartalmát az e.Graphics segítségével.

2. Mit jelent az e.HasMorePages?

Ha igaz, a nyomtató újabb lapot kér, és a PrintPage újra lefut. Ha hamis, a nyomtatás véget ér.

3. Miért mező a pozicio, és nem helyi változó?

Mert a lapok között is meg kell őriznie az értékét, a helyi változó pedig minden PrintPage hívásnál újra létrejönne.

4. Mire való a BeginPrint?

A nyomtatás elején egyszer fut le: beolvassa a szöveget, és nullázza a pozíciót.

5. Hogyan teszteled a nyomtatást papír nélkül?

A Microsoft Print to PDF virtuális nyomtatóval, vagy a nyomtatási előnézettel.

6. Miért nem jelenik meg a formázás a nyomtatásban?

Mert a kód a sima szöveget (Text) egyetlen betűtípussal rajzolja ki. A formázott nyomtatáshoz külön, haladó megoldás kell.

Mit tudsz már?

  • Összekötöd a PrintDocument, a PrintDialog és a PrintPreviewDialog komponenst.
  • Érted a BeginPrint és a PrintPage szerepét.
  • Többoldalas szöveget tördelsz a HasMorePages segítségével.
  • PDF-nyomtatóval papír nélkül teszteled a nyomtatást.
  • Tudod, mi a megoldás korlátja.

Következő lecke: jegyzettömb és szövegszerkesztő záró projekt

A MiniWord minden fontos funkciója megvan. A fejezet záró projektjében összeállítjuk a teljes szövegszerkesztőt.