====== Jednoduchý kreslící program - výběr barev ======
https://gitlab.fjfi.cvut.cz/culikzde/pw
Přejdeme do "design režimu" (záložka //DrawFrom.cs [design]// nebo //Shift-F7//)
===== Odstranění chyby, pokud se do "design režimu" nelze přepnout =====
Pokud se místo "design" okna ohlásí chyba jako např.
DrawForm does not contain a definition for colorPanel_Paint,
otevřte soubor DrawForm.Designer.cs (např. poklepáním na zprávu o chybě)
a odstraňte řádku odkazující na colorPanel_Paint.
// odstraňte řádku odkazující na již vymazanou funkci (např. colorPanel_Paint)
this.colorPanel.Paint += new System.Windows.Forms.PaintEventHandler(this.colorPanel_Paint);
Chyba nejspíše vznikla tak, že jste v designu dvakrát "kliknuli" na barevný panel,
vznikla funkce void colorPanel_Paint (...) { ... }, kterou jste asi vymazali,
ale v DrawForm.Designer.cs zůstala reakce na kliknutí,
===== Reakce na kliknutí v barevném panelu =====
* Přidáme **ColorDialog** z **Toolboxu**, ze skupiny **Dialogs**
* ColorDialog vhodíme kamkoliv do okna, zástupná ikonka se stejně sama umístí až pod oknem
* Přejmenujeme na **colorDialog** (s malým písmenem na začátku, bez jedničky na konci) (v **Properties** řádka **(name)**)
* Barevný dialog použijeme pro změnu barvy barevného (v našem příkladě modrého) panelu
* Vyberem náš modrý **colorPanel**, v **Properties** vybereme události (events, ikona s bleskem)
* Dvakrát ťukneme do prázdného druhého sloupce na řádce **MouseDown**
* Vznikne prázdná funkce
private void colorPanel_MouseDown (object sender, MouseEventArgs e)
{
}
* Hlavičku takovéto funkce sami nepište, ale vždy "naklikejte" v "design režimu", jinak by chyběla vazba podobná té kterou jsem odstraňovali v minulém odstavci.
* Nyní již můžete, třeba s pomocí schránky, doplnit tělo funkce.
private void colorPanel_MouseDown (object sender, MouseEventArgs e)
{
Panel panel = sender as Panel;
// Panel panel = (Panel) sender;
if (e.Button == MouseButtons.Left && Control.ModifierKeys != Keys.Shift)
{
pen = new Pen (panel.BackColor);
}
else if (e.Button == MouseButtons.Right)
{
brushColor = panel.BackColor;
brush = new SolidBrush (brushColor);
}
else if (e.Button == MouseButtons.Middle || e.Button == MouseButtons.Left && Control.ModifierKeys == Keys.Shift)
{
colorDialog.Color = panel.BackColor;
if (colorDialog.ShowDialog () == DialogResult.OK)
{
panel.BackColor = colorDialog.Color;
pen = new Pen (panel.BackColor);
}
}
}
Parametr **sender** obsahuje odkaz na prvek, který událost způsobil. \\
Nyní to může být pouze náš **colorPanel**, ale později přidáme další panely s jinými barvami. \\
A tak musíme zjistit na který panel bylo kliknuto.
Sender je typu **object**, z kterého jsou odvozené všechny další třídy v jazyce C#. \\
Proměnná typu //object// může obsahovat odkaz (ukazatel) na libovolný objekt. \\
Typ je příliš obecný, neumožňuje nám pracovat např. s barvou panelu. \\
Parametr musíme přetypovat, máme dva možné zápisy.
==== Přetypování (typ) výraz ====
Panel p = (Panel) sender;
Přetypování //**(**typ**)** výraz// je co do zápisu podobné jazyku C, \\
tvoří ho jméno typu v kulatých závorkách a za ním přetypovávaný výraz, který v závorkch být nemusí.
Na rozdíl od jazyka C, pokud za běhu programu nejde výraz přetypovat na uvedený typ, **vznikne výjimka**.
==== Přetypování výraz as typ ====
Panel p = sender as Panel;
Přetypováví //výraz **as** typ// \\
pokud za běhu programu výraz nejde přetypovat na uvedený typ, je výsledkem **null**.
==== Relace výraz is typ ====
if (sender is Panel) { ... }
Relace //výraz **is** typ// \\
vrací **true** pokud lze výraz přetypovat.
==== Levé tlačítko myši - barva pro kreslení čar a okrajů ====
Pokud bylo stisknuto levé tlačítlo myši, vezmeme současnou barvu panelu a vytvoříme "tužku" zadané barvy, odkaz na ni uložíme do proměnné pen
if (e.Button == MouseButtons.Left)
{
pen = new Pen (panel.BackColor);
}
==== Pravé tlačítko - barva výplně ====
Při stisku pravého tlačítka barvu uložíme pro pozdější použití do proměnné **brushColor** a vytvoříme jednobarevný "štětec".
else if (e.Button == MouseButtons.Right)
{
brushColor = panel.BackColor;
brush = new SolidBrush (brushColor);
}
==== Prostřední tlačítko - dialogové okno pro výběr barvy panelu ====
Pokud stiskneme prostřední tlačítko (kolečko na myši) změníme barvu panelu. \\
K tomu použijeme již na začátku kapitoly připravený dialog pro výběr barvy. \\
Metoda **ShowDialog** nabídne uživateli okénko pro výběr barvy, výsledkem funkce je **OK** pokud si uzživatel vybral barvu. \\
Většinou se nedoboručuje funkce s [[https://cs.wikipedia.org/wiki/Vedlej%C5%A1%C3%AD_%C3%BA%C4%8Dinek|vedlejším účinkem]] volat v podmínce if ( ), \\
ale při zobrazení dialogového okna to bývá tolerováno.
if (colorDialog.ShowDialog () == DialogResult.OK)
{
}
Barevný dialog inicializujeme na současnou barvu panelu. \\
Pokud si uživatel vybral barvu, změníme také barvu "tužky", což uživatel nejsíše předpokládá.
else if (e.Button == MouseButtons.Middle)
{
colorDialog.Color = panel.BackColor;
if (colorDialog.ShowDialog () == DialogResult.OK)
{
panel.BackColor = colorDialog.Color;
pen = new Pen (panel.BackColor);
}
}
Pokud myš nemá prostřední tlačítko, můžeme použít Shift a levé tlačítko. \\
Vraťe se k zdrojovému kódu celé metody colorPanel_MouseDown a podívejte se na **Control.ModifierKeys** a **Keys.Shift**.
[[https://gitlab.fjfi.cvut.cz/culikzde/pw/-/blob/3ea205ac/Draw/DrawForm.cs|stav z tohoto okamžiku na gitlab]]
===== Šířka čar =====
Přidáme **NumericUpDown**
* pojmenujeme ho **penWidthNumeric**
* nastavíme **Minimum** na 1
* nastavíme **Maximum** na 10
* "ukotvení" k pravému okraji: vlastnost **Anchor** na **Top, Right**
Můžeme přidat **Label**
* pojmenujeme ho **penWidthLabel**
* **Text** změníme na **pen width**
* **Anchor** nastavíme na **Top, Right**
{{draw_colors.png}}
Dvojklikem na **penWidthNumeric** přídáme metodu
private void penWidthNumeric_ValueChanged (object sender, EventArgs e)
{
pen.Width = (int) penWidthNumeric.Value;
}
Upravíme část programu ve funkci **colorPanel_MouseDown**
if (e.Button == MouseButtons.Left && Control.ModifierKeys != Keys.Shift)
{
pen = new Pen (panel.BackColor, (int) penWidthNumeric.Value);
}
...
else if (e.Button == MouseButtons.Middle || e.Button == MouseButtons.Left && Control.ModifierKeys == Keys.Shift)
{
...
pen = new Pen (panel.BackColor, (int) penWidthNumeric.Value);
}
Pokud Vám nevyhovuje změna barvy tužky při změně barvy panelu, tak poslední přiřazení zakomentujte
Prvek NumericUpDown jako hodnotu může obsahovat i desetinná čísla, a proto musíme Value převédst na int.
Pro připomenutí počáteční červená tužka a žlutý štětec jsou přednastaveny v konstruktoru
pen = new Pen (Color.Red);
brushColor = Color.Yellow;
brush = new SolidBrush (brushColor);
===== Menu ====
* Přidáme **MenuStrip** z toolboxu ze skupiny Menus & Toolbars
* Přejmenujeme na **mainMenu**
Menu se umístí někam na nevhodné místo. \\
Zobrazíme si Document Outline a myší posuneme pořadí prvků. \\
(v hlavním menu Visual Studia **View / Other windows / Document Outline** nebo **Ctrl+Alt+T**) \\
Uvnitř okna DrawForm by mělo být pořadí prvků:
* pictureBox
* toolPanel
* mainMenu
* colorDialog
První by měl být hlavního prvek uprostřed okna, a až za ním další prvky směrem k okraji okna
V právě přidaném menu klikneme do políčka **Type Here**
* Přidáme text **&File**, znak **&** se projeví v podobě podtrženého písmene F a můžeme používat klávesovou zkratku Alt-F.
* Přejmenujeme //fileToolStripMenuItem// na **fileMenu** (jako obvykle v **Properties** řádka **name**)
Pod "File" menu opět nalezneme **Type Here**.
* Přidáme text **&Quit**, znak **&** nám v sub-menu umožní používat písmeno Q pro "zrychlenou volbu".
* Přejmenujeme na **quitMenuItem**
Vpravo od "File" opět nalezneme //Type Here//.
* Přidáme text **&Edit**
* Přejmenujeme **editMenu**
* Můžeme nastavit vlastnost **ShortcutKeys** na **Ctrl+Q** ( po vyplnění malého dialogu opět klikněte do druhého sloupce tabulky, vpravo od ShortcutKey, aby se nastavení neztratilo)
Pod "Edit" menu.
* Přidáme text **add &Color**
* Přejmenujeme na **addColorMenuItem**
* Můžeme nastavit **ShortcutKeys** na **F9**
{{draw_menu.png}}
Ve vytvořeném menu "File" dvakrát klikneme na námi přidanou položku "Quit", vznikne metoda quitMenuItem_Click
private void quitMenuItem_Click (object sender, EventArgs e)
{
Close ();
}
Funkce Close je deklarována uvnitř třídy **Form**, ze které je naše třída **DrawForm** odvozena. \\
Volání Close () je ekvivalentní this.Close () a uzavře naše okno a v tomto případě skončí i celý program.
===== Více barevných panelů =====
Dvakrát klikneme ve vytvořeném menu "Edit" na položku "add Color"
private void addColorMenuItem_Click (object sender, EventArgs e)
{
addColorPanel (Color.Orange);
}
V konstruktoru také přidáme několik barevných panelů
public DrawForm ()
{
InitializeComponent ();
// modry panel jiz existuje
addColorPanel (Color.Yellow);
addColorPanel (Color.Red);
addColorPanel (Color.Green);
pen = new Pen (colorPanel.BackColor); // modra barva podle prvniho panelu
brushColor = Color.Yellow;
brush = new SolidBrush (brushColor);
/* ... */
}
A zbývá napsat funkci **addColorPanel**, tu musíme napsat celou.
private int panelCnt = 1;
void addColorPanel (Color c)
{
Panel p = new Panel (); // vytvorime novy panel
p.BackColor = c;
// novy panel umistime vpravo od soucasnych panelu
p.Left = colorPanel.Left + panelCnt * (colorPanel.Width + colorPanel.Left);
panelCnt++;
p.Top = colorPanel.Top; // ze vzoroveho panelu zkopirujeme souradnici y a velikost
p.Width = colorPanel.Width;
p.Height = colorPanel.Height;
p.MouseDown += colorPanel_MouseDown; // pridame reakci na stisknuti mysi
p.Parent = toolPanel; // pridame do toolPanelu
}
* Vytvoříme nový panel voláním ** new Panel () **
* Ale až ** p.Parent = toolPanel; ** novy panel zobrazi
* Nastavíme různé vlastnosti, např. ** p.BackColor = c; **
* Některé vlastnosti zkopírujeme ze vzorového **colorPanel**u
* Přidáme reakci na stisk myši ** p.MouseDown += colorPanel_MouseDown; **
===== Celý program =====
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Draw
{
public partial class DrawForm : Form
{
private Pen pen;
private Brush brush;
private Color brushColor;
private enum Shape { line, rectangle, ellipse };
public DrawForm ()
{
InitializeComponent ();
// modry panel jiz existuje
addColorPanel (Color.Yellow);
addColorPanel (Color.Red);
addColorPanel (Color.Green);
pen = new Pen (colorPanel.BackColor); // modra barva podle prvniho panelu
brushColor = Color.Yellow;
brush = new SolidBrush (brushColor);
comboBox.SelectedIndex = (int)Shape.line;
int w = pictureBox.Width;
int h = pictureBox.Height;
Bitmap b = new Bitmap (w, h);
Graphics g = Graphics.FromImage (b);
Brush br = new SolidBrush (Color.White);
g.FillRectangle (br, 0, 0, w, h);
pictureBox.Image = b;
}
private void quitMenuItem_Click (object sender, EventArgs e)
{
Close ();
}
private int panelCnt = 1;
void addColorPanel (Color c)
{
Panel p = new Panel (); // vytvorime novy panel
p.BackColor = c;
// novy panel umistime vpravo od soucasnych panelu
p.Left = colorPanel.Left + panelCnt * (colorPanel.Width + colorPanel.Left);
panelCnt++;
p.Top = colorPanel.Top; // ze vzoroveho panelu zkopirujeme souradnici y a velikost
p.Width = colorPanel.Width;
p.Height = colorPanel.Height;
p.MouseDown += colorPanel_MouseDown; // pridame reakci na stisknuti mysi
p.Parent = toolPanel; // pridame do toolPanelu
}
private void addColorMenuItem_Click (object sender, EventArgs e)
{
addColorPanel (Color.Orange);
}
private void colorPanel_MouseDown (object sender, MouseEventArgs e)
{
Panel panel = sender as Panel;
// Panel panel = (Panel) sender;
if (e.Button == MouseButtons.Left && Control.ModifierKeys != Keys.Shift)
{
pen = new Pen (panel.BackColor, (int) penWidthNumeric.Value);
}
else if (e.Button == MouseButtons.Right)
{
brushColor = panel.BackColor;
brush = new SolidBrush (brushColor);
}
else if (e.Button == MouseButtons.Middle || e.Button == MouseButtons.Left && Control.ModifierKeys == Keys.Shift)
{
colorDialog.Color = panel.BackColor;
if (colorDialog.ShowDialog () == DialogResult.OK)
{
panel.BackColor = colorDialog.Color;
pen = new Pen (panel.BackColor, (int) penWidthNumeric.Value);
}
}
}
private void penWidthNumeric_ValueChanged (object sender, EventArgs e)
{
pen.Width = (int)penWidthNumeric.Value;
}
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_butons.png}}
[[https://gitlab.fjfi.cvut.cz/culikzde/pw/-/blob/d87f4a73/Draw/DrawForm.cs|stav z tohoto okamžiku na gitlab]]