10 Tworzenie własnych okien dialogowych i ich klas


Rozdział 10
Tworzenie własnych okien dialogowych i ich klas
Stosowanie modalnych okien dialogowych do pobierania danych Tworzenie
niemodalnych okien dialogowych
Wymiana i zatwierdzanie danych z okien dialogowych
Zdobywanie wiedzy jak używać i dostosowywać okna dialogowe wymiany i
potwierdzania danych
Tworzenie klasy okna dialogowego
Możemy tworzyć nowe okna dialogowe, które będą wywoływane z głównego okna aplikacji w
odpowiedzi na czynności wykonane przez użytkownika. Dla każdego nowego okna dialogowego
będziemy musieli utworzyć jego szablon i obsługującą je klasę, wyprowadzoną z klasy CDialog.
Szablon okna utworzyć można za pomocą edytora zasobów, natomiast klasę dostarczającą oknu
funkcjonalności przy użyciu CIassWizard. Następnie możemy dodać do owej klasy inne funkcje
obsługujące kontrolki nowego okna, w taki sam sposób jak rozszerzaliśmy możliwości głównego okna
dialogowego.
Okna dialogowe występują najczęściej w formach, które większość z nas dobrze zna. Standardową
procedurą jest wyświetlenie takiego okna z pewnymi ustawieniami początkowymi, pozwalającymi
użytkownikowi dokonywać ich zmian, a następnie zatwierdzić owe zmiany za pomocą przycisku OK
lub też z nich zrezygnować naciskając Cancel. Programista musi pamiętać o tym, iż okno dialogowe
powinno zachowywać lokalną kopię danych, które ulegają zmianie. Gdy użytkownik kliknie OK,
zmiany te zostają wprowadzone w życie, w przeciwnym wypadku nie dzieje się nic.
Typowym przypadkiem okna dialogowego jest okno modalne. Podczas wyświetlania takiego okna
reszta aplikacji jest niedostępna, co zmusza użytkownika do zamknięcia


222 Poznaj Visual C++ 6
okna, zanim odzyska kontrolę nad aplikacją. Okna modalne mogą otwierać kolejne okna, a okno
najwyższe zawsze przejmuje kontrolę, zwracając ją po zamknięciu oknu, z którego zostało wywołane.
Okna niemodalne sÄ… alternatywÄ… w stosunku do okien modalnych. Okna niemodalne nie blokujÄ…
dostępu do aplikacji podczas ich wyświetlania. Pływające paski narzędziowe, takie jak pasek Controls,
są przykładem niemodalnych okien dialogowych.
Istota niemodalnych okien dialogowych zostanie objaśniona nieco dalej w tym rozdziale, jednak
obydwa rodzaje okien wymagają klasy źródłowej, z której pozyskają swoją funkcjonalność, co zostanie
wykorzystane przy tworzeniu szablonu okna.
PATRZ RÓWNIEŻ
• O tworzeniu szablonu okna dialogowego czytaj w rozdziale 3.
Dodawanie szablonu nowego okna dialogowego
Zanim przystąpimy do tworzenia klasy nowego okna, musimy najpierw utworzyć jego
szablon i umieścić w nim kontrolki.
Umieszczenie w projekcie szablonu nowego okna dialogowego
1. Otwórz kartę ResourceView w panelu Project Workspace.
2. Kliknij prawym klawiszem najwyższą pozycję i wybierz z menu Insert, co wywoła okno
Insert Resource (rysunek 10.1).
Inieil Resouice




Resource type:
t^ Acceterator II
Bitmap l gt Curspr
B!
IDD_DIALOGBAfl IDD_FORMVIEW
IDD_OLE_PROPPAGE_LARGE
IDD_OLE_PROPPAGE_SMALL
IDD_PROPPAGE_LARGE
IDD_PRDPPAGE_MEDIUM H
IDD_PROPPAGE_SMALL ig) HTML


03 Icon
Rysunek 10. l. Okno Insert Resource wyświetlające opcje dla szablonu nowego okna dialogowego
3. Wybierz pozycję Dialog, by utworzyć typowe okno dialogowe. Możesz wybrać również inny typ
okna, klikając widoczny obok znak plusa i wybrać typ z listy.
4. Kliknij New, co spowoduje otwarcie szablonu nowego w edytorze zasobów.


Tworzenie własnych okien dialogowych i ich klas______________________223
5. Kliknij prawym klawiszem myszy w obrębie szablonu i wybierz z menu skrótów pozycję Properties.
6. W oknie właściwości wprowadź w polu ID nazwę dla nowego okna.
PATRZ TAKŻE
• O tworzeniu szablonów okien dialogowych czytaj w rozdziale 3.
* Więcej informacji o tworzeniu aplikacji na bazie okien dialogowych znajdziesz w rozdziale 3.
Wyprowadzanie klas z klasy CDialog za pomocÄ… CIassWizard
Po dodaniu szablonu nowego okna dialogowego, możemy umieścić w nim wszelkie potrzebne
kontrolki, a także zmienić właściwości okna. By móc korzystać z nowego okna, należy najpierw
utworzyć dla niego nową klasę, która odpowiedzialna będzie za wyświetlenie okna wraz z kontrolkami
oraz obsługującą komunikaty pochodzące od okna i jego obiektów. Klasa bazowa CDialog zawiera
procedury ładujące i wyświetlające okno dialogowe. Z klasy tej wyprowadzić można własną klasę,
dziedziczÄ…cÄ… te funkcjÄ™. CIassWizard w pewnym stopniu automatyzuje to zadanie.
Tworzenie klasy dla nowego okna dialogowego za pomocÄ… CIassWizard
1. Zaznacz pozycjÄ™ reprezentujÄ…cÄ… na panelu ResourceView nowe okno.
2. Naciśnij CtrI+W lub wybierz pozycję CIassWizard z menu View, by wywołać CIassWizard.
3. CIassWizard automatycznie wykryje nowe okno dialogowe i wyświetli okno Adding a Ciass,
widoczne na rysunku 10.2.
IDD_MYNEWDLG is a new rasource. Sińce ii is
a dialog i&source you probab^i want to oeate a
new das^ for it. You ean alio select an eidsting r- ,
f" Cteale a new ciass f ^eW. an
eniating dass
Rysunek 10.2. CIassWizard wyświetla okno Adding a Ciass po wykryciu nowego okna dialogowego.
4. Kliknij OK, by zaakceptować domyślną funkcję Create a New Ciass.
5. CIassWizard wyświetli kolejne okno dialogowe New Ciass, widoczne na rysunku 10.3.


224 Poznaj Visual C++ 6
:•• CIass information • ••••••••••• •••••••••••••••••• ...•••.•..•••••................ ..........; ?^



: MamÄ™: jCMyNewDlg ;
i • , -.,...........,...."""...". ^"..-...."/.J-;..^."^^,....".". l Cance!


l Fite narne: jMyNewDIg.cpp • •:. l ' """ | . .Change,,. |





l Basectas^ ^Dialog ^j | ,


. Dialog !D; IDDMYNEWDLG j^i III"^!^..^^!^:^
- Automation •••• •••• ••.......•••• .. ........................
................ ....... l f7 None ; ; f" ^utowation :


^r".."",,^..,i. |,:,,,"-,.^...,-, |


