Na zakończenie pierwszej "fali" redagowania o języku C# i poruszaniu podstawowych tematów, przygotowałem na finał artykuł bonusowy. Zawarłem w nim informacje o tworzeniu graficznego interfejsu użytkownika. Istnieje taki framework, który nazywa się "Windows Forms" w języku C#. Co prawda, został już parę lat temu "wymieniony" na "Windows Presentation Foundation" przez sam Microsoft, niemniej jednak warto się o nim co nieco dowiedzieć choćby dla celów ćwiczebnych. Dowiecie się jak utworzyć graficzne okienko w trzech tych samych środowiskach wymienionych na samym początku serii. Poznacie również parę kilka podstawowych komponentów.

"WINDOWS FORMS" W JĘZYKU C# ŻYJE JUŻ KAWAŁ CZASU!

13 lutego tego roku, wymieniony framework do budowy graficznych aplikacji, miał dwudzieste urodziny! Czasy jego świetności już upłynęły, zwłaszcza kiedy wdrożono jego następcę, "Windows Presentation Foundation" w skrócie WPF. Nic nadal nie stoi na przeszkodzie, żeby z niego korzystać do produkcji aplikacji graficznych w czasach obecnych.

Artykuł będzie wyjątkowo rozległy z powodu tłumaczeń jak skompilować aplikację w postaci graficznej w trzech tych samych różnych środowiskach wymienionych przy pierwszym programie: "Visual Studio 2019", "Visual Studio Code" oraz sam wiersz poleceń i kompilacja ręczna. Każda opcja wymaga innego podejścia zarówno przy tworzeniu projektu, jak i samej kompilacji więc nie dziwota, że trzeba na to kolejnych akapitów.

TWORZENIE NOWEGO PROJEKTU GRAFICZNEGO

Zaczynamy od samego początku. Proces tworzenia projektu używającego "Windows Forms" w języku C# nie wygląda tak samo, jak przy aplikacji konsolowej. Trzeba posłużyć się wyspecjalizowanym rodzajem projektu przeznaczonego do obsługi aplikacji graficznych, a choćby z tego powodu żeby nie wyskakiwało zbędne już okienko z terminalem (na cholerę nam one, skoro będziemy mieli okno graficzne?).

VISUAL STUDIO 2019

W "Visual Studio" sprawa jest prosta. Wyrażając chęć utworzenia nowego projektu, robimy przystanek na oknie z listą szablonów projektów. Tym razem patrzymy nie na "Aplikację konsolową", tylko zaznaczamy "Aplikacja Windows Forms" (jest jeszcze drugi szablon o tej samej nazwie z dopiskiem ".NET Framework" w nawiasie, ale prawdopodobnie będzie chodzić tylko na systemach Windows). Gdyby było trudno znaleźć ten szablon, przełączcie sobie filtr najbardziej wysunięty na prawo i wybierzcie "Klasyczny". Struktura docelowa pozostaje bez zmian, ".NET Core 3.1 (Długoterminowe wsparcie)".

Tworzenie projektu "Windows Forms" w "Visual Studio 2019"

Szablon projektu "Windows Forms" na aplikację okienkową w języku C#.

VISUAL STUDIO CODE

"VSCode" także jest w stanie utworzyć dla Was bezboleśnie projekt aplikacji graficznej. Po zainstalowaniu sobie odpowiedniej wtyczki, utworzenia pustego katalogu i przeciągnięcia go do okna programu, otwieramy sobie nowy terminal ("Terminal", "Nowy terminal") i wprowadzamy komendę poznaną podczas pisania pierwszego programu w języku C#, tylko o nieco zmodyfikowanej formie:

dotnet new winforms --name [nazwa]

Wciskacie "Enter" i po chwili zostaną dodane wszystkie zależności jakie będą potrzebne, aby kod "widział" definicje po wstawieniu dyrektywy "using". Bez tego ani rusz!

WIERSZ POLECEŃ

Działanie poprzez gołe CMD jest zmienne tylko w przypadku kompilowania. Mając otwarty wiersz poleceń (najlepiej z uprawnieniami admina), nakazujemy skompilować program wprowadzając komendę o następującej postaci:

csc /t:winexe [nazwa pliku].cs

To sprawi, że plik wykonywalny nie wyświetli już okna konsoli, które w tym przypadku będzie tylko zawadzać i zostanie tylko aplikacja okienkowa.

STARTUJEMY OD ZERA!

