Create Multiple Document Interface(MDI)

This example is comprised of three files:

Source code(createmdi.cpp)
//=============================================================================
//CREATE MULTIPLE DOCUMENT INTERFACE(MDI) - Copyright © 2000,2005 Ken Fitlike
//=============================================================================
//API functions used: CreateWindowEx,DefFrameProc,DefMDIChildProc,
//DispatchMessage,EnumChildWindows,GetDlgItem,GetMenu,GetMessage,
//GetModuleHandle,GetParent,GetStockObject,GetSubMenu,GetSystemMetrics,
//LoadImage,MessageBox,MoveWindow,PostQuitMessage,RegisterClassEx,SendMessage,
//SetWindowLongPtr,ShowWindow,TranslateAccelerator,TranslateMDISysAccel,
//UpdateWindow,TranslateMessage,WinMain.
//=============================================================================
//Demonstrates the creation of a simple multiple document interface(mdi).
//=============================================================================
#include <windows.h>  //include all the basics
#include <tchar.h>    //string and other mapping macros
#include <string>
#include "resources.h"

//define an unicode string type alias
typedef std::basic_string<TCHAR> ustring;
//=============================================================================
//main window message processing function declarations
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
void OnCommand(const HWND,int,int,const HWND);
int OnCreate(const HWND,CREATESTRUCT*);

//mdi child window message processing declarations
LRESULT CALLBACK MdiChildWndProc_01(HWND,UINT,WPARAM,LPARAM);
LRESULT CALLBACK MdiChildWndProc_02(HWND,UINT,WPARAM,LPARAM);

//non-message function declarations
HWND CreateMDIChildWnd(const HWND,const HINSTANCE,DWORD,const RECT&,const int,
                       const ustring&,const ustring&);
HWND CreateMDIClient(const HWND,const HINSTANCE);
inline int ErrMsg(const ustring&);
BOOL CALLBACK MdiCloseChildProc(HWND,LPARAM);  //EnumChildProc
bool RegisterMdiChild(WNDPROC,const ustring&,const HINSTANCE);

//mdi client window identifier
namespace {
  const int IDC_MDI_CLIENT=500;
}
//=============================================================================
int WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR pStr,int nCmd)
{
ustring classname=_T("SIMPLEWND");
WNDCLASSEX wcx={0};  //used for storing information about the wnd 'class'

wcx.cbSize         = sizeof(WNDCLASSEX);           
wcx.lpfnWndProc    = WndProc;             //wnd Procedure pointer
wcx.hInstance      = hInst;               //app instance
//use 'LoadImage' to load wnd class icon and cursor as it supersedes the 
//obsolete functions 'LoadIcon' and 'LoadCursor', although these functions will 
//still work. Because the icon and cursor are loaded from system resources ie 
//they are shared, it is not necessary to free the image resources with either 
//'DestroyIcon' or 'DestroyCursor'.
wcx.hIcon         = reinterpret_cast<HICON>(LoadImage(0,IDI_APPLICATION,
                                            IMAGE_ICON,0,0,LR_SHARED));
wcx.hCursor       = reinterpret_cast<HCURSOR>(LoadImage(0,IDC_ARROW,
                                              IMAGE_CURSOR,0,0,LR_SHARED));
wcx.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_BTNFACE+1);   
wcx.lpszMenuName  = MAKEINTRESOURCE(IDR_MENU1);   //wnd menu
wcx.lpszClassName = classname.c_str(); 
//the window 'class' (not c++ class) has to be registered with the system
//before windows of that 'class' can be created
if (!RegisterClassEx(&wcx))
  {
  ErrMsg(_T("Failed to register wnd class"));
  return -1;
  }

int desktopwidth=GetSystemMetrics(SM_CXSCREEN);
int desktopheight=GetSystemMetrics(SM_CYSCREEN);

HWND hwnd=CreateWindowEx(0,                     //extended styles
                         classname.c_str(),     //name: wnd 'class'
                         _T("Multiple Document Interface(MDI)"), //wnd title 
                         WS_OVERLAPPEDWINDOW,   //wnd style
                         desktopwidth/4,        //position:left
                         desktopheight/4,       //position: top
                         desktopwidth/2,        //width
                         desktopheight/2,       //height
                         0,                     //parent wnd handle
                         0,                     //menu handle/wnd id
                         hInst,                 //app instance
                         0);                    //user defined info
if (!hwnd)
  {
  ErrMsg(_T("Failed to create wnd"));
  return -1;
  }

ShowWindow(hwnd,nCmd); 
UpdateWindow(hwnd);

HACCEL hAccel=0;    //no accelerator table in this example. 

//start message loop - windows applications are 'event driven' waiting on user,
//application or system signals to determine what action, if any, to take. Note 
//that an error may cause GetMessage to return a negative value so, ideally,  
//this result should be tested for and appropriate action taken to deal with 
//it(the approach taken here is to simply quit the application). Note the
//extra commands to ensure default mdi keyboard message processing; the 
//TranslateAccelerator command is present only for demonstration purposes as
//there are no application defined accelerator keys in this example.
HWND hMdiClient=GetDlgItem(hwnd,IDC_MDI_CLIENT);
MSG msg;
while (GetMessage(&msg,0,0,0)>0)
  {
  if (!TranslateMDISysAccel(hMdiClient,&msg)&&   //mdi specific keyboard msgs
      !TranslateAccelerator(hwnd,hAccel,&msg))   //all other keyboard msgs
    {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }
  }
return static_cast<int>(msg.wParam);
}
//=============================================================================
//main window message processing functions
//=============================================================================
LRESULT CALLBACK WndProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
static HWND hMdiClient;

switch (uMsg)
  {
  case WM_COMMAND:
    OnCommand(hwnd,LOWORD(wParam),HIWORD(wParam),
              reinterpret_cast<HWND>(lParam));
    //must call DefFrameProc to ensure that min/max/close default behaviour
    //of mdi child windows can occur.
    return DefFrameProc(hwnd,hMdiClient,WM_COMMAND,wParam,lParam);
  case WM_CREATE:
    hMdiClient=CreateMDIClient(hwnd,GetModuleHandle(0));
    return OnCreate(hwnd,reinterpret_cast<CREATESTRUCT*>(lParam));
  case WM_DESTROY:
    DefFrameProc(hwnd,hMdiClient,WM_COMMAND,wParam,lParam);
    PostQuitMessage(0);    //signal end of application
    return 0;
  default:
    //let system deal with msg
    return DefFrameProc(hwnd,hMdiClient,uMsg,wParam,lParam);
  }
}
//=============================================================================
void OnCommand(const HWND hwnd,int id,int notifycode,const HWND hCntrl)
{
//handles WM_COMMAND message of the main, parent window

const static HWND hMdiClient=GetDlgItem(hwnd,IDC_MDI_CLIENT);

switch (id)
  {
  case IDCANCEL:         //menu item: file --> exit
    DestroyWindow(hwnd);
    break;
  case IDM_WND_TILE:     //menu item: window --> tile
    SendMessage(hMdiClient,WM_MDITILE,0,0);
    break;
  case IDM_WND_CASCADE:  //menu item: window --> cascade
    SendMessage(hMdiClient,WM_MDICASCADE,0,0);
    break;
  case IDM_WND_ARRANGE:  //menu item: window --> arrange
    SendMessage(hMdiClient,WM_MDIICONARRANGE,0,0);
    break;
  case IDM_WND_CLOSEALL:  //menu item: window --> close all
    //EnumChildWindows fn enumerates all child windows of the
    //mdi client window calling the MdiCloseChildProc CALLBACK fn
    //for each child window that exists.
    EnumChildWindows(hMdiClient,MdiCloseChildProc,0) ;
    break;
  }
}
//=============================================================================
int OnCreate(const HWND hwnd,CREATESTRUCT *cs)
{
//handles the WM_CREATE message of the main, parent window; return -1 to fail
//window creation
//
//register and create two mdi child windows. Note that the parent of the mdi 
//child windows is the mdi client window.
HWND hMdiClient=GetDlgItem(hwnd,IDC_MDI_CLIENT);
      
RegisterMdiChild(MdiChildWndProc_01,_T("mdi_child_01"),cs->hInstance);
RECT rc={0,0,400,200};
//create an mdi child window of the class just registered
CreateMDIChildWnd(hMdiClient,cs->hInstance,WS_CLIPCHILDREN,rc,
                  IDM_FIRSTCHILD,_T("MDI Child Window#1"),_T("mdi_child_01"));

RegisterMdiChild(MdiChildWndProc_02,_T("mdi_child_02"),cs->hInstance);
//create an mdi child window of the class just registered
rc.left=40;
rc.top=40;
CreateMDIChildWnd(hMdiClient,cs->hInstance,WS_CLIPCHILDREN,rc,
                  (IDM_FIRSTCHILD+1),_T("MDI Child Window#2"),
                  _T("mdi_child_02"));

return 0;
}
//=============================================================================
//mdi child window message processing functions
//=============================================================================
LRESULT CALLBACK MdiChildWndProc_01(HWND hwnd,UINT uMsg,WPARAM wParam,
                                    LPARAM lParam)
{
static HWND hEdit;

switch (uMsg)
  {
  case WM_CREATE:
    {
    CREATESTRUCT *cs=reinterpret_cast<CREATESTRUCT*>(lParam);
    //make client area an edit control
    hEdit=CreateWindowEx(WS_EX_CLIENTEDGE,
                         _T("edit"),_T("edit control"),
                         WS_VISIBLE|WS_CHILD|ES_MULTILINE|WS_VSCROLL,
                         0,0,0,0,hwnd,0,cs->hInstance,0);
    //change the font to default gui font
    SendMessage(hEdit,WM_SETFONT,
                reinterpret_cast<WPARAM>(GetStockObject(DEFAULT_GUI_FONT)),0);
    return 0;
    }
  case WM_SIZE:
    //change the control extended style so that it doesn't have an extra
    //border when maximized.
    if (wParam==SIZE_MAXIMIZED)
      {
      SetWindowLongPtr(hEdit,GWL_EXSTYLE,0);
      }
    else
      {
      SetWindowLongPtr(hEdit,GWL_EXSTYLE,WS_EX_CLIENTEDGE);
      }
    //ensure edit control always fits exactly within its parent's client 
    //area
    MoveWindow(hEdit,0,0,LOWORD(lParam),HIWORD(lParam),1);
  default:
    return DefMDIChildProc(hwnd,uMsg,wParam,lParam);
  }
}
//=============================================================================
LRESULT CALLBACK MdiChildWndProc_02(HWND hwnd,UINT uMsg,WPARAM wParam,
                                    LPARAM lParam)
{
static HWND hListbox;

switch (uMsg)
  {
  case WM_CREATE:
    {
    CREATESTRUCT *cs=reinterpret_cast<CREATESTRUCT*>(lParam);
    //make client area a listbox control
    hListbox=CreateWindowEx(WS_EX_CLIENTEDGE,
                            _T("listbox"),_T("listbox control"),
                            WS_VISIBLE|WS_CHILD|LBS_NOINTEGRALHEIGHT,
                            0,0,0,0,hwnd,0,cs->hInstance,0);
    //change the font to default gui font
    SendMessage(hListbox,WM_SETFONT,
                (WPARAM)GetStockObject(DEFAULT_GUI_FONT),0);
    //add a string to the listbox
    SendMessage(hListbox,LB_ADDSTRING,0,(LPARAM)_T("Listbox"));
    return 0;
    }
  case WM_SIZE:
    //change the control extended style so that it doesn't have an extra
    //border when maximized.
    if (wParam==SIZE_MAXIMIZED)
      {
      SetWindowLongPtr(hListbox,GWL_EXSTYLE,0);
      }
    else
      {
      SetWindowLongPtr(hListbox,GWL_EXSTYLE,WS_EX_CLIENTEDGE);
      }
    //ensure listbox control always fits exactly within its parent's client 
    //area this also ensures any previous change made by SetWindowLongPtr
    //is drawn.
    MoveWindow(hListbox,0,0,LOWORD(lParam),HIWORD(lParam),1);
  default:
    return DefMDIChildProc(hwnd,uMsg,wParam,lParam);
  }
}
//=============================================================================
//non-message processing functions
//=============================================================================
HWND CreateMDIChildWnd(const HWND hParent,const HINSTANCE hInst,DWORD dwStyle,
                       const RECT& rc,const int id,const ustring& caption, 
                       const ustring& classname)
{
dwStyle|=WS_CHILD|WS_VISIBLE;
return CreateWindowEx(WS_EX_MDICHILD,               //extended styles
                      classname.c_str(),            //control 'class' name
                      caption.c_str(),              //control caption
                      dwStyle,                      //control style 
                      rc.left,                      //position: left
                      rc.top,                       //position: top
                      rc.right,                     //width
                      rc.bottom,                    //height
                      hParent,                      //parent window handle
                      //control's ID
                      reinterpret_cast<HMENU>(static_cast<INT_PTR>(id)),
                      hInst,                        //application instance
                      0);                           //user defined info
}
//=============================================================================
HWND CreateMDIClient(const HWND hParent,const HINSTANCE hInst)
{
//In order to get default handling for the menu item called 'window' it is 
//necessary to pass the address of a CLIENTCREATSTRUCT as the 'lpParam' of 
//CreateWindowEx. 
CLIENTCREATESTRUCT ccs; 
ccs.hWindowMenu=GetSubMenu(GetMenu(hParent),1); //menu handle for menu item 
                                                //called 'window'
ccs.idFirstChild=IDM_FIRSTCHILD; //id of the first mdi child window

return CreateWindowEx(0,
                      _T("mdiclient"),
                      0,
                      WS_VISIBLE|WS_CHILD|WS_CLIPCHILDREN|WS_VSCROLL|
                      WS_HSCROLL,
                      0,0,0,0, 
                      hParent,
                      reinterpret_cast<HMENU>(static_cast<INT_PTR>
                        (IDC_MDI_CLIENT)), 
                      hInst, 
                      reinterpret_cast<void*>(&ccs)); 
}
//=============================================================================
inline int ErrMsg(const ustring& s)
{
return MessageBox(0,s.c_str(),_T("ERROR"),MB_OK|MB_ICONEXCLAMATION);
}
//=============================================================================
BOOL CALLBACK MdiCloseChildProc(HWND hwnd,LPARAM lParam)
{
//EnumChildProc that destroys mdi child windows
SendMessage(GetParent(hwnd),WM_MDIDESTROY,(WPARAM)hwnd,0);
return TRUE;
}
//=============================================================================
bool RegisterMdiChild(WNDPROC wp,const ustring& s,const HINSTANCE hInst)
{
HCURSOR    hCursor;
HICON      hIcon;
WNDCLASSEX wcx={0};

hIcon=(HICON)LoadImage(0,IDI_APPLICATION,IMAGE_ICON,0,0,LR_SHARED);
hCursor=(HCURSOR)LoadImage(0,IDC_ARROW,IMAGE_CURSOR,0,0,LR_SHARED);
wcx.cbSize           = sizeof(WNDCLASSEX);          
wcx.style            = CS_HREDRAW|CS_VREDRAW;       
wcx.lpfnWndProc      = wp;                                                  
wcx.cbWndExtra       = sizeof(HWND);                
wcx.hInstance        = hInst;                      
wcx.hIcon            = hIcon;                       
wcx.hCursor          = hCursor;                                        
wcx.lpszClassName    = s.c_str();                                             

//Register the wnd class with the Windows system
if (!RegisterClassEx(&wcx))
  {
  ErrMsg(_T("Failed to register mdi child wnd class"));
  return false;
  }
return true;
}
//=============================================================================
Resource Definitions(resources.h)
//=============================================================================
//Resource script constants for CreateMdi example.
//=============================================================================
#define IDR_MENU1                       200
#define IDM_HELP_ABOUT                  5000
#define IDM_WND_TILE                    5001
#define IDM_WND_CASCADE                 5002
#define IDM_WND_ARRANGE                 5003
#define IDM_WND_CLOSEALL                5004
#define IDM_FIRSTCHILD                  5500
//=============================================================================
Resource Script(script.rc)
//=============================================================================
//CREATEMDI MENU - Copyright (c) 2000,2005 Ken Fitlike
//resource script
//=============================================================================
#include <afxres.h>
#include "resources.h"
//=============================================================================
//Menu
//=============================================================================
IDR_MENU1 MENU 
{
  POPUP "&File"
    {
    MENUITEM "E&xit",                       IDCANCEL
    }
  POPUP "&Window"
    {
    MENUITEM "&Arrange",                    IDM_WND_ARRANGE
    MENUITEM "&Cascade",                    IDM_WND_CASCADE
    MENUITEM "&Tile",                       IDM_WND_TILE
    MENUITEM "&C&lose All",                 IDM_WND_CLOSEALL
    }
  POPUP "&Help"
    {
    MENUITEM "&About...",                   IDM_HELP_ABOUT, GRAYED
    }
}
//=============================================================================