Aplikacja wielookienkowa
Aplikacja wielookienkowa może pracować w dwóch trybach:
- w trybie okna nadrzędnego i wielu okien potomnych umieszczonych na pulpicie środowiska Windows
- w trybie okna nadrzędnego i wielu okien potomnych umieszczonych wewnątrz okna nadrzędnego
Rodzaj trybu pracy okna aplikacji wielookienkowej zależy od tego czy wartość this.IsMdiContainer formatki okna nadrzędnego ustawimy na true. Ta właściwość oznacza, że jest zdefiniowane okno nadrzędne- ojciec
Możemy to zrobić w oknie właściwości tworzonej formatki okna (wygodniejsze rozwiązanie).
Lub w kodzie programu w momencie ładowania głównego okna (zdarzenie Load)
Wskazówka:
private void Form1_Load(object sender, EventArgs e)
{
this.IsMdiContainer = true;
}
Układ okien potomnych umieszczonych wewnątrz okna rodzica
Ten tryb pracy uzyskuje się w oknach potomnych gdy w momencie tworzenia okna potomnego przypiszemy właściciela- rodzica
Wskazówka:
f.MdiParent = this;//jest w obszarze okna rodzica
Każde okno, z tak przypisanym rodzicem będzie tworzone wewnątrz okna rodzica
Układ okien potomnych umieszczonych poza oknem rodzica
Ten tryb pracy uzyskuje się w oknach potomnych gdy w momencie tworzenia okna potomnego nie przypiszemy właściciela- rodzica
Wskazówka:
f.MdiParent = null;//nie jest w obszarze okna rodzica
Okna potomne umieszczone są poza obszarem rodzica
Cel: Napisz aplikację wielookienkową zawierającą menu, z którego można otwierać nowe okna.
Krok 1. Układ komponentów
- MenuStrip- sztuk 1
- Form- sztuk co najmniej 3
Uwaga: Komponent Form nie jest dostępny w oknie Przybornika. Nowy formularza (klasa Form) dodaję się z menu Projekt/ Dodaj formularz Windows Forms
Krok 3. Ustalamy nadrzędną formatkę- okno główne
W aktywnej formatce we właściwościach okna wybieramy opcję kontenera MDI (IsMdiContainer ustawiamy na True)
Krok 3. Dodajemy nowe formatki okien
Przechodzimy do opcji Projekt głównego menu środowiska Visual Studio i wybieramy opcję Dodaj formularz Windows Forms
W otrzymanym oknie dialogowym wykonujemy poniższe czynności
Nazwę można zmienić (nie wolno używać spacji), ale wygodniej jest zachować proponowaną.
Czynności powtarzamy dla wszystkich dodawanych okien w tworzonej aplikacji. Prawidłowo wykonanie tego kroku powinno dać widok kolejnych formularzy formatek okien
Krok 4. Tworzymy menu okna głównego
Menu okna głównego posłuży nam do otwierania kolejnych okien. Dodatkowo przypiszemy im klawisze skrótu. W obszarze okna głównego osadzamy komponent MenuStrip i tworzymy menu
Dodanie klawiszy skrótów opcjom menu wykonujemy we właściwościach aktywnego ToolStripMenuItem tak jak pokazuje to poniższa ilustracja
Efekt końcowy przypisanych klawiszy skrótów przedstawi się jak poniżej
Krok 5. Deklaracja zmiennych okien formatek
Do dalszych przewidywanych celów projektowych działania aplikacji wielookienkowej zmiennym nadamy charakter publiczny
Wskazówka:
public partial class Form1 : Form
{
public Form2 form2;
public Form3 form3;
public Form4 form4;
public Form1()
{
InitializeComponent();
}
Krok 6. Wywołanie okna formatki
Jest kilka możliwości rozwiązania utworzenia okna formatki podrzędnej tak zwanego dziecka okna głównego. Wybór metody rozwiązania będzie wpływać na sposób przesyłania danych pomiędzy oknami. Poniżej przedstawię sposób otwarcia jednego konkretnego okna z jednoczesnym zabezpieczeniem przed wielokrotnym powieleniem. Zdarzenie będzie obsługiwane w metodzie Click komponentu ToolStripMenuItem
Wskazówka:
private void Okno1ToolStripMenuItem_Click(object sender, EventArgs e)
{
//otwarcie jednego konkretnego okna
//zabezpiecz przed wielokrotnym powielaniem tworzenia okna
if (form2 == null)
{
form2 = new Form2();
form2.MdiParent = this;
form2.Tag = "okno1";
form2.FormClosed += new FormClosedEventHandler(zwolnijUchwyt);
form2.Show();
}
else form2.Activate();
}
Uwaga. Powyższa metoda zawiera nadpisanie standardowej funkcji zamykającej FormClosed. Jest to konieczne, ponieważ musimy zwolnic uchwyt do zmiennej reprezentującej okno. Jeżeli byśmy tego nie zrobili, to powyższe rozwiązanie zadziałałoby jeden raz, dopóki byśmy nie zamknęli wywołanego okna dziecka w działającej aplikacji.
Postać funkcji, którą musimy nadpisać ma postać (przewidujemy rozwiązanie uniwersalne dla wszystkich utworzonych podokien wywołanych tą metodą):
Wskazówka:
public void zwolnijUchwyt(object sender, FormClosedEventArgs e)
{
Form f =sender as Form;
switch (f.Tag.ToString())
{
case "okno1": form2 = null; break;
case "okno2": form3 = null; break;
case "okno3": form4 = null; break;
}
}
Skompiluj i uruchom aplikację sprawdzając jej działanie.
Krok 7. Uniwersalne wywołanie okna dla metody pierwszej
Zastosowanie uniwersalnej funkcji skraca kod programu. Kod funkcji, którą będziemy mogli zastosować dla dowolnego okna musi zawierać dwa parametry- argumenty. Parametr klasy Form i parametr łańcucha znaków string dla właściwości Tag tworzonego okna.
Wskazówka:
//uniwersalna funkcja otwierająca nowe okno dla metody pierwszej
public void RobOkno(Form f, string tag)
{
f.MdiParent = this;
f.Tag = tag;
f.FormClosed += new FormClosedEventHandler(zwolnijUchwyt);
f.Show();
f.Activate();
}
Użycie powyższej funkcji w obsłudze komunikatu zdarzenia Click (dopinamy je we właściwościach w metodzie Click komponentu ToolStripMenuItem) jest następujące
Wskazówka:
private void Okno2ToolStripMenuItem_Click(object sender, EventArgs e)
{
//zabezpiecz przed wielokrotnym powielaniem tworzenia okna
if (form3 == null) RobOkno(form3 = new Form3(), "okno2");
else form3.Activate();
}
Skompiluj program i sprawdź efekt działania. Prawidłowo działająca na tym etapie aplikacja pozwoli na wielokrotne otwieranie i zamykanie okien dzieci. Przedstawia to poniższa ilustracja
Metoda II wywołania okien w aplikacji wielookienkowej
Ta metoda opiera się na zastosowaniu instancji do klasy okna dziecka. Nie wymaga nadpisywania zdarzenia zamknięcia okna. Zwolnienie uchwytu instancji wykonuje się w każdym oknie z osobna w metodzie FormClosed
Krok 1. Rozbudowanie opcji menu
Menu głównego okna należy rozbudować o kolejne opcje
Krok 2. Dodajemy kolejne formatki okien dzieci
Powtarzamy czynności z kroku 3 metody I
Krok 3. Deklarujemy instancję klasy okna oraz funkcję zwracającą uchwyt do klasy okna
Zmienna instancji klasy musi być prywatna, a funkcja ją zwracająca publiczna. Obie muszą być statyczne, co dla kompilatora oznacza, że nie będą nadpisywane w dalszym rozwijaniu kodu .projektu. Każda nowo dodana klasa okna dziecka powinna zawierać ten kod
Wskazówka:
public partial class Form7 : Form
{
private static Form7 instancja;
public static Form7 Instancja
{
get{
if (instancja == null) instancja = new Form7();
return instancja;
}
}
Krok 4. Zwolnienie uchwytu instancji
Zwolnienie uchwytu instancji wykonuje się w każdym oknie z osobna w metodzie FormClosed
Wskazówka:
private void Form7_FormClosed(object sender, FormClosedEventArgs e)
{
instancja = null;
}
Kompilacja programu na tym etapie nie pozwoli otworzyć dodanych okien ale umożliwi sprawdzenie czy kod nie zawiera błędów
Krok 5. Uniwersalna funkcja tworząca instancję okna
Przechodzimy do zakładki kodu formatki okna głównego. Tworzymy dwuargumentową funkcję tworząca okno dziecka
Wskazówka:
//Uniwersalna funkcja tworząca instancję okna metoda druga
void RobOknoInstancji(Form f,string tag)
{
f.MdiParent = this;
f.Tag = tag;
f.Show();
f.Activate();
}
Zwróć uwagę, że ciało funkcji nie zwiera warunku zabezpieczającego przed wielokrotnym konstruowaniem tego samego okna dziecka. Warunek ten już został w prowadzony w publicznej statycznej funkcji Instancja w kodzie każdej tak dodawanej formatki.
Krok 6. Wywołanie okna poprzez instancję klasy
Użycie powyższej funkcji wymaga obsługę komunikatu zdarzenia Click (dopinamy je we właściwościach w metodzie Click komponentu ToolStripMenuItem)
Wskazówka:
private void OknoInstancja3ToolStripMenuItem_Click(object sender, EventArgs e)
{
RobOknoInstancji(Form7.Instancja, "OknoInstnacj3");
//przykład wypełnienia metody Text instnacji nowego okna
Form7.Instancja.Text = "Okno Instancji 3";
Form7.Instancja.Width = this.Width / 2;
Form7.Instancja.Height = this.Height / 2;
}
Powyższa funkcja zawiera kilka przykładowych odwołań do podstawowych właściwości wywołanych okien dzieci.
Po skompilowaniu i uruchomieniu projektu prawidłowo działająca aplikacja wygląda jak poniżej
Pełny kod pliku okna głównego Form1.cs
Wskazówka:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace wieleOkien
{
public partial class Form1 : Form
{
public Form2 form2;
public Form3 form3;
public Form4 form4;
public Form1()
{
InitializeComponent();
}
private void ZamknijToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Close();
}
private void Okno1ToolStripMenuItem_Click(object sender, EventArgs e)
{
//otwarcie jednego konkretnego okna
//zabezpiecz przed wielokrotnym powielaniem tworzenia okna
if (form2 == null)
{
form2 = new Form2();
form2.MdiParent = this;
form2.Tag = "okno1";
form2.FormClosed += new FormClosedEventHandler(zwolnijUchwyt);
form2.Show();
}
else form2.Activate();
}
//uniwersalna funkcja otwierająca nowe okno dla metody pierwszej
public void RobOkno(Form f, string tag)
{
f.MdiParent = this;
f.Tag = tag;
f.FormClosed += new FormClosedEventHandler(zwolnijUchwyt);
f.Show();
f.Activate();
}
public void zwolnijUchwyt(object sender, FormClosedEventArgs e)
{
Form f =sender as Form;
switch (f.Tag.ToString())
{
case "okno1": form2 = null; break;
case "okno2": form3 = null; break;
case "okno3": form4 = null; break;
}
}
private void Okno2ToolStripMenuItem_Click(object sender, EventArgs e)
{
//zabezpiecz przed wielokrotnym powielaniem tworzenia okna
if (form3 == null) RobOkno(form3 = new Form3(), "okno2");
else form3.Activate();
}
private void Okno3ToolStripMenuItem_Click(object sender, EventArgs e)
{
//zabezpiecz przed wielokrotnym powielaniem tworzenia okna
if (form4 == null) RobOkno(form4 = new Form4(), "okno3");
else form4.Activate();
}
//Uniwersalna funkcja tworząca instancję okna metoda druga
void RobOknoInstancji(Form f,string tag)
{
f.MdiParent = this;
f.Tag = tag;
f.Show();
f.Activate();
}
private void OknoInstancja1ToolStripMenuItem_Click(object sender, EventArgs e)
{
RobOknoInstancji(Form5.Instancja, "OknoInstnacj1");
//przykład wypełnienia metody Text instnacji nowego okna
Form5.Instancja.Text = "Okno Instancji 1";
}
private void OknoInstancja2ToolStripMenuItem_Click(object sender, EventArgs e)
{
RobOknoInstancji(Form6.Instancja, "OknoInstnacj2");
//przykład wypełnienia metody Text instnacji nowego okna
Form6.Instancja.Text = "Okno Instancji 2";
Form6.Instancja.Width = this.Width / 2;
Form6.Instancja.Height = this.Height / 2;
}
private void OknoInstancja3ToolStripMenuItem_Click(object sender, EventArgs e)
{
RobOknoInstancji(Form7.Instancja, "OknoInstnacj3");
//przykład wypełnienia metody Text instnacji nowego okna
Form7.Instancja.Text = "Okno Instancji 3";
Form7.Instancja.Width = this.Width / 2;
Form7.Instancja.Height = this.Height / 2;
}
}
}
Pełny kod jednego z okien potomnych wywoływanych metodą I
Wskazówka:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace wieleOkien
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void Button1_Click(object sender, EventArgs e)
{
//rzutuj na uchwyt ojca
Form1 f = (Form1)this.MdiParent;
if (f.form3 == null) {
f.form3 = new Form3();
f.RobOkno(f.form3, "okno2");
//lub taką konstrukcję
//f.RobOkno(f.form3 = new Form3(), f, "okno2");
}
else f.form3.Activate();
}
private void Button2_Click(object sender, EventArgs e)
{
//rzutuj na uchwyt ojca
Form1 f = (Form1)this.MdiParent;
if (f.form4 == null)f.RobOkno(f.form4 = new Form4(), "okno3");
else f.form4.Activate();
}
}
}
Pełny kod jednego z okien potomnych wywoływanych metodą II (przez instancję)
Wskazówka:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace wieleOkien
{
public partial class Form5 : Form
{
private static Form5 instancja;
public static Form5 Instancja {
get
{
//zabezpiecz przed wielokrotnym tworzeniem klasy okna
if (instancja == null) instancja = new Form5();
return instancja;
}
}
public Form5()
{
InitializeComponent();
}
private void Form5_FormClosed(object sender, FormClosedEventArgs e)
{
//zwolnij uchwyt instancji klasy w momencie zamykania okna
instancja = null;
}
private void Button1_Click(object sender, EventArgs e)
{
Form6.Instancja.MdiParent = this.MdiParent;
Form6.Instancja.Text="Okno II otwarte z okna I";
Form6.Instancja.Show();
Form6.Instancja.Activate();
}
private void Button2_Click(object sender, EventArgs e)
{
Form7.Instancja.MdiParent = this.MdiParent;
Form7.Instancja.Text = "Okno III otwarte z okna I";
Form7.Instancja.Show();
Form6.Instancja.Activate();
}
}
}