Tyle! Od tej pory każdy z Was powinien mieć przygotowany projekt. W zależności od środowiska możecie mieć otwarty albo kod źródłowy, albo edytor wizualny do projektowania aplikacji na kształt WYSIWYG (pewnie w "Visual Studio" tak macie). Ja nie będę prezentował jak umieszczać komponenty (nazywane też "kontrolkami") za pomocą samych kliknięć. Wolę wszystko programować ręcznie dlatego też przykłady będę demonstrował od strony kodowej.

Moja pierwsza prośba jest taka: wywalcie teraz wszystkie pliki źródłowe z rozszerzeniem ".cs" w jakiej formie by nie były prezentowane, i utwórzcie sobie dwa nowe pliki o dowolnej nazwie: jeden, który będzie reprezentował główne okno aplikacji, a drugi który będzie posiadał statyczną metodę "Main". Ma mieć taką postać:

using System.Windows.Forms;

class Program
{
	static void Main(string[] args)
	{
		Application.Run(new MainForm());
	}
}

Metoda statyczna "Run" klasy "Application" jest odpowiedzialna za oficjalne pobudzenie aplikacji graficznej do życia. Framework "Windows Forms" w języku C# ma swoje miejsce w przestrzeni nazw "System.Windows.Forms" i taka też dyrektywa musi się znaleźć u góry klasy.

A jaką postać ma mieć ten drugi plik, to już w kolejnym podpunkcie.

UTWORZENIE NAJPROSTSZEGO OKNA

Jak mieliście okazję się przyjrzeć, potrzebna nam jest dyrektywa "using System.Windows.Forms". Ona przechowuje między innymi klasy odpowiedzialne za komponenty wstawiane do okna aplikacji, takie jak przyciski czy etykiety, które zostaną ukazane w dalszej części artykułu.

Na samym początku zajmijmy się utworzeniem najprostszej struktury okna aplikacji. Oto kod źródłowy, na razie bez żadnych dodatków wpływających na efekt końcowy:

using System.Windows.Forms;

class MainForm : Form
{
	
}

Klasa reprezentująca okno musi dziedziczyć od "Form". To jest okno dialogowe będące punktem startowym każdej nowo powstałej aplikacji graficznej. Kiedy skompilujemy i uruchomimy program w takim stanie, pojawi się surowe okienko nieposiadające nawet tytułu. Brawo. To znak, że nasz program funkcjonuje należycie. Zwróćcie uwagę, że mimo braku jakiegokolwiek kodu dotyczącego wyglądu okna, znajdują się trzy przyciski jakie widzimy na każdym pasku programu obsługującego graficzny interfejs użytkownika: minimalizuj, maksymalizuj i zamknij. Wszystkie trzy działają prawidłowo.

Puste okno

Wygląd pustego okna.

MODYFIKACJA WYGLĄDU OKNA

Na sam początek spróbujemy wpłynąć na wygląd samego okienka modyfikując dla przykładu jego tytuł czy wymiary. Tytuł na górnym pasku zmienimy za pomocą właściwości "Text" (ponieważ przeniosłem się z edycją do klasy potomnej klasy "Form", mogę podać samą nazwę właściwości jaka mnie interesuje, bez żadnych dodatków):

Text = "Moje pierwsze okno w Windows Forms";

Za szerokość odpowiada "Width", a za wysokość właściwość o nazwie "Height":

Width = 640;
Height = 480;

Po ponownym skompilowaniu i uruchomieniu programu, Wasze okienko będzie już znacznie większe i będzie posiadało napis na "listwie" u samej góry.

OKNO KOMUNIKATU

Samo okieneczko to za mały szpan jak na pierwsze poznawanie "Windows Forms" w języku C#. Poznajcie jak się ekspresowo tworzy wyskakujące komunikaty informacyjne! Zrobicie to przy pomocy jednej instrukcji używając statycznej metody "Show" klasy "MessageBox":

MessageBox.Show("To jest treść komunikatu.", "Tytuł komunikatu");

Pierwszy parametr formalny to łańcuch znaków dla treści komunikatu. Drugi parametr także ma być łańcuchem, ale teraz dotyczy tytułu okna komunikatu. Cała filozofia.

ZDARZENIE PO WYJŚCIU Z APLIKACJI

"Application" nie kończy się wyłącznie na metodzie "Run". Możecie wśród innych konstrukcji odnaleźć także zdarzenie "ApplicationExit". Dodając do niego procedurę obsługi zdarzenia (metodę, która musi mieć dwa specyficzne parametry, szczegóły niżej), możecie zaprogramować wykonanie określonego kodu w chwili wychodzenia z programu po kliknięciu krzyżyka. Dla przykładu to:

Application.ApplicationExit += OnExit;

