Windows Forms to podstawowa biblioteka do konstruowania „klasycznego” interfejsu Windows. U podstaw tej biblioteki można znaleźć pewne elementy kontrolek „wbudowanych” z VB 6.0, pewne składowe JFC czy wiele koncepcji z innych bibliotek przeznaczonych do tworzenia interfejsu użytkownika. W każdym razie wraz z .NET programista otrzymuje bardzo rozbudowaną bibliotekę pozwalającą stworzyć taki interfejs użytkownika, który zadowoli najbardziej wymagającego klienta.
Omówienie wszystkich obiektów Windows Forms jest bardzo trudne – ale tak naprawdę większość obiektów jest albo już dobrze znana (z na przykład z VB 6.0) lub można szybko zapoznać się z nimi już po kilku próbach. Warto jednak wyróżnić kilka nowych elementów, które mogą przydać się przy programowaniu z WinForms.
/RAMKA
Co z kontrolkami ActiveX?
W projektach Windows Forms można także skorzystać z kontrolek ActiveX. Należy tylko... dodać je do paska narzędziowego. VS.NET samo zajmie się wygenerowaniem odpowiedniej warstwy pośredniej (tzw. COM Interopt) która zapewnia komunikację ze starszą technologią. Należy także pamiętać, że nie wszystkie kontrolki mogą być używane w świecie .NET. Problemy dotyczą zwłaszcza tzw. kontrolek windowless. Warto także sprawdzić, czy czasem analogiczna funkcjonalność nie jest realizowana przez jakiś obiekt .NET-owy.
/KONIEC RAMKI
Każda kontrolka Windows Forms ma 2 ciekawe właściwości – Anchor i Dock. Anchor określa te brzegi pojemnika (np. formatki), z którymi krawędzie kontrolki mają zachowywać stałą odległość. Na przykład, jeżeli Anchor dla pola tekstowego będzie równe „Left, Right”, to pole będzie się rozszerzać w miarę jak kontrolka zwiększa swój rozmiar. Właściwość Dock określa sposób dokowanie w obrębie pojemnika kontrolki (np. do górnej krawędzi).
Dokładne opisanie zasad działania Dock oraz Anchor mija się z celem – po kilku próbach każdy programista będzie dokładnie rozumiał, jak one działają. Warto tylko pamiętać o jeszcze jednym obiekcie – pojemniku wprowadzonym w WinFroms – Panel. Kontrolka Panel przypomina kontrolkę GroupBox. Jednak – Panel może zawierać elementy które nie mieszczą sie w zadanym obszarze - zostaną wtedy pokazane paski przewijania. Natomiast GroupBox może mieć tytuł.
Wśród nowych kontrolek warto wymienić kilka:
CheckedListBox – kontrolka, która zawiera listę elementów wraz z opcją wyboru każdego elementu
Splitter – który pozwala zmieniać rozmiar zadokowanej kontrolki (w ten sposób można łatwo stworzyć taki interfejs jak np. w Microsoft Outlook; z listą ikon po lewej stronie)
NotyfiIcon – kontrolka pozwala łatwo wyświetlać własne ikony w pasku koło zegara
OpenFileDialog/PrintDialog - w .NET zamiast jednego dużego obiektu realizującego wszystkie standardowe okna dialogowe Windows, dostępnych jest kilka kontrolek, które odpowiadają za konkretny typ okna.
PrintPreviewControl – kontrolka, która pozwala „podejrzeć” wygląd dokumentu przed jego wydrukiem
W .NET nie jest dostępny specjalny typ formatki MDI. Natomiast dowolnej formatce można ustawić właściwośc IsMdiContainer, co spowoduje, że zacznie zachowywać się jak pojemnik MDI. Warto dodać, że tą właściwość można zmieniać podczas działania aplikacji. Aby inna formatka stała się formatką typu child danej formatki MDI, należy odpowiednio zainicjować właściwość MDIParent, jak w poniższym przykładzie:
Me.IsMdiContainer = True
'[...]
Dim NewMDIChild As New Form1()
NewMDIChild.MDIParent = Me
NewMDIChild.Show()
W przypadku projektu Windows Forms należy wybrać tą formatkę, od której rozpoczyna się wykonywanie programu. Programista VB.NET ma do wyboru – albo w ustawieniach projektu wybiera formatkę startową, albo podaje, że wykonywanie programu ma się rozpocząć od procedury Main (musi być ona zdefiniować w jakimś module). Aby uruchomić główną formatkę, w kodzie Main należy napisać:
Application.Run(New MojaFormatka())
W przypadku kodu w C#, w pierwszej dodanej formatce generowana jest funkcja Main, np:
[STAThread]
static void Main() {
Application.Run(new Form1());
}
W przypadku gdy program ma się rozpocząć od uruchomienia innej formatki – należy odpowiednio zmodyfikować ten kod.
/RAMKA/
Warto zwrócić uwagę, że w metodzie Main, w projekcie Windows Forms w C# definiowany jest atrybut [STA Thread]. Powoduje to, że wiele równoległych wątków musi podlegać rozrządowi (ang. marshal) przed odwołaniem się do dowolnego elementu danego pakietu. Wynika to stąd, że Windows Forms wykorzystuje niektóre obiekty OLE (na przykład – te które służą do obsługi schowka) – a akurat metody związane ze schowkiem muszą być wywoływane z wątku, który zainicjował OLE.
/KONIEC RAMKI/
W VS.NET dostępny jest specjalny typ projektu – Windows Application. Wtedy automatycznie generowany jeszt szkielet programu wykorzystującego Windows Forms. Można także np. do projektu typu Console Application dodać po prostu nowy element – Windows Form.
Po uruchomieniu Visual Studio (lub otworzeniu odpowiedniego elementu projektu), na ekranie pokazany zostanie standardowy (znany z wielu narzędzi RAD) ekran projektanta, w którym można „narysować” odpowiednią formatkę.
Jednak w odróżnieniu od innych narzędzi, to co jest „rysowane” w projektancie przekłada się tak naprawdę na ciąg poleceń w danym języku programowania. Jeżeli np. ustalamy rozmiar pola tekstowego, to VS.NET generuje odpowiedni fragment kodu (w sekcji pliku o nazwie „ Windows Form Designer generated code ”). Poniżej pokazany jest fragment kodu (w VB.NET), który powstaje po wstawieniu pola tekstowego i zmianie tła oraz początkowego tekstu wyświetlanego w tym polu (proszę zwrócić uwagę, że po utworzeniu kontrolek, muszą być one dodane do kolekcji Controls danej formatki)
Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
[...]
Me.TextBox1 = New System.Windows.Forms.TextBox()
Me.SuspendLayout()
'
'TextBox1
Me.TextBox1.BackColor = System.Drawing.SystemColors.InactiveBorder
Me.TextBox1.Location = New System.Drawing.Point(16, 56)
Me.TextBox1.Name = "TextBox1"
Me.TextBox1.TabIndex = 2
Me.TextBox1.Text = "Tekst"
Me.Controls.AddRange(New System.Windows.Forms.Control() {Me.MyTextBox1, [...]})
Me.ResumeLayout(False)
(metody SuspendLayout i ResumeLayout blokują/odblokowują zgłaszanie zdarzenia o zmianie układu elementów na formatce – przyspiesza to proces inicjowania kontrolek)
W .NET obsługa zdarzeń oparta jest o tzw. delegaty (patrz artykuł poświęcony językom C# i VB.NET). Tak więc nie ma tu znanych z VB procedur obsługi zdarzeń o ustalonej nazwie. Delegaty są znacznie wygodniejszym mechanizmem który pozwala programiście dynamicznie dołączać/odłączać się od danego zdarzenia, czy też tworzyć procedury obsługujące wiele różnych zdarzeń. Jest natomiast bardzo ważne aby deklaracja procedury/funkcji odpowiadała danej deklaracji delegatu – oraz oczywiście by została „zarejestrowana” tak by była wywoływana w momencie wystąpienia zdarzenia.
Aby dodać obsługę nowego zdarzenia w projekcie C# należy wybrać w oknie właściwości przycisk odpowiadający za zdarzenia, a następnie wybrać odpowiednią pozycję. Można albo dwukrotnie kliknąć na odpowiednie zdarzenie, albo wpisać własną nazwę funkcji.
Dodawanie zdarzeń w C# #WF1.PNG
W projektach VB.NET, należy (podobnie jak w VB 6) wybrać w oknie kodu odpowiednią kontrolkę, a nastepnie z belki obok – konkretne zdarzenie. VB.NET wygeneruje odpowiednią „prostą” obsługę zdarzenia.
Dodawanie zdarzeń w VB.NET #WF2.PNG
W C#, aby zarejestrować procedurę obsługi zdarzenia (w tym przypadku TextChanged), generowany jest następujący kod:
private void InitializeComponent()
{
this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged);
private void textBox1_TextChanged(object sender, System.EventArgs e) {
Aby usunąć procedurę obsługi zdarzenia, trzeba napisać (operator new nie jest tu pomyłką!):
this.textBox1.TextChanged -= new System.EventHandler(this.textBox1_TextChanged);
W VB.NET są do wyboru dwie opcje. Standardowy projektant tworzy następujący kod
Friend WithEvents MyTextBox As System.Windows.Forms.TextBox
Private Sub TextBox1_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyTextBox.TextChanged
End Sub
Słowo kluczowe Handles określa jakie zdarzenie obsługuje dana procedura. Można je ręcznie zmienić, tak by w jednej procedurze obsługiwane były dwa lub więcej zdarzeń:
[...] Handles MyTextBox.TextChanged, MyTextBox1.TextChanged
Oczywiście – nic nie stoi na przeszkodzie, by zdarzenie MyTextBox.TextChanged było obsługiwane także przez inną procedurę.
Można także dynamicznie określić procedurę obsługi zdarzeń. W VB.NET, trzeba wykorzystać słowo kluczowe AddHandler – które dla danego delegatu (czyli – tego elementu który jest wywoływany w momencie zajścia zdarzenia), przypisuje „adres” procedury obsługi. Można to wykonać np. w procedurze obsługi Load:
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
AddHandler MyTextBox.TextChanged, AddressOf MyOwnProc
Private Sub MyOwnProc(ByVal sender As System.Object, ByVal e As System.EventArgs)
Aby usunąć procedurę obsługi zdarzenia – należy użyć słowa kluczowego RemoveHandler
Poniżej pokazany jest przykład prostej formatki, gdzie dynamicznie tworzonych jest 10 przycisków – każdy z nich ma te same procedury obsługi zdarzenia. W tym przypadku zdefiniowane zostały dwie – jedna wyświetla nazwę a druga tekst przycisku. Warto podkreślić, że nazwa (właściwość Name) kontrolek WinForms nie musi być unikatowa, ani nawet nie musi zostać poprawnie zainicjowana.
Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim i As Integer
Dim btn As System.Windows.Forms.Button
For i = 0 To 10
btn = New System.Windows.Forms.Button()
btn.Location = New Point(5, 5 + i * 25)
btn.Height = 20 : btn.Width = 100
btn.Text = "Przycisk " + i.ToString()
btn.Name = "BTN"
AddHandler btn.Click, AddressOf MyClick
AddHandler btn.Click, AddressOf MyClick1
Me.Controls.Add(btn)
Next
Private Sub MyClick(ByVal sender As System.Object, ByVal e As System.EventArgs)
btn = sender
MsgBox("Nazwa przycisku: " + btn.Name)
Private Sub MyClick1(ByVal sender As System.Object, ByVal e As System.EventArgs)
MsgBox("Tekst na przycisku: " + btn.Text)
Co prawda przestrzeń kontrolek i dostępne obiekty ASP.NET i Windows.Forms znacznie się różnią, ale podstawowa filozofia tworzenia formatek – czy to Windows czy to Web – pozostaje taka sama. W Web Froms tak samo obsługiwane są zdarzenia, podobnie możemy dynamicznie dodawać kontrolki do formatki w czasie działania aplikacji. Nad prawidłowym przekazywaniem zdarzeń pomiędzy przeglądarką klienta a serwerem czuwa .NET Framework (a dokładniej – odpowiednio wygenerowane formularze). Programista, aby obsłużyć akcję „naciśnięcia” przycisku dodaje procedurę obsługi zdarzenia Click – tak samo jak dla przycisku na Windows.Forms.
Kolejną bardzo ciekawą możliwością Windows Forms jest dziedziczenie wizualne formatek. Dzięki temu, można zdefiniować ogólny „szablon” formatki, ze standardową obsługa przycisków czy ustalonym wyglądem, a następnie dziedziczyć tą bazową formatkę w pozostałych elementach projektów – co znacznie upraszcza zachowanie spójności interfejsu w całym projekcie (a przy okazji bardzo upraszcza pracę programisty). Oczywiście po dziedziczeniu, wszelkie zmiany w formatce bazowej są uwzględniane w formatkach dziedziczących (nie tak jak w innych narzędziach, gdzie układ wizualny przy dziedziczeniu jest „kopiowany” do formatki potomnej”)
Poniżej zostanie omówiony przykład zastosowania mechanizmu dziedziczenia formatek. W formatce bazowej zdefiniowane są 2 przyciski (Button1 i Button2; w procedurze obsługi Click wyświetlane są odpowiednie komunikaty)
Public Class frmBase
Inherits...
manitech