Grafikus C# – Windows Forms tanfolyam › 2.7 lecke

Windows Forms · 2.7 lecke · haladó

Sorkizárt igazítás: a RichTextBox korlátja és a megoldás

A RichTextBox balra, középre és jobbra tud igazítani, de sorkizártat nem, pedig a Wordben ez az egyik leggyakrabban használt igazítás. A megoldáshoz le kell nyúlnunk a Windows saját szövegszerkesztő komponenséhez. Ez haladó téma, ezért kész segédosztályt használunk, és közben megértjük, mit csinál.

  • kb. 60 perc
  • haladó, kész kóddal
  • sorkizárt
Anime stílusú fiú és lány egy C# programozási feladatot beszél meg

A lecke végére

  • érted, miért hiányzik a sorkizárt a RichTextBoxból;
  • ismered a Windows üzenetküldés (SendMessage), a DLL és a DllImport alapgondolatát;
  • beépíted a projektbe a kész RichTextBoxHelper segédosztályt;
  • egy gombbal sorkizártra állítod a bekezdéseket;
  • megoldod, hogy a megnyitott sorkizárt dokumentum is helyesen jelenjen meg;
  • ellenőrzöd az eredményt mentés, újranyitás és Word segítségével.

Mielőtt elkezded

Ehhez a leckéhez kell a három igazítógomb a 2.6 lecke után. Nem kell mindent érteni a kódból: a segédosztályt kimásolhatod, de a leckében lépésről lépésre elmagyarázzuk.

2.6 Bekezdésigazítás →

Miért hiányzik a sorkizárt?

A Windows Forms RichTextBox valójában a Windows saját RichEdit szerkesztő vezérlőjét csomagolja be. A RichEdit tud sorkizártat, de a .NET csak a legfontosabb funkciókat tette elérhetővé: a HorizontalAlignment felsorolásban ezért csak három érték van. A hiányzó részeket közvetlenül a Windowstól kell kérnünk.

Jósolj!

Mit gondolsz: ha a Wordben sorkizárttal elmentett RTF fájlt nyitsz meg a MiniWordben, az igazítás megmarad a fájlban, vagy elveszik?

Jóslat ellenőrzése

Az RTF fájlban az igazítás ott van, a RichEdit pedig kezelni is tudja. Hogy a MiniWord helyesen megjelenítse, az ehhez szükséges beállítást a Windowstól kell kérni. Ezt a lecke 4. szakasza mutatja meg.

Négy fogalom, amit most kell ismerned

A Windowsban minden ablak és vezérlő üzeneteket kap, amelyeket a rendszer küld. Mi most egy ilyen üzenetet fogunk küldeni a szövegdobozunknak: „állítsd sorkizártra ezt a bekezdést”.

Üzenet (message)Egy kérés, amelyet a Windows egy ablaknak küld, például „nyomták meg a gombot” vagy „igazítsd sorkizártra”. A számoknak neve is van, például EM_SETPARAFORMAT.
SendMessageA Windows függvénye, amellyel üzenetet küldünk egy ablaknak. Olyan, mintha levelet adnánk fel egy címre.
DLL és DllImportA DLL egy programkönyvtár a Windowsban (például user32.dll). A DllImport segítségével a C# kódunk meg tudja hívni a függvényeit.
Handle (kezelő)Az ablak azonosítója a Windowsban. A rtbDokumentum.Handle megmondja, melyik ablaknak címezzük az üzenetet.

Nem kell mindent megjegyezned

Ez a technika az ablakos programozás haladó témája. A feladathoz elég, ha a segédosztályt beépíted, és tudod, mit csinál. A részleteket a lecke végén, a „Mélyebben” szakaszban találod.

A segédosztály beépítése

A Windows hívását egy külön osztályba tesszük, hogy a Form1 kódja áttekinthető maradjon.

  1. A Solution Explorerben kattints jobb gombbal a projektre, és válaszd az Add → Class lehetőséget.
  2. A fájl neve legyen RichTextBoxHelper.cs, majd kattints az Add gombra.
  3. Töröld ki a fájl teljes tartalmát, és másold be az alábbi kódot.
📷 Képernyőkép helye

Mit tegyél ide: az „Add New Item” ablak, amelyben a Class elem és a RichTextBoxHelper.cs fájlnév látszik. Mellette a Solution Explorer, benne az új fájllal.Javasolt fájlnév: winforms-2-7-add-class.webp · Alt szöveg: Új osztály hozzáadása a projekthez a Visual Studióban

RichTextBoxHelper.csC#
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

public static class RichTextBoxHelper
{
private const int EM_SETPARAFORMAT = 0x447;
private const int EM_SETTYPOGRAPHYOPTIONS = 0x4CA;
private const int TO_ADVANCEDTYPOGRAPHY = 1;
private const int PFM_ALIGNMENT = 8;
private const int PFA_JUSTIFY = 4;

[StructLayout(LayoutKind.Sequential)]
private struct PARAFORMAT2
{
public int cbSize;
public uint dwMask;
public short wNumbering;
public short wReserved;
public int dxStartIndent;
public int dxRightIndent;
public int dxOffset;
public short wAlignment;
public short cTabCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public int[] rgxTabs;
public int dySpaceBefore;
public int dySpaceAfter;
public int dyLineSpacing;
public short sStyle;
public byte bLineSpacingRule;
public byte bOutlineLevel;
public short wShadingWeight;
public short wShadingStyle;
public short wNumberingStart;
public short wNumberingStyle;
public short wNumberingTab;
public short wBorderSpace;
public short wBorderWidth;
public short wBorders;
}

[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, ref PARAFORMAT2 lParam);

// Bekapcsolja a speciális szövegmegjelenítést (ez kell a sorkizárthoz).
public static void EngedelyezSorkizartot(RichTextBox rtb)
{
SendMessage(rtb.Handle, EM_SETTYPOGRAPHYOPTIONS, (IntPtr)TO_ADVANCEDTYPOGRAPHY, (IntPtr)TO_ADVANCEDTYPOGRAPHY);
}

// Sorkizártra állítja a kijelölt bekezdéseket.
public static void Sorkizart(RichTextBox rtb)
{
EngedelyezSorkizartot(rtb);

PARAFORMAT2 pf = new PARAFORMAT2();
pf.cbSize = Marshal.SizeOf(pf);
pf.dwMask = PFM_ALIGNMENT;
pf.wAlignment = PFA_JUSTIFY;
pf.rgxTabs = new int[32];

SendMessage(rtb.Handle, EM_SETPARAFORMAT, IntPtr.Zero, ref pf);
}
}

Pontosan másold át!

Ebben a kódban minden szám és név számít: a kisbetű-nagybetű, a mezők sorrendje és a számértékek is. Egyetlen elírás esetén a Windows nem szól, hanem egyszerűen nem történik semmi. Ha a kódot átgépeled, ellenőrizd vissza minden sort.

A gomb és a Load esemény

Tegyél a formázó sorba egy btnSorkizart nevű gombot Sorkizárt felirattal. Az eseménykezelője egyetlen sor:

Form1.csC#
private void btnSorkizart_Click(object sender, EventArgs e)
{
RichTextBoxHelper.Sorkizart(rtbDokumentum);
rtbDokumentum.Focus();
}

private void Form1_Load(object sender, EventArgs e)
{
RichTextBoxHelper.EngedelyezSorkizartot(rtbDokumentum);
}
RichTextBoxHelper.Sorkizart(rtbDokumentum)Meghívja a segédosztály metódusát: a kijelölt bekezdéseket sorkizártra állítja.
Form1_LoadAz ablak betöltésekor egyszer bekapcsolja a speciális szövegmegjelenítést. Így a sorkizárt akkor is jól jelenik meg, ha egy sorkizárt RTF fájlt nyitsz meg, még a gomb megnyomása előtt.

A Form1_Load eseménykezelő létrehozását a 0.8 leckében tanultad (dupla kattintás az ablak üres részére).

📷 Képernyőkép helye

Mit tegyél ide: a MiniWord egy hosszabb, több soros bekezdéssel, amely sorkizárt: a sorok jobb széle is egyenes. Fent látszik a Sorkizárt gomb.Javasolt fájlnév: winforms-2-7-sorkizart.webp · Alt szöveg: A MiniWord egy sorkizártra igazított bekezdéssel

Próbáld ki alaposan

  1. Írj vagy másolj be egy hosszabb szöveget (legalább 4–5 sorosat) Enter nélkül, és kattints a Sorkizárt gombra. A sorok mindkét széle egyenes lesz, de az utolsó sor nem nyúlik, mert az a bekezdés vége.
  2. Kattints a Balra gombra: a bekezdés visszaáll balra igazítottra. A sorkizárt tehát a többi igazítással váltogatható.
  3. Mentsd el RTF fájlként, zárd be a programot, majd nyisd meg újra a fájlt. A sorkizárt megmarad.
  4. Nyisd meg a mentett fájlt a Wordben is, és vesd össze a két megjelenést.
  5. Készíts a Wordben egy sorkizárt dokumentumot, mentsd RTF-ként, és nyisd meg a MiniWordben.
