Megosztás a következőn keresztül:


A Több dokumentumfelület használata

Ez a szakasz a következő feladatok elvégzését ismerteti:

A feladatok szemléltetéséhez ez a szakasz példákat tartalmaz a Multipadről, amely egy tipikus többdokumentumos felületi (MDI-) alkalmazás.

Gyermek- és keret ablakosztályok regisztrálása

Egy tipikus MDI-alkalmazásnak két ablakosztályt kell regisztrálnia: egyet a keretablakához, egyet pedig a gyermekablakaihoz. Ha egy alkalmazás több dokumentumtípust (például számolótáblát és diagramot) támogat, minden típushoz regisztrálnia kell egy ablakosztályt.

A keretablak osztályszerkezete hasonló a nem MDI-alkalmazások főablakának osztályszerkezetéhez. Az MDI gyermekablakok osztályszerkezete kissé eltér a nem MDI-alkalmazások gyermekablakainak struktúrájától az alábbiak szerint:

  • Az osztályszerkezetnek ikonnal kell rendelkeznie, mert a felhasználó úgy minimalizálhat egy MDI-gyermekablakot, mintha normál alkalmazásablak lenne.
  • A menü nevének NULLkell lennie, mert egy MDI-gyermekablaknak nem lehet saját menüje.
  • Az osztályszerkezetnek további helyet kell lefoglalnia az ablakszerkezetben. Ezzel a területtel az alkalmazás adatokat, például fájlnevet társíthat egy adott gyermekablakhoz.

Az alábbi példa bemutatja, hogyan regisztrálja a Multipad a keret- és gyermekablak-osztályokat.

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; 
} 

Keret- és gyermekablakok létrehozása

Az ablakosztályok regisztrálása után egy MDI-alkalmazás létrehozhatja az ablakait. Először létrehozza az ablakkeretet a CreateWindow vagy CreateWindowEx függvénnyel. A keretablak létrehozása után az alkalmazás ismét létrehozza az ügyfélablakát CreateWindow vagy CreateWindowExhasználatával. Az alkalmazásnak meg kell adnia az MDICLIENT-et az ügyfélablak osztályneveként; MDICLIENT egy előre regisztrált ablakosztály, amelyet a rendszer határoz meg. A lpvParam paraméterének a CreateWindow vagy CreateWindowEx függvények esetében egy CLIENTCREATESTRUCT struktúrára kell mutatnia. Ez a struktúra az alábbi táblázatban leírt tagokat tartalmazza:

Tag Leírás
hWindowMenu Az MDI gyermekablakainak vezérléséhez használt ablakmenü kezelése. A gyermekablakok létrehozásakor az alkalmazás menüelemként hozzáadja a címeket az ablak menüjéhez. A felhasználó ezután aktiválhat egy gyermekablakot az ablak menüjének címére kattintva.
idFirstChild Az első MDI gyermekablak azonosítóját adja meg. Az első létrehozott MDI gyermekablakhoz ez az azonosító van rendelve. További ablakok jönnek létre növekményes ablakazonosítókkal. Ha egy gyermekablak megsemmisül, a rendszer azonnal újra hozzárendeli az ablakazonosítókat, hogy a tartományuk egybefüggő maradjon.

 

Amikor egy gyermekablak címe hozzáadódik az ablakmenühöz, a rendszer egy azonosítót rendel a gyermekablakhoz. Amikor a felhasználó egy gyermek ablak címén kattint, a keretablak egy WM_COMMAND üzenetet kap a wParam paraméter azonosítójával. Meg kell adnia egy értéket az idFirstChild tag számára, amely nem ütközik a keretablak menüjében lévő menüelem-azonosítókkal.

A Multipad keretablakának eljárása létrehozza az MDI-ügyfélablakot a WM_CREATE üzenet feldolgozása során. Az alábbi példa az ügyfélablak létrehozását mutatja be.

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; 

A gyermekablakok címei az ablakmenü aljára kerülnek. Ha az alkalmazás a AppendMenu függvénnyel sztringeket ad hozzá az ablakmenühöz, ezeket a sztringeket felülírhatják a gyermekablakok címei az ablakmenü újrafestésekor (gyermekablak létrehozásakor vagy megsemmisítésekor). Egy olyan MDI-alkalmazásnak, amely karakterláncokat ad hozzá az ablakmenüjéhez, használnia kell az InsertMenu függvényt, és ellenőrzi, hogy a gyermekablakok címei nem írták-e felül ezeket az új karakterláncokat.