Rysunek 10.3. Okno dialogowe New Ciass pozwala nadać nazwę nowej klasie
6. Możesz wprowadzić nazwę dla nowej klasy w polu Name. Obowiązującą w przypadku klas
wyprowadzanych z klas MFC konwencjÄ… jest umieszczanie jako pierwszego znaku nazwy
litery C.
7. Po wprowadzeniu nazwy w polu File name powinna zostać wyświetlona nazwa pliku
implementującego nową klasę. Tę nazwę również możesz zmienić naciskając przycisk
Change.
8. W polu Base Ciass pozostaw wyświetloną pozycję CDialog, dopóki nie zostanie
zaimplementowana strona właściwości.
9. W polu Dialog ID powinien być wyświetlony identyfikator nowo utworzonego okna
dialogowego. Jeśli tak nie jest, możesz wybrać właściwy identyfikator rozwijając listę.
10. Kliknij OK, by zatwierdzić utworzenie nowej klasy wraz z jej plikami implementacyjnym
(.cpp) oraz nagłówkowym (.h).
11. CIassWizard wyświetli następnie kartę Message Maps, co pozwoli na dodanie funkcji
obsługujących zdarzenia związane z oknem dialogowym. Możesz tutaj zdefiniować
funkcje obsługi oraz przypisać zmienne oknu i jego kontrolkom.
12. Gdy zakończysz tworzenie funkcji i przypisywanie zmiennych, kliknij OK, aby zamknąć
CIassWizard.
PATRZ TAKŻE
• WiÄ™cej informacji na temat tworzenia aplikacji na bazie okna dialogowego znajdziesz w
rozdziale 3.
• Aby dowiedzieć siÄ™ wiÄ™cej na temat obsÅ‚ugi komunikatów, przejdź do rozdziaÅ‚u 4.


Tworzenie własnych okien dialogowych i ich klas 225
Inicjalizowanie nowej klasy okna dialogowego
Po utworzeniu nowa klasa powinna być widoczna na liście użytych w projekcie klas, w
panelu CIassYiew. Po kliknięciu widocznego obok znaku plusa, powinniśmy zobaczyć nazwę
konstruktora (o takiej samej nazwie jak nazwa nowej klasy).
Gdy klikniemy dwukrotnie pozycję konstruktora, w oknie edytora powinien znaleźć się
poniższy kod:
CMyNewDlg::CMyNewDlg(CWnd* pParent /*=NULL*/
: CDialog(CMyNewDlg::IDD, pParent)
{
//{(AFX_DATA_INIT(CmyNewDlg)
// CIassMizard zainicjalizuje zmiennÄ…
11}}AFX_DATA_INIT >
Zauważmy, iż zależna od okna dialogowego składowa CMyNewDlg:: IDD zostaje
przekazana konstruktorowi klasy bazowej CDialog. W definicji nowej klasy powinna znaleźć
się definicja tej składowej jako enumerator przyjmujący wartość w postaci identyfikatora okna
dialogowego:
enum ( IDD = IDD_MYNEWDLG } ;
Klasa bazowa CDialog używa tej wartości do załadowania właściwego okna dialogowego,
gdy klasa CMyNewDlg jest już w użyciu. Kiedy za pomocą CIassWizard dodamy nowe
zmienne składowe, pomiędzy komentarzami AFX_DATA_INIT zapisany zostanie kod
inicjalizujÄ…cy te zmienne.
Możemy również dodać zmienne składowe do definicji klasy bez pomocy CIassWizard.
Zmienne te musimy wtedy zainicjalizować w konstruktorze z właściwymi dla nich
wartościami początkowymi.
Kod generowany przez CIassWizard
Podczas modyfikacji kodu generowanego przez CIassWizard należy zachować ostrożność. Kreator ten
może przerwać działanie nie znajdując oczekiwanego identyfikatora. Dodając własny kod
inicjalizujący, należy umieścić również odpowiedni komentarz. :
Wywołując CIassWizard możemy wybrać interesującą nas klasę z listy Ciass Name. Po
dodaniu nowego okna dialogowego do aplikacji utworzonej także na bazie okna dialogowego,
oprócz klasy głównego okna, powinna być widoczna także klasa nowego okna. Za każdym
razem, gdy dodajemy do programu nową zmienną składową lub funkcję obsługi komunikatu
dla kontrolek umieszczonych w oknie, musimy upewnić się, iż z listy Ciass Name
wybraliśmy właściwą pozycję. W przeciwnym wypadku CIassWizard umieści te elementy w
aktualnie wybranej klasie (niekoniecznie tam, gdzie chcemy!).


226_____________________________________Poznaj Visual C++ 6
Wyświetlanie modalnych okien dialogowych
W tym miejscu rozważyć warto, w jaki sposób wywoływane są nowe okna dialogowe. Generalnie
rzecz biorąc, są one otwierane na skutek działania użytkownika, takiego jak wybór pozycji menu lub
kliknięcie przycisku w oknie-rodzicu.
Okno potomne możemy wyświetlić korzystając z funkcji obsługi obiektu sterującego okna-rodzica.
Odbywa się to w dwóch krokach. W pierwszym należy utworzyć model klasy okna. Można to uczynić
poprzez lokalną deklarację obiektu tejże klasy, wpisując w funkcji obsługi następującą linię:
CMyCustomDlg dlgMyCustom(this) ;
W powyższej linii CmyCustomDlg jest klasą okna dialogowego, utworzoną przez CIassWizard.
Konstruktorowi przekazany zostaje specjalny wskaźnik języka C++, this. Ponieważ wywoływana
funkcja obsługi jest składową klasy wyprowadzonej z klasy typu CWnd (jak Cdialog, Cview itd.), nowe
okno otrzyma informacje od właściwego okna-przodka. Jak wiemy z poprzedniego punktu, szablon okna
dialogowego automatycznie inicjalizuje klasÄ™ bazowÄ… Cdialog.
Aby poinformować kompilator o utworzeniu wyprowadzonej klasy, co jest niezbędne podczas
kompilacji modułu ją powołującego (CmyCustomDlg w linii powyżej), musimy pamiętać o dołączeniu
pliku nagłówkowego (.h) na samym początku modułu powołującego, łącząc z rozkazem preprocesora
Å‚include.
Przykładowo, jeśli klasa utworzona dla nowego okna zapisana została w pliku MyCu-stomDIg.h i
dodajemy linię powołującą tę klasę, która odwołuje się do definicji nowej klasy w pliku
implementacyjnym CustomDIg.cpp, powinniśmy na początku tego pliku dopisać linię:
#include "MyCustomDIg.h"
Drugim etapem jest wyświetlenie i rozpoczęcie modalnej operacji okna dialogowego. Należy w tym
celu wywołać funkcję okna dialogowego DoModal (). Wyświetlone zostanie w ten sposób okno
dialogowe wraz z kontrolkami, oczekujące w lokalnej pętli komunikatów. Okno to będzie kierowało
komunikaty kontrolek do własnej implementacji, do czasu zamknięcia go przez użytkownika.
Zamykanie modalnego okna dialogowe za pomocÄ… funkcji EndDialog()
Do zamykania modalnych okien dialogowych powinno się stosować funkcję End-Dialog (), a nie
DestroyKindow (). EndDialog () daje pewność, iż wszystkie zasoby GDI zostaną zwolnione przed
zamknięciem okna. Funkcji EndDialog () użyć można nawet wewnątrz funkcji OninitDialogO, co
spowoduje zamknięcie okna, jeszcze przed jego wyświetleniem.