po "podpięciu" metody o następującej definicji:

private void OnExit(object sender, EventArgs ea)
{
	MessageBox.Show("Do widzenia!", "Cześć!");
}

spowoduje wyświetlenie wyskakującego komunikatu. Większość metod mających "robić" za procedurę musi mieć dwa parametry formalne: pierwszy typu "object", a drugi bardziej oryginalny typu "EventArgs" (wymaga dyrektywy "using System"). Pierwszy parametr to "reprezentant" obiektu, który wysłał sygnał czyli od którego nastąpiło zdarzenie. Drugi parametr to klasa bazowa dla klas dysponujących jakimiś danymi na temat zdarzeń. Przydaje się to jako suplement dla zdarzeń, które nie posiadają klasy "EventArgs". Po więcej informacji na ten temat proponuję zajrzeć do dokumentacji, gdyż szczegóły nie będą tu opisywane.

Jeszcze dopiszę krótką informację o konwencji nazewniczej, która zaleca nazywanie procedury obsługi zdarzeń rozpoczynając od słowa "On". Chodzi o odróżnienie metod wykonywanych po staremu od tych wykonywanych pod wpływem zdarzenia.

MENU GŁÓWNE Z POZYCJAMI I PODPOZYCJAMI

Idźmy dalej. Gdy popatrzymy na nasze zajebiste okienko, okaże się że nie ma niczego czym mogłoby zachwycić. Nie ma w sobie śladu interaktywności. "Windows Forms" w języku C# posiada w sobie wiele komponentów graficznych (tzw. "kontrolek"), które umożliwiają bezkarne programowanie interakcji z użytkownikiem. Wśród takich najbardziej podstawowych elementów jest menu główne w postaci "listwy" jaka znajdzie się na samej górze, tuż pod tą listwą systemową.

Przyjrzymy się teraz tworzeniu menu głównego. Menu główne w "Windows Forms" składa się z kilku osobnych elementów. Ta "listwa" to jedno, ale są jeszcze poszczególne pozycje takie jak "Plik" czy "Edycja". Wobec tego poznajemy w tym momencie dwie klasy: "MenuStrip" i "ToolStripMenuItem" (przed ".NET Core" w wersji 3.1 są to klasy "MainMenu" i "MenuItem").

MENUSTRIP

"MenuStrip" to jest obiekt "przechowalnia". Na jego barkach spoczywać będzie trzymanie wszystkich pozycji i podpozycji (jedna może "siedzieć" w drugiej). Najpierw tworzymy sobie obiekt (kod jaki prezentuję zakłada, że zostanie on umieszczony wewnątrz konstruktora klasy "MainForm"):

MenuStrip ms = new MenuStrip();

a potem żeby go ujrzeć w oknie, trzeba skorzystać z metody "Add" właściwości "Controls". Jednym zdaniem to składuje wszystkie elementy umieszczone na oknie:

Controls.Add(ms);

Uruchamiając kod, zobaczymy taki efekt:

Menu główne

Wygląd menu głównego (biała "listwa").

Jak wspomniałem. To jest dopiero sama "listwa". Gdyby było trudno zobaczyć menu, polecam przypisać inny kolor przy użyciu właściwości "BackColor":

BackColor = Color.Red;

Aby uzyskać dostęp do typu wyliczeniowego "Color", należy wstawić dyrektywę "using System.Drawing" u góry kodu.

Jeżeli macie ".NET Core" w wersji starszej niż 3.1, zamiast wywoływania metody "Add" na menu głównym, przypisujecie obiekt do właściwości "Menu":

Menu = mm;
TOOLSTRIPMENUITEM

Przydałoby się wypełnić menu jakimiś pozycjami jakie są dobrze znane użytkownikom różnych aplikacji ("Plik", "Edycja", "Pomoc" itd.). Samo "MenuStrip" nie załatwi sprawy, tu potrzeba nowej instancji typu "ToolStripMenuItem". Za pomocą obiektu klasy "ToolStripMenuItem", możemy utworzyć nową pozycję która będzie nieodłączną częścią menu głównego. Ponadto, będzie od razu reagować na kursor myszy kiedy znajdzie się on nad nazwą tejże pozycji. Wszystko czego potrzeba do szczęścia jest utworzenie sobie nowej instancji typu "ToolStripMenuItem":

ToolStripMenuItem tsmi = new ToolStripMenuItem("Plik");

Zaraz, to nie wszystko! Musimy jeszcze dodać kontrolkę przy użyciu tego samego sposobu. Tym razem to nie będzie właściwość "Controls" klasy "MainForm", tylko właściwość "Items" obiektu typu "MainMenu" (ponieważ "doczepiamy" pozycję do menu głównego, a nie do okna):

