Table of Contents
Jednoduchý kreslící program - obdélník a elipsa
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.
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
- 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)
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 comboBoxu 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; }