Multi Document Interface. Segítségével egyszerre több dokumentumablak szerkeszthető. Ezket az egy alaklmazáshoz tartozó ablakokat child window-nak nevezzük. Evileg végtelen számú nyitható meg egy -egy típusból. Tehát ugyanabból a Class-ból egyszerre bármennyi változó lehet. Így létrehozásuk dinamikusan futás időben a (new operátorral) történik, és természetesen kapcsolatot kell tartaniuk az őket létrehozó szülő (Parent) ablakkal. Így elöljáróban csak ennyit. Nézzük kicsit képletesebben.

Ahogy az SDI-nél, a főablak itt is egy frame window. Ennek egy speciális child ablaka az MDICLIENT window. Ez kezeli a már SDI-nél is létező CLIENT Area-t, beleértve az összes child window-al. A child window szerkezete tulajdonképpen megegyezik a már megismert Frame window-al.

A Frame (Most Child) window szerkezete:

A figyelmesebbek észrevehetik, hogy az ábra pontosan ugyanaz, mint az SDI-s frame window.

Tehát, mint látszik, a Child window szinte ugyanolyam mint az eddig már megismert Frame window, csak ugye egyszerre több lehet belőle, és van szülő ablaka. Mely szülő a Main Frame. Normális esetben a megjelnítést egy View Class végzi, miközben a Dokumentummal kapcsolatot tart. Az egyszerűség kedvéért most View és Dokumentumkezelést kihagyom, ezekről majd később bővebben.

Tehát a példaprogram a legegyszerűbb esetet próbálja feldolgozni, így talán még meg is lehet érteni.

Hogyan is kell elkezdeni egy ilyen programot. Első nekifutásra az AppWizard a legegyszerűbb megoldás. Ugyanúgy, ahogy eddig, a típusnál pedig értelemszerűen MDI-t kell választani. Nem kell mindenféle extrát beixelni, nem biztos, hogy átláthatóbbá teszi a problémát. Ha ez megvan, tisztogatnunk kell egy kicsit. Törölni kell a View és Doc Class-okat, mivel nem lesz rájuk szükség.

Ha minden megvan, a következő Classok fognak látszani:
 

Class Feladata Származtatjuk: A programban:
Az alkalmazás CwinApp CMDIApp
A Főablak CMDIFrameWnd CMainFrame
A Child Window CMDIChildWnd CChildFrame

Ezeken kívül természetesen még sok-sok más lehetne, de ez a váz. 

Mint látható minden egyes Class-nak jól definiált feladata van. Nézzük először a legegyszerűbbet, és legismerősebbet, az alkalmazás objektumát.:

class CMDIApp : public CWinApp { public: CMDIApp(); //{{AFX_VIRTUAL(CMDIApp) public: virtual BOOL InitInstance(); //}}AFX_VIRTUAL // Implementation //{{AFX_MSG(CMDIApp) afx_msg void OnAppAbout(); //}}AFX_MSG DECLARE_MESSAGE_MAP() };

Van itt egy konstruktor, egy kezelőfüggvény, ami az About-ot nyitja, meg az InitInstance. Ezek közül egyedül az InitInstance() érdekes igazán. Mint nevéből is kiderül, itt kell megteremteni a kezdőfeltételeket.

BOOL CMDIApp::InitInstance() { #ifdef _AFXDLL Enable3dControls();   // Call this when using MFC in a shared DLL #else Enable3dControlsStatic(); // Call this when linking to MFC statically #endif Eddig csak a 3d Control betöltése került sorra, de ezután jönnek a fontos dolgok. CMainFrame* pMainFrame = new CMainFrame; if (!pMainFrame->LoadFrame(IDR_MAINFRAME))   return FALSE; m_pMainWnd = pMainFrame;

Létrehozzuk a MainFrame-et (Ld. Az ábra), azaz a főablakot. Ez dinamikusan, new operátorral történik. A főablak címét a CWinApp m_pMainWnd változójába kell tenni, tulajdonképpen itt kapcsolódik az ablak az alkalmazással. Az ablakhoz természetesen be kell töletni a hozzá tartozó erőforrást, ez a LoadFrame függvénnyel történik, és egyszerűen az azonosítót kell megadni. Az erőforrás itt, és általában a Frame ablakoknál egy egyszerű menüsor, vagy ikonsor. 

pMainFrame->ShowWindow(m_nCmdShow); pMainFrame->UpdateWindow(); return TRUE;

Miután megvan az ablak erőforrássával együtt, meg kell jeleníteni. A ShowWindow függvényt meghívva kirajzolódik az ablak. Egyedül a főablaknál kell meghívni ezt a függvényt, tehát egy alklamazás során csak egyszer. Ezekután az Update meghívásával kiváltunk egy WM_PAINT üzenetet, melynek hatására a Client Area újrarajzolódik. Esetünkben megjelenik. 

A MainFrame objektuma:

CMainFrame: class CMainFrame : public CMDIFrameWnd { public: CMainFrame();   //konstruktor public: virtual ~CMainFrame();  //destruktor protected: //{{AFX_MSG(CMainFrame) afx_msg void OnNew(); //}}AFX_MSG DECLARE_MESSAGE_MAP() };

Egy konstruktor, egy destruktor, meg egy üzenetkezelőfüggvény. A konstruktor és a destruktor jelen esetben üres. Az üzenetkezelő függvény végzi a Child ablakok létrehozását, szempontunkból ez az érdekes.
Először az ablak objektumánk létrehozása a new paranccsal. Az objektumra mutató pointert kapunk.

CChildFrame *pChildWnd = new CChildFrame; //Ez lesz a Child Window

Ahoz, hogy egy általunk létrehozott Class-ból ablak legyen, regisztráltatni kell az ablakot. Ez annyit tesz, hogy a framework bejegyzi az ablakot a megadott tulajdonságokkal. Ez az AfxRegisterWndClass függvénnyel történik.

LPCTSTR AFXAPI AfxRegisterWndClass( UINT nClassStyle, HCURSOR hCursor = 0, HBRUSH hbrBackground = 0, HICON hIcon = 0 );

Visszatérési értéke egy C típusú string, amely az azonosítót tartalmazza. U.n. class name. Erre majd szükség lesz az ablak létrehozásánál.

A Paraméterek:

nClassStyle Az ablak stílusa. A stílusok kombinációit kell megadni a bitenkénti vagy (|) operátorral. Ezek a stílusok előre meghatározott konstansok. Van belőlük egy halom, a help-ben bárki megtalálja. A jelnleg használtak a CS_HREDRAW, és a CS_VREDRAW. Azt biztosítják, hogy mind horizontális, mind vertikális méretváltozáskor az ablak újrarajzolódjon.
hCursor A kurzor azonosítója. Lehet általunk gyártott is, de általában a LoadCursor függvény által betöltött szabvány kurzorokat használjuk. Erről majd a továbbiakban.
hbrBackground A háttér. Meghatározza a kitöltési színt és mintát. Típusa szerint HBRUSH, ami egy CBrush objektum handlere.
hIcon Az ikon handlere. Hogy ezt megkapjuk, a kurzorhoz hasonlóan úgy kell betölteni, a LoadIcon függvény segítségével.

A megemlített további függvények:

A kurzor betöltéséhez:

HCURSOR LoadCursor( HINSTANCE hInstance, LPCTSTR lpCursorName )

Visszatérési értéke a kurzor handlere

Paraméterek:

hInstance Annak a DLL-nek, vagy EXE-nek,vagy erőforrásállománynak az azonosítója, amely a kurzort tartalmazza. Szabványos kurzorokk betöltésénél ez NULL.
lpCursorName kurzor azonosítója. Előre meghatározott konstansok:
IDC_APPSTARTING nyíl+homokóra
IDC_ARROW sima nyíl
IDC_CROSS Crosshair
IDC_IBEAM I-beam
IDC_ICON megrajzolt ikon
IDC_NO nincs kurzor
IDC_SIZE méretezési kurzor
IDC_SIZEALL méretezési kurzor négyfelé nyíllal
IDC_SIZENESW jobbra fel,balra le nyíllal
IDC_SIZENS fel-le nyíllal
IDC_SIZENWSE balra fel, jobbra le
IDC_SIZEWE  jobbra-balra
IDC_UPARROW felfele mutató nyíl
IDC_WAIT Mindenki kedvence, a homokóra

Az Ikon betöltése:

HICON LoadIcon( HINSTANCE hInstance, LPCTSTR lpIconName)

Vissztérési értéke az Icon handlere

Paraméterek:

hInstance Ahogy a kurzorbetöltésnél, annak az erőforrásnak az azonosítója, ahol az Ikon van. Ez általában a saját programunk. A saját programunk handle-ét a AfxGetInstanceHandle() függvénnyel tudjuk meg.
lpIconName Az ikon azonosítója (name -je). A MAKEINRESOURCE makróval tudunk az ID ből name-t csinálni.
 

Tehát egy példa az ablak bejegyzésére, és name-jének eltárolására:

LPCTSTR lpszChildClass =   AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,        LoadCursor(NULL, IDC_NO),        (HBRUSH)(COLOR_WINDOW+1),        LoadIcon(AfxGetInstanceHandle(),

Ez nem egy egyszerű dolog, de még mielőtt az ablak megjelenne, a menu-t is be kell tölteni. A CWnd menu.LoadMenu függvényével történik.

Menu betöltése:

pChildWnd->menu.LoadMenu(IDR_LINEFRAME);

Ezekután a Child window m_hMenuShared tagváltozójának is meg kell adni a menut. Mivel ez a változó protected, csak az objektumon belülről lehet átírni.

m_hMenuShared = menu.m_hMenu;

Ha ez megvan, hozzuk létre az ablakot. A CWnd Create függvényével.

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

Visszatérési érték nem nulla, ha sikeres, nulla ha sikertelen.

Paraméterek:

lpszClassName Az AfxRegisterWndClass által adott Class Name.
lpszWindowName C típusú string, amely a címet tartalmazza.
dwStyle Az ablak stílusának további attributumai. Van belőle egy pár. 
Alapértelmezésben: WS_CHILD | WS_VISIBLE | WS OVERLAPPEDWINDOW
rect Az ablak pozíciója. Legkényelmesebb a rectDefault, ekkor az elhelyezés automatikus lesz.
pParentWnd A szülő ablak címe. Simán this.
pContext Az ablak Context-je. Ezt általában kell megadni, alapértelmezés NULL

Egy példa:

pChildWnd->Create(lpszChildClass, _T("I'm a Child"),        WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW,        rectDefault,        this); }

És már meg is van a Child Window.