Delen via


De interface voor meerdere documenten gebruiken

In deze sectie wordt uitgelegd hoe u de volgende taken uitvoert:

Ter illustratie van deze taken bevat deze sectie voorbeelden van Multipad, een typische MDI-toepassing (Multiple Document Interface).

Klassen onderliggend en framevenster registreren

Een typische MDI-toepassing moet twee vensterklassen registreren: één voor het framevenster en één voor de onderliggende vensters. Als een toepassing meer dan één type document ondersteunt (bijvoorbeeld een spreadsheet en een grafiek), moet er voor elk type een vensterklasse worden geregistreerd.

De klassestructuur voor het framevenster is vergelijkbaar met de klassestructuur voor het hoofdvenster in niet-MDI-toepassingen. De klassestructuur voor de MDI-kindvensters verschilt iets van de structuur voor kindvensters in niet-MDI toepassingen als volgt:

  • De klassestructuur moet een pictogram hebben, omdat de gebruiker een MDI-onderliggend venster kan minimaliseren alsof het een normaal toepassingsvenster was.
  • De menunaam moet worden NULL-, omdat een onderliggend MDI-venster geen eigen menu kan hebben.
  • De klassestructuur moet extra ruimte in de vensterstructuur reserveren. Met deze ruimte kan de toepassing gegevens, zoals een bestandsnaam, koppelen aan een bepaald onderliggend venster.

In het volgende voorbeeld ziet u hoe Multipad zijn frame en child window classes registreert.

BOOL WINAPI InitializeApplication() 
{ 
    WNDCLASS wc; 
 
    // Register the frame window class. 
 
    wc.style         = 0; 
    wc.lpfnWndProc   = (WNDPROC) MPFrameWndProc; 
    wc.cbClsExtra    = 0; 
    wc.cbWndExtra    = 0; 
    wc.hInstance     = hInst; 
    wc.hIcon         = LoadIcon(hInst, IDMULTIPAD); 
    wc.hCursor       = LoadCursor((HANDLE) NULL, IDC_ARROW); 
    wc.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE + 1); 
    wc.lpszMenuName  = IDMULTIPAD; 
    wc.lpszClassName = szFrame; 
 
    if (!RegisterClass (&wc) ) 
        return FALSE; 
 
    // Register the MDI child window class. 
 
    wc.lpfnWndProc   = (WNDPROC) MPMDIChildWndProc; 
    wc.hIcon         = LoadIcon(hInst, IDNOTE); 
    wc.lpszMenuName  = (LPCTSTR) NULL; 
    wc.cbWndExtra    = CBWNDEXTRA; 
    wc.lpszClassName = szChild; 
 
    if (!RegisterClass(&wc)) 
        return FALSE; 
 
    return TRUE; 
} 

Frame en onderliggende vensters maken

Nadat de vensterklassen zijn geregistreerd, kan een MDI-toepassing de vensters maken. Eerst wordt het framevenster gemaakt met behulp van de functie CreateWindow of CreateWindowEx. Nadat het framevenster is gemaakt, maakt de toepassing het clientvenster opnieuw met behulp van CreateWindow- of CreateWindowEx-. De toepassing moet MDICLIENT opgeven als klassenaam van het clientvenster; MDICLIENT- is een vooraf geregistreerde vensterklasse die door het systeem is gedefinieerd. De parameter lpvParam van CreateWindow of CreateWindowEx- moet verwijzen naar een CLIENTCREATESTRUCT- structuur. Deze structuur bevat de leden die in de volgende tabel worden beschreven:

Lid Beschrijving
hWindowMenu Greep naar het venstermenu dat wordt gebruikt voor het beheren van MDI-onderliggende vensters. De toepassing voegt de titels van de onderliggende vensters toe aan het venstermenu als menu-items naarmate ze worden aangemaakt. De gebruiker kan vervolgens een onderliggend venster activeren door in het venstermenu op de titel ervan te klikken.
idFirstChild Hiermee specificeert u de identificatiecode van het eerste MDI-onderliggende venster. Aan het eerste MDI-onderliggende venster dat is gemaakt, wordt deze identificatie toegewezen. Er worden extra vensters gemaakt met incrementele venster-id's. Wanneer een kindvenster wordt vernietigd, worden de venster-id's onmiddellijk opnieuw toegewezen om het bereik aaneengesloten te houden.

 

Wanneer de titel van een onderliggend venster wordt toegevoegd aan het venstermenu, wijst het systeem een id toe aan het onderliggende venster. Wanneer de gebruiker op de titel van een onderliggend venster klikt, ontvangt het framevenster een WM_COMMAND bericht met de identificatie in de parameter wParam. Je moet een waarde opgeven voor het idFirstChild lidmaatschap die geen conflicten veroorzaakt met menu-itemidentificaties in het menu van het vensterframe.

De framevensterprocedure van Multipad creëert het MDI-clientvenster tijdens het verwerken van het WM_CREATE bericht. In het volgende voorbeeld ziet u hoe het clientvenster wordt gemaakt.

case WM_CREATE: 
    { 
        CLIENTCREATESTRUCT ccs; 
 
        // Retrieve the handle to the window menu and assign the 
        // first child window identifier. 
 
        ccs.hWindowMenu = GetSubMenu(GetMenu(hwnd), WINDOWMENU); 
        ccs.idFirstChild = IDM_WINDOWCHILD; 
 
        // Create the MDI client window. 
 
        hwndMDIClient = CreateWindow( "MDICLIENT", (LPCTSTR) NULL, 
            WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL, 
            0, 0, 0, 0, hwnd, (HMENU) 0xCAC, hInst, (LPSTR) &ccs); 
 
        ShowWindow(hwndMDIClient, SW_SHOW); 
    } 
    break; 

Titels van onderliggende vensters worden onderaan het venstermenu toegevoegd. Als de toepassing tekenreeksen toevoegt aan het venstermenu met behulp van de functie AppendMenu, kunnen deze tekenreeksen worden overschreven door de titels van de onderliggende vensters wanneer het venstermenu opnieuw wordt geschilderd (wanneer een onderliggend venster wordt gemaakt of vernietigd). Een MDI-toepassing die tekenreeksen toevoegt aan het venstermenu, moet de functie InsertMenu gebruiken en controleren of de titels van onderliggende vensters deze nieuwe tekenreeksen niet hebben overschreven.

Gebruik de stijl WS_CLIPCHILDREN bij het maken van het MDI-clientvenster, zodat het venster niet over zijn onderliggende vensters heen wordt getekend.

De hoofdberichtlus schrijven

De hoofdberichtlus van een MDI-toepassing is vergelijkbaar met die van een niet-MDI-toepassing die acceleratorsleutels verwerkt. Het verschil is dat de MDI-berichtenlus de functie TranslateMDISysAccel aanroept voordat wordt gecontroleerd op door de toepassing gedefinieerde acceleratorsleutels of voordat het bericht wordt verzonden.

In het volgende voorbeeld ziet u de berichtenlus van een typische MDI-toepassing. Houd er rekening mee dat GetMessage- -1 kan retourneren als er een fout optreedt.

MSG msg;
BOOL bRet;

while ((bRet = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0)
{
    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else 
    { 
        if (!TranslateMDISysAccel(hwndMDIClient, &msg) && 
                !TranslateAccelerator(hwndFrame, hAccel, &msg))
        { 
            TranslateMessage(&msg); 
            DispatchMessage(&msg); 
        } 
    } 
}

De functie TranslateMDISysAccel vertaalt WM_KEYDOWN berichten in WM_SYSCOMMAND berichten en verzendt deze naar het actieve MDI-onderliggende venster. Als het bericht geen MDI-acceleratorbericht is, retourneert de functie FALSE. In dat geval gebruikt de toepassing de TranslateAccelerator functie om te bepalen of een van de door de toepassing gedefinieerde acceleratortoetsen is ingedrukt. Als dat niet het geval is, verzendt de lus het bericht naar de juiste vensterprocedure.

De raamprocedure voor het venster schrijven

De vensterprocedure voor een MDI-framevenster is vergelijkbaar met die van het hoofdvenster van een niet-MDI-toepassing. Het verschil is dat een framevensterprocedure alle berichten doorgeeft die niet worden verwerkt aan de functie DefFrameProc in plaats van aan de functie DefWindowProc. Bovendien moet de procedure voor het frame-venster ook enkele berichten doorsturen die wel worden verwerkt, inclusief berichten die in de volgende tabel worden vermeld.

Bericht Antwoord
WM_COMMAND Hiermee activeert u het MDI-kindvenster dat de gebruiker kiest. Dit bericht wordt verzonden wanneer de gebruiker een onderliggend MDI-venster kiest in het venstermenu van het MDI-framevenster. De vensteridentificatie die bij dit bericht hoort, identificeert het MDI-kindvenster dat geactiveerd moet worden.
WM_MENUCHAR Hiermee opent u het venstermenu van het actieve MDI-onderliggende venster wanneer de gebruiker op de toetsencombinatie Alt+ – (min) drukt.
WM_SETFOCUS Geeft de toetsenbordfocus door aan het MDI-clientvenster, dat op zijn beurt doorgeeft aan het actieve MDI-kindvenster.
WM_SIZE De grootte van het MDI-clientvenster wordt aangepast aan het clientgebied van het nieuwe framevenster. Als de procedure van het hoofdvenster de grootte van het MDI-clientvenster wijzigt naar een andere grootte, mag het bericht niet worden doorgegeven aan de functie DefWindowProc.

 

De procedure van het framevenster in Multipad heet MPFrameWndProc. De verwerking van andere berichten door MPFrameWndProc is vergelijkbaar met die van niet-MDI-toepassingen. WM_COMMAND berichten in Multipad worden verwerkt door de lokaal gedefinieerde CommandHandler-functie. Voor opdrachtberichten die Multipad niet verwerkt, roept CommandHandler de functie DefFrameProc aan. Als Multipad standaard geen DefFrameProc- gebruikt, kan de gebruiker een onderliggend venster niet activeren vanuit het venstermenu, omdat het WM_COMMAND bericht dat wordt verzonden door te klikken op het menu-item van het venster verloren zou gaan.

De procedure voor het kindvenster schrijven

Net als bij de procedure van het framevenster gebruikt een MDI-subvensterprocedure standaard een speciale functie voor het verwerken van berichten. Alle berichten die de onderliggende vensterprocedure niet verwerkt, moeten worden doorgegeven aan de functie DefMDIChildProc in plaats van aan de functie DefWindowProc. Daarnaast moeten sommige berichten voor vensterbeheer worden doorgegeven aan DefMDIChildProc-, zelfs als de toepassing het bericht verwerkt, zodat MDI correct werkt. Hieronder volgen de berichten die de toepassing moet doorgeven aan DefMDIChildProc.

Bericht Antwoord
WM_CHILDACTIVATE Hiermee wordt de activeringsverwerking uitgevoerd wanneer MDI-kindvensters worden geschaald, verplaatst of getoond. Dit bericht moet worden doorgegeven.
WM_GETMINMAXINFO Berekent de grootte van een gemaximaliseerd MDI-onderliggend venster op basis van de huidige grootte van het MDI-clientvenster.
WM_MENUCHAR Geeft het bericht door aan het MDI-framevenster.
WM_MOVE Berekent de schuifbalken van de MDI-client opnieuw als deze aanwezig zijn.
WM_SETFOCUS Hiermee wordt het kindvenster geactiveerd als dit niet het actieve MDI-kindvenster is.
WM_SIZE Voert de noodzakelijke bewerkingen uit om de grootte van een venster te wijzigen, met name voor het maximaliseren of herstellen van een MDI-subvenster. Het niet doorgeven van dit bericht aan de DefMDIChildProc functie produceert zeer ongewenste resultaten.
WM_SYSCOMMAND Behandelt venstermenu (voorheen systeemmenu) opdrachten: SC_NEXTWINDOW, SC_PREVWINDOW, SC_MOVE, SC_SIZEen SC_MAXIMIZE.

 

Een onderliggend venster maken

Als u een onderliggend MDI-venster wilt maken, kan een toepassing de functie CreateMDIWindow aanroepen of een WM_MDICREATE bericht verzenden naar het MDI-clientvenster. (De toepassing kan de functie CreateWindowEx gebruiken met de WS_EX_MDICHILD-stijl om onderliggende MDI-vensters te maken.) Een MDI-toepassing met één thread kan beide methoden gebruiken om een onderliggend venster te maken. Een thread in een multithreaded MDI-toepassing moet de functie CreateMDIWindow of CreateWindowEx gebruiken om een onderliggend venster in een andere thread te maken.

De lParam parameter van een WM_MDICREATE bericht is een far pointer naar een MDICREATESTRUCT structuur. De structuur bevat vier dimensieleden: x en y, die de horizontale en verticale posities van het venster aangeven, en cx- en cy, die de horizontale en verticale gebieden van het venster aangeven. Elk van deze leden kan expliciet worden toegewezen door de toepassing, of ze kunnen worden ingesteld op CW_USEDEFAULT, in welk geval het systeem een positie, grootte of beide selecteert volgens een trapsgewijze algoritme. In ieder geval moeten alle vier de leden worden geïnitialiseerd. Multipad maakt gebruik van CW_USEDEFAULT voor alle dimensies.

Het laatste lid van de MDICREATESTRUCT structuur is de stijl lid, die mogelijk stijl-bits voor het venster bevat. Als u een onderliggend MDI-venster wilt maken dat een combinatie van vensterstijlen kan hebben, geeft u de MDIS_ALLCHILDSTYLES vensterstijl op. Wanneer deze stijl niet is opgegeven, heeft een onderliggend MDI-venster de WS_MINIMIZE, WS_MAXIMIZE, WS_HSCROLLen WS_VSCROLL stijlen als standaardinstellingen.

Multipad maakt de onderliggende MDI-vensters door gebruik te maken van de lokaal gedefinieerde AddFile-functie, die zich bevindt in het bronbestand MPFILE.C. Met de functie AddFile wordt de titel van het onderliggende venster ingesteld door het szTitle--lid van de MDICREATESTRUCT-structuur van het venster toe te wijzen aan de naam van het bestand dat wordt bewerkt, of aan 'Naamloos'. Het szClass-lid is ingesteld op de naam van de MDI-kindvensterklasse die is geregistreerd in de functie InitializeApplication van Multipad. Het hOwner- object is ingesteld op de instantiehandle van de toepassing.

In het volgende voorbeeld ziet u de functie AddFile in Multipad.

HWND APIENTRY AddFile(pName) 
TCHAR * pName; 
{ 
    HWND hwnd; 
    TCHAR sz[160]; 
    MDICREATESTRUCT mcs; 
 
    if (!pName) 
    { 
 
        // If the pName parameter is NULL, load the "Untitled" 
        // string from the STRINGTABLE resource and set the szTitle 
        // member of MDICREATESTRUCT. 
 
        LoadString(hInst, IDS_UNTITLED, sz, sizeof(sz)/sizeof(TCHAR)); 
        mcs.szTitle = (LPCTSTR) sz; 
    } 
    else 
 
        // Title the window with the full path and filename, 
        // obtained by calling the OpenFile function with the 
        // OF_PARSE flag, which is called before AddFile(). 
 
        mcs.szTitle = of.szPathName; 
 
    mcs.szClass = szChild; 
    mcs.hOwner  = hInst; 
 
    // Use the default size for the child window. 
 
    mcs.x = mcs.cx = CW_USEDEFAULT; 
    mcs.y = mcs.cy = CW_USEDEFAULT; 
 
    // Give the child window the default style. The styleDefault 
    // variable is defined in MULTIPAD.C. 
 
    mcs.style = styleDefault; 
 
    // Tell the MDI client window to create the child window. 
 
    hwnd = (HWND) SendMessage (hwndMDIClient, WM_MDICREATE, 0, 
        (LONG) (LPMDICREATESTRUCT) &mcs); 
 
    // If the file is found, read its contents into the child 
    // window's client area. 
 
    if (pName) 
    { 
        if (!LoadFile(hwnd, pName)) 
        { 
 
            // Cannot load the file; close the window. 
 
            SendMessage(hwndMDIClient, WM_MDIDESTROY, 
                (DWORD) hwnd, 0L); 
        } 
    } 
    return hwnd; 
} 

De aanwijzer die is doorgegeven in de parameter lParam van het WM_MDICREATE bericht, wordt doorgegeven aan de functie CreateWindow en wordt weergegeven als het eerste lid in de CREATESTRUCT-structuur, die wordt doorgegeven in het WM_CREATE bericht. In Multipad initialiseert het kindvenster zichzelf tijdens de verwerking van het WM_CREATE-bericht door documentvariabelen in de extra gegevens te initialiseren en door het kindvenster van het bewerkingscontrole te maken.