📷 Képernyőkép helye

Mit tegyél ide: két egymás melletti kép ugyanarról a bekezdésről: bal oldalon a MiniWord, jobb oldalon a Word. Mindkettőben sorkizárt, azonos tördelés látszik.Javasolt fájlnév: winforms-2-7-miniword-es-word.webp · Alt szöveg: Ugyanaz a sorkizárt bekezdés a MiniWordben és a Wordben

Megvan a négy igazítás

A MiniWord mostantól balra, középre, jobbra és sorkizártra is tud igazítani, ahogy a Word.

Mélyebben: mit csinál a kód?

Ez a szakasz opcionális. Ha érdekel, mit jelentenek a kódban a számok és a nevek, nyisd le a magyarázatot:

A konstansok jelentése
NévSzerepe
EM_SETPARAFORMATAz üzenet száma, amely azt kéri a szövegdoboztól, hogy állítsa be a kijelölt bekezdés formátumát.
PFM_ALIGNMENTMegmondja, hogy a bekezdés formátumából csak az igazítást szeretnénk beállítani, mást nem.
PFA_JUSTIFYAz igazítás értéke: a 4 jelenti a sorkizártat. (A balra a 1, a jobbra a 2, a középre a 3.)
EM_SETTYPOGRAPHYOPTIONSAz az üzenet, amellyel a speciális szövegmegjelenítést kapcsoljuk be.
TO_ADVANCEDTYPOGRAPHYA kapcsoló értéke: a sorkizárt megjelenítéséhez ez kell.
A PARAFORMAT2 struktúra

A PARAFORMAT2 egy adatcsomag (struct), amelyben a bekezdés összes formázási beállítása benne van: behúzás, igazítás, térköz, szegély és így tovább. A Windows pontosan ezt a szerkezetet várja, ezért kell a mezőket ugyanabban a sorrendben megadni. A kódban csak az cbSize (a csomag mérete), a dwMask (melyik mezők érvényesek) és a wAlignment (az igazítás) mezőt állítjuk be.

Miért van két SendMessage?

Az egyik egyszerű számokat küld (a speciális megjelenítés bekapcsolására), a másik egy PARAFORMAT2 csomagot (az igazítás beállítására). A C# két külön deklarációt kér, mert az üzenetek paraméterei eltérő típusúak.

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. Nem ismeri a StructLayout-ot

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

Fordítási hiba: hiányzik a using System.Runtime.InteropServices; sor a fájl elejéről. Ebben a névtérben van a StructLayout, a DllImport és a Marshal.

2. Nem történik semmi

A gombra kattintasz, de a bekezdés nem változik.

Ok és javítás

Lehetséges okok: elgépelt szám vagy mező a segédosztályban, vagy a bekezdés egyetlen rövid sor, amelyben a sorkizárt nem látható. Írj be egy hosszabb, több soros bekezdést, és ellenőrizd a kódot a leckében szereplő mintával.

3. Az utolsó sor nem kitöltött

A sorkizárt bekezdés utolsó sora nem ér el a jobb szélig.

Ok és javítás

Ez helyes viselkedés: a sorkizártnál a bekezdés utolsó sora balra igazított marad, mint a Wordben is.

4. Nem ismeri a RichTextBoxHelper osztályt

// CS0103: The name 'RichTextBoxHelper' does not exist in the current context
Ok és javítás

A segédosztály fájlját nem adtad hozzá a projekthez, vagy az osztály neve eltér a fájlban. Ellenőrizd a Solution Explorerben, hogy a RichTextBoxHelper.cs a projekt része-e, és hogy az osztály neve pontosan RichTextBoxHelper.

5. A megnyitott sorkizárt fájl nem sorkizárt

Egy Wordben sorkizártra állított RTF fájlt nyitsz meg, de a sorok jobb széle egyenetlen.

Ok és javítás

Valószínűleg nem fut le a RichTextBoxHelper.EngedelyezSorkizartot(rtbDokumentum); hívás a Form1_Load eseményben. Ellenőrizd, hogy az eseménykezelő létrejött-e és össze van-e kötve az ablak Load eseményével (0.8 lecke).

Mini projekt: négy igazítás a MiniWordben