mm.Items.Add(tsmi);

Teraz jest dobrze. Odpalcie program i przyglądajcie się nowej pozycji przyczepionej do listwy menu głównego!

Menu główne z podpozycją w "Windows Forms" w języku C#

Wygląd menu głównego z podpozycją "Plik".

Oficjalna dokumentacja Microsoft zawiera znacznie więcej informacji na ten temat więc skieruję Was do odpowiednich kart, jeśli chcecie poznać cały potencjał klas "MenuStrip" i "ToolStripMenuItem".

ETYKIETA

Etykieta to nic innego jak łańcuch znaków znajdujący się na oknie w postaci komponentu. W kodzie źródłowym jest on identyfikowany nazwą "Label". Celem utworzenia sobie takiej etykiety, postępujemy tak samo jak powyżej.

  1. Instancja:
    Label label = new Label();
  2. Przypisanie łańcucha znaków właściwości "Text":
    label.Text = "Moja etykieta";
  3. Dodanie do okna:
    Controls.Add(label);

Już! Uruchamiając powtórnie program, ujrzymy naszą etykietę w oknie:

Etykieta w "Windows Forms" w języku C#

Wygląd etykiety.

Warto jeszcze poznać kilka właściwości, które są łatwe do opanowania a pozwalają całkiem elastycznie dostosowywać wygląd do wymagań projektowych.

AUTOSIZE

Pierwsza rzecz to właściwość "AutoSize", która dostosowuje automatycznie rozmiar etykietki w zależności do obecnie posiadanego łańcucha znaków. Jeśli łańcuch jest mniejszy, to i etykieta stanie się węższa i tak dalej. Przyjmuje boolean'a:

label.AutoSize = true;
POZYCJONOWANIE

Kolejna istotna rzecz to pozycjonowanie. Do edycji pozycji w osi X i Y służą właściwości "Left" i "Top":

label.Left = 32;
label.Top = 64;

