====== Jednoduchý kreslící program - obdélník a elipsa ======
https://gitlab.fjfi.cvut.cz/culikzde/pw
===== Upravíme panel s nástroji =====
V našem oknu dosud máme **toolPanel** (typu Panel) a **pictureBox** (typu PictureBox).
Přidáme z toolboxu //Panel//
Nastavíme **BackColor** např. na modrou barvu, přejmenujeme na **colorPanel** (nastavíme vlastnost **(name)** na colorPanel). \\
Pannelů pro výběr barvy potřebujeme více, ale další kopie vytvoříme až programem. \\
Z vizuálního návrhu použijeme jako vzorpvou velikost prvního colorPanelu a vzdálenost od okraje toolPanelu.
{{draw_tools.png}}
Přidáme //ComboBox//
* přejmenujeme na **comboBox** (jména nejsou příliš vynalézavá, alespoň odstaním 1 na konci jména)
* nastavíme **DropDownStyle** na **DropDownList**, aby uživatel nemohl kolonku editovat a mohl jen vybírat z připravebých hodnot
* nastavíme **Anchor** na **Top** a **Right**, při změné velikosti okna si comboBox bude udržovat původní vzdálenost od pravého okraje okolního panelu
{{draw_anchor.png}}
* nastavíme **Items** na line, rectangle a ellipse. Jednotlivé hodnoty zadáme na samostané řádky ( bez dalších oddělovačů, za poslední hodnotou NENÍ žádná prázdná řádka, jinak by vznikla jedna položka bez jména)
{{draw_items.png}}
Nastavit jako počáteční hodnotu na //line// musíme až jedním příkazem v konstruktoru.
Zavedeme si __výčtový typ__ **Shape**. \\
A do comboBox.SelectedIndex uložíme konstantu pro čáru přetypovanou na int (nulu). \\
Hodnoty výčtivého typu se zapisují ve tvaru //jméno typu//, //tečka// a //jméno položky//, např. **Shape.ellipse**. \\
Přetypování se zapisuje jako v jazyce C, např. **(int) n**, ale toto přetypování kontroluje mnohem více situací než v jazyce C.
( Nebo si nadefinujeme tři celočíselné konstanty line, rectangle a ellipse. )
public partial class DrawForm : Form
{
private enum Shape { line, rectangle, ellipse };
// const int line = 0;
// const int rectangle = 1;
// const int ellipse = 2;
public DrawForm ()
{
InitializeComponent ();
comboBox.SelectedIndex = (int) Shape.line;
===== Proměnné pro používanou barvu =====
Deklarujeme proměnnou **pen** typu **Pen** a v této "pracovní tužce" budeme uchovávat barvu pro kreslení čar a okrajů.
Barvu pro vyplňování obrazců uložíme do proměnné **brushColor** typu **Color**.
Pro vyplňování budeme používat "štětec" uložený v proměnné **brush** typu **Brush**.
Proměnné //pen// a //brush// jsou objektového typu. \\
V C# jsou to odkazy (v C ukazatele). \\
( V deklaraci typu nepoužíváme ** * ** jako v C. ) \\
( Také nepoužíváme ** - > **, píšeme jen tečku. )
Ze základní třídy **Brush** je odvozeno několik tříd. My budeme používat **SolidBrush** pro jednobarevnou výplň. \\
Později použijeme výplně s barevnými přechody.
K barvě štětce se již tak snadno nedostaneme, proto ji skladujeme v proměnné **brushColor**.
Typ **Color** je deklarován jako **struct** (nikoliv **class** jako předešlé objektové typy.) \\
V C# **struct** znamená, že hodnoty (v našem případě typu Color) nejsou dostupné přes ukazatel (v C++ by to byl typ s "hvězdičkou" ** Color * ** ), \\
ale jsou uloženy přímo ve vícebytové proměnné (dle velikosti struktury) (v C++ by to byl typ "bez hvězdičky" ** Color ** )
public partial class DrawForm : Form
{
private Pen pen;
private Brush brush;
private Color brushColor;
private enum Shape { line, rectangle, ellipse };
public DrawForm ()
{
InitializeComponent ();
pen = new Pen (Color.Red);
brushColor = Color.Yellow;
brush = new SolidBrush (brushColor);
comboBox.SelectedIndex = (int)Shape.line;
Proměnné pen a brush jsou na úrovni třídy a tak jsou automaticky inicializovány na **null**. \\
My do nich uložíme červenou tužku a žlutý štětec.
**private**, **protected**, **public** platí jen pro následující deklaraci (až do sředníku) \\
( Pokud neuvedene ani jedno klíčové slovo, pro třídy a struktury se použije private. )
Globální proměnné v C# nejsou povoleny. \\
Pro nás mají proměnné na úrovni třídy dobrou vlastnost, že pokud vytvoříme několik instancí okna, tak každé může používat svoji barvu.
===== Nakreslíme obdélník a elipsu =====
Vnitřek elipsy vyplňíme funkcí **FillEllipse**, okraj nakreslíme funkcí **DrawEllipse**
g.FillEllipse (brush, X0, Y0, e.X - X0, e.Y - Y0);
g.DrawEllipse (pen, X0, Y0, e.X - X0, e.Y - Y0);
Proměnná **g** je **Graphics** pripojený na náš **pictureBox.Image**
Prarametry jsou použitá tužka nebo štětec, \\
souřadnice levého horního roku opsanému obdélníku dané elipsy, \\
šířka a výška obdélníku.
Pro záporné šířka a/nebo výšky se nakreslí elipsy, které uživatel nejspíše měl na mysli. \\
( Stisknout myš, táhnout nahoru a/nebo vlevo a potom pustit myš. ) \\
( Souřadná soustava obrázku má počátek v levém horním rohu a osa y míří dolů. )
V případě obdélníku funkce **FillRectangle** a **DrawRectangle** pro záporné výšky a šířky nic nenakreslí. \\
Musíme si počáteční X0,Y0 případně prohodit s koncovým e.X,e.Y \\
( Nesmíme změnit původní X0 a Y0, budeme je potřebovat při kreslení dalšího náhledu na vznikající obdélník. )
Vše se odehrává v **pictureBox_MouseMove**. \\
Pokud je myš stisknuta obnovíme obraz na stav při stisku a podíváme se do **comboBox**u jakým nástrojem máme kreslit.
private int X0, Y0;
private bool press = false;
private Image save;
private void pictureBox_MouseDown (object sender, MouseEventArgs e)
{
X0 = e.X;
Y0 = e.Y;
press = true;
save = new Bitmap (pictureBox.Image);
}
private void pictureBox_MouseMove (object sender, MouseEventArgs e)
{
if (press)
{
Graphics g = Graphics.FromImage (pictureBox.Image);
g.DrawImage (save, 0, 0);
Shape inx = (Shape) comboBox.SelectedIndex;
if (inx == Shape.line)
{
g.DrawLine (pen, X0, Y0, e.X, e.Y);
}
else if (inx == Shape.rectangle)
{
int X1 = X0;
int Y1 = Y0;
int X2 = e.X;
int Y2 = e.Y;
if (X2 < X1) { int t = X1; X1 = X2; X2 = t; }
if (Y2 < Y1) { int t = Y1; Y1 = Y2; Y2 = t; }
g.FillRectangle (brush, X1, Y1, X2 - X1, Y2 - Y1);
g.DrawRectangle (pen, X1, Y1, X2 - X1, Y2 - Y1);
}
else if (inx == Shape.ellipse)
{
g.FillEllipse (brush, X0, Y0, e.X - X0, e.Y - Y0);
g.DrawEllipse (pen, X0, Y0, e.X - X0, e.Y - Y0);
}
pictureBox.Invalidate ();
}
}
private void pictureBox_MouseUp (object sender, MouseEventArgs e)
{
press = false;
}
{{draw_ellipse.png}}
[[https://gitlab.fjfi.cvut.cz/culikzde/pw/-/blob//3677f15e/Draw/DrawForm.cs|stav z tohoto okamžiku na gitlab]]