A WS_CLIPCHILDREN stílus használatával hozza létre az MDI-ügyfélablakot, hogy megakadályozza az ablak festését a gyermekablakon.

A fő üzenet ciklusának írása

Az MDI-alkalmazások fő üzenethurkja hasonló egy nem MDI-alkalmazáshoz, amely a gyorsítókulcsokat kezeli. A különbség az, hogy az MDI üzenethurok meghívja a TranslateMDISysAccel függvényt az alkalmazás által definiált gyorsítókulcsok keresése vagy az üzenet elküldése előtt.

Az alábbi példa egy tipikus MDI-alkalmazás üzenethurkát mutatja be. Vegye figyelembe, hogy GetMessage hiba esetén -1 adhat vissza.

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); 
        } 
    } 
}

A TranslateMDISysAccel függvény WM_KEYDOWN üzeneteket fordít le WM_SYSCOMMAND üzenetekre, és elküldi őket az aktív MDI gyermekablakba. Ha az üzenet nem MDI-gyorsítóüzenet, a függvény HAMISad vissza, ebben az esetben az alkalmazás a TranslateAccelerator függvénnyel állapítja meg, hogy az alkalmazás által definiált gyorsítókulcsok bármelyikét lenyomták-e. Ha nem, a hurok elküldi az üzenetet a megfelelő ablakeljárásnak.

A keretablak kezelési eljárásának írása

Az MDI-keretablakok ablakeljárása hasonló egy nem MDI-alkalmazás főablakához. A különbség az, hogy egy keretablak-eljárás az összes olyan üzenetet átadja, amelyet nem kezel a DefFrameProc függvénynek, nem pedig a DefWindowProc függvénynek. Emellett a keretablak eljárásának bizonyos, általa kezelt üzeneteket is át kell adnia, beleértve az alábbi táblázatban felsorolt üzeneteket is.

Üzenet Válasz
WM_COMMAND Aktiválja a felhasználó által választott MDI gyermekablakot. Ezt az üzenetet akkor küldi el a rendszer, ha a felhasználó egy MDI gyermekablakot választ az MDI-keret ablakának menüjéből. Az üzenethez tartozó ablakazonosító azonosítja az aktiválandó MDI gyermekablakot.
WM_MENUCHAR Az aktív MDI gyermekablak ablakmenüjének megnyitása, amikor a felhasználó lenyomja az ALT+ – (mínusz) billentyűkombinációt.
WM_SETFOCUS Átadja a billentyűzetfókuszt az MDI-ügyfélablaknak, amely viszont átadja azt az aktív MDI gyermekablaknak.
WM_SIZE Átméretezi az MDI-ügyfélablakot, hogy elférjen az új keretablak ügyfélterületén. Ha a keretablak eljárása az MDI-ügyfélablakot más méretre méretezi, akkor nem kellene továbbítania az üzenetet a DefWindowProc függvénynek.

 

A Multipad keretablak eljárásának neve MPFrameWndProc. Az MPFrameWndProc más üzenetek kezelése hasonló a nem MDI-alkalmazásokhoz. WM_COMMAND többpados üzeneteket a helyileg meghatározott CommandHandler függvény kezeli. Ha a Multipad nem kezeli a parancsüzeneteket, a CommandHandler meghívja a DefFrameProc függvényt. Ha a Multipad alapértelmezés szerint nem használja DefFrameProc, a felhasználó nem tudja aktiválni a gyermekablakot az ablak menüjéből, mert az ablak menüelemére kattintva küldött WM_COMMAND üzenet elveszne.

A gyermek ablak eljárásának megírása

A keretablak-eljáráshoz hasonlóan az MDI gyermekablak-eljárása is egy speciális függvényt használ az üzenetek alapértelmezés szerinti feldolgozásához. A gyermekablak eljárás által nem kezelt összes üzenetet a DefMDIChildProc függvénynek kell átadni, nem pedig a DefWindowProc függvénynek. Ezenkívül néhány ablakfelügyeleti üzenetet át kell adni DefMDIChildProc, még akkor is, ha az alkalmazás kezeli az üzenetet, hogy az MDI megfelelően működjön. Az alábbi üzeneteket kell átadnia az alkalmazásnak DefMDIChildProc.

