Praca z oknami dialogowymi
Transkrypt
Praca z oknami dialogowymi
MICROSOFT FOUNDATION CLASSES PRACA Z OKNAMI DIALOGOWYMI PROSTE, MODALNE OKNA DIALOGOWE Najczęściej okna dialogowe tworzy się w edytorze zasobów. Należy utworzyć klasę dialogową przy użyciu ClassWizarda (w poniższym przykładzie CMyDlg). Za pomocą ClassWizarda można też dodać do klasy zmienne powiązane z poszczególnymi oknami kontrolnymi (podwójny klik + ctrl, lub też korzystając z zakładki Member variables). Wówczas użycie okna może przedstawiać się następująco: #include "MyDlg.h" ... void CXxxx::OnShowDialog() { CMyDlg dlg; dlg.m_data = m_data; dlg.DoModal(); m_data = dlg.m_data; } // definicja klasy // funkcja powiązana z opcją menu // inicjalizacja danych dialogu // wywołanie dialogu // zachowanie danych dialogu Funkcja DoModal uruchamia pętlę modalną dialogu i zwraca wartość określającą jej wynik, najczęściej kod klawisza, którym zakończono pracę z dialogiem (IDOK, IDCANCEL, w istocie jest to wartość przekazana do funkcji EndDialog, por. niżej). INICJALIZACJA I ZAKOŃCZENIE PRACY DIALOGU Właściwym miejscem dla umieszczenia sekwencji inicjalizującej jest funkcja reagująca na komunikat WM_INITDIALOG – najczęściej OnInitDialog. Na zakończenie pracy z dialogiem jest wywoływana, odpowiednio, funkcja OnOk lub OnCancel. Istotnym ich elementem jest, iż obie wywołują EndDialog – funkcję kończącą pętlę modalną okna. W analogiczny sposób można wymusić zakończenie pracy okna dialogowego w dowolnej sytuacji. DDX I DDV Opisane w pierwszym punkcie powiązanie zmiennych składowych klasy okna dialogowego z oknami kontrolnymi następuje poprzez zastosowanie techniki DDX (dialog data exchange). Bliźniacza technika, zwana DDV (dialog data verification) pozwala na zweryfikowanie poprawności wprowadzanych danych. Działalność ClassWizarda sprowadza się w tym zakresie do wstawienia wywołań funkcji DDX/DDV do standardowej funkcji wirtualnej dialogów, DoDataExchange. Mechanizm DDX/DDV pobudza się do działania wywołując odpowiednio: UpdateData(FALSE); UpdateData(TRUE); // inicjalizacja zawartości okien kontrolnych // zapamiętanie zawartości okien kontrolnych (jak przy OK) Pierwsze z powyższych wywołań jest znajduje się domyślnie w OnInitDialog, drugie – w OnOk (ale nie w OnCancel), dzięki czemu w przypadkach tak prostych, jak podany w pierwszym punkcie, wydaje się, że cały mechanizm działa w pełni automatycznie. Jednak w przypadku, gdy z DDX/DDV chcemy skorzystać w trakcie pracy pętli modalnej, będziemy zmuszeni do jawnego wywołania UpdateData. Alternatywną możliwością jest pominięcie tego mechanizmu i zastosowanie funkcji GetDlgItemText, SetDlgItemText, GetDlgItemInt i SetDlgItemInt. Jest to szczególnie zalecane, gdy pracujemy tylko z jednym oknem kontrolnym, a dialog zawiera ich wiele (UpdateData(FALSE) może wtedy powodować migotanie). PRACA Z OKNAMI NIEMODALNYMI Oto kilka zasad, których trzymać się należy tworząc okna niemodalne: 1. Okna niemodalne nie zakładają pętli modalnej, zatem zamiast DoModal stosujemy po prostu Create. 2. Okna niemodalne żyją zwykle dłużej, niż zmienne automatyczne w funkcji, która je tworzy; stosujemy zatem zmienne dynamiczne zamiast automatycznych. 3. Funkcja EndDialog ustawia flagę, która powoduje wyjście z pętli modalnej. Okna niemodalne nie posiadają pętli modalnej, nie należy więc w stosunku do nich stosować EndDialog. Zamiast tego wywołujemy De- stroyWindow. Wymaga to jednak zwykle przepisania OnOk, OnCancel i OnClose, które domyślnie wywołują EndDialog. 4. Skoro dialog jest zmienną tworzoną dynamicznie, należy ją kiedyś usunąć: właściwym miejscem jest PostNcDestroy(). 5. W zasobach dialogu należy zaznaczyć opcję Visible (STYLE WS_VISIBLE). Przykład: class CModelessDlg : public CDialog { ... void OnOk() { UpdateData(TRUE); DestroyWindow(); } // analogicznie przepisać też OnCancel i OnClose void PostNcDestroy() { delete this; // wygląda okropnie, ale działa dobrze } }; CModelessDlg pDlg = new CModelessDlg; pDlg->Create(IDD_DIALOG1); // utworzenie zmiennej dynamicznej // utworzenie okna dialogowego WSPÓŁPRACA Z OKNAMI KONTROLNYMI Operacje wykonywane przez użytkownika w stosunku do okien kontrolnych (na przykład naciśnięcie przycisku lub zmiana zawartości okna edycyjnego) powodują wysłanie do właściciela (na ogół okna dialogowego) specjalnego komunikatu, zwanego notyfikacją. W przypadku starszych okien kontrolnych, zachowujących zgodność z Windows 3.1, jest to szczególna forma komunikatu WM_COMMAND. Nowsze kontrolki (common controls) stosują specjalny komunikat WM_NOTIFY. W jednym i drugim przypadku nie trudno wprowadzić do klasy okna dialogowego funkcje reagujące na notyfikacje – oczywiście za pomocą ClassWizarda. Mechanizm notyfikacji daje możliwość reagowania na zdarzenia dotyczące okien potomnych przez okno rodzicielskie. Unikamy w ten sposób często potrzeby tworzenia klas potomnych dla okien kontrolnych nawet, jeśli ich zachowanie ma w istotny sposób odbiegać od standardu – wystarczy, że klasę okna rodzicielskiego odpowiednio uwrażliwimy na notyfikacje nadchodzące od okien potomnych. TWORZENIE WŁASNYCH KLAS OKIEN KONTROLNYCH – SUBCLASSING Tworzenie własnych klas okien kontrolnych ma sens wtedy, gdy chcemy wielokrotnie używać specyficznych okien bez konieczności każdorazowego modyfikowania okna rodzica. Własne klasy okien kontrolnych można implementować w oparciu o istniejące klasy, stosując normalne dziedziczenie C++. Warto skorzystać z mechanizmu odbitych notyfikacji (message reflection): każdą notyfikację przesłaną do rodzica można przechwycić na poziomie okna potomnego wysyłającego tę notyfikację (w praktyce sprowadza się to jak zwykle do wykorzystania ClassWizarda). Aby powiązać obiekt własnej klasy potomnej z elementem zasobu dialogowego, należy stworzyć odpowiednią zmienną składową i zainicjalizować ją, najlepiej w obrębie OnInitDialog, w następujący sposób: m_myControl.SubclassDlgItem(IDC_MY_CTRL_ID, this); PODSUMOWANIE Uwaga! Tak zainicjalizowana zmienna nie może być zadeklarowana jednocześnie w technice DDX/DDV! DoModal Create UpdateData(BOOL) Set/GetDlgItemText/Int EndDialog DestroyWindow SubclassDlgItem OnInitDialog OnOk OnCancel OnClose PostNcDestroy uruchamia dialog – przeprowadza modalną pętlę komunikatów tworzy dialog niemodalny uruchamia – na żądanie – mechanizm DDX/DDV (TRUE jak przy OK) bezpośredni dostęp do zawartości okien kontrolnych kończy modalną pętlę komunikatów (nieprzydatne dla dialogów niemodalnych) usuwa okno (nieprzydatne dla dialogów modalnych) przydziela zasobowi wskazany obiekt okna kontrolnego dowolnej klasy wywoływana podczas inicjalizacji dialogu wywoływane na zakończenie dialogu (przy różnych okazjach) w dialogach niemodalnych – miejsce na umieszczenie delete this