Dlaczego takie nazwy właściwości, a nie inne? Ponieważ mają one oznaczać, że to jest pozycja lewego górnego rogu obiektu, a nie jego środek (to obowiązuje każdą kontrolkę "Windows Forms" w języku C#). Jeśli chcemy wyśrodkować naszą etykietę, musimy skorzystać ze wzorów na ręczne wycentrowywanie:

X = (szerokość okna - szerokość elementu) / 2;
Y = (wysokość okna - wysokość elementu) / 2;

Za wymiary okna nieuwzględniające obwódki systemowej, odpowiada właściwość "ClientSize". Ona z kolei zawiera w sobie właściwości "X" oraz "Y". Tyle informacji ode mnie wystarczy żebyście już doskonale wiedzieli jak zrealizować to zadanie. Tutaj kolejność wykonywania kodu gra istotną rolę więc najpierw przypiszcie pożądany łańcuch znaków właściwości "Text" a dopiero potem przypiszcie powyższe wzory do współrzędnych. To nie działa dynamicznie i trzeba za każdym razem to aktualizować w sposób manualny.

BACKCOLOR

"BackColor" odpowiada za kolor tła etykiety (nie samego tekstu, spójrz niżej!). Podstawiając jeden z kolorów za pomocą składowej typu wyliczeniowego "Color" (wymaga dyrektywy "using System.Drawing"):

label.BackColor = Color.Yellow;

możemy swobodnie dostosowywać kolor tła pod etykietą.

FORECOLOR

Chodziło Wam o kolor tekstu? To uderzacie do "ForeColor". To również korzysta z dyrektywy "using System.Drawing":

label.ForeColor = Color.Red;

"enum" jest taki sam jak u poprzednika.

CONTENTALIGNMENT

I jeszcze jeden "property" na dziś. "ContentAlignment", który także korzysta z przestrzeni nazw "System.Drawing". Pozwala on ustawić jedną z dziewięciu "kotwic" treści etykiety na każdy kierunek świata + sam środek. Tak przykładowo wygląda przypisanie celem wyśrodkowania etykiety względem samego jej pola:

label.ContentAlignment = ContentAlignment.MiddleCenter;

Resztę składowych prezentuje ta tabela:

Nazwa Znaczenie
BottomCenter Wyrównanie w kierunku południowym.
BottomLeft Wyrównanie w kierunku południowo-zachodnim.
BottomRight Wyrównanie w kierunku południowo-wschodnim.
MiddleCenter Wyrównanie do środka.
MiddleLeft Wyrównanie w kierunku zachodnim.
MiddleRight Wyrównanie w kierunku wschodnim.
TopCenter Wyrównanie w kierunku północnym.
TopLeft Wyrównanie w kierunku północno-zachodnim.
TopRight Wyrównanie w kierunku północno-wschodnim.

 

Jeśli bardzo Was interesuje temat co jeszcze możemy zrobić za pomocą tej klasy, kliknijcie tutaj aby przejść do dokumentacji Microsoftu.

PRZYCISK

Przycisk, który można kliknąć czyli klasa "Button". Kolejny podstawowy element jakim dysponuje framework "Windows Forms" w języku C#. Cały czas postępujemy tak samo, nic się nie zmienia. Instancja:

Button button = new Button();

Dodanie do okienka:

Controls.Add(button);

...chociaż warto dodać jeszcze przypisanie łańcucha znaków do właściwości "Text":

button.Text = "Kliknij mnie!";

oraz wprowadzić obsługę zdarzeń kliknięcia do zdarzenia "Click" poprzez utworzenie delegata:

button.Click += OnButtonClick;

A to przykładowa metoda:

private void OnButtonClick(object sender, EventArgs ea)
{
	MessageBox.Show("Kliknąłeś przycisk!", "Komunikat");
}

Tak wygląda przycisk w oknie:

Przycisk w "Windows Forms" w języku C#

Wygląd przycisku.

i efekt jego kliknięcia:

Komunikat po naciśnięciu przycisku

Wygląd komunikatu wyskakującego po kliknięciu przycisku.

a więcej informacji znajdziecie w oficjalnej dokumentacji.

POLE TEKSTOWE

Za tworzenie pola tekstowego odpowiada klasa "TextBox". Pole tekstowe służy do wprowadzania do niego dowolnych znaków z klawiatury, tak jak podczas podawania nazwy użytkownika czy hasła:

TextBox tb = new TextBox();

Dodajecie tak samo za pomocą metody "Controls.Add" jednak zanim to uczynicie, możecie skorzystać z właściwości "MultiLine" i "WordWrap". Pierwsza wymieniona zezwoli na wprowadzanie wielu linii tekstu. Wtedy polecam dodatkowo przypisać większą wysokość za pomocą właściwości "Height". Druga sprawi, że długie wyrazy będą przesuwane do następnego wiersza (nazywane jest to również "zwijaniem wyrazów"):

textBox.Multiline = true;
textBox.WordWrap = true;
textBox.Height = 80;

Po zastosowaniu takich zabiegów, uruchomienie programu zostawi taki widok:

Pole tekstowe w "Windows Forms" w języku C#

Wygląd pola tekstowego.

Możecie także zablokować możliwość wprowadzania znaków do pola tekstowego ustawiając właściwość "ReadOnly" na "true". Jak poprzednio - całą resztę na temat "TextBox" znajdziecie w dokumentacji.

LISTA ROZWIJANA

Ostatni komponent jaki mam zamiar przedstawić to lista rozwijana ("ComboBox"). To jest pole z przyciskiem po prawej ze strzałką skierowaną w dół. Po jego kliknięciu rozwija się kilkanaście pozycji i użytkownik wybiera jedną z nich. To tak w telegraficznym skrócie. Wystarczy, że zainteresujecie się jak utworzyć przykładową listę rozwijaną:

comboBox = new ComboBox();

oraz jak za pomocą metody "Add" dodać nową pozycję do listy (tutaj przykładem jest łańcuch znaków, ale w rzeczywistości typem jest "object"):

comboBox.Items.Add("Pozycja #1");

oprócz tego jest "AddRange", która pozwala na dodawanie wielu pozycji przy czym potrzebny jest bardziej złożony zapis, bo teraz oczekiwać będzie tablicy:

comboBox.Items.AddRange(new object[]{"Pozycja #2","Pozycja #3"});

W efekcie końcowym, po odpaleniu programu z powyższymi komendami, ujrzymy taką listę:

Lista rozwijana w "Windows Forms" w języku C#

Wygląd listy rozwijanej.

i jak zwykle, odwołam Was do dokumentacji Microsoftu, gdybyście chcieli poznać szczegóły.


Na tym już zakończę prezentację frameworka "Windows Forms" w języku C#. To nie są wszystkie komponenty / kontrolki jakimi dysponuje. To miało Wam jedynie pokazać jak to działa i jak ogólnie się tym posługiwać. Niniejszy artykuł konkluduje pierwszy cykl artykułów o języku C#.

PODOBNE ARTYKUŁY