[[pw:draw3]]
 

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 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.

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

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

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 colorPanelu
  • 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;
        }
    }
}

stav z tohoto okamžiku na gitlab

 
pw/draw3.txt · Last modified: 2020/10/27 21:59 by 88.103.111.44
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki