MICROSOFT FOUNDATION CLASSES

Transkrypt

MICROSOFT FOUNDATION CLASSES
MICROSOFT FOUNDATION CLASSES
NAJCZĘŚCIEJ PRZEPISYWANE FUNKCJE SKŁADOWE PODSTAWOWYCH KLAS
OBIEKT APLIKACJI CWINAPP
InitInstance
Zarządzanie inicjalizacją aplikacji
ExitInstance
Zarządzanie zakończeniem aplikacji
OnIdle
Pozwala wykorzystać wolny czas aplikacji
PreTranslateMessage
Hak do wprowadzenia zaawansowanych filtrów do komunikatów
DOKUMENTY
Serialize
Typowa odczyt – zapis
OnNewDocument
By przejąć kontrolę nad tworzeniem nowego dokumentu. W SDI ta funkcja jest
wywoływana wielokrotnie – dokumenty są powtórnie wykorzystywane
OnOpenDocument
Dodatkowa inicjalizacja tuż po otwarciu lub nietypowa procedura odczytu
CanCloseFrame
Kontrola zamknięcia okna dokumentu
SaveModified
Normalnie, pyta przy zamykaniu dokumentu o to, czy zachować plik (jeśli został on
zmieniony). Można tę standardową procedurę podmienić na własną.
WIDOKI
OnDraw
Tworzenie wizualnej strony widoku, definiuje też wygląd wydruku. Stosujemy operacje
graficzne dostępne dla obiektu CDC.
OnUpdate
Zarządzanie inteligentnym (wybiórczym) odświeżaniem widoku
OnInitialUpdate
Jednorazowa, wstępna inicjalizacja widoku
OnPrepareDC
Przygotowanie kontekstu urządzenia przed wyświetlaniem i drukowaniem. Pozwala na
przykład na wprowadzenie zaawansowanych ustawień mapingu
OnPrepartePrinting
OnBeginPronting
OnEndPronting
Przepisujemy te funkcje, gdy potrzebujemy sprytnego drukowania!
OKNA
PreCreateWindow
Wczesna ingerencja w proces tworzenia okna; pozwala zmienić jego styl i inne parametry
OnCreate
Późna ingerencja w proces tworzenia okna; nadaje się do inicjalizacji danych związanych
z oknem i utworzenia okien potomnych
RAMKI POTOMNE
OnCreateClient
Wygodne miejsce do zaplanowania przyszłej zawartości ramki
OKNA DIALOGOWE
OnInitDialog
Idealne miejsce do umieszczenia wszelkich procedur inicjalizacyjnych.
MICROSOFT FOUNDATION CLASSES
ZAAWANSOWANE ASPEKTY TWORZENIA DOKUMENTÓW I WIDOKÓW
1. Jak stworzyć dodatkowy typ dokumentu?
Dołącz nowy szablon dokumentu (CDocTemplate) do listy szablonów przechowywanych
przez obiekt aplikacji. Wskaźniki na tworzone szablony warto przechować – por. p. 2.
CSample1App::InitInstance()
{
...
...
m_pTemplate1 = new CMultiDocTemplate(
IDR_SAMPLETYPE,
RUNTIME_CLASS(CSampleDoc),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CSampleView));
AddDocTemplate(m_pSampleTemplate);
m_pTemplate1 = new CMultiDocTemplate(
IDR_STRTYPE,
RUNTIME_CLASS(CDoc2),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CView2));
AddDocTemplate(m_pStrTemplate);
// ten szablon utworzył dla nas AppWizard
// przechowamy jego wskaźnik
// w zmiennej składowej
// a to jest szablon
// drugiego typu dokumentu
2. Jak utworzyć nowy dokument bez konieczności wyboru jego typu w oknie dialogowym?
Skorzystaj wprost z usług obiektu szablonu dokumentu (CDocTemplate). Aby to zrobić,
niezbędne jest zachowanie jego wskaźnika (por. p. 1). Oto uchwyty odpowiednich opcji menu
obsługiwanych przez obiekt aplikacji (zawsze odpowiada on za tworzenie nowych
dokumentów):
void CSample1App::OnFileNewDoc1()
{
m_pTemplate1->OpenDocumentFile(NULL);
}
void CSample1App::OnFileNewDoc2()
{
m_pTemplate2->OpenDocumentFile(NULL);
}
3. Jak dodać ramkę z nowym widokiem istniejącego dokumentu?
Korzystamy ponownie z usług szablonu dokumentu. Funkcja CDocument::GetDocTemplate
zwraca szablon, który utworzył ten dokument. CDocTemplate::CreateNewFrame utworzy
taką samą, nową ramkę i wypełni ją widokiem powiązanym ze wskazanym dokumentem
(w tym przypadku: this). Pozostaje tylko odświeżyć zawartość ramki (CFrameWnd::InitialUpdateFrame). Oto uchwyt opcji obsługiwany przez obiekt dokumentu:
void CSample1Doc::OnFileAddStandardView()
{
CFrameWnd* pFrame = GetDocTemplate()->CreateNewFrame(this, NULL);
pFrame->InitialUpdateFrame(this, TRUE);
}
4. Jak dodać do istniejącego dokumentu ramkę z nietypowym widokiem?
Procedura podana w p. 3 jest przydatna w przypadku, gdy nowa ramka ma zawierać widok tej
samej klasy. Jeśli chcemy utworzyć ramkę z nietypowym widokiem, powołujemy specjalny
szablon dokumentu:
void CSample1Doc::OnFileAddAnotherView()
{
static CMultiDocTemplate temp(IDR_SAMPLETYPE,
RUNTIME_CLASS(CSampleDoc),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CAnotherView));
// w tym miejscu podajemy typ widoku
CFrameWnd* pFrame = temp.CreateNewFrame(this, NULL);
pFrame->InitialUpdateFrame(this, TRUE);
}
Uwaga: obiekt szablonu musi istnieć w trakcie korzystania z ramki, dlatego został
zdefiniowany jako statyczny.
5. Jak uzyskać ramkę z dynamicznym splitterem?
Procedura postępowania jest nieco podobna, jak poprzednio (p. 4), z tym, że teraz nietypowym
elementem szablonu dokumentu staje się typ ramki:
void CSample1Doc::OnFileAddDynamicSplit()
{
static CMultiDocTemplate temp(IDR_SAMPLETYPE,
RUNTIME_CLASS(CSampleDoc),
RUNTIME_CLASS(CChildSplitFrame), // specjalna ramka ze splitterem
RUNTIME_CLASS(CSampleView));
CFrameWnd* pFrame = temp.CreateNewFrame(this, NULL);
pFrame->InitialUpdateFrame(this, TRUE);
}
Trzeba jeszcze stworzyć nową klasę ramek (pochodzących z CFrameWnd lub
CMDIChildFrame, w zależności od tego, czy aplikacja jest SDI czy MDI). W nowej klasie
dodajemy zmienną składową klasy CSplitterWnd i inicjalizujemy ją w metodzie
OnCreateClient:
CSplitterWnd m_wndSplitter;
// w definicji klasy ramek CChildSplitFrame
BOOL CChildSplitFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
// utwórz splitter (2 na 2, minimalny rozmiar panelu: 10 na 10)
return m_wndSplitter.Create(this, 2, 2, CSize(10, 10), pContext);
}
6. Jak uzyskać splitter zawierający dwa widoki różnych klas?
W tym celu tworzymy tzw. splitter statyczny. Procedura postepowania jest taka sama,
jak przy splitterze dynamicznym, zmienia się tylko sposób inicjalizacji klienta
(OnCreateClient):
BOOL CChildSplit2Frame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
if (!m_wndSplitter.CreateStatic(this, 1, 2))
return FALSE;
if (!m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CView1), CSize(200, 200), pContext)
|| !m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CView2), CSize(200, 200), pContext))
{
m_wndSplitter.DestroyWindow();
return FALSE;
}
return TRUE;
}
6. Jak uzyskać splitter zawierający dowolną liczbę widoków?
Splitter może zawierać 1 lub 2 kolumny i 1 lub 2 wiersze. Z tego wynika, że przy użyciu
jednego splittera możemy uzyskać dwa lub cztery panele. Jeżeli potrzebna jest inna,
nietypowa konfiguracja, niezbędne jest stworzenie splitterów zagnieżdżonych. W poniższym
przykładzie pokazano sposób utworzenia dwóch splitterów. Splitter nadrzędny
(m_wndSplitter) zawiera dwa panele: górny i dolny. W panelu dolnym umieszczony zostaje
jeden z widoków, zaś w panelu górnym tworzymy splitter podrzędny (m_wndSplitter2),
zawierający dwa inne widoki. W sumie uzyskujemy potrójny podział okna.
CSplitterWnd m_wndSplitter; // w definicji klasy ramek CChildSplitFrame
CSplitterWnd m_wndSplitter2; // w definicji klasy ramek CChildSplitFrame
BOOL CChildSplit3Frame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
// stwórz górny i dolny panel, dolny wypełnij dolnym widokiem (CTextView)
if (!m_wndSplitter.CreateStatic(this, 2, 1))
return FALSE;
if (!m_wndSplitter.CreateView(1, 0, RUNTIME_CLASS(CView1), CSize(100, 100), pContext))
{
m_wndSplitter.DestroyWindow();
return FALSE;
}
m_wndSplitter.SetRowInfo(0, 150, 0);
// stwórz dolny spliter: lewy prawy panel górnego panelu nadrzędnego
// właściwe umieszczenie splittera uzyskuje się podając spliiter nadrzędny
// jako okno rodzicielskie i id panelu m_wndSplitter.IdFromRowCol jako id obiektu
m_wndSplitter2.CreateStatic(&m_wndSplitter, 1, 2,
WS_CHILD | WS_VISIBLE, m_wndSplitter.IdFromRowCol(0, 0));
if (!m_wndSplitter2.CreateView(0, 0, RUNTIME_CLASS(CView2), CSize(200, 200), pContext)
|| !m_wndSplitter2.CreateView(0, 1, RUNTIME_CLASS(CView2), CSize(200, 200), pContext))
{
m_wndSplitter2.DestroyWindow();
return FALSE;
}
return TRUE;
}
MICROSOFT FOUNDATION CLASSES
D R U K O W A N I E
FUNKCJE WIDOKÓW WYKORZYSTYWANE PRZY DRUKOWANIU:
OnFilePrint
Przesłoń tę funkcję, jeśli chcesz zrobić wszystko sam.
OnPreparePrinting Normalnie funkcja wywołuje dialog CPrintDialog (DoPreparePrinting). Wskaźnik
do obiektu dialogu znajduje się w CPrintInfo::m._pPD. Przesłoń w celu stworzenia
własnego dialogu lub określenia liczby stron dokumentu (CPrintInfo::SetMaxPage()).
Może to być jednak trudne, bo nie jest jeszcze na tym etapie znany kontekst
urządzenia.
OnBeginPrinting
Dobre miejsce na zaalokowanie zasobów potrzebnych przy drukowaniu (na przykład
czcionek). Również właściwe miejsce do różnego typu inicjalizacji – znany już jest
kontekst urządzenia. Można tu także ustalić liczbę stron dokumentu.
OnPrepareDC
Modyfikacja kontekstu urządzenia. Funkcja jest wykorzystywana również przy
wyświetlaniu na ekranie. Można w tym miejscu obsługiwać paginację (numer
aktualnej strony dostępny jest w CPrintInfo::m_nCurPage). Jest to też ostatnie
miejsce, gdzie można „w locie” kontrolować liczbę stron – ustawiając flagę
m_bContinuePrinting struktury CPrintInfo.
OnPrint
Drukuje stronę dokumentu. Normalnie wywołuje funkcję OnDraw. Przeważnie
przesłania się, by zaimplementować wydruk stópek i nagłówków. Funkcja ta może
też całkowicie przejąć zadanie drukowania strony, oddzielając je tym samym od
OnDraw.
OnDraw
Uniwersalna funkcja służąca zarówno do wyświetlania danych na ekranie, jak
i drukowania. Połączenie tych dwóch funkcji w jednym miejscu ułatwia WYSIWYG.
OnEndPrinting
Zwolnij w tym miejscu zasoby stworzone w OnBeginPrinting.
Wiele funkcji otrzymuje parametr typu CPrintInfo. Wybrane, najprzydatniejsze składowe tej struktury to:
SetMaxPage()
SetMinPage()
m_nCurPage
m_bContinuePrinting
m_bPreview
m_nNumPreviewPages
pozwala ustawić liczbę stron dokumentu
pozwala ustawić minimalny numer strony dokumentu
zawiera numer aktualnie drukowanej strony
pozwala w „locie” sterować liczbą stron wydruku (por. OnPrepareDC)
ustawione w trybie podglądu wydruku
zawiera liczbę stron podglądu wydruku (1 lub 2)
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
MICROSOFT FOUNDATION CLASSES
BIBLIOTEKI ŁADOWANE DYNAMICZNIE DLL
REGULAR DLL WITH MFC STATICALLY LINKED
REGULAR DLL USING SHARED MFC DLL
Biblioteki tego typu mogą być wykorzystywane przez programy nie napisane w MFC. Biblioteki stosujące statyczne linkowanie MFC mogą być nawet używane w systemach, gdzie MFC w ogóle nie jest zainstalowane.
Druga odmiana wymaga obecności w systemie DLL-i MFC, za to rozmiary plików tych bibliotek są zdecydowanie mniejsze.
Tworząc funkcje warto przestrzegać następujących zasad:
1. Stosujemy funkcje C, a nie C++ (extern “C”). Unikamy w ten sposób udekorowanych nazw C++. Dzięki
temu łatwo będzie skorzystać z DLL w programach pisanych w językach innych, niż C++.
2. Eksportujemy raczej funkcje, nie całe klasy – z tych samych, co powyżej, przyczyn.
3. Funkcje eksportowane opatrujemy przedrostkiem __declspec(dllexport).
4. Nagłówki funkcje przeznaczonych do importowania przez inne moduły opatrujemy przedrostkiem
__declspec(dllimport).
Punkty (1) i (2) możemy zignorować, jeśli biblioteki mają być stosowane wyłącznie przez programistów C++.
Punkty (3) i (4) możemy zignorować wprowadzając odpowiednie zapisy do pliku DEF.
Oto przykładowy, prosty plik nagłówkowy biblioteki:
#ifdef __cplusplus
extern "C"
{
#endif
__declspec(dllimport) void ShowMsg(char *p);
#ifdef __cplusplus
}
#endif
Uwaga! Biblioteki tego typu zawierają obiekt klasy CWinApp (każdy moduł MFC musi zawierać taki obiekt) –
jest to warunkiem korzystania z funkcji MFC. W przypadku korzystania z MFC.DLL niezbędne jest ustalenie
kontekstu wywołania w obrębie MFC przy każdym wywołaniu funkcji, która korzystać będzie z mechanizmów
MFC. Przykładowa funkcja przybiera więc postać:
__declspec(dllexport) void ShowMsg(char *p)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
AfxMessageBox(p);
}
// obowiązkowe w DLL używającym MFC DLL
MFC EXTENSION DLL (USING SHARED MFC DLL)
Biblioteki tego typu mogą być używane wyłącznie przez programy korzystające z MFC, za to można eksportować – i na ogół się to robi – klasy wywiedzione z klas bazowych MFC (stąd nazwa extension dll). DLL tego
typu współdzieli z wywoływaną aplikacją obiekt aplikacji i zasoby; obydwa moduły mogą współużytkować
dowolne obiekty MFC.
Nie ma potrzeby stosowania extern "C"; wygodnie jest natomiast stosować przedrostki __declspec(dllimport)
i __declspec(dllexport), szczególnie, że można je umieszczać w nagłówkach klas!
Przydatne jest standardowe makro AFX_EXT_CLASS, które pozwala stosować ten sam nagłówek do eksportowania klas i funkcji z DLL, jak i importowania przez inne moduły. Jest ono zdefiniowane mniej więcej tak:
#ifdef _AFXDLL
#define AFX_EXT_CLASS
#else
#define AFX_EXT_CLASS
#endif
__declspec(dllexport)
__declspec(dllimport)
MICROSOFT FOUNDATION CLASSES
OBSŁUGA
BAZ
DANYCH
STANDARDY OBSŁUGIWANE PRZEZ MFC:
•∋ ODBC (Open DataBase Connectivity)
•∋ DAO (Data Access Objects)
•∋ OLE DB
NAJWAŻNIEJSZE KLASY:
ODBC
CDatabase
CRecordset
CRecordView
–
–
–
DAO
CDaoDatabase
CDaoRecordset
CDaoRecordView
CDaoWorkspace
CDaotableDef
CDaoQueryDef
opis
połączenie z bazą danych
zbiór rekordów wybranych instrukcją SELECT, często cała tabela
widok formularza bazy danych
sesja – od zalogowania się aż do wylogowania
tabela bazy danych
kwerenda bazy danych (zwykle zdefiniowana w bazie danych)
KORZYSTANIE Z OBIEKTÓW BAZ DANYCH (CDATABASE/CDAODATABASE)
W typowych aplikacjach programista w ogóle nie ma styczności z obiektami tej klasy. Są one stosowane przy tworzeniu rekordsetów, jednakże można wymusić ich automatyczne tworzenie (poprzez podanie
NULL). Celowe jest utworzenie obiektu tej klasy, jeśli chcemy, by kilka rekordsetów korzystało z tego samego
połączenia, oraz przy realizacji pewnych globalnych operacji na bazie.
KORZYSTANIE Z REKORDSETÓW (CRECORDSET/CDAORECORDSET)
W praktyce główny obiekt reprezentujący dane. W najprostszym przypadku odnosi się do całej tabeli; jednak przy otwieraniu rekordsetu można użyć instrukcji SELECT języka SQL celem odfiltrowania potrzebnego zbioru rekordów. Rekordset reprezentuje w danej chwili dane związane z określonym wierszem
tabeli (zestawem pól jednego rekordu); stosowne funkcje pozwalają przesuwać się wzdłuż tabeli. Dostęp do
danych można łatwo zrealizować za pomocą zmiennych składowych, które można wiązać z odpowiednimi
polami bazy danych. Odbywa się to w podobny sposób, jak wiązanie okien kontrolnych dialogu ze zmiennymi składowymi obiektu CDialog. Wartości opisywanych zmiennych są przy każdym przesunięciu wzdłuż
tabeli odświeżane – zarówno w sensie zapisu zmodyfikowanych wartości rekordu do bazy, jak i odczytu
wartości dla nowego rekordu. Dowiązanie zmiennych rekordsetu do pól bazy danych jest realizowane przez
ClassWizarda.
TWORZENIE I OTWIERANIE REKORSETU:
CRecordet (CDaoRecordset) – konstruktor pobiera wskaźnik na obiekt CDatabase (CDaoDataBase).
Podanie NULL spowoduje otwarcie nowego połączenia na podstawie danych zwróconych przez
GetDefaultConnect.
GetDefaultConnect – określa domyślne źródło danych.
Open – otwiera rekordset. Można określić instrukcję języka SQL definiującą zbiór rekordów (np. „SELECT * FROM Pracownicy WHERE pensja > 1000”). Domyślnie brana jest instrukcja dostarczona
przez GetDefaultSQL.
GetDefaultSQL – określa domyślną instrukcję SQL stosowaną przy otwieraniu.
m_strSort – (zmienna składowa) określa sposób sortowania rekordów (klauzula SQL ORDER BY).
m_strFilter – (zmienna składowa) określa sposób filtrowania rekordów (klauzula SQL WHERE).
ATRYBUTY (WYBRANE):
CanAppend – określa, czy do bazy można dodawać rekordy (por. AddNew).
CanBookmark – określa, czy można stosować zakładki (por. GetBookmark i SetBookmark).
CanTransact – określa, czy baza obsługuje transakcje.
CanUpdate – określa, czy można dodawać, zmieniać lub usuwać rekordy.
GetRecordCount – zwraca liczbę rekordów w tabeli.
GetTableName – zwraca nazwę tabeli.
NAWIGACJA (URUCHAMIAJĄ WYMIANĘ DANYCH MIĘDZY REKORDSETEM A BAZĄ DANYCH):
MoveFirst, MoveLast, MovePrev, MoveNext – przesuwa wskaźnik bieżącego rekordu na początek/koniec bazy lub do poprzedniego/poprzedniego rekordu.
Move – przesuwa wskaźnik bieżącego rekordu o podaną liczbę pozycji.
SetAbsolutePosition – przesuwa wskaźnik bieżącego rekordu do bezwzględnej pozycji w tabeli.
GetBookmark, SetBookmark – obsługa nawigacji przy pomocy zakładek.
Find, FindFirst, FindLast, FindPrev, FindNext (tylko CDaoRecordset) – nawigacja z wyszukiwaniem
według określonych kryteriów (jak w SELECT WHERE).
IsBOF, IsEOF – sprawdza, czy wskaźnik bieżącego rekordu znajduje się na przed pierwszym / za ostatnim rekordem w tabeli.
MODYFIKOWANIE BAZY DANYCH:
AddNew – przygotowuje do dodania rekordu – wywołaj Update by zakończyć operację.
Edit – przygotowuje do edycji (modyfikacji) rekordu – wywołaj Update by zakończyć operację.
Update – kończy operacje zainicjowane przez AddNew/Edit poprzez zapis danych do bazy.
CancelUpdate – anuluje uprzednie wywołanie AddNew/Edit.
Delete – zaznacza bieżący rekord do usunięcia.
IsDeleted – określa, czy bieżący rekord został zaznaczony do usunięcia.
INNE:
DoFieldExchange – realizuje wymianę danych pomiędzy rekordsetem a bazą danych.
Requery – ponownie przeprowadza zapytanie do bazy danych, tj. odświeża zawartość rekordsetu.
KORZYSTANIE Z ARCHITEKTURY DOKUMENT–WIDOK
(CRECORDVIEW/CDAORECORDVIEW)
W przypadku aplikacji bazodanowych rolę dokumentu spełnia de facto obiekt bazodanowy, a w
szczególności główny używany przez program rekordset lub zestaw rekordsetów. Rola dokumentu (obiektu
CDocument) ogranicza się zatem do tego, że zawiera on co najmniej jedną zmienną składową tego typu.
Obiekty widoku rekordu obsługują typowy formularz bazodanowy. Klasa pochodzi od CFormView,
stanowi więc pod względem funkcjonalnym mieszankę dokumentu i widoku.
Z każdym obiektem widoku wiąże się jeden obiekt rekordsetu; z poszczególnymi oknami kontrolnymi można wiązać zarówno własne zmienne widoku, jak i zmienne tego rekordestu. W ten sposób okna
kontrolne mogą być pośrednio (poprzez zmienne rekordsetu) związane z polami bazy danych. Tworzenie
takich powiązań jest wspierane przez ClassWizarda.
WYBRANE FUNKCJE SKŁADOWE:
OnGetRecordset – funkcja wirtualna, zwraca wskaźnik na rekordset związany z widokiem; zwykle
zwraca jest to wskaźnik na odpowiednią zmienną obiektu dokumentu.
OnMove – funkcja wirtualna wywoływana podczas nawigacji (przy każdej zmianie bieżącego rekordu)
DoDataExchange – funkcja analogiczna do CDialog::DoDataExchange; realizuje wymianę danych
między oknami kontrolnymi a zmiennymi (widoku i rekordsetu) – można ją wykorzystać do wprowadzenia niestandardowych metod prezentacji danych.
TWORZENIE APLIKACJI BAZODANOWYCH
Podstawową aplikację bazodanową generuje AppWizard. Zawiera ona obiekt rekordsetu, dowiązany do
wskazanej przez programistę tabeli i zawierający komplet zmiennych powiązanych z jej polami (kolumnami).
Ponadto aplikacja zawiera widok formularza bazy oraz zestaw opcji i przycisków na toolbarze pozwalających na
nawigację wzdłuż tabeli. Zadaniem użytkownika jest wprowadzenie do wyświetlanego przez widok, początkowo
pustego formularza własnych okien kontrolnych i dowiązanie ich – za pomocą ClassWizarda – do zmiennych
składowych rekordsetu. Efektem jest prosta aplikacja pozwalająca na przeglądanie i edycję rekordów.
Aby wyświetlić dane w sposób niekonwencjonalny – np. za pomocą innych okien kontrolnych, niż
obsługiwane przez Class Wizarda dla danego typu danych – można wprowadzić dodatkowe przekształcenia
w funkcji widoku DoDataExchange. Dla inicjalizacji okien tylko do odczytu (np. przy prezentacji wyników
przeliczenia wartości pól bazy lub listy wartości pochodzącej z innej, połączonej relacją tabeli) stosownym
miejscem jest też OnMove.
Często analizuje się inne, niż główna tabele. Warto wówczas za pomocą ClassWizarda stworzyć specjalną klasę pochodzącą od CRecordset. W funkcji Open możemy narzucić filtrację instrukcją SQL SELECT. Korzystając z while (!theSet.IsEOF()) { ... ; theSetMoveNext(); } można następnie przeglądnąć cały zbiór rekordów.
MICROSOFT FOUNDATION CLASSES
TECHNOLOGIA COM – SERWERY AUTOMATYZACJI
COM I ACTIVEX W PRAKTYCE
MFC ukrywa większość szczegółów związanych z technologią COM, wspierając głównie programowanie z wykorzystaniem interfejsu IDispatch. Programowanie COM z użyciem MFC jest łatwe, ale w
efekcie otrzymuje się moduły dość duże, o efektywności odbiegającej od optymalnej. Jeśli konieczne jest
stworzenie małego, szybkiego modułu (szczególnie kontrolki ActiveX), warto wziąć pod uwagę przejście do
biblioteki ATL (ActiveX Template Library).
AUTOMATYZACJA
Automatyzacja pozwala na korzystanie z modułów COM jako z obiektowej biblioteki udostępniającej klasy i ich właściwości oraz metody. Polega na sterowaniu pracą modułów typu EXE lub DLL (tzw. serwerów automatyzacji) za pomocą innego modułu (klienta lub sterownika). W MFC stosuje się dwa sposoby
podejścia do tworzenia serwerów:
· Automatyzacja aplikacji na bazie klasy dokumentu – wymaga zaznaczenia opcji w trakcie tworzenia
aplikacji przez AppWizard (opcja Automation w trzecim kroku); umożliwia łatwą automatyzację
aplikacji MFC,
· Automatyzacja na bazie obiektu dowolnej klasy wywiedzionej bezpośrednio lub pośrednio z klasy
CCmdTarget – wymaga utworzenia klasy za pomocą ClassWizarda z zaznaczeniem opcji automatyzacji (najwygodniej: Automation: Creatable by type ID); zwykle stosowana, gdy chcemy umieścić
serwer automatyzacji w module DLL, ale również w bardziej rozbudowanych aplikacjach.
DODAWANIE WŁAŚCIWOŚCI
Właściwości obiektów automatyzacji definiuje się za pomocą zakładki Automation ClassWizarda.
Dodanie właściwości (przycisk Add Property) wymaga określenia nazwy zewnętrznej (rejestrowanej
w systemie i widocznej dla klientów), typu danych, sposobu implementacji oraz dodatkowych danych zależnych od tego sposobu. Dostępne są dwa sposoby implementacji:
· Poprzez funkcje Get/Set (Get/Set methods), w obrębie których implementuje się dostęp do danych.
Sposób ten gwarantuje większą elastyczność. Wizard proponuje nazwy obu funkcji; wymazanie jednej z nich pozwala zdefiniować właściwość tylko do odczytu bądź tylko do zapisu.
· Poprzez zmienną składową (Member variable, opcja włączona domyślnie) – wartość właściwości
jest przechowywana w zmiennej i obsługiwana automatycznie przez MFC. Określić można nazwę
zmiennej oraz funkcje notyfikacji (Notification function), która będzie wywołana po każdej zmianie
wartości właściwości.
DODAWANIE METOD
Metody definiuje się za pomocą tej samej zakładki, co właściwości.
Dodanie metody (przycisk Add Method) wymaga określenia nazwy zewnętrznej (rejestrowanej w
systemie i widocznej dla klientów), nazwy wewnętrznej (identyfikatora lub identyfikatorów używanych w
kodzie źródłowym C++), zwracanego typu danych oraz, ewentualnie, listy parametrów.
STEROWNIKI (KLIENCI) AUTOMATYZACJI
Moduł korzystający z serwera automatyzacji musi wywołać funkcję AfxOleInit (na ogół w
CXxxApp::InitInstance).
W celu utworzenia klasy sterownika automatyzacji wywołuje się opcję AddClass ClassWizarda, z
podopcją From a type library, a następnie wskazuje plik z biblioteką typów. Plik taki jest generowany przy
kompilacji serwerów automatyzacji, a także dołączany do katalogów instalacyjnych produktów będących
takimi serwerami (np. MS Office).
Utworzona klasa lub zestaw klas umożliwia dostęp do właściwości i metod wskazanej klasy lub klas
serwera poprzez specjalny zestaw pomocniczych funkcji. Obiekty tych klas należy inicjalizować poprzez
wywołanie jednaj z funkcji składowych:
· CreateDispatch – tworzy nowy obiekt z interfejsem IDispatch na podstawie nazwy lub CLSID
· AttachDispatch – tworzy obiekt na podstawie uchwytu LPDISPATCH.
MICROSOFT FOUNDATION CLASSES
TECHNOLOGIA COM – KONTROLKI ACTIVEX
KONTROLKI ACTIVEX
Od pozostałych typów obiektów COM kontrolki ActiveX odróżniają się na ogół faktem posiadania
interfejsem użytkownika (choć oficjalna specyfikacja tak tego nie określa). Oprogramowanie korzystające z
kontrolki (klient) nazywa się kontenerem. Kod wykonywalny kontrolek ActiveX umieszczany jest w specjalnym pliku o rozszerzeniu OCX. Technicznie rzecz biorąc nie różni się on zresztą od DLL.
Projekt kontrolki ActiveX tworzy się wybierając odpowiedni typ projektu na pierwszej planszy
AppWizarda. Kontrolka ActiveX jest reprezentowana przez obiekt klasy potomnej COleControl. Pod wieloma względami praca z kontrolkami jest zbliżona do pracy z serwerami automatyzacji.
DODAWANIE WŁAŚCIWOŚCI I METOD
Właściwości i metody dodaje się tak samo, jak do serwerów automatyzacji. Jedynym nowym elementem są tzw. właściwości i metody magazynowe (stock) i otaczające (ambient).
Magazynowe właściwości i metody to standardowe elementy zrozumiałe zarówno dla kontrolki, jak
i jej kontenera, i dla obu mający predefiniowane znaczenia. Mają one na celu zapewnienie elementarnej
zgodności pomiędzy różnymi implementującymi je kontrolkami a kontenerami. W przypadku ActiveX pole
do edycji nazwy zewnętrznej jest listą typu combo; aby stworzyć właściwość lub metodę magazynową należy wybrać jedną z proponowanych na tej liście nazw i włączyć opcję Stock. Odtąd będą one dostępne – za
pośrednictwem Get/SetNazwa w przypadku właściwości lub pod swoją nazwą w przypadku metod.
Właściwości otaczające są zaimplementowane w kontenerze kontrolki (stąd nazwa) i stąd zawsze
dostępne – bez potrzeby wcześniejszego tworzenia czy deklarowania. Dostępne są poprzez odwołania do
predefiniowanych funkcji klasy COleControl (np. AmbientBackColor) lub poprzez GetAmbientProperty.
DODAWANIE ZDARZEŃ
Zdarzenia informują kontener, w którym umieszczono kontrolkę, o zaistnieniu określonego warunku
– są więc zgłaszane przez kontrolkę, i zazwyczaj dalej nieobsługiwane. Zdarzenia dodaje się za pomocą zakładki ActiveX Events ClassWizarda. Po użyciu przycisku AddEvent podaje się nazwę zdarzenia – zewnętrzną i wewnętrzną, a także listę przesyłanych parametrów. Istnieje też możliwość zadeklarowania zdarzenia magazynowego. Zgłoszenie zdarzenia odbywa się poprzez wywołanie funkcji o nazwie podanej w
polu InternalName (zwykle FireNazwaZdarzenia).
RYSOWANIE
Rysowanie kontrolek ActiveX nie odbiega w zasadzie od rysowania zwykłych okien w MFC. Procedurę rysującą, OnDraw, tworzy AppWizard. Parametry jej wywołania są nieco bogatsze niż w przypadku
konwencjonalnych okien.
TRWAŁOŚĆ (PERSISTENCY)
Trwałe właściwości (persistent properties) to właściwości, których wartości są przechowywane poza
okresami aktywności kontrolki. Jeśli chcemy ustawić właściwości w arkuszu właściwości Visual Basica lub
konfigurować je ze strony WWW, musimy uczynić je trwałymi. Z drugiej strony trwałość właściwości wydłuża czas ładowania i inicjalizacji kontrolek, tak więc nie należy mnożyć trwałych właściwości bez potrzeby.
Aby włączyć trwałość dla dowolnej właściwości, należy odnaleźć funkcję DoPropExchange (jest
ona tworzona przez AppWizarda) i dodać na jej końcu następujące wywołania (po jednym dla każdej trwałej
właściwości):
PX_TTT(pPX, _T("Nazwa"), zmienna, wart_domyślna);
Klasa COleControl oferuje zestaw funkcji PX_TTT dla różnych typów danych (np. PX_Long,
PX_String); należy wybrać właściwą spośród nich. Obiekt pPX steruje wymianą danych i jest dostarczany do
DoPropExchange jako parametr. Nazwa odpowiada zewnętrznej nazwie właściwości. Dane będą wpisywane
lub pobierane do lub z podanej zmiennej. Przy odczycie danych może zostać zastosowana wartość domyślna.
Podana funkcja jest wywoływana zarówno przy zapisie wartości właściwości, jak i przy ich odtwarzaniu. To,
który z tych dwóch przypadków występuje, można ustalić wywołując pPX->IsLoading().
TWORZENIE ARKUSZY WŁAŚCIWOŚCI
Arkusze właściwości pomagają skonfigurować kontrolkę na etapie jej projektowania (np. w Visual
Basic czy FrontPage). Służą do edycji wartości trwałych właściwości kontrolki.
System uzupełnia arkusz o własną stronę pozwalającą edytować wszystkie trwałe właściwości. Jeśli
to nie wystarcza, można skorzystać z własnej strony właściwości, której wstępny projekt dostarcza AppWizard. Należy użyć edytora zasobów by zaprojektować wygląd strony właściwości, a następnie ClassWizarda
by dodać zmienne odpowiadające elementom okna. W polu optional property name wpisujemy zewnętrzną
nazwę właściwości.
UWAGI O AKTUALIZACJI WŁAŚCIWOŚCI
W razie zmiany wartości trwałych wartości warto wprowadzić dwa dodatkowe wywołania w funkcji
typu Set lub w funkcji notyfikacji:
· funkcja SetModifiedFlag informuje system, że stan kontrolki został zmieniony i powinien zostać
zapamietany,
· funkcja BoundPropertyChanged informuje kontener o zmianie wartości właściwości; parametrem jest identyfikator dispinterfejsu o nazwie dispidNazwaWłaściwości. Wymagana m.in. do w
pełni poprawnej współpracy z Visual Basic.
Funkcja SetModifiedFlag jest automatycznie wstawiana przez ClassWizarda; dla kontrolek, które nie
mają być trwałe, można ja usunąć.
W pewnych przypadkach zmiana właściwości powinna natychmiast skutkować zmianą wyglądu
kontrolki; w takim przypadku można wywołać InvalidateControl.
PEWNE WSKAZÓWKI DOTYCZĄCE OPTYMALIZACJI KONTROLEK
Aby przyspieszyć proces ładowania i inicjalizacji kontrolek można:
· ograniczyć do minimum liczbę właściwości, metod i zdarzeń (kandydatem do usunięcia jest metoda About),
· ograniczyć do minimum liczbę właściwości trwałych,
· zlikwidować własne strony właściwości, poprzestając na standardowej,
· ograniczać liczbę odwołań do MFC (można też przejść na ATL).
KORZYSTANIE Z KONTROLEK
Użycie kontrolek ActiveX w Visual C++ wymaga wprowadzenia kontrolki do projektu za pomocą
opcji Project|Add to project|Components and controls. Kontrolka będzie w pełni obsługiwana zarówno przez
ClassWizarda (tworzona jest klasa C++), jak i edytor zasobów (kontrolka jest wyświetlana na palecie obiektów, obsługiwane są arkusze właściwości itd.).
Użycie kontrolek ActiveX w Visual Basic wymaga zaznaczenia kontrolki w okienku uzyskiwanym
opcją Projects|Components. Powoduje to wprowadzenie kontrolki do palety dostepnych obiektów.
Użycie kontrolek ActiveX w FrontPage wymaga użycia opcji Insert|Advanced|ActiveX control,
zwykle w połączeniu z przyciskiem Customize. Możliwe jest edytowanie właściwości kontrolki za pomocą
dostępnych arkuszy właściwości.
Użycie kontrolek ActiveX w HTML wymaga zastosowania nastepującej konstrukcji:
<object classid="clsid:0D276E33-465D-450D-B682-B296AD7DD9A7" id="OLECtrl1" width="206"
height="174">
<param name="_Version" value="65536">
<param name="_ExtentX" value="5450">
<param name="_ExtentY" value="4604">
<param name="_StockProps" value="9">
<param name="ForeColor" value="0">
<param name="BackColor" value="16777215">
</object>
Identyfikator klasy clsid można łatwo odnaleźć w pliku *.ODL (jako uuid ... coclass).
Kontrolki łatwo współpracują z językami skryptowymi, szczególnie z VBScript. Do zdefiniowanej
powyżej kontrolki można się odwoływać przez OLECtrl1.NazwaWłaściwości lub OLECtrl1.NazwaMetody(...), zaś funkcje zdefiniowane jako Sub OleCtrl1_NazwaZdarzenia(...) obsługują generowane zdarzenia.
OXGAME CONTROL
PRZYKŁADOWA KONTROLKA ACTIVEX
– GRA W KÓŁKO I KRZYŻYK
PROPERTIES:
OLE_COLOR
OLE_COLOR
OLE_COLOR
OLE_COLOR
short
short
short
short
METHODS:
void
BOOL
short
EVENTS:
BackColor
ForeColor
CircleColor
CrossColor
Starts
Move
Who
AtPos(x, y)
stock
stock
variable
variable
var
Get, r/o
Get, r/o
Get, param, r/o
kto rozpoczyna grę
numer ruchu (0 = początek gry)
kto ma teraz ruch
NewGame()
DoMove(x, y)
Status()
rozpoczyna nową grę
wykonuje ruch
status gry (kto wygrał?)
NowMove(who)
GameOver(winner)
oczekiwanie na ruch gracza
koniec gry
FRAGMENTY KODU ŹRÓDŁOWEGO IMPLEMENTACJI KONTROLKI:
COXGameCtrl::COXGameCtrl()
{
// AppWizard
InitializeIIDs(&IID_DOXGame,
&IID_DOXGameEvents);
m_circleColor = 0x00ff0000;
m_crossColor = 0x000000ff;
m_starts = OX_CIRCLE;
NewGame();
}
void COXGameCtrl::OnCircleColorChanged()
{
SetModifiedFlag();
BoundPropertyChanged(dispidCircleColor);
InvalidateControl();
}
void COXGameCtrl::OnCrossColorChanged()
{
SetModifiedFlag();
BoundPropertyChanged(dispidCrossColor);
InvalidateControl();
}
short COXGameCtrl::GetMove()
{
return m_move;
}
short COXGameCtrl::GetWho()
{
if (m_move >= 9)
return OX_NONE;
if ((m_move & 1) == 0)
return m_starts;
else
return OX_CIRCLE+OX_CROSS - m_starts;
}
void COXGameCtrl::OnStartsChanged()
{
SetModifiedFlag();
BoundPropertyChanged(dispidStarts);
}
short COXGameCtrl::GetAtPos(short x, short y)
{
return m_pos[x][y];
}
void COXGameCtrl::NewGame()
{
m_move = 0;
memset(m_pos, 0, sizeof(m_pos));
InvalidateControl();
FireNowMove(GetWho());
}
BOOL COXGameCtrl::DoMove(short x, short y)
{
if (m_move<9 && GetAtPos(x, y)==OX_NONE)
{
m_pos[x][y] = GetWho();
InvalidateControl();
m_move++;
int nWinner;
if ((nWinner = Status()) != OX_NONE)
{
m_move = 9;
FireGameOver(nWinner);
}
else
FireNowMove(GetWho());
return TRUE;
}
else
{
FireIllegalMove(x, y);
return FALSE;
}
}
void COXGameCtrl::DoPropExchange(
CPropExchange* pPX)
{
ExchangeVersion(pPX,
MAKELONG(_wVerMinor, _wVerMajor));
COleControl::DoPropExchange(pPX);
PX_Long(pPX, "CrossColor",
(long&)m_crossColor, 0x000000ff);
PX_Long(pPX, "CircleColor",
(long&)m_circleColor, 0x00ff0000);
PX_Short(pPX, "Starts", m_starts,
OX_CIRCLE);
}
short COXGameCtrl::Status()
{
for (int i = 0; i < 3; i++)
{
if (m_pos[i][0] != OX_NONE
return m_pos[i][0];
if (m_pos[0][i] != OX_NONE
return m_pos[0][i];
}
if (m_pos[0][0] != OX_NONE &&
return m_pos[0][0];
if (m_pos[2][0] != OX_NONE &&
return m_pos[2][0];
&& m_pos[i][0] == m_pos[i][1] && m_pos[i][1] == m_pos[i][2])
&& m_pos[0][i] == m_pos[1][i] && m_pos[1][i] == m_pos[2][i])
m_pos[0][0] == m_pos[1][1] && m_pos[1][1] == m_pos[2][2])
m_pos[2][0] == m_pos[1][1] && m_pos[1][1] == m_pos[0][2])
// bez zwycięzcy
if (m_move >= 9) return OX_DRAW;
else
return OX_NONE;
}
void COXGameCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
COleControl::OnLButtonDown(nFlags, point);
int sx, sy; GetControlSize(&sx, &sy);
DoMove((3 * point.x) / sx, (3 * point.y) / sy);
}
int COXGameCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (COleControl::OnCreate(lpCreateStruct) == -1) return -1;
NewGame();
return 0;
}
void COXGameCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
// przygotowanie narzędzi
CBrush brushBack; brushBack.CreateSolidBrush(TranslateColor(GetBackColor()));
CPen
penFore;
penFore.CreatePen(PS_SOLID, 5, (TranslateColor(GetForeColor())));
CPen
penCircle; penCircle.CreatePen(PS_SOLID, 5, (TranslateColor(m_circleColor)));
CPen
penCross; penCross.CreatePen(PS_SOLID, 5, (TranslateColor(m_crossColor)));
CBrush *pOldBrush = pdc->SelectObject(&brushBack);
CPen *pOldPen = pdc->SelectObject(&penFore);
// położenie planszy, wymiar planszy i komórki
int x0 = rcBounds.left;
int y0 = rcBounds.top;
int dx = rcBounds.Width() / 3;
int dy = rcBounds.Height() / 3;
// kolor tła
pdc->FillRect(rcBounds, &brushBack);
// kratka
int x, y, i, j;
pdc->SelectObject(&penFore);
for (i = 0, x = x0 + dx, y = y0 + dy; i < 2; i++, x += dx, y += dy)
{
pdc->MoveTo(x, y0); pdc->LineTo(x, y0 + rcBounds.Height());
pdc->MoveTo(x0, y); pdc->LineTo(x0 + rcBounds.Width(), y);
}
// znaczki
for (i = 0, x = x0; i < 3; i++, x += dx)
for (j = 0, y = y0; j < 3; j++, y += dy)
if (m_pos[i][j] == OX_CIRCLE)
{
pdc->SelectObject(penCircle);
pdc->Ellipse(x + 5, y + 5, x + dx - 5, y + dy - 5);
}
else
if (m_pos[i][j] == OX_CROSS)
{
pdc->SelectObject(penCross);
pdc->MoveTo(x + 5, y + 5); pdc->LineTo(x + dx - 5, y + dy - 5);
pdc->MoveTo(x + 5, y + dy - 5); pdc->LineTo(x + dx - 5, y + 5);
}
pdc->SelectObject(pOldPen);
pdc->SelectObject(pOldBrush);
}
PLIK HTML:
<html>
<head>
<meta http-equiv="Content-Language" content="pl">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1250">
<meta name="GENERATOR" content="Microsoft FrontPage 4.0">
<meta name="ProgId" content="FrontPage.Editor.Document">
<title>Kółko i krzyżyk</title>
<script language=vbs>
Sub OXGame1_GameOver(winner)
if winner = 1 Then
theForm.T1.Value = ""
alert("Zwycięzcą jest: KÓŁKO!")
theForm.T1.Value = "Zwycięzcą jest: KÓŁKO!"
else
if winner = 2 Then
theForm.T1.Value = ""
alert("Zwycięzcą jest: KRZYŻYK!")
theForm.T1.Value = "Zwycięzcą jest: KRZYŻYK!"
else
theForm.T1.Value = ""
alert("--- REMIS ---")
theForm.T1.Value = "REMIS"
end if
end if
End Sub
Sub OXGame1_NowMove(who)
if who = 1 Then
theForm.T1.Value = "Teraz gra: KÓŁKO"
else
theForm.T1.Value = "Teraz gra: KRZYŻYK"
end if
End Sub
Sub OXGame1_IllegalMove(x, y)
End Sub
Sub B1_onclick()
OXGame1.NewGame
End Sub
</script>
</head>
<body>
<p><b><font size="6">Kółko i krzyżyk</font></b></p>
<p>
<object classid="clsid:0D276E33-465D-450D-B682-B296AD7DD9A7" id="OXGame1" width="206"
height="174">
<param name="_Version" value="65536">
<param name="_ExtentX" value="5450">
<param name="_ExtentY" value="4604">
<param name="_StockProps" value="9">
<param name="ForeColor" value="0">
<param name="BackColor" value="16777215">
<param name="CrossColor" value="255">
<param name="CircleColor" value="16711680">
<param name="Starts" value="1">
</object>
</p>
<form method="POST" name="theForm">
<p> <input type="text" name="T1" size="24" value="Teraz gra: KÓŁKO"><input type="button"
value="Nowa gra" name="B1"></p>
</form>
</body>
</html>
BIBLIOTEKA MFC
PYTANIA NA EGZAMIN USTNY 2001
Egzamin składa się z trzech pytań wylosowanych z poniższej listy. Odpowiedzi powinny
charakteryzować działanie odpowiednich mechanizmów i technologie zastosowane w bibliotece
MFC. Poza najbardziej podstawowymi dla danego zagadnienia, nie jest wymagana szczegółowa
znajomość identyfikatorów klas i ich elementów składowych.
1. Application framework – wyjaśnij pojęcie i opisz główne klasy na nie się składające.
2. Klasa aplikacji. Funkcja składowa InitInstance.
3. Architektura dokument – widok. Zakres odpowiedzialności obiektów.
4. Architektura dokument – widok. Metody odświeżania zawartości widoku.
5. Klasa CView i jej podklasy.
6. Rysowanie i narzędzia graficzne (GDI).
7. Aplikacje z wieloma typami dokumentów i dokumenty z wieloma typami widoków.
8. Okna dzielone (splitter windows).
9. Dystrybucja komunikatów i mapy komunikatów.
10. Architektura drukowania w MFC.
11. Dzielenie dokumentów na strony podczas drukowania.
12. Okna dialogowe modalne i niemodalne, inicjalizacja i wymiana danych (DDX i DDV).
13. Okna kontrolne w dialogach; tworzenie okien własnych klas (subclassing) i ich obsługa.
14. Biblioteki DLL w programach MFC i biblioteki MFC DLL.
15. Obsługa baz danych: klasy CDatabase i CRecordet.
16. Przeglądanie, sortowanie i filtrowanie danych. Wykorzystanie poleceń języka SQL.
17. Aplikacje bazodanowe w architekturze dokument – widok.
18. Tworzenie i wykorzystanie serwerów automatyzacji.
19. Tworzenie kontrolek ActiveX.
20. COM: właściwości, metody i zdarzenia. Właściwości magazynowe i otaczające. Trwałość.
21. Implementacja podstawowych protokołów internetowych.
22. Obsługa sieci Internet przy pomocy gniazd.
23. Aplikacje wielowątkowe. Wątki robocze i wątki interfejsu użytkownika.
24. Aplikacje wielowątkowe: synchronizacja wątków.