Tworzenie własnych okien dialogowych i ich klas 227
Jeśli podczas wyświetlania nowego okna dialogowego wystąpi błąd, funkcja DoMo-dal ()
bezzwłocznie zwróci kod w postaci liczby całkowitej -l lub IDABORT. Gdy jednak okno zostanie otwarte
bez przeszkód, funkcja DoModa l () kończy działanie wraz z wywołaniem funkcji EndDialogO, która
powoduje zamknięcie okna dialogowego. Funkcji EndDialogO przekazana zostaje wartość
całkowitoliczbowa, zwrócona przez DoModal() jako kod wyjściowy.
Domyślna implementacja OnOK() oraz OnCancel () powoduje automatyczne wywoływanie funkcji
EndDialogO, wraz z przekazaniem kodów wyjściowych l DOK oraz IDCANCEL zwróconych przez funkcję
DoModal (). Przykładowo, możemy zapisać następujące wywołanie DoModal () dla otwarcia okna
dialogowego:
int nRetCode = dIgMyCustom.DoModal() ;
Kiedy okno dialogowe zostaje zamknięte (zwykle za pomocą przycisku OK lub Can-cel), zwrócony
kod (zwykle IDOK lub IDCANCEL) zostanie przypisany do nRetCode. Powinniśmy w tym miejscu
sprawdzić zawartość nRetCode by wywołać odpowiednią reakcję programu, zależną od czynności
wykonanej przez użytkownika, taką jak modyfikacja danych aplikacji, gdy zwróconą wartością jest
IDOK. Uzyskać to możemy poprzez dokonanie następującego wpisu:
if (nRetCode == IDOK) {
// Zachowaj zmiany ]
Możemy spowodować również programowe zamknięcie okna poprzez wywołanie z własnego kodu
funkcji EndDialog () i przekazanie jej wymaganego kodu.
Dodawanie zmiennej składowej dla zachowywania danych z okna
dialogowego
Większość aplikacji wymaga, aby okna dialogowe były inicjowane, wtedy posiadając kopię
aktualnych danych (lub ustawień). Możliwe jest wówczas dokonywanie w sposób bezpośredni lub
pośredni modyfikacji lokalnej kopii danych, będącej w dyspozycji okna dialogowego. Gdy użytkownik
kliknie przycisk OK, co spowoduje zamknięcie okna, zmiany te powinny zostać wprowadzone w
głównych danych aplikacji.
Oznacza to, iż musimy przekazać obiektowi okna dialogowego bieżące wartości pobrane od
aplikacji przed wywołaniem funkcji DoModal (), a następnie wprowadzić zmiany w wartościach, gdy w
momencie zakończenia działania funkcji DoModal () zostanie spełniony warunek kliknięcia OK.
W wielu wypadkach lokalną kopię danych dla okna dialogowego przechować możemy w
zmiennych (dodanych za pomocą CIassWizard) przypisanych do kontrolek, a następnie pobrać z nich
nowe wartości, gdy okno zamykamy. Przykładowo, możemy wyświetlić proste okno dialogowe, w
którym dokonuje się modyfikacji danych dotyczących


228 Poznaj Visual C++ 6
imienia i nazwiska oraz wieku, za pomocą dwóch pól edycji. Takie przykładowe okno
widzimy na rysunku 10.4.

Rysunek 10.4. Proste okno dialogowe do pobierania informacji
Do pola edycji, w którym wpisywane są dane imienia i nazwiska, przypisać możemy zmienną CString
(m_strName), do drugiego pola natomiast zmienną typu int (m_Age), w której zostanie zapisany wiek
osoby wpisującej. Przypisania dokonać możemy za pomocą CIassWizard i karty Member Yariable dla
klasy okna dialogowego (CMyCustomDlg),jaknarysunku 10.5.
Zmienne inicjujemy po zadeklarowaniu obiektu okna dialogowego, lecz przed wyświetleniem okna.
Oto odpowiedni kod:
CMyCustomDlg dIgMyCustom(this) ;
dlgMyCustom.m_nAge = 31 ;
dlgMyCustom.m_strName = "Jon Bates" ;
digMyCustom.DoModal() ;
Użytkownik może od tej chwili modyfikować wyświetlone wartości, po czym można sprawdzić, czy
funkcja DoModal () zwróciła l DOK oraz odczytać wartości obecnie zapisane w zmiennych
przydzielonych oknu. Wymaga to wpisania następującego kodu:


if (
(digMyCustom.DoModal() == IDOK)
CString strMsg ;
strMsg.Format("New Name
•%s' , AgÄ™ = %d",
dIgMyCustom.m nAge)
dIgMyCustom.m strName
AfxMessageBox(strMsg) ;


Po zakończeniu działania funkcji DoModal (), dzięki powyższemu kodowi, zmieniona zawartość
pola edycji imienia i nazwiska zostanie zapisana jako zawartość dIgMyCustom.m strName, nowo
wprowadzony wiek natomiast w dlgMyCustom.m_nAge. Wartości te zostaną następnie użyte do
sformatowania łańcucha znakowego CString i wyświetlone w oknie komunikatu. W rzeczywistej
aplikacji dane te mogą zostać przekazane do obszaru danych głównych.


Tworzenie własnych okien dialogowych i ich klas 229
aaa


Boj«Ä…
ICuitomDig
CIsss oame;
•^j
jcMyCu:>ornDlg
Add Cja...



c:\...\Cu.!tomDlg\M^Cu3tomD!g h, e:\"
\MyCustofnD!g.cpp ConttoliDs: Type
Membef





1&C_NA
ME
IDCAMCE
L IDOK
CSlling



Desciiption: intwith'/.
yinKWJmVabe',
MaKimumYaiue^







Rysunek 10.5. CIassWizard wyświetlający przydzielone oknu dialogowemu zmienne
PATRZ TAKŻE
• WiÄ™cej o dodawaniu i przydzielaniu obiektom sterujÄ…cym skÅ‚adowych zmiennych mówimy w rozdziale 5.
Wymiana i zatwierdzanie danych z okien dialogowych
Zajmiemy się teraz przekazywaniem wartości zapisanych w zmiennych składowych z oraz do pól
edycji. Jakkolwiek zmienne przydzielamy do kontrolek za pomocÄ… CIassWizard, funkcja okna
dialogowego DoDataExchange () zawierajÄ…ca kod konieczny dla przeprowadzenia wymiany danych
zostaje wygenerowana automatycznie. Funkcja ta odpowiedzialna jest za transfer danych w obu
kierunkach.
Proces transferu danych nosi nazwę Data Exchange i inicjowany jest poprzez wywołanie funkcji
okna dialogowego UpdateDataO. Funkcji tej może zostać przekazany parametr typu Boolean, zwany
b3aveAndValidate. Po przekazaniu FALSE, wartość zapisana w zmiennej składowej zostanie pobrana i
posłuży do ustawienia wartości wyświetlanej w polu edycji. Gdy natomiast przekazany zostanie
parametr TRUE, nastąpi proces odwrotny, czyli pobranie wartości z pola edycji i zapisanie w zmiennej
składowej. Funkcja UpdateData () wywoływana jest automatycznie przez funkcję klasy bazowej
CDialog w celu ustawienia początkowych wartości w polach edycji, po otwarciu okna dialogowego. Inna
funkcja klasy bazowej, onOK() (wywoływana, gdy użytkownik kliknie przycisk OK), powoduje
przekazanie parametru TRUE, co z kolei determinuje przeniesienie danych z pól edycji do zmiennych
składowych.


230 Poznaj Visual C++ 6
Wymiana danych nie ogranicza siÄ™ do okien dialogowych
Technika stosowania funkcji UpdateData () w połączeniu z DoDataExchange () ma zastosowanie nie
tylko w przypadku okien dialogowych. Obie funkcje są składowymi klasy cwnd, co oznacza, że
możemy zaimplementować mechanizm wymiany i zatwierdzania danych dla każdej klasy
wyprowadzonej z cwnd.
Okno dialogowe może również dokonywać zatwierdzania danych wprowadzonych przez
użytkownika. Procedury sprawdzające wykonywane są poprzez wpis dokonany w funkcji
DoDataExchange (), która uruchamiana jest poprzez wywołanie UpdateDa-ta(), gdy jako wartość
parametru b3aveAndValidate przekazana zostanie TRUE. Gdy wprowadzone przez użytkownika dane są
poprawne, UpdateData () zwraca TRUE (a bieżąca implementacja CDialog: :OnOK() zamyka okno
dialogowe). Gdy dane są niewłaściwe, do użytkownika wysłane zostaje ostrzeżenie, a UpdateData
zwraca FALSE.
Stosowanie funkcji Data Exchange (DDX)
Przyjrzyjmy się bliżej wpisowi powodującemu zatwierdzanie danych, przez przeglądnięcie
zawartości funkcji DoDataExchange (). Weźmy na przykład funkcję DoDataExchange () użytą w oknie
dialogowym opisanym wcześniej - jej zapis wygląda następująco:
void CMyCustomDlg::DoDataExchange(CDataExchange* pDX) (
CDialog::DoDataExchange(pDX) ;
//{(AFX_DATA_MAP(CMyCustomDlg)
DDX_Text(pDX, IDC_AGE, m_nAge) ;
DDX_Text(pDX, IDC_NAME, m_strName) ;
//}}AFX_DATA_MAP }
Zauważmy, iż funkcji zostaje przekazany wskaźnik obiektu CDataExchange, (pDX). Obiekt
CDataExchange przechowuje szczegółowe informacje dotyczące kierunku transferu danych oraz okna
docelowego. Wskaźnik ten zostaje dalej przekazany wraz z identyfikatorem pola edycji i nazwą
przydzielonej zmiennej do wywołania funkcji DDX_Text (wygenerowanej przez CIassWizard) w celu
zaimplementowania transferu danych. Istnieje kilka typów funkcji DDX_, służących do obsługi różnych
kontrolek i rodzajów danych. Kilka z najczęściej wykorzystywanych przedstawia tabela 10.1.
CIassWizard umieszcza powyższy kod automatycznie pomiędzy komentarzami AFX_DATA_MAP, gdy
przydzielamy zmienne obiektom sterujÄ…cym za pomocÄ… karty Member Yariable w CIassWizard. Mo-
żemy umieścić własne wpisy, dokonując ich na końcu kodu funkcji DoDataExchange (), za wpisami
dokonanymi przez CIassWizard.


Tworzenie własnych okien dialogowych i ich klas 231
Tabela 10.1. Różne typy funkcji DDX
Nazwa funkcji Typ obiektów sterujących Rodzaje obsługiwanych danych





DDX_Text
DDX_Check
DDX Radio
Pola edycji
Pola wybom
Grupy przełączników alter-
natywnych
BYTE, short, int, UINT, long,
DWORD, String, LPTSTR, float,
double, COleCurrency, COleDa-
teTime
int
int



CString -
CString
CString
CString int
int int
DDX_LBString Listy rozwijane
DDX_LBStringExact Listy rozwijane
DDX_CBString Listy kombinowane
DDX_CBStringExact Listy kombinowane
DDX_LBlndex Indeks listy rozwijanej
DDX_CBlndex Indeks listy kombinowanej
DDX_Scroll paski przewijania





Funkcje powyższe sprawdzają przekazany wskaźnik obiektu CDataExchange i na podstawie jego
składowej m_bSaveAndValidate ustalają kierunek przepływu danych. Dopisując linię
if (pDX-> m_bSaveAndValidate TRUE)
możemy sprawdzić ten znacznik i spowodować wykonanie określonych zadań, w zależności od kierunku
transferu danych.
Funkcje DDX implementują kod WIN32, który ustawia lub pobiera wartości z kontro-lek, używając
komunikatów Windows (pamiętajmy, iż każdy obiekt sterujący jest osobnym oknem). Możemy
zaimplementować własne funkcje DDX_ lub dopisać kod do DoDa-taExchange () dla wywołania
interakcji z kontrolkami okna dialogowego.
Może okazać się konieczne wywołanie funkcji UpdateDataO z własnego kodu, by w ten sposób
wymusić kierunek transferu. Może się tak zdarzyć, gdy implementujemy funkcje obsługujące
komunikaty okien dialogowych, które wykorzystują bieżące wartości wprowadzone do kontrolek.
Przypuśćmy, iż chcemy przechwycić zmiany dokonywane przez użytkownika w polu edycji, by na
bieżąco wyświetlać w pasku tytułowym okna zachodzące zmiany. Moglibyśmy w tym celu użyć
CIassWizard do dodania obsługi powiadomienia pola edycji EN_CHANGE i skorzystać z funkcji
SetWindowText () dla aktualizacji napisu w pasku tytułowym. Jednakże, bez dodatkowego wywołania
UpdateData(TRUE), zwartość przypisanej zmiennej składowej nie będzie uaktualniana wraz ze
zmianami zachodzÄ…cymi w polu


232_____________________________________Poznaj Visual C++ 6
edycji. Poprzez wpisanie odpowiedniego kodu w funkcji obsługi, zyskamy pewność wywołania funkcji
DoDataExchange (), zapisującej zawartość pola edycji w zmiennej CString.
Zatem funkcja obsługi (o nazwie opartej o nazwę przykładowego okna dialogowego) powinna mieć
następujący zapis:
void CMyCustomDlg::OnChangeName()
(
UpdateData(TRUE) ;
SetWindowText(m strName ;
}
Po naciśnięciu klawisza, wywołana zostanie OnChangeName (), a updateData (TRUE) zaktualizuje
zawartość zmiennej składowej m_strName, poprzez wywołanie DDX_Text. Zmieniona zawartość
zmiennej zostanie następnie przesłana do paska tytułowego okna, za pośrednictwem SetWindowText ().
Gdy wywołamy UpdateData (FALSE) , zawartość pól edycji zostanie zastąpiona zawartością
przydzielonych zmiennych składowych. Możemy wykorzystać tę możliwość w celu przywrócenia
domyślnych wartości, gdy użytkownik kliknie przycisk anulowania operacji.
PATRZ TAKŻE
• O różnych rodzajach kontrolek możesz przeczytać w rozdziale 5, 6 oraz 7.
Stosowanie funkcji Data Yalidation (DDV)
Gdy za pomocą CIassWizard przypisujemy kontrolkom okna dialogowego zmienne składowe,
przechowujące dane różnego typu, na przykład CString czy int, musimy jednocześnie dostarczyć
parametry umożliwiające zatwierdzenie wprowadzonych wartości. Parametry te możemy ustalić przez
wywołanie CIassWizard i zaznaczenie na karcie Member Variables docelowej zmiennej, a następnie
wpisanie odpowiednich wartości w okienkach umieszczonych u dołu okna CIassWizard (oznaczonych na
rysunku 10.6).
Dla zmiennych typu CString określić możemy maksymalną liczbę znaków, jaka może znaleźć się w
łańcuchu znakowym wprowadzanym przez użytkownika. Zmienne typu int natomiast mogą posiadać
ograniczenia wpisywanej przez użytkownika wartości. Program oczekiwał będzie na wpisanie wartości
mieszczÄ…cej siÄ™ w wyznaczonym przedziale.


Tworzenie własnych okien dialogowych i ich klas 233





O
^K

MessageMapa
MeflibetYatiabtes Au
Piwd
.

omahon J ACHYS^ Ev'ents J CIassInio.i ^"C^:
AddOas... •-


Cu,^mD!g ^
c:\,.AClBtOBDIa'>MiCuloinOl
B.h. c\.\M>Cu CiirSsIÅ‚BKi:'. Typ

LMrCurtoni&lg -^j """1 1" 1 i AddVaiiabte...



e Merr-ber Oetete Yafiable

!'^

IDC NAWĘ C11! IOCANCEL
IDOK:
'• •:v i:1!:!';;'!*'*!*'' lescrplion, ni
wahyalidaiiópl;^: i^^^
^dmum Vatue: | RS

•r^ rn sttNarne ...,,•..





-y'/'-
:tl.f;'


.•--

Mastnium Value: | [65


:&:'• ' |0^j Ca"cs!



Rysunek 10.6. Ustalanie zakresu dopuszczalnych wartości dla zmiennej typu int
O Wartości dopuszczalne
Kiedy ograniczenia powyższe zostaną wprowadzone, CIassWizard wygeneruje odpowiednie linie
DDV_ i zapisze je w funkcji DoDataExchange (). Dla przykładu, ograniczenia zilustrowane na rysunku
10.6 spowodują wygenerowanie następującej linii:
DDV_MinMaxInt(pDX, m_nAge , 18 65)
Wywołanie funkcji DDV nastąpi bezpośrednio po wywołaniu funkcji DDX, która spowoduje
uaktualnienie wartości zapisanej w zmiennej m_nAge. Uaktualnione dane zostaną poddane
zatwierdzeniu. Jeśli wprowadzona przez użytkownika wartość wykracza poza dopuszczalny zakres
wartości, wyświetlone zostanie ostrzeżenie, a funkcja UpdateData (), inicjująca wymianę danych zwróci
FALSE, co oznacza niepowodzenie procedury zatwierdzenia (rysunek 10.7).
CUSTOMDLG
Please enter an integer between 18 and 65.
Rysunek 10.7. Okno nakazujące użytkownikowi wprowadzenie właściwej wartości
Wygenerowany przez CIassWizard kod służący zatwierdzaniu wprowadzonej liczby znaków
wygląda w ten sposób:
DDV_MaxChars(pDX , m_strName , 15);


234 Poznaj Visual C++ 6
Funkcje zatwierdzające DDV, z których możemy skorzystać, wymienione są w tabeli 10.2. Procedury
w nich zawarte mogą zostać użyte dla każdego typu danych, pobieranych z obiektów sterujących, takich
jak pola edycji, paski przewijania czy też przełączniki alternatywne (opcji). Generalnie rzecz biorąc,
ograniczają one wprowadzane wartości numeryczne w ten sposób, by mieściły się w zadanym zakresie
numerycznym. Funkcje DDV możemy dodawać samodzielnie, musimy być jednak ostrożni i przestrzegać
zasady umieszczania tych wpisów w sekcji AFX_DATA_MAP. W przeciwnym razie, może to zakłócić pracę
CIassWizard.
Tabela 10.2. Standardowe funkcje DDV, generowane przez CIassWizard


Funkcja DDV
Obsługiwany typ Opis
danych


DDV MaxChars

CString

Ogranicza liczbę wprowadzanych znaków

DDV MinMaxByte

BYTE

Ogranicza wartości liczbowe

DDV MinMaxDouble

double

Ogranicza wartości liczbowe

DDVMinMaxDWord

DWord

Ogranicza wartości liczbowe

DDVMinMaxFloat

float

Ogranicza wartości liczbowe

DDVMinMaxInt

int

Ogranicza wartości liczbowe

DDV MinMaxLong

long

Ogranicza wartości liczbowe

DDV MinMaxUnsigned

unsigned

Ogranicza wartości liczbowe

PATRZ TAKŻE
• Informacje o różnych rodzajach kontrolek sÄ… w rozdziaÅ‚ach 5, 6 oraz 7.
Tworzenie własnych funkcji zatwierdzających
W szczególnych przypadkach utworzyć możemy funkcje zatwierdzające, na wzór funkcji DDV_.
Konieczne jest w tym celu napisanie funkcji akceptującej wskaźnik do obiektu CDataExchange (pDX),
zawierającej odwołanie do zmiennej, której zawartość ma zostać zatwierdzona i posiadającej ewentualne
parametry dodatkowe.
Pierwszym warunkiem, jaki spełniać musi taka funkcja, jest sprawdzenie, czy obiekt CDataExchange
znajduje się w trybie zatwierdzania. Sprawdzenie to wykonane może zostać poprzez zbadanie wartości
wyrażenia pDX->m_nbSaveAndValidate, która musi wskazywać TRUE. Jeśli tą zwróconą wartością nie
jest TRUE, oznacza to, że kontrolki zainicjowane są przez zmienne składowe, a procedura zatwierdzania
nie jest konieczna i funkcja może zakończyć działanie. W innym przypadku musimy dokonywać
zatwierdzania wartości zapisanych w zmiennych według założonych kryteriów.


Tworzenie własnych okien dialogowych i ich klas 235
Jeśli wartości będące przedmiotem zatwierdzania zostaną przyjęte, funkcja może zakończyć
działanie w sposób normalny, w przeciwnym razie wyświetlone powinno zostać okno komunikatu.
Funkcja Fail () powinna zostać wywołana poprzez wskaźnik obiektu CDataExchange, w celu
poinformowania wywoływanej funkcji UpdateData (), iż proces zatwierdzania nie powiódł się.
Gdybyśmy na przykład chcieli rozszerzyć funkcjonalność procedury zatwierdzającej z bieżącego
przykładu o wykluczenie możliwości wprowadzenia wartości 31, utworzona funkcja powinna mieć
postać:
void DDV_ValidateAge(CDataExchange* pDX, int& nCheckAge) {
if(pDX->m_bSaveAndValidate &&
(nCheckAge < 18 || nCheckAge > 65 || nCheckAge == 31))
(
AfxMessageBox(
"Ages must be between 18 and 65, but not 31") ;
pDX->Fail() ;
} }
Ponieważ parametr nCheckAge stanowi odwołanie do zmiennej, opcjonalnie możemy dokonać
zresetowania jego wartości do wartości dozwolonej, jeśli wprowadzona przez użytkownika wartość
okazała się niewłaściwa.
Zatwierdzanie danych okien dialogowych
W przypadku umieszczania makr DDV_. . . w nadpisanej funkcji DoDataExchange () powinny one
znaleźć się tuż za makrem DDX_ ... danych składowych, które zatwierdzają. Kompletną listę makr do
wymiany danych zawiera plik nagłówkowy afxdd_.h.
Następnie z funkcji DoDataExchange () wywołana może zostać nasza funkcja DDV_ValidateAge,
po uaktualnieniu zawartości składowej zmiennej przez odpowiednią funkcję DDX. Wywołanie to
powinno mieć postać:
DDV_ValidateAge(pDX, m_nAge) ;
W ten sposób zatwierdzone mogą zostać jedynie wartości z zakresu od 18 do 65, z wyłączeniem 31.
Używanie niemodalnych okien dialogowych
Okna dialogowe niemodalne, w przeciwieństwie do okien modalnych, nie przejmują kontroli nad
działaniem aplikacji. Zamiast tego, używane są zwykle podczas normalnej pracy aplikacji, przekazując
jej z powrotem komunikaty i szczegółowe odpowiedzi. Okna te występują zwykle w postaci
pływających pasków narzędziowych, pozwalając użytkow-


236_____________________________________Poznaj Visual C++ 6
nikowi na dokonanie wyboru i klikanie przycisków w celu dokonania zmian ustawień lub wyboru
narzędzia.
Niemodalne okna dialogowe konstruuje się także za pomocą otwartego w edytorze zasobów
szablonu. Możemy usunąć zbędne w ich przypadku przyciski OK oraz Cancel. Klasy obsługujące te
okna wyprowadzane są również z klasy CDialog. Różnice pomiędzy oknami obu rodzajów polegają na
sposobie ich otwierania i zamykania.
Tworzenie i usuwanie niemodalnego okna dialogowego
Zamiast uruchamiania pętli modalnej za pomocą funkcji DoModal () do wyświetlania modalnego
okna dialogowego, niemodalne okno wyświetlić możemy przy użyciu funkcji Create () klasy CDialog.
Możemy umieścić wywołanie funkcji Create () w konstruktorze wyprowadzonej klasy okna dialogowego
w celu utworzenia egzemplarza klasy, gdy jej obiekt został już utworzony. Możemy również oddzielić
egzemplarz klasy i wywołać funkcję Create w celu dołączenia kodu inicjalizującego. Funkcja Create ()
posługuje się dwoma parametrami. Pierwszym jest identyfikator okna dialogowego, które ma zostać
utworzone, drugi parametr zaś jest opcjonalnym wskaźnikiem do obiektu okna-rodzica (domyślnie
głównego okna aplikacji). Powodzenie procedury utworzenia okna funkcja Create () stwierdza
zwróceniem wartości TRUE, a FALSE w wypadku niepowodzenia.
Gdy funkcja Create () zwraca TRUE, niemodalne okno dialogowe jest już utworzone, lecz nie może
jeszcze być wyświetlone. Aby je ujrzeć, należy wywołać funkcję okna ShowWindow (), przekazując jej
znacznik SW_SHOW.
O ile modalne okno dialogowe może pozostawać w zasięgu jedynie funkcji obsługi, dostęp do okna
niemodalnego musi być możliwy również dla innych części programu. W związku z tym niemodalne
okno dialogowe powinno być tworzone dynamicznie, przez użycie operatora new, a jego adres powinien
być zapisany w globalnym lub składowym wskaźniku.
Okno dialogowe możemy zamknąć stosując operator C++, delete. Destruktor klasy bazowej
CDialog dokona usunięcia obiektu okna.
Możemy wypróbować tę technikę, tworząc za pomocą AppWizard kolejny przykładowy program
bazujÄ…cy na konstrukcji okna dialogowego. Po jego utworzeniu, dodamy szablon nowego, niemodalnego
okna dialogowego, nadajÄ…c mu identyfikator IDD_MODELESS (opis odpowiedniej procedury znajdziemy w
punkcie "Umieszczenie w projekcie szablonu nowego okna dialogowego"). Następnie utworzymy klasę
obsługującą nowe okno, którą nazwiemy CModeless. Operację tę przeprowadzić należy według
algorytmu w podrozdziale "Tworzenie klasy dla nowego okna dialogowego za pomocÄ… CIassWizard".
Po utworzeniu klasa powinna być widoczna na liście klas, w panelu CIassYiew. Gdy klikniemy
widoczny obok znak plusa, w rozwiniętej gałęzi ujrzymy konstruktor funkcji CModeless (). Klikniemy
więc dwukrotnie tę pozycję, by rozpocząć edycję konstruktora. Wprowadzimy następnie treść listingu
10.1, by umożliwić programowi wywoływanie funkcji Create () oraz ShowWindow ().


Tworzenie własnych okien dialogowych i ich klas___________________ 237
Listing 10.1. LST10_1.CPP - dodanie funkcji Create() i ShowWindow() do konstruktora
niemodalnego okna dialogowego
1 CModeless::CModeless(CWnd* pParent /*=NULL*/)
2 : CDialog(CModeless::IDD, pParent)
3 {
4 //((AFX_DATA_INIT(CModeless)
5 // NOTĘ: CIassWizard umieści tu kod inicjalizujący
6 //}}AFX_DATA_INIT
7
8 // ** Utworzenie niemodalnego okna dialogowego
9 if (Create(CModeless::IDD,pParent)) O
10 (
11 // ** Wyświetlenie okna
12 ShowWindow(SW_SHOW); @
13 }
14 }
O Okno dialogowe zostaje utworzone przez funkcjÄ™ Create () © Funkcja
showMindow () powoduje wyświetlenie okna
Dodatkowe linie (8 do 13) ustawiajÄ… okno dialogowe do funkcjonowania jako niemo-dalne. Linia 9
wywołuje funkcję Create () i przekazuje jej wygenerowany przez CIassWizard enumerator CModeless: :
IDD, przechowujący identyfikator okna oraz wskaźnik pParent do okna-rodzica. Gdy funkcja Create ()
poinformuje o powodzeniu swego działania zwracając wartość TRUE, wywołana w linii 12 funkcja
showWindow () ze znacznikiem SW_SHOW spowoduje wyświetlenie okna dialogowego.
Aby otworzyć nowe niemodalne okno dialogowe, powinniśmy umieścić obiekt przycisku w klasie
okna. Przycisk ten otrzyma identyfikator IDC_START_MODELESS i będzie służył do wywoływania okna.
Drugi przycisk natomiast, o identyfikatorze IDC_STOP_ MODELESS będzie to okno zamykał. Obydwa
przyciski widoczne sÄ… na rysunku 10.8.
Rysunek 10.8. Dodane do okna głównego aplikacji przyciski, otwierający i zamykający niemodalne
okno dialogowe
Dodajmy także funkcje obsługi komunikatów BN_CLICKED dla każdego z tych przycisków, a później
kod implementujący owe funkcje, co pozwoli nam przywoływać i zamykać niemodalne okno (listing
10.2). Funkcja OnStartModeless () będzie służyła do otwierania okna, a OnStopModeless () do jego
zamykania.


238_____________________________________Poznaj Visual C++ 6
Listing 10.2. LST10_2.CPP - tworzenie i usuwanie niemodalnego okna dialogowego przez funkcje obsługi
przycisków głównego okna dialogowego
1 // ** Umieszczenie definicji żądanej klasy
2 Å‚include "modeless.h"
3
4 // ** Zadeklarowanie globalnego wskaźnika do obiektu okna niemodalnego
5 CModeless* g_pDlgModeless = NULL; O
6
7 void CMLaunchDlg::OnStartModeless()
8 {
9 // ** Utworzenie nowego okna dialogowego, jeśli globalny wskaźnik
10 // ** wskazuje NULL, co oznacza iż okno dialogowe
11 // ** nie zostało jeszcze utworzone
12 i f (!g_pDlgModeless) @
13 g_pDlgModeless = new CModeless(this); @
14 )
15
16 void CMLaunchDlg::OnStopModeless()
17 {
18 // ** Jeśli wskaźnik niemodalnego okna zawiera wskazanie
19 // ** okno dialogowe może zostać zamknięte i usunięte
20 if (g_pDlgModeless)
21 (
22 delete g pDIgModeless; @
23 g_pDlgModeless = NULL;
24 }
25 }
O Dostęp do globalnego wskaźnika uzyskać można z każdego miejsca w aplikacji, jednak wymagana
może być deklaracja extern
© JeÅ›li okno dialogowe nie jest uaktywnione, utworzone zostaje przez ustanowienie nowego obiektu
klasy CModeless
© Destruktor automatycznie dokona usuniÄ™cia okna
Linia 2 powyższego listingu powoduje wczytanie z pliku modeless.h niezbędnych definicji klasy
CModeless. W linii 5 zadeklarowany zostaje globalny wskaźnik obiektu okna dialogowego CModeless
(g_pDlgModeless) i zainicjalizowany z wartością NULL. Warunkowe sprawdzenie wartości NULL
wskaźnika g_pDlgModeless przeprowadzone zostaje przez funkcję OnStartModeless () w celu
upewnienia się, iż okno nie jest aktywne. Jeśli zostanie to potwierdzone, w linii 13 tworzone jest nowe
okno, które wskazuje poprzez


Tworzenie własnych okien dialogowych i ich klas 239
wskaźnik this gtówne okno aplikacji jako okno-rodzica. Nowe okno dialogowe zostanie wyświetlone
natychmiast po przeprowadzeniu procedury jego utworzenia poprzez funkcje Create () oraz
ShowWindow () zaimplementowane w konstruktorze okna dialogowego.
W wyniku kliknięcia przycisku Stop Modeless w linii 16 następuje wywołanie funkcji
OnStopModeless (). Wartość wskaźnika g_pDlgModeless sprawdza się w linii 20 w celu stwierdzenia, iż
okno dialogowe istnieje (gdy wartość wskaźnika jest różna od NULL). Okno dialogowe zostaje zamknięte
w linii 22 (usunięte przez destruktor). Na koniec w linii 23 wartość wskaźnika ustawiona zostaje jako
NULL, co pozwala na ponowne utworzenie nowego okna dialogowego.
Po skompilowaniu i uruchomieniu aplikacji, będziemy mogli dynamicznie tworzyć i zamykać okno
dialogowe poprzez klikanie przycisków.
PATRZ TAKŻE
• By otrzymać informacje o tworzeniu funkcji obsÅ‚ugi zdarzeÅ„ i komunikatów o kukniÄ™ciu przycisków,
zajrzyj do rozdziału 5.
Przesyłanie i pobieranie danych z niemodalnego okna dialogowego
Ustalanie wartości czy wywoływanie funkcji składowych niemodalnego okna dialogowego
wykonywać możemy w każdym momencie istnienia obiektu okna, uzyskując dostęp do nich poprzez
wskaźnik. Przekazywanie danych pomiędzy kontrolkami okna a przydzielonymi zmiennymi składowymi
odbywa siÄ™ na tych samych zasadach, co w przypadku okien niemodalnych, poprzez funkcje
UpdateDataO oraz DoDataExchange (). Zmiennym składowym możemy nadawać wartości z aplikacji
głównej, a poprzez wywołanie funkcji UpdateData () z parametrem FALSE spowodować transfer
zawartości zmiennych do obiektów sterujących.
Z okna niemodalnego możemy także wywoływać funkcje lub ustalać zawartość zmiennych innych
obiektów aplikacji w odpowiedzi na czynności wykonane przez użytkownika. Aby uzyskać tę
możliwość, musimy przekazać oknu niemodalnemu wskaźnik do żądanego obiektu (lub użyć wskaźnika
globalnego), przez co uzyskuje ono dostęp do obiektu. Konstruktor niemodalnego okna dialogowego jest
doskonałym miejscem na umieszczenie tych wskaźników. Podczas tworzenia okna możemy
modyfikować listę parametrów konstruktora w celu ich umieszczenia. Wskaźniki te przechować można
w lokalnych zmiennych składowych niemodalnego okna dialogowego, dzięki czemu wszystkie jego
funkcje mają dostęp do żądanych obiektów. Musimy także pamiętać o umieszczeniu rozkazu
preprocesora # indu de na poczÄ…tku pliku definiujÄ…cego klasy niemodalnego okna dialogowego, by
kompilator mógł rozpoznać deklaracje zmiennych.
Dodamy zatem do przykładowego programu kod, który zademonstruje opisane wyżej działanie. Do
głównego okna aplikacji dodamy pole listy, a w nim umieścimy komunikaty wysyłane przez dwa nowe
przyciski, umieszczone w niemodalnym oknie dialogowym.


240 Poznaj Visual C++ 6
Pole edycji dodane do tego samego okna pobierać będzie tekst wyznaczony w oknie głównym.
Opisane zmiany spowodują, iż aplikacja będzie wyglądała jak na rysunku 10.9.
•Bl


Start Modeiess
Stop Modclc
OK
Cancel






"POP™ i
"POP" ;:;.
"POP-ilS
"POPIS-POP"
i^ "pop-'si;
"pow"
|:,|: "POW" 1:5
"POW" lis
TOW"
la-fow"
|K "POW"
Rysunek 10.9. Interakcja pomiędzy aplikacją a jej niemodalnym oknem dialogowym
Po pierwsze, musimy zmodyfikować konstruktor niemodalnego okna dialogowego w taki sposób,
aby wskaźnikiem okna-rodzica był CMLaunchDlg, zamiast domyślnego wskaźnika cwnd. Zmianę tę
możemy wprowadzić w definicji konstruktora w pliku nagłówkowym definicji klasy CModeless
(Modeless.h). Wpis ten będzie powodował akceptację wyłącznie wskaźnika CMLaunchDlg, a powinien
mieć następującą postać:
CModeless(CMLaunchDlg* pParent) ; // konstruktor standardowy
Edycję pliku Modeless.h rozpoczynamy dwukrotnym kliknięciem pozycji CModeless na
liście klas w panelu ClassView.
Skoro kompilator jest już poinformowany o zastosowaniu CMLaunchDlg, musimy
jeszcze umieścić na początku pliku Modeless.h następujący rozkaz preprocesora:
Å‚inciude "MLaunchDIg.h"
Do przechowania przekazanego wskaźnika będziemy potrzebowali korespondującej zmiennej
składowej. Dodać ją możemy jako składową chronioną po operatorze protec-ted, wpisując linię:
CMLaunchDlg* m pParent
Aby zapisać przekazany wskaźnik w zmiennej składowej, musimy także zmienić funkcję
konstruktora w taki sposób, aby inicjalizowała wskaźnik z przekazanego parametru pParent. Oto treść
wprowadzonych zmian:
CModeless::CModeless(CMLaunchDig* pParent) : CDialog(CMoldeless::IDD, pParent) , m
pParent(pParent)


Tworzenie własnych okien dialogowych i ich klas 241
Zauważmy, iż okno gtówne pparent może zostać przekazane jako okno-rodzic klasie bazowej
CDialog. pparent można użyć także do zainicjalizowania lokalnej składowej W sekcji m_pParent
(pparent).
W oknie głównym aplikacji umieścić możemy pole listy, w którym wyświetlane będą komunikaty
wysyłane przez modalne okno dialogowe - widać to na rysunku 10.9. Polu temu przydzielić możemy
zmiennÄ… klasy CListBox, m_DisplayList za pomocÄ… kreatora CIassWizard, jak pokazano na rysunku
10.10. Następnie dodajmy do niemodalnego okna dialogowego dwa przyciski i pole edycji. Przyciski
będą uruchamiały dwie funkcje obsługi, wysyłające do pola listy w głównym oknie aplikacji dwa różne
komunikaty. Stosując CIassWizard należy utworzyć owe dwie funkcje, obsługujące komunikaty
BN_CLICKED pochodzące od dwóch nowych przycisków i wyświetlające określone łańcuchy znakowe w
polu listy głównego okna.
Zamykanie niemodalnych okien dialogowych przez funkcjÄ™ DestroyWindow()
Gdy w oknie niemodalnym znajdują się przyciski OK oraz Cancel należy zmienić postać domyślnej
implementacji OnOK() i OnCancel (), ponieważ powodują one wywołanie funkcji EndDialogO , co w
efekcie daje ukrycie okna, zamiast jego destrukcji. Aby osiągnąć właściwy rezultat, należy
spowodować wywołanie funkcji DestroyWindow (), które może nastąpić z OnOK () lub OnCancel ().
Dodajmy zatem następujący kod:
void CModeless::OnPop()
(
m pParent ->m_DisplayList.AddString("**POP**") ;
} void CModeless::OnPow ()
(
m_pParent ->m_DisplayList.AddString("**POW**") ;
}
Nowe funkcje wykorzystują nowy, osadzony wskaźnik CMLaunchDlg, (m_pParent) w celu
uzyskania dostępu do zmiennej głównego okna aplikacji, m_DisplayList. Funkcja pola listy AddString ()
umieszcza następnie w tym polu łańcuch znakowy informujący, który z przycisków kliknął użytkownik
w niemodalnym oknie dialogowym.
Aby zademonstrować możliwości dostępu do niemodalnego okna dialogowego z głównego okna
aplikacji, umieścimy w oknie niemodalnym pole edycji, za pomocą edytora zasobów. Następnie,
korzystając z CIassWizard, dodać możemy zmienną składową CString, m_DispMsg, którą przydzielimy
polu edycji.


242 Poznaj Visual C++ 6
Kiedy stosować systemowe modalne okna dialogowe
Jedna z opcji stylu okna dialogowego umożliwia utworzenie systemowego modalne-go
okna dialogowego. Zapobiega to możliwości przeniesienia się użytkownika do innego okna
lub aplikacji przed zamknięciem okna dialogowego. Okien tego typu należy używać
jedynie w przypadku okien alarmujących, że operacje wykonane przez inną aplikację
mogłyby uszkodzić konfigurację systemu.
Dostęp do owej zmiennej uzyskać można z każdego miejsca aplikacji, gdy niemodalne
okno jest aktywne poprzez wskaźnik g_pDlgModeless. Aby wyświetlić tekst w polu edycji,
należy wymusić transfer danych, wywołując funkcję UpdateDataO ze znacznikiem FALSE.
Wpisać należy, co następuje:
if (g_pDlgModeless) // Upewnienie siÄ™, czy okno dialogowe jest aktywne {
g pDIgModeless ->m DispMsg=CString ("I'm a modeless dialog") ;
g_pDlgModeless ->UpdateData(FALSE) ;
}
PATRZ TAKŻE
• O tworzeniu funkcji obsÅ‚ugi zdarzeÅ„ / komunikatów o kukniÄ™ciu przycisków czytaj w rozdziale
5.
t Więcej szczegółów o stosowaniu pól listy uzyskasz w rozdziale 6.
Obsługa komunikatu o zamknięciu niemodalnego okna dialogowego
Pozostała jeszcze jedna rzecz do omówienia w związku ze stosowaniem niemodalnych okien
dialogowych. Pomimo iż usunęliśmy z niego przyciski OK oraz Cancel, w prawym górnym rogu okna
pozostał przycisk zamykający z widocznym nań krzyżykiem. Jeśli zamkniemy niemodalne okno za
pomocą tego przycisku, okno zostanie zamknięte, lecz jeśli aplikacja nie przechwyci komunikatu,
pozostawiony zostanie globalny wskaźnik do okna oraz zajęty obszar pamięci. Może to spowodować
niedostatek pamięci, co z kolei będzie przyczyną problemów z otwarciem nowego niemodalnego okna
już po zamknięciu i poprzedniego.
Aby rozwiązać ten problem, konieczne jest przechwycenie komunikatu WM_CLOSE, by możliwe
było usunięcie obiektu okna i wyzerowanie wskaźnika. Za pomocą okna New Windows Message and
Event Handlers możemy utworzyć funkcję obsługi komunikatu f o zamknięciu okna przez
kliknięcie przycisku zamykającego. Okno New Windows... wywołujemy klikając prawym klawiszem
myszy pozycję CModeless w panelu ClassView, a następnie wybierając pozycję Add Windows
Message Handler.


Tworzenie własnych okien dialogowych i ich klas 243
Poniższe linie kodu służą do uruchomienia funkcji obsługi OnCiose (), aby mogła ona powodować
usunięcie obiektu okna i zresetowanie globalnego wskaźnika:
extern CModeless* g pDIgModeless;
void CModeless::OnClose() ;
{
CDialog::OnClose () ;
delete this ;
g_pDlgModeless = NULL;
Deklaracja extern użyta została do zadeklarowania wskaźnika globalnego. Jest to ten sam wskaźnik
g_pDlgModeless, który zadeklarowany został w MLaunchDIg.cpp.
Funkcja klasy bazowej OndoseO wywołana zostaje dla przeprowadzenia procesu zamknięcia okna.
Gdy funkcja kończy działanie, obiekt niemodalnego okna dialogowego (this) zostaje usunięty z pamięci.
Wskaźnik g_pDlgModeless ulega następnie wyzero-waniu, przyjmując wartość NULL, a główne okno
aplikacji może powołać do życia nowe niemodalne okno dialogowe.
Usunięcie przycisku zamykającego
Zamiast obsługi komunikatu WM_CLOSE, możemy uniemożliwić użytkownikowi korzystanie z
przycisku zamykającego przez usunięcie zaznaczenia opcji System Menu z karty Styks, znajdującej się
wśród kart właściwości niemodalnego okna dialogowego w edytorze zasobów (rysunek 10.11). Brak tej
opcji spowoduje usunięcie ikony zamykania okna oraz menu, w związku z czym użytkownik nie będzie
mógł zamknąć okna. Stosuje się to w sytuacjach, gdy niemodalne okno dialogowe musi być otwarte
przez cały czas.
l Dialog Ptoperties : :



-W

?"
Geneia



Style
s

MorÄ™ S
tyłeś

Entended
Styles



Style:



p

litle bar

r

Clifi siblings

^

Popup

d

r

ISystem
meny

r

Clip chiidren

' gotdec "': : :

r

Mininize
bon

r

Horizontalscroll



Dialog
Fiame

d

r

Maximi;eb
ox

r

Vertical taoll

Rysunek 10.11. Usunięcie opcji System Menu dla niemodalnego okna dialogowego


Wyszukiwarka

Podobne podstrony:
03 Projektowanie i tworzenie okien dialogowych
AutoCAD Civil 3D 2006 Tworzenie własnych podzespołów z polilinii
MS Office 2000 i 2002 XP Tworzenie własnych aplikacji w VBA
Tworzenie własnych symboli
Nabycie udziałów własnych celem ich odsprzedaży
20 Tworzenie okien z wieloma widokami
10 dialogi
tworzenie pędzli i ich edycja
Lista wybranych tematow esejow i ich Autorow w r ak 10 2011
10 GIMP tworzenie grafiki na potrzeby WWW (cz3)
10 profesjonalnych porad dotyczących tworzenia beatów
Wyklad 8,9,10,11 Kwasy karboksylowe i ich pochodne
10 ROZPOZNAWANIE ZAPOBIEGANIE ORAZ WYKRYWANIE PRZESTĘPSTW I ICH SPRAWCÓW W RAMACH PROWADZONEJ PRA
Brand Equity czyli rynkowe efekty tworzenia marki
WSM 10 52 pl(1)

więcej podobnych podstron