Állítsd össze a teljes igazító sort:

  1. Gombok: btnBalra, btnKozep, btnJobbra, btnSorkizart.
  2. A RichTextBoxHelper.cs fájl a projektben van.
  3. A Form1_Load bekapcsolja a speciális megjelenítést.
  4. Írj egy dokumentumot, amelyben mind a négy igazítás szerepel.
  5. Mentsd el RTF fájlként, nyisd meg újra, és nézd meg a Wordben is.

Ha ez mind működik, a MiniWord szövegformázási része kész a feladat összes pontjához.

Önálló gyakorlófeladatok

Először készíts saját megoldást. A lenyitható rész csak ellenőrzéshez van.

1. Üres dokumentum

Ha a dokumentum üres, a Sorkizárt gomb jelezzen, hogy nincs mit igazítani.

Egy lehetséges megoldás
private void btnSorkizart_Click(object sender, EventArgs e)
{
if (rtbDokumentum.Text.Length == 0)
{
MessageBox.Show("Nincs mit igazítani, a dokumentum üres.");
}
else
{
RichTextBoxHelper.Sorkizart(rtbDokumentum);
}

rtbDokumentum.Focus();
}

2. A megnyitott dokumentum

Készíts a Wordben egy sorkizárt RTF dokumentumot, nyisd meg a MiniWordben, és ellenőrizd, hogy a Load beállítás nélkül és vele is hogyan jelenik meg.

Mit tapasztalhatsz?

A Load beállítás nélkül előfordulhat, hogy a megnyitott dokumentum sorkizárása nem látszik, amíg egyszer meg nem nyomod a Sorkizárt gombot. A Load beállítással a megnyitás után azonnal helyesen jelenik meg. Próbáld ki mindkettőt a saját gépeden.

3. Magyarázd el!

Írd le öt-hat mondatban a saját szavaiddal, mit csinál a Sorkizart() metódus: kinek küld üzenetet, mit kér, és miért kellett ehhez a Windows saját függvényét használni.

Egy lehetséges válasz

A metódus egy üzenetet küld a szövegdoboznak a Windows SendMessage függvényével. Az üzenet azt kéri, hogy a kijelölt bekezdés igazítása legyen sorkizárt. Ezt azért kellett így megoldani, mert a .NET RichTextBox SelectionAlignment tulajdonsága nem ismeri a sorkizártat, a mögötte lévő Windows komponens viszont igen.

Gyakorlótár: 6 további feladat

Három szinten gyakorolhatsz: alap, közepes és kihívás. A feladatok a lecke RichTextBoxHelper osztályát használják, ezért előbb másold be a projektbe. Csak a próbálkozás után nyisd le a megoldást.

Alap szint

1. Miért nincs Justify?

A SelectionAlignment tulajdonságnak a HorizontalAlignment felsorolás értékeit adhatod. Milyen értékeket ismer, és mit jelent ez a sorkizárt igazításra nézve?

Megoldás megnyitása

A HorizontalAlignment csak három értéket ismer: Left, Center, Right. A sorkizárt (Justify) nincs közöttük, ezért a SelectionAlignment segítségével nem érhető el. A sorkizárt igazítást a RichEdit komponens saját üzenetével kell kérni, ezt végzi el a lecke segédosztálya.

2. Hol engedélyezed?

A lecke szerint a sorkizárt használata előtt egy engedélyező hívás szükséges. Melyik eseménykezelőbe teszed, és miért oda?

Megoldás megnyitása

A Form1_Load eseménybe, mert az az ablak megjelenése előtt egyszer fut le, így a RichTextBox már a használat előtt fel van készítve. Ugyanez a szabály más egyszeri beállításokra is igaz.

Form1.csC#
private void Form1_Load(object sender, EventArgs e)
{
RichTextBoxHelper.EngedelyezSorkizartot(rtbDokumentum);
}

Közepes szint

3. Sorkizárt gomb

A btnSorkizart gomb állítsa sorkizártra a kurzor bekezdését, és a fókuszt adja vissza a szövegdoboznak.

Megoldás megnyitása

A Focus() azért fontos, mert a gomb megnyomása elvenné a billentyűzet fókuszát a szövegdoboztól, és a felhasználónak újra bele kellene kattintania.

Form1.csC#
private void btnSorkizart_Click(object sender, EventArgs e)
{
RichTextBoxHelper.Sorkizart(rtbDokumentum);
rtbDokumentum.Focus();
}

4. Az egész dokumentum sorkizártra

Egy btnMindSorkizart gomb állítsa az egész dokumentumot sorkizártra, majd a kurzor kerüljön a szöveg elejére.

