Programowanie z wykorzystaniem klas MFC w Visual C++

autor: Kuba Bieszke

Stworzenie okna dla aplikacji Windows

Dodanie kontrolki

Dodanie właściwości kontrolki

Dodanie zdarzenia dla kontrolki

Dodanie zdarzenia dla właściwości kontrolki

Dodanie zdarzenia zewnętrznego

Dodatkowe przykłady

Wszelkie komentarze w kodzie są koloru zielonego i nie są one obowiązkowe przy kompilacji, ale dzięki nim próbowałem przybliżyć funkcje, które spełnia dany kod. Wszelkie nazwy takie jak menu itp. pisane są czcionką pochyloną. Każdy to chce poznać strukturę programowania w MFC musi sam zagłębić się w poniżej przytoczone kody i poeksperymentować z różnymi parametrami lub obiektami. Plik najlepiej zapisać na własnym dysku, a kody tutaj zamieszczone można skopiować bezpośrednio do Visual C++.

Tworzenie podstawowego okna dla aplikacji Windows.

Aby rozpocząć pisanie programu tworzymy nowy projekt Win32 Application, w menu Project/Settings... klikamy na zakładce General i wybieramy z listy Use MFC in a Shared DLL. Następnie dodajemy plik *.cpp, w którym zamieszczamy taki kod:

// button1.cpp
#include <afxwin.h>