Üzenet Válasz
WM_CHILDACTIVATE Aktiválási feldolgozást hajt végre, amikor az MDI gyermekablakok méretüket változtatják, áthelyezik őket, vagy megjelennek. Ezt az üzenetet át kell adni.
WM_GETMINMAXINFO Kiszámítja a teljes MDI-gyermekablak méretét az MDI-ügyfélablak aktuális mérete alapján.
WM_MENUCHAR Átadja az üzenetet az MDI keretablakának.
WM_MOVE Újraszámítja az MDI-ügyfél görgetősávokat, ha vannak ilyenek.
WM_SETFOCUS Aktiválja a gyermekablakot, ha az nem az aktív MDI gyermekablak.
WM_SIZE Végrehajtja az ablak méretének módosításához szükséges műveleteket, különösen az MDI gyermekablakának maximalizálása vagy visszaállítása érdekében. Ha nem továbbítja ezt az üzenetet a DefMDIChildProc függvénynek, az rendkívül nemkívánatos eredményeket eredményez.
WM_SYSCOMMAND Kezeli az ablak (korábbi nevén rendszer) menüparancsait: SC_NEXTWINDOW, SC_PREVWINDOW, SC_MOVE, SC_SIZEés SC_MAXIMIZE.

 

Gyermekablak létrehozása

MDI gyermekablak létrehozásához egy alkalmazás meghívhatja a CreateMDIWindow függvényt, vagy WM_MDICREATE üzenetet küldhet az MDI-ügyfélablakba. (Az alkalmazás a CreateWindowEx függvényt használhatja a WS_EX_MDICHILD stílussal az MDI gyermekablakainak létrehozásához.) Az egyszálas MDI-alkalmazások bármelyik módszerrel létrehozhatnak egy gyermekablakot. A többszálas MDI-alkalmazások szálainak a CreateMDIWindow vagy CreateWindowEx függvényt kell használniuk egy másik szál gyermekablakának létrehozásához.

Egy WM_MDICREATE üzenet lParam paramétere egy távoli mutató egy MDICREATESTRUCT struktúrára. A szerkezet négy dimenziótagot tartalmaz: x és y, amelyek az ablak vízszintes és függőleges helyzetét jelzik, valamint cx és cy, amelyek az ablak vízszintes és függőleges kiterjedését jelzik. Ezen tagok bármelyikét explicit módon hozzárendelheti az alkalmazás, vagy beállíthatja őket CW_USEDEFAULT- ebben az esetben a rendszer egy kaszkádolt algoritmus szerint kiválaszt egy pozíciót, méretet vagy mindkettőt. Mindenesetre mind a négy tagot inicializálni kell. A Multipad az összes dimenzióhoz a CW_USEDEFAULT értéket használja.

A MDICREATESTRUCT szerkezet utolsó tagja a stílus tag, amely az ablak stílusbiteit tartalmazhatja. Ha olyan MDI gyermekablakot szeretne létrehozni, amely az ablakstílusok tetszőleges kombinációjával rendelkezhet, adja meg a MDIS_ALLCHILDSTYLES ablakstílust. Ha ez a stílus nincs megadva, az MDI gyermekablakában alapértelmezett beállításként a WS_MINIMIZE, a WS_MAXIMIZE, a WS_HSCROLLés a WS_VSCROLL stílusok vannak megadva.

A Multipad a helyileg definiált AddFile függvénnyel hozza létre az MDI gyermekablakait (az MPFILE forrásfájlban található). C). Az AddFile függvény úgy állítja be a gyermekablak címét, hogy hozzárendeli a szTitle tagot az ablak MDICREATESTRUCT szerkezetéhez a szerkesztett fájl nevére vagy a "Névtelen" értékre. A szClass tag a Multipad InitializeApplication függvényében regisztrált MDI gyermekablak-osztály nevére van állítva. Az hOwner tag az alkalmazás példánykezelőjére van állítva.

Az alábbi példa a Multipad AddFile függvényét mutatja be.

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; 
} 

A WM_MDICREATE üzenet lParam paraméterében átadott mutatót a rendszer a CreateWindow függvénynek továbbítja, és az CREATESTRUCT szerkezet első tagjaként jelenik meg az WM_CREATE üzenetben. A Multipadben a gyermekablak inicializálja magát WM_CREATE üzenetfeldolgozás során a dokumentumváltozók inicializálásával a saját kiegészítő adataiban, valamint a szerkesztési vezérlő gyermekablakának létrehozásával.