C++ WinForms: Zmiana działania przycisku zamykania okna

Zajęło mi to tyle czasu, że postanowiłam się tym z Wami podzielić.

Jeżeli piszemy w C++, korzystając z Windows Forms, to oczywiście wiemy, jak ciężko znaleźć pomoc w Internecie – po prostu NIKT TEGO NIE ROBI. No dobra, może ktoś to robi, ale tych osób nie jest tak dużo, jak tych korzystających z C#.

Może gdybyśmy spytali na stackoverflow, to otrzymalibyśmy jakąś pomoc, ale oczywiście chcemy wszystko tu i teraz, bez ryzyka, że będziemy czekać sto lat na odpowiedź.

Przechodząc do meritum – co jeśli chcemy zmienić kod odpowiadający za zamykanie okna naszej aplikacji? Może się to przydać np. w sytuacji, w której radośnie tworzymy sobie notatnik i potrafimy bez problemu wymusić na programie, żeby po wybraniu opcji „Zakończ” dostępnej w Menu -> Plik, najpierw spytał, czy chcemy zapisać naszą pracę, a dopiero potem zakończył swoje działanie.

Problem pojawia się, gdy ktoś jest leniwy, i nie chce mu się otwierać Menu -> Plik -> Zakończ, tylko po prostu klika na krzyżyk widoczny po prawej stronie, albo w ogóle korzysta ze skrótu klawiszowego Alt + F4, a chce być pewien, że program spyta o zapisanie zmian. Gdyby tylko ludzie nie byli tacy leniwi…

Na tę okoliczność możemy się przygotować, overridując metodę OnClosing. Poniżej pokazuję, jak to dokładnie wygląda, gdy po prostu chcemy, żeby po naciśnięciu krzyżyka lub skrótu Alt + F4 formularz (czyli okno programu) jednak nie zamykał się, a ze stoickim spokojem pozostawał na ekranie:

protected: virtual void OnClosing(CancelEventArgs^ e) override
	{
		e->Cancel = true;
	}

A tutaj już kod odpowiadający za to, żebyśmy mogli wybrać, czy zapisać plik przed zamknięciem, czy nie, a może po prostu nic nie robić i „zawrócić”. To mój pierwszy raz przy opisywaniu kodu dla większej publiczności niż ja i prowadzący zajęcia, więc jeśli coś mogę zrobić lepiej, dajcie znać 🙂

protected: virtual void OnClosing(CancelEventArgs^ e) override
	{
		// wiadomość, która się pokaże w MessageBoxie;
		// nazwa pliku znana (plik był zapisywany
		// lub otwierany wcześniej) bądź domyślna
		String^ message = "Czy zapisać zmiany w pliku " + nazwapliku + " przed zakończeniem?";
		// "nazwa" okna dialogowego
		String^ caption = "Ważne pytanie";
		// wybieramy jakie chcemy przyciski w oknie z zapytaniem - tak, nie, anuluj
		MessageBoxButtons buttons = MessageBoxButtons::YesNoCancel;
		// obiekt który ma przechować odpowiedź
		System::Windows::Forms::DialogResult result;
		result = MessageBox::Show(message, caption, buttons, MessageBoxIcon::Warning);

		//jeśli wybierzemy Yes
		if (result == System::Windows::Forms::DialogResult::Yes)
		{
			// próbujemy zapisać plik
			try
			{
				// jeśli nazwa pliku jest pusta, czyli plik nie był zapisywany
				//ani nie pochodzi z otwarcia
				if (this->nazwapliku == "")
				{
					// miejsce, które otworzy się w oknie dialogowym zapisu
					saveFileDialog1->InitialDirectory = "d:\\";
					// możliwe formaty pliku
					saveFileDialog1->Filter = "Plik tekstowy (txt) (*.txt)|*.txt| Dowolny plik (*.*) | *.*";
					// domyślna nazwa zapisywanego pliku
					saveFileDialog1->FileName = "Nazwa";

					// wybraliśmy ok, plik się zapisuje
					// przy pomocy Writera - obiektu, który będzie "pisał" w pliku
					if (saveFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK)
					{
						try
						{
							this->nazwapliku = saveFileDialog1->FileName;
							System::IO::StreamWriter ^ writer = gcnew
								System::IO::StreamWriter(saveFileDialog1->FileName);
							// tresc - nazwa naszego Textboxa
							writer->Write(tresc->Text);
							writer->Close();
						}

						// wyłapuje ewentualne błędy
						catch (Exception ^ ex)
						{
							MessageBox::Show(ex->Message);
							MessageBox::Show("Zapis zakończył się niepowodzeniem.");
						}
					}
				}

				// jeśli nazwa pliku jest już znana program nadpisuje treść tego pliku
				else
				{
					System::IO::StreamWriter ^ writer = gcnew
						System::IO::StreamWriter(this->nazwapliku);
					writer->Write(tresc->Text);
					writer->Close();
				}

			}
			catch (Exception ^ ex)
			{
				MessageBox::Show(ex->Message);
				MessageBox::Show("Zapis zakończył się niepowodzeniem.");
			};

		}

		// wybieramy nie
		if (result == System::Windows::Forms::DialogResult::No)
		{
			// program zamyka się bez zapisywania
		}

		//wybieramy anuluj
		if (result == System::Windows::Forms::DialogResult::Cancel)
		{
			// rezygnujemy z robienia czegokolwiek i wracamy do naszego pliku tekstowego
			e->Cancel = true;
		}

	}

Rzecz jasna, aby to działało, musimy mieć już wcześniej w naszej aplikacji SaveFileDialog, czyli metodę odpowiedzialną za wywołanie okna dialogowego odpowiedzialnego za zapisywanie pliku.

!!! Kolejna rzecz do zrobienia to to, aby program przed spytaniem pytał, czy coś się w ogóle w tym pliku zmieniło, bo jeśli nie, to po co go zapisywać? Albo jeśli jest nowy i pusty – nie ma potrzeby nic zapisywać, no chyba że komuś bardzo zależy na tym, żeby zapisać sobie pusty plik tekstowy na dysku – to wtedy skorzysta sobie po prostu z Ctrl + S.

To też ciekawa kwestia, jak obsługiwać skróty klawiszowe, korzystając z C++ i Windows Forms? Ale to już temat na kolejny wpis, bo też trochę czasu straciłam na tym, aby do tego dojść.

Jeśli macie jakieś pytania, zauważacie błędy, albo po prostu chcecie pokazać, że żyjecie i czytacie tego bloga – dajcie znak w komentarzu.

Do następnego!

***

Przypominam, że mojego bloga możecie obserwować, nawet nie mając konta na wordpressie. Piszę o tym, bo jest sporo osób, które tu wpadają, ale nie zostawiają po sobie żadnego śladu 🙂 Można to zrobić w prawym dolnym rogu strony, klikając „Obserwuj” i używając swojego adresu email:

obserwacja

Nie zapomnijcie jednak o zaakceptowaniu tej obserwacji, klikając link w mailu, który do Ciebie przyjdzie! W skrzynce gmail może pojawić się w zakładce „Oferty”.

Nie, nie mam 1283 obserwujących. Nie wiem skąd jest ta liczba, serio.


2 myśli w temacie “C++ WinForms: Zmiana działania przycisku zamykania okna

  1. Dobra robota!
    Mogę tylko dodać kilka uwag od siebie, byś mogła trochę poprawić swoje umiejętności:
    1. MessageBoxButtons buttons = MessageBoxButtons::YesNoCancel;
    Ta linijka nie jest Ci potrzebna w takim przypadku, jak tutaj. Lepiej bezpośrednio w Show jako argument podać MessageBoxButton::YesNoCancel, bo w sposób taki, jaki masz, robi się trochę kocioł w kodzie moim zdaniem. O ile teksty (zmienne typu String) warto mieć napisane wcześniej oddzielnie (bo łatwiej się je modyfikuje i czyta), o tyle w przypadku enum nie jest to potrzebne.
    2. Podręczniki są różnie nastawione do instrukcji switch case, ale w tym przypadku – kiedy każdy if zależny jest od wartości zmiennej result – nawet czytelniej będzie zastąpić to switch case’m, a bloki wykonywalne w środku zamienić jakimiś małymi, prywatnymi metodami. W praktyce dosyć często używa się instrukcji switch case ze względu na to, że kod jest po prostu czytelniejszy (zapobiegamy tzw. spaghetti code ;)).
    Serdecznie pozdrawiam i życzę powodzenia na drodze do zostania koderem!

    Polubienie

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj /  Zmień )

Zdjęcie na Google

Komentujesz korzystając z konta Google. Wyloguj /  Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj /  Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj /  Zmień )

Połączenie z %s