Megoldás megnyitása

A segédosztály a kijelölt bekezdésekre hat, ezért előbb mindent kijelölünk. Utána megszüntetjük a kijelölést.

Form1.csC#
private void btnMindSorkizart_Click(object sender, EventArgs e)
{
rtbDokumentum.SelectAll();
RichTextBoxHelper.Sorkizart(rtbDokumentum);
rtbDokumentum.Select(0, 0);
rtbDokumentum.Focus();
}

Kihívás szint

5. Négy igazító gomb közös kóddal

A balra, középre, jobbra és sorkizárt gomb használjon egy közös, Igazit nevű metódust, amely a fókuszt is visszaadja a szövegdoboznak.

Megoldás megnyitása

A három hagyományos igazítás a SelectionAlignment tulajdonságot állítja, a sorkizárt a segédosztályt hívja. A közös rész (a fókusz visszaadása) külön metódusba kerül, hogy ne ismétlődjön.

Form1.csC#
private void Igazit(HorizontalAlignment igazitas)
{
rtbDokumentum.SelectionAlignment = igazitas;
rtbDokumentum.Focus();
}

private void btnBalra_Click(object sender, EventArgs e) { Igazit(HorizontalAlignment.Left); }
private void btnKozep_Click(object sender, EventArgs e) { Igazit(HorizontalAlignment.Center); }
private void btnJobbra_Click(object sender, EventArgs e) { Igazit(HorizontalAlignment.Right); }

private void btnSorkizart_Click(object sender, EventArgs e)
{
RichTextBoxHelper.Sorkizart(rtbDokumentum);
rtbDokumentum.Focus();
}

6. Tesztterv a sorkizárthoz

Írj tesztet (lépésenként), amellyel megbizonyosodsz arról, hogy a sorkizárt működik. Mit kell látnod?

Megoldás megnyitása

(1) Írj be vagy illessz be egy hosszú, többsoros bekezdést (legalább 5–6 sor). (2) Tedd a kurzort a bekezdésbe, és kattints a Sorkizárt gombra. (3) Elvárt: a bekezdés sorai a bal és a jobb szélen is egyenesek, a szóközök szélesebbek. A bekezdés utolsó sora nem nyúlik ki a jobb szélig, mert az mindig balra igazított marad. (4) Húzd szélesebbre vagy keskenyebbre az ablakot: a sorok újratördelődnek, de a két szél egyenes marad. (5) Kattints a Balra gombra: a jobb szél újra szabálytalan lesz. (6) Mentsd RTF-be, nyisd meg újra, és nézd meg, hogy a sorkizárt megmaradt-e. Ha valamelyik lépésnél más történik, a hibát jegyezd fel pontosan.

Gyors önellenőrzés

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

1. Miért nem lehet a SelectionAlignment tulajdonsággal sorkizártat beállítani?

Mert a HorizontalAlignment felsorolásnak nincs sorkizárt eleme, a .NET nem teszi elérhetővé a RichEdit komponens ezen képességét.

2. Mit csinál a SendMessage?

Üzenetet küld egy ablaknak vagy vezérlőnek a Windowsban.

3. Mire való a DllImport?

Segítségével a C# kód meghívhat egy DLL-ben (például a user32.dll-ben) lévő Windows függvényt.

4. Mi a Handle?

Az ablak azonosítója a Windowsban: ez alapján tudja a rendszer, melyik ablaknak szól az üzenet.

5. Miért kell a Form1_Load eseménybe a EngedelyezSorkizartot hívás?

Hogy a speciális megjelenítés már az ablak betöltésekor be legyen kapcsolva, így a megnyitott sorkizárt dokumentum is helyesen jelenik meg.

6. Miért marad balra igazított a sorkizárt bekezdés utolsó sora?

Mert a sorkizárt igazítás minden sorra érvényes, kivéve a bekezdés utolsó sorát. A Wordben is így van.

Mit tudsz már?

  • Érted, miért hiányzik a sorkizárt a RichTextBoxból.
  • Ismered az üzenet, a SendMessage, a DLL és a Handle fogalmát.
  • Beépítetted a RichTextBoxHelper segédosztályt.
  • Sorkizártra állítasz egy gombbal.
  • A megnyitott sorkizárt dokumentum is helyesen jelenik meg.
  • A MiniWord mind a négy igazítást tudja.

Következő lecke: szövegkeresés és találatok kiemelése

A formázás kész. A következő leckében a MiniWord megtanul keresni a szövegben, és az összes találatot kiemeli.