Sdílet prostřednictvím


Použití rozhraní pro více dokumentů

Tato část vysvětluje, jak provádět následující úlohy:

Pro ilustraci těchto úloh obsahuje tato část příklady z multipadu, což je typická aplikace s více dokumenty (MDI).

Registrace tříd dceřiných a rámových oken

Typická aplikace MDI musí registrovat dvě třídy oken: jednu pro okno rámečku a jednu pro podřízená okna. Pokud aplikace podporuje více než jeden typ dokumentu (například tabulku a graf), musí pro každý typ zaregistrovat třídu okna.

Struktura třídy pro rámečkové okno je podobná struktuře třídy pro hlavní okno v aplikacích, které nejsou MDI. Struktura třídy podřízených oken MDI se mírně liší od struktury podřízených oken v aplikacích jiných než MDI následujícím způsobem:

  • Struktura třídy by měla mít ikonu, protože uživatel může minimalizovat podřízené okno MDI, jako by to bylo normální okno aplikace.
  • Název nabídky by měl být null, protože podřízené okno MDI nemůže mít vlastní nabídku.
  • Struktura třídy by měla rezervovat nadbytečné místo ve struktuře okna. S tímto prostorem může aplikace přidružit data, jako je název souboru, ke konkrétnímu podřízenému okně.

Následující příklad ukazuje, jak Multipad registruje své okno a třídy podřízených oken.

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

Vytváření rámcových a podřízených oken

Po registraci tříd oken může aplikace MDI vytvořit svá okna. Nejprve vytvoří okno rámce pomocí funkce CreateWindow nebo CreateWindowEx. Po vytvoření okna rámce aplikace znovu vytvoří okno klienta pomocí CreateWindow nebo CreateWindowEx. Aplikace by měla jako název třídy klientského okna zadat MDICLIENT; MDICLIENT je předregistrovaná třída okna definovaná systémem. Parametr lpvParamCreateWindow nebo CreateWindowEx by měl odkazovat na strukturu CLIENTCREATESTRUCT. Tato struktura obsahuje členy popsané v následující tabulce:

Člen Popis
hWindowMenu Identifikátor pro nabídku okna sloužící k ovládání podřízených oken MDI. Při vytváření podřízených oken aplikace přidá jejich názvy do nabídky okna jako položky nabídky. Uživatel pak může aktivovat podřízené okno kliknutím na jeho název v nabídce okna.
idFirstChild Určuje identifikátor prvního podřízeného okna MDI. Prvnímu vytvořenému podřízenému oknu MDI je přiřazen tento identifikátor. Další okna se vytvářejí s přírůstkovými identifikátory oken. Když dojde ke zrušení podřízeného okna, systém okamžitě znovu přiřadí identifikátory okna, aby zachoval jejich rozsah souvislý.

 

Při přidání názvu podřízeného okna do nabídky okna systém přiřadí identifikátor podřízeného okna. Když uživatel klikne na titulek podřízeného okna, rámcové okno obdrží zprávu WM_COMMAND s identifikátorem v parametru wParam. Měli byste zadat hodnotu pro idFirstChild člena, která není v konfliktu s identifikátory položek v menu rámcového okna.

Při zpracování zprávy WM_CREATE vytvoří procedura okna s rámečkem Multipad okno klienta MDI. Následující příklad ukazuje, jak se vytvoří okno klienta.

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; 

Názvy podřízených oken jsou přidávány do spodní části nabídky okna. Pokud aplikace přidá řetězce do nabídky okna pomocí funkce AppendMenu, mohou být tyto řetězce přepsány názvy podřízených oken při překreslení nabídky okna (pokaždé když je podřízené okno vytvořeno nebo zničeno). Aplikace MDI, která přidává řetězce do nabídky okna, by měla použít funkci InsertMenu a ověřit, zda názvy podřízených oken nepřepsaly tyto nové řetězce.

Pomocí stylu WS_CLIPCHILDREN vytvořte okno klienta MDI, abyste zabránili překreslování okna přes jeho podřízená okna.

Vytvoření hlavní smyčky zpráv

Hlavní smyčka zpráv aplikace MDI je podobná hlavní smyčce aplikace, která není MDI, při zpracování akcelerátorových kláves. Rozdíl je v tom, že smyčka zpráv MDI volá funkci TranslateMDISysAccel před kontrolou klíčů akcelerátorů definovaných aplikací nebo před odesláním zprávy.

Následující příklad ukazuje smyčku zpráv typické aplikace MDI. Všimněte si, že GetMessage může vrátit -1, pokud dojde k chybě.

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

Funkce TranslateMDISysAccel překládá WM_KEYDOWN zprávy do WM_SYSCOMMAND zpráv a odesílá je do aktivního podřízeného okna MDI. Pokud zpráva není zprávou akcelerátoru MDI, vrátí funkce FALSE, v takovém případě aplikace používá funkci TranslateAccelerator k určení, zda byla stisknuta některá z kláves akcelerátoru definovaná aplikací. Pokud ne, smyčka odešle zprávu do příslušné okenní procedury.

Psaní procedury rámcového okna

Postup okna pro MDI rámcové okno je podobný jako u hlavního okna aplikace bez MDI. Rozdíl je v tom, že procedura okna rámečku předává všechny zprávy, které nezpracuje do funkce DefFrameProc namísto funkce DefWindowProc. Kromě toho musí procedura okna rámečku předat také některé zprávy, které zpracovává, včetně těch uvedených v následující tabulce.

Zpráva Odpověď
WM_COMMAND Aktivuje podřízené okno MDI, které uživatel zvolí. Tato zpráva se odešle, když uživatel zvolí podřízené okno MDI z nabídky okna okna rámce MDI. Identifikátor okna, který tuto zprávu doprovází, identifikuje podřízené okno MDI, které se má aktivovat.
WM_MENUCHAR Otevře nabídku okna aktivního podřízeného okna MDI, když uživatel stiskne kombinaci kláves ALT+ – (minus).
WM_SETFOCUS Předá fokus klávesnice do okna klienta MDI, které ho zase předá do aktivního podřízeného okna MDI.
WM_SIZE Změní velikost okna klienta MDI tak, aby se vešla do klientské oblasti nového rámcového okna. Pokud procedura okna rámce změní velikost okna klienta MDI na jinou velikost, neměla by funkce zprávu předat DefWindowProc.

 

Procedura okna rámečku v Multipadu se nazývá MPFrameWndProc. Zpracování jiných zpráv pomocí funkce MPFrameWndProc je podobné zpracování jiných než MDI aplikací. WM_COMMAND zprávy v Multipad jsou zpracovávány místně definovanou funkcí CommandHandler. Příkazy, které Multipad nezpracuje, jsou předány funkci DefFrameProc voláním CommandHandler. Pokud multipad nepoužívá DefFrameProc ve výchozím nastavení, uživatel nemůže aktivovat podřízené okno z nabídky okna, protože WM_COMMAND zpráva odeslaná kliknutím na položku nabídky okna by byla ztracena.

Zápis procedury podřízeného okna

Podobně jako procedura okna s rámečkem používá procedura podřízeného okna MDI ve výchozím nastavení speciální funkci pro zpracování zpráv. Všechny zprávy, které procedura podřízeného okna nezpracuje, musí být předány funkci DefMDIChildProc namísto funkci DefWindowProc. Kromě toho musí být některé zprávy správy oken předány funkci DefMDIChildProc, a to i když aplikace zprávu již zpracovává, pro správnou funkci MDI. Níže jsou uvedené zprávy, které aplikace musí předat DefMDIChildProc.

Zpráva Odpověď
WM_CHILDACTIVATE Provádí zpracování aktivace, když se mění velikost, přesouvají nebo zobrazují podřízená okna MDI. Tato zpráva musí být předána.
WM_GETMINMAXINFO Vypočítá velikost maximalizovaného podřízeného okna MDI na základě aktuální velikosti okna klienta MDI.
WM_MENUCHAR Předá zprávu do okna rámce MDI.
WM_MOVE Přepočítá posuvníkové lišty klienta MDI, pokud jsou přítomny.
WM_SETFOCUS Aktivuje podřízené okno, pokud není aktivním podřízeným oknem MDI.
WM_SIZE Provádí operace nezbytné ke změně velikosti okna, zejména pro maximalizaci nebo obnovení podřízeného okna MDI. Nepředání této zprávy funkci DefMDIChildProc vede k vysoce nežádoucím výsledkům.
WM_SYSCOMMAND Zpracovává příkazy nabídky okna (dříve známé jako systémové): SC_NEXTWINDOW, SC_PREVWINDOW, SC_MOVE, SC_SIZEa SC_MAXIMIZE.

 

Vytvoření podřízeného okna

Pokud chcete vytvořit podřízené okno MDI, může aplikace buď volat funkci CreateMDIWindow, nebo odeslat WM_MDICREATE zprávu do okna klienta MDI. (Aplikace může k vytvoření podřízených oken MDI použít funkci CreateWindowEx se stylem WS_EX_MDICHILD.) Jednovláknová aplikace MDI může k vytvoření podřízeného okna použít některou z metod. Vlákno ve vícevláknové aplikaci MDI musí používat funkci CreateMDIWindow nebo CreateWindowEx k vytvoření podřízeného okna v jiném vlákně.

Parametr lParam zprávy WM_MDICREATE je vzdálený ukazatel na strukturu MDICREATESTRUCT. Struktura zahrnuje čtyři rozměrové členy: x a y, které označují vodorovné a svislé pozice okna, a cx a cy, které označují vodorovné a svislé rozsahy okna. Každý z těchto členů může být explicitně přiřazen aplikací nebo mohou být nastaveny na CW_USEDEFAULT, v takovém případě systém vybere pozici, velikost nebo obojí podle kaskádového algoritmu. V každém případě musí být inicializovány všechny čtyři členy. Multipad používá CW_USEDEFAULT pro všechny rozměry.

Posledním členem struktury MDICREATESTRUCT je styl člen, který může obsahovat bity stylů pro okno. Pokud chcete vytvořit podřízené okno MDI, které může mít libovolnou kombinaci stylů oken, zadejte MDIS_ALLCHILDSTYLES styl okna. Pokud tento styl není zadaný, má podřízené okno MDI výchozí nastavení WS_MINIMIZE, WS_MAXIMIZE, WS_HSCROLLa WS_VSCROLL styly.

Multipad vytvoří podřízená okna MDI pomocí místně definované funkce AddFile (nachází se ve zdrojovém souboru MPFILE. C). Funkce AddFile nastaví název podřízeného okna tím, že přiřadí členu szTitle struktury MDICREATESTRUCT buď název souboru, který se upravuje, nebo "Bez názvu". Člen szClass je nastaven na název třídy podřízeného okna MDI, která je zaregistrována ve funkci InitializeApplication v programu Multipad. Člen hOwner je nastaven na popisovač instance aplikace.

Následující příklad ukazuje funkci „AddFile“ v Multipadu.

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

Ukazatel předaný v parametru lParam zprávy WM_MDICREATE je předán funkci CreateWindow a zobrazí se jako první člen ve struktuře CREATESTRUCT předané ve zprávě WM_CREATE. V Multipadu se podřízené okno inicializuje během zpracování zprávy WM_CREATE inicializací proměnných dokumentu v dodatečných datech a vytvořením podřízeného okna editcontrolu.