Lekce 11 - Kreslení na Canvas v C# .NET WPF
V předešlém cvičení, Řešené úlohy k 6.-10. lekci WPF v C# .NET, jsme si procvičili nabyté zkušenosti z předchozích lekcí.
V dnešním tutoriálu se naučíme kreslit geometrické
tvary Rectangle
pomocí Canvas
v C# .NET
WPF. Začneme aplikaci k evidenci volných a obsazených sedadel v
kině.
Kreslení na Canvas
Vytvoříme aplikaci, která má za úkol zastřešit prodej vstupenek do
kina. Jak víme, v sálu je mnoho sedadel a pracovník kina by měl v aplikaci
vidět, která sedadla jsou již obsazená. Mohlo by nás napadnout naklikat pro
jednotlivá sedadla prvek Button
. Pokud by však kino mělo 15 řad
a každá řada 30 sedadel, máme to 450 prvků Button
. Existuje
lepší cesta, než začít bušit prvky Button1
,
Button2
... A jak by se potom obsluhovaly? V případě, že
potřebujeme vykreslit něco náročnějšího, než jen jeden nebo dva
obrázky, využijeme prvek Canvas
, česky plátno. To spočívá v
tom, že na formulář umístíme pouze jeden
Canvas
a do něj budeme vykreslovat to, co
potřebujeme.
Aplikaci značně zjednodušíme. Bude umět zobrazit jen jeden sál, který
bude zpočátku prázdný. Uživatel nakliká myší obsazená sedadla a poté
stiskne tlačítko Uložit, které do zvolené lokace uloží
jednoduchý txt
soubor s informací o obsazenosti sálu.
Ukládání si zkusíme proto, abychom se naučili pracovat s dialogy.
Návrh formuláře
Vytvořme si novou WPF Aplikaci. Titulek okna nastavme na
Evidence kinosálu
. Oknu nastavíme vlastnost
WindowStartupLocation
na hodnotu CenterScreen
. Tím
zajistíme, že se okno při spuštění aplikace zobrazí ve středu
obrazovky.
Pro návrh formuláře využijeme z předešlých lekcí již jistě dobře
známý prvek Grid
. Vzhled aplikace není nikterak složitý, a tak
si mřížku rozdělíme na dva řádky. Jeden řádek bude obsahovat prvek
Canvas
a ve druhém bude prvek Button
s textem
Uložit
. U tlačítka si ještě přidáme událost
Click
a necháme si vygenerovat metodu v Code Behind, která se
vždy zavolá, když uživatel na tlačítko klikne. Náš formulář by měl
vypadat asi takto:

A zde je jeho odpovídající XAML kód:
<Window x:Class="Kino.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-;;compatibility/2006" xmlns:local="clr-namespace:Kino" mc:Ignorable="d" Title="Evidence kinosálu" WindowStartupLocation="CenterScreen" Height="450" Width="600"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="350"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Canvas Name="MyCanvas" Margin="20 50 0 0"></Canvas> <Button Name="Ulozit" Content="Uložit" Grid.Row="1" Height="20" Width="100" Click="Ulozit_Click"></Button> </Grid> </Window>
Logická vrstva
K aplikaci přidáme třídu Kinosal
. Bude mít jeden privátní
atribut, kterým bude dvourozměrné pole sedadel.
Dvourozměrné pole jsme probírali v lekci Vícerozměrná pole v C# .NET.
2D pole si můžeme představit jako tabulku. Jednorozměrné (klasické) pole je vlastně jen jeden řádek. S 2D polem poté pracujeme úplně stejně, jako s jednorozměrným, jen musíme uvést dvě souřadnice (X a Y). V mnoha jazycích se dělá 2D pole jako pole polí, C# umí definovat přímo 2d pole a to takto:
class Kinosal { private readonly bool[,] sedadla = new bool[30, 15]; }
Sedadla jsou typu bool
, protože nás zajímá jen jestli je
volné nebo obsazené. 30
je šířka pole, 15
jeho
výška.
Do třídy ještě přidáme 2 privátní konstanty:
... private const int velikost = 16; private const int mezera = 2;
Jedna udává velikost vykreslovaného sedadla v pixelech a druhá mezeru mezi sedadly v pixelech. Zvykněme si konstanty používat. Až budeme chtít sedadla zvětšit, stačí pouze přepsat jednu konstantu a nemusíme luštit vykreslovací kód.
Můžeme přejít k metodám.
Vložení obdélníků
Již jsme si zmínili, že budeme kreslit na plátno. Jednotlivé obdélníky
reprezentující sedadla budou jako objekty, které na plátno vložíme pomocí
metody VlozObdelniky()
. Toto plátno je typu Canvas
,
které bude vstupním parametrem metody.
Je potřeba přidat jmenný prostor using System.Windows.Shapes
,
který nám zpřístupní třídu Rectangle
(obdélník). Když
budeme jednotlivé obdélníky vkládat na prvek Canvas
, tak jim
vždy nastavíme šířku a výšku na konstantu velikost
. Dále
nastavíme příslušnou barvu ve vlastnosti Fill
pomocí statické
třídy Brushes
, která obsahuje hotové instance barev.
Visual Studio automaticky generuje jmenné prostory. V naší
aplikaci vygeneruje jmenný prostor using System.Windows.Drawing
,
který je nutné pro správnou funkci aplikace smazat.
Pomocí dvou vnořených cyklů projedeme všechna sedadla v poli a na
plátno vykreslíme buď zelený nebo červený čtverec. Vnějším cyklem
budeme projíždět řádky, vnitřním sloupce v aktuálním řádku. Barvu
(přesněji štětec) určíme podle toho, zda je sedadlo na dané souřadnici
true
nebo false
:
... public void VlozObdelniky(Canvas MyCanvas) { for (int j = 0; j < sedadla.GetLength(1); j++) { for (int i = 0; i < sedadla.GetLength(0); i++) { Rectangle rectangle = new Rectangle { Height = velikost, Width = velikost, }; rectangle.Fill = sedadla[i, j] ? Brushes.Red : Brushes.Green; MyCanvas.Children.Add(rectangle); Canvas.SetLeft(rectangle, i * (velikost + mezera)); Canvas.SetTop(rectangle, j * (velikost + mezera)); } } }
Všimněme si, že v cyklech nepoužíváme hodnoty 30
a
15
, ale používáme metodu GetLength()
s parametry
0
a 1
. Tato metoda slouží pro získání velikosti
dvourozměrného pole. 0
je šířka, 1
je výška
(samozřejmě záleží na nás, kterou dimenzi si určíme jako výšku a
kterou jako šířku).
Pevnou velikost neuvádíme pochopitelně z důvodu, že v
budoucnu můžeme pole zvětšit/zmenšit a museli bychom v kódu hledat kde
všude jsme hodnoty 30
a 15
použili. Těmto
problémům je vždy lepší se vyhnout a pracovat s délkou pole.
Za zmínku stojí i samotné vykreslení obdélníku. Můžeme vidět, že
vykreslení probíhá tak, že se každá instance třídy
Rectangle
přidá jako potomek do našeho prvku
Canvas
. Aby se obdélníky zobrazily přesně tam, kde chceme, tak
musíme ještě nastavit jejich pozici. Využijeme statickou třídu
Canvas
, která obsahuje metody SetLeft()
a
SetTop()
, které představují souřadnice levého horního rohu
obdélníku. Jelikož je každé sedadlo široké 16
pixelů +
2
pixely mezera, musíme jeho souřadnici touho hodnotou
pronásobit. Pokud je v i
např. hodnota 2
(kreslíme
tedy 3. sloupec), kreslíme na X souřadnici 36
, nikoli na
2
To samé platí
pro souřadnici Y.
Propojení formuláře s logickou vrstvou
Základ logiky máme hotový, pojďme ji propojit s formulářem. Přejdeme
do Code Behind formuláře a vytvoříme privátní instanci kinosálu s
modifikací readonly
:
readonly Kinosal kinosal = new Kinosal();
Nyní už je to opravdu velmi jednoduché. Na instanci kinosálu zavoláme
metodu VlozObdelniky()
a jako parametr jí předáme náš
Canvas
, který jsme si definovali ve formuláři XAML. Aby došlo k
vykreslení při startu aplikace, tak tento kód umístíme v Code Behind do
konstruktoru formuláře:
...
public MainWindow()
{
InitializeComponent();
kinosal.VlozObdelniky(MyCanvas);
}
...
Testování
Po spuštění aplikace uvídíme tento výsledek:

V příští lekci, Zpracování kliknutí na obdélník v C# .NET WPF, dokončíme aplikaci pro evidenci kinosálu v C# .NET WPF. Implementujeme označení sedadel kinosálu a jejich uložení do souboru.
Měl jsi s čímkoli problém? Zdrojový kód vzorové aplikace je ke stažení každých pár lekcí. Zatím pokračuj dál, a pak si svou aplikaci porovnej se vzorem a snadno oprav.