// Deklaracja klasy aplikacji
class CButtonApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
// Stworz wyjscie dla klasy aplikacji
CButtonApp ButtonApp;
// Deklaracja klasy glownego okna
class CButtonWindow : public CFrameWnd
{
public:
CButtonWindow();
};
// Funkcja InitInstance jest wywoływana
// kiedy uruchamia sie aplikacja
BOOL CButtonApp::InitInstance()
{
m_pMainWnd = new CButtonWindow();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
// Konstrukcja klasy okna
CButtonWindow::CButtonWindow()
{
// Stworzenie okna
Create(NULL,
"Test",
WS_OVERLAPPEDWINDOW,
CRect(0,0,200,200));
}

Komentarze w prosty sposób opisują kod tej aplikacji(jeśli ktoś się chce w to zagłębić należy poszukać w Helpie). Nazwy CButtonApp, ButtonApp, CButtonWindow są nadane przeze mnie i mogą zostać zmienione. Sam szczegółowy opis parametrów okna znajduje się w ostatnich pięciu linijkach. Help Microsoft'a podaje następująca budowę tworzenia okna (CWnd::Create):

virtual BOOL Create( LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext = NULL);

lpszClassName - określenie klasy, NULL oznacza domyślne atrybuty CWnd
lpszWindowName - nazwa aplikacji ukazująca się na górnym pasku aplikacji
dwStyle - styl okna (w Helpie: Window Styles, mogą być oddzielane przez znak |)
rect - położenie okna
oraz pParentWnd, nID, pContext bardziej zaawansowane, nie użyte w naszym programie.

Po kompilacji i uruchomieniu ukaże się okno o nazwie Test

Dodanie przycisku - Button

Dodanie nowego obiektu polega na wywołaniu go w Deklaracji klasy głównego okna i stworzeniu go, podając jego parametry (podobnie jak budowanie okna). Wszystko co dołączamy do aplikacji musi być zadeklarowane w Deklaracji klasy głównego okna np. dodatkowe obiekty, Massage map(o czym później) itp.

// button2.cpp
#include <afxwin.h>
#define IDB_BUTTON 100

// Deklaracja klasy aplikacji
class CButtonApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
// Stworz wyjscie dla klasy aplikacji
CButtonApp ButtonApp;
// Deklaracja klasy glownego okna
class CButtonWindow : public CFrameWnd
{
CButton *button;
public:
CButtonWindow();
};
// Funkcja InitInstance jest wywoływana
// kiedy uruchamia sie aplikacja

BOOL CButtonApp::InitInstance()
{
m_pMainWnd = new CButtonWindow();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
// Konstrukcja klasy okna
CButtonWindow::CButtonWindow()
{
// Stworzenie okna
Create(NULL,
"Test",
WS_OVERLAPPEDWINDOW,
CRect(0,0,200,200));

// Stworz przycisk
button = new CButton();
button->Create("Nacisnij",
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
CRect(20,20,100,100),
this,
IDB_BUTTON);
}

Linia #define IDB_BUTTON 100 służy do nadania numer Id obiektowi, który dodajemy co w przyszłości może się przydać. Numerację zaczynamy od liczby 100, ponieważ od 1 do 99 są przypisane obiektom systemowym.

Dodanie własciwości przycisku, który przy uruchomieniu okna dostosuje swój rozmiar.

Dokładnie chodzi o to, że przycisk przy każdym uruchomieniu będzie w odległości 20 pixeli od każdej krawędzi okna. W parametrach okna usunięto jego rozmiary i dodano jeden parametr r, który jest skonfigurowany wcześniej.

// button3.cpp
#include <afxwin.h>
#define IDB_BUTTON 100

// Deklaracja klasy aplikacji
class CButtonApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
// Stworz wyjscie dla klasy aplikacji
CButtonApp ButtonApp;
// Deklaracja klasy glownego okna
class CButtonWindow : public CFrameWnd
{
CButton *button;
public:
CButtonWindow();
};
// Funkcja InitInstance jest wywoływana
// kiedy uruchamia sie aplikacja
BOOL CButtonApp::InitInstance()
{
m_pMainWnd = new CButtonWindow();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
// Konstrukcja klasy okna
CButtonWindow::CButtonWindow()
{
CRect r;
// Stworzenie okna
Create(NULL,
"Test",
WS_OVERLAPPEDWINDOW,
CRect(0,0,200,200));

// Pobierz rozmiar okna
GetClientRect(&r);
r.InflateRect(-20,-20);

// Stworz przycisk
button = new CButton();
button->Create("Nacisnij",
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
r,
this,
IDB_BUTTON);
}

Dodanie Message map, czyli zdarzenia do wykonania.

Message maps są to zdarzenia, które mają miejsce gdy spełnione zostaną oczekiwania programu np. ingerencja użytkownika lub jakichś innych zdarzeń. W naszym programie po kliknięciu przycisku odezwie się wewnętrzny głośnik komputera. Kod dotyczący wykonania zdarzenia znajduje się w części Funkcja do wykonania.

// button4.cpp
#include <afxwin.h>
#define IDB_BUTTON 100

// Deklaracja klasy aplikacji
class CButtonApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
// Stworz wyjscie dla klasy aplikacji
CButtonApp ButtonApp;
// Deklaracja klasy glownego okna
class CButtonWindow : public CFrameWnd
{
CButton *button;
public:
CButtonWindow();
afx_msg void KlikButton();
DECLARE_MESSAGE_MAP()
};

// Funkcja do wykonania
void CButtonWindow::KlikButton()
{
MessageBeep(-1);
}
// Message map
BEGIN_MESSAGE_MAP(CButtonWindow, CFrameWnd)
ON_BN_CLICKED(IDB_BUTTON, KlikButton)
END_MESSAGE_MAP()

// Funkcja InitInstance jest wywoływana
// kiedy uruchamia sie aplikacja

BOOL CButtonApp::InitInstance()
{
m_pMainWnd = new CButtonWindow();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
// Konstrukcja klasy okna
CButtonWindow::CButtonWindow()
{
CRect r;
// Stworzenie okna
Create(NULL,
"Test",
WS_OVERLAPPEDWINDOW,
CRect(0,0,200,200));

// Pobierz rozmiar okna
GetClientRect(&r);
r.InflateRect(-20,-20);

// Stworz przycisk
button = new CButton();
button->Create("Nacisnij",
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
r,
this,
IDB_BUTTON);
}

W części oznaczonej komentarzem Message map określamy zdarzenie, które jest oczekiwane, u nas kliknięcie myszką na przycisku czyli ON_BN_CLICKED(IDB_BUTTON, KlikButton)

Dodanie zdarzenia związanego z właściwością przycisku.

W przykładzie tym rozszerzymy funkcję dostosowania rozmiaru przycisku do krawędzi okna, która będzie wywoływana nie tylko przy uruchamianiu okna, ale także przy zmianie jego rozmiaru. Użyjemy do tego Message maps.

// button5.cpp
#include <afxwin.h>
#define IDB_BUTTON 100

// Deklaracja klasy aplikacji
class CButtonApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
// Stworz wyjscie dla klasy aplikacji
CButtonApp ButtonApp;
// Deklaracja klasy glownego okna
class CButtonWindow : public CFrameWnd
{
CButton *button;
public:
CButtonWindow();
afx_msg void KlikButton();
afx_msg void OnSize(UINT, int, int);
DECLARE_MESSAGE_MAP()
};

// Funkcja do wykonania
void CButtonWindow::KlikButton()
{
MessageBeep(-1);
}
// Funkcja do wykonania
void CButtonWindow::OnSize(UINT nType, int cx,
int cy)
{
CRect r;
GetClientRect(&r);
r.InflateRect(-20,-20);
button->MoveWindow(r);
}

// Message map
BEGIN_MESSAGE_MAP(CButtonWindow, CFrameWnd)
ON_BN_CLICKED(IDB_BUTTON, KlikButton)
ON_WM_SIZE()
END_MESSAGE_MAP()

// Funkcja InitInstance jest wywoływana
// kiedy uruchamia sie aplikacja
BOOL CButtonApp::InitInstance()
{
m_pMainWnd = new CButtonWindow();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
// Konstrukcja klasy okna
CButtonWindow::CButtonWindow()
{
CRect r;
// Stworzenie okna
Create(NULL,
"Test",
WS_OVERLAPPEDWINDOW,
CRect(0,0,200,200));

// Pobierz rozmiar okna
GetClientRect(&r);
r.InflateRect(-20,-20);

// Stworz przycisk
button = new CButton();
button->Create("Nacisnij",
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
r,
this,
IDB_BUTTON);
}

Dodanie zdarzenia zewnętrznego, nie związanego z obiektami.

Na koniec dodamy zegar, który będzie wywoływał pikanie głośnika co sekundę.

// button6.cpp
#include <afxwin.h>
#define IDB_BUTTON 100
#define IDT_TIMER1 200

// Deklaracja klasy aplikacji
class CButtonApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
// Stworz wyjscie dla klasy aplikacji
CButtonApp ButtonApp;
// Deklaracja klasy glownego okna
class CButtonWindow : public CFrameWnd
{
CButton *button;
public:
CButtonWindow();
afx_msg void KlikButton();
afx_msg void OnSize(UINT, int, int);
afx_msg void OnTimer(UINT);
DECLARE_MESSAGE_MAP()
};

// Funkcja do wykonania
void CButtonWindow::KlikButton()
{
MessageBeep(-1);
}
// Funkcja do wykonania
void CButtonWindow::OnSize(UINT nType, int cx,
int cy)
{
CRect r;
GetClientRect(&r);
r.InflateRect(-20,-20);
button->MoveWindow(r);
}
// Funkcja do wykonania
void CButtonWindow::OnTimer(UINT id)
{
MessageBeep(-1);
}

// Message map
BEGIN_MESSAGE_MAP(CButtonWindow, CFrameWnd)
ON_BN_CLICKED(IDB_BUTTON, KlikButton)
ON_WM_SIZE()
ON_WM_TIMER()
END_MESSAGE_MAP()

// Funkcja InitInstance jest wywoływana
// kiedy uruchamia sie aplikacja
BOOL CButtonApp::InitInstance()
{
m_pMainWnd = new CButtonWindow();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
// Konstrukcja klasy okna
CButtonWindow::CButtonWindow()
{
CRect r;
// Stworzenie okna
Create(NULL,
"Test",
WS_OVERLAPPEDWINDOW,
CRect(0,0,200,200));

// Nastaw zegar
SetTimer(IDT_TIMER1, 1000, NULL); // 1000 ms.

// Pobierz rozmiar okna
GetClientRect(&r);
r.InflateRect(-20,-20);

// Stworz przycisk
button = new CButton();
button->Create("Nacisnij",
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
r,
this,
IDB_BUTTON);
}

Dodatkowe przykłady

1. Dość rozbudowany, buduje scroll bar czyli pasek przewijania, który można przeciągać w lewo i w prawo. Znajduje się tu kilka dodatkowych deklaracji.

// sb1.cpp
#include <afxwin.h>
#define IDM_SCROLLBAR 100
const int MAX_RANGE=100;
const int MIN_RANGE=0;
// Deklaracja klasy aplikacji
class CScrollBarApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
// Stworz wyjscie dla klasy aplikacji
CScrollBarApp ScrollBarApp;
// Deklaracja klasy glownego okna
class CScrollBarWindow : public CFrameWnd
{
CScrollBar *sb;
public:
CScrollBarWindow();
afx_msg void OnHScroll(UINT nSBCode, UINT nPos,
CScrollBar* pScrollBar);
DECLARE_MESSAGE_MAP()
};

// Funkcja do wykonania
void CScrollBarWindow::OnHScroll(UINT nSBCode,
UINT nPos, CScrollBar* pScrollBar)
{
int pos;
pos = sb->GetScrollPos();
switch ( nSBCode )
{
case SB_LINEUP:
pos -= 1;
break;
case SB_LINEDOWN:
pos += 1;
break;
case SB_PAGEUP:
pos -= 10;
break;
case SB_PAGEDOWN:
pos += 10;
break;
case SB_TOP:
pos = MIN_RANGE;
break;
case SB_BOTTOM:
pos = MAX_RANGE;
break;

case SB_THUMBPOSITION:
pos = nPos;
break;
default:
return;
}
if ( pos < MIN_RANGE )
pos = MIN_RANGE;
else if ( pos > MAX_RANGE )
pos = MAX_RANGE;
sb->SetScrollPos( pos, TRUE );
}
// Message map
BEGIN_MESSAGE_MAP(CScrollBarWindow, CFrameWnd)
ON_WM_HSCROLL()
END_MESSAGE_MAP()
// Funkcja InitInstance jest wywoływana
// kiedy uruchamia sie aplikacja
BOOL CScrollBarApp::InitInstance()
{
m_pMainWnd = new CScrollBarWindow();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
// Konstrukcja klasy okna
CScrollBarWindow::CScrollBarWindow()
{
CRect r;
// Stworzenie okna
Create(NULL,
"CScrollBar Tests",
WS_OVERLAPPEDWINDOW,
CRect(0,0,200,200));

// Pobierz rozmiar okna
GetClientRect(&r);
// Stworz scroll bar
sb = new CScrollBar();
sb->Create(WS_CHILD|WS_VISIBLE|SBS_HORZ,
CRect(10,10,r.Width()-10,30),
this,
IDM_SCROLLBAR);
sb->SetScrollRange(MIN_RANGE,MAX_RANGE,TRUE);
}

2. Aplikacja wyświetla dwa okna edit i gdy wpiszemy do dolnego jakiś tekst i naciśniemy button taki sam tekst pojawi się w górnym oknie. Do przesyłania tekstu użyto danych typu string dla, których powstał obiekt o nazwie s1.

#include <afxwin.h>
#include <afxcmn.h>
#define IDB_BUTTON 100
#define IDB_EDIT1 110
#define IDB_EDIT2 120

// Deklaracja klasy aplikacji
class CButtonApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
// Stworz wyjscie dla klasy aplikacji
CButtonApp ButtonApp;
// Deklaracja klasy glownego okna
class CButtonWindow : public CFrameWnd
{
CButton *button;
CEdit* edit1;
CString s1;
CEdit* edit2;

public:
CButtonWindow();
afx_msg void KlikButton();
DECLARE_MESSAGE_MAP()
};


// Message map
BEGIN_MESSAGE_MAP(CButtonWindow, CFrameWnd)
ON_BN_CLICKED(IDB_BUTTON, KlikButton)
END_MESSAGE_MAP()

// Funkcja InitInstance jest wywoływana
// kiedy uruchamia sie aplikacja
BOOL CButtonApp::InitInstance()
{
m_pMainWnd = new CButtonWindow();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
// Konstrukcja klasy okna
CButtonWindow::CButtonWindow()
{

// Stworzenie okna
Create(NULL,
"Naciśnij NAPIS",
WS_OVERLAPPEDWINDOW,
CRect(0,0,400,200));

// Stworz przycisk
button = new CButton();
button->Create("NAPIS",
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
CRect(20,20,100,100),
this,
IDB_BUTTON);

edit1 = new CEdit();
edit1->Create(WS_CHILD|WS_VISIBLE|WS_BORDER|ES_MULTILINE,
CRect(200,10,300,50),
this,
IDB_EDIT1);

edit2 = new CEdit();
edit2->Create(WS_CHILD|WS_VISIBLE|WS_BORDER|ES_MULTILINE,
CRect(200,70,300,110),
this,
IDB_EDIT2);

}
// Funkcja do wykonania
void CButtonWindow::KlikButton()
{
edit2->GetWindowText(s1);
edit1->SetWindowText(s1);

}