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.
