Freigeben über


Verwenden der Schnittstelle für mehrere Dokumente

In diesem Abschnitt wird erläutert, wie Die folgenden Aufgaben ausgeführt werden:

Um diese Aufgaben zu veranschaulichen, enthält dieser Abschnitt Beispiele aus Multipad, einer typischen MDI-Anwendung (Multiple-Document Interface).

Registrieren untergeordneter und Framefensterklassen

Eine typische MDI-Anwendung muss zwei Fensterklassen registrieren: eine für ihr Rahmenfenster und eine für die untergeordneten Fenster. Wenn eine Anwendung mehrere Dokumenttypen unterstützt (z. B. eine Tabelle und ein Diagramm), muss sie für jeden Typ eine Fensterklasse registrieren.

Die Klassenstruktur für das Rahmenfenster ähnelt der Klassenstruktur für das Standard-Fenster in Nicht-MDI-Anwendungen. Die Klassenstruktur für die untergeordneten MDI-Fenster unterscheidet sich geringfügig von der Struktur für untergeordnete Fenster in Nicht-MDI-Anwendungen wie folgt:

  • Die Klassenstruktur sollte über ein Symbol verfügen, da der Benutzer ein untergeordnetes MDI-Fenster so minimieren kann, als wäre es ein normales Anwendungsfenster.
  • Der Menüname sollte NULL sein, da ein untergeordnetes MDI-Fenster kein eigenes Menü haben kann.
  • Die Klassenstruktur sollte zusätzlichen Platz in der Fensterstruktur reservieren. Mit diesem Bereich kann die Anwendung Daten, z. B. einen Dateinamen, einem bestimmten untergeordneten Fenster zuordnen.

Das folgende Beispiel zeigt, wie Multipad seine Frame- und untergeordneten Fensterklassen registriert.

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

Erstellen von Frame- und untergeordneten Fenstern

Nach dem Registrieren der Fensterklassen kann eine MDI-Anwendung ihre Fenster erstellen. Zunächst erstellt er sein Rahmenfenster mithilfe der CreateWindow - oder CreateWindowEx-Funktion . Nach dem Erstellen des Rahmenfensters erstellt die Anwendung ihr Clientfenster, erneut mithilfe von CreateWindow oder CreateWindowEx. Die Anwendung sollte MDICLIENT als Klassennamen des Clientfensters angeben. MDICLIENT ist eine vorab registrierte Fensterklasse, die vom System definiert wird. Der lpvParam-Parameter von CreateWindow oder CreateWindowEx sollte auf eine CLIENTCREATESTRUCT-Struktur verweisen. Diese Struktur enthält die in der folgenden Tabelle beschriebenen Member:

Member BESCHREIBUNG
hWindowMenu Handle zum Fenstermenü, das zum Steuern untergeordneter MDI-Fenster verwendet wird. Wenn untergeordnete Fenster erstellt werden, fügt die Anwendung ihre Titel dem Fenstermenü als Menüelemente hinzu. Der Benutzer kann dann ein untergeordnetes Fenster aktivieren, indem er im Fenstermenü auf den Titel klickt.
idFirstChild Gibt den Bezeichner des ersten untergeordneten MDI-Fensters an. Dem ersten erstellten untergeordneten MDI-Fenster wird dieser Bezeichner zugewiesen. Zusätzliche Fenster werden mit inkrementierten Fensterbezeichnern erstellt. Wenn ein untergeordnetes Fenster zerstört wird, weist das System die Fensterbezeichner sofort neu zu, um ihren Bereich zusammenhängend zu halten.

 

Wenn der Titel eines untergeordneten Fensters zum Fenstermenü hinzugefügt wird, weist das System dem untergeordneten Fenster einen Bezeichner zu. Wenn der Benutzer auf den Titel eines untergeordneten Fensters klickt, erhält das Rahmenfenster eine WM_COMMAND Nachricht mit dem Bezeichner im wParam-Parameter . Sie sollten einen Wert für den idFirstChild-Member angeben, der nicht mit Menüelementbezeichnern im Menü des Rahmenfensters in Konflikt steht.

Die Framefensterprozedur des Multipads erstellt das MDI-Clientfenster, während die WM_CREATE Meldung verarbeitet wird. Das folgende Beispiel zeigt, wie das Clientfenster erstellt wird.

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; 

Die Titel untergeordneter Fenster werden am unteren Rand des Fenstermenüs hinzugefügt. Wenn die Anwendung dem Fenstermenü mithilfe der AppendMenu-Funktion Zeichenfolgen hinzufügt, können diese Zeichenfolgen durch die Titel der untergeordneten Fenster überschrieben werden, wenn das Fenstermenü neu gezeichnet wird (wenn ein untergeordnetes Fenster erstellt oder zerstört wird). Eine MDI-Anwendung, die ihrem Fenstermenü Zeichenfolgen hinzufügt, sollte die InsertMenu-Funktion verwenden und überprüfen, ob die Titel untergeordneter Fenster diese neuen Zeichenfolgen nicht überschrieben haben.

Verwenden Sie die WS_CLIPCHILDREN Stil, um das MDI-Clientfenster zu erstellen, um zu verhindern, dass das Fenster über seine untergeordneten Fenster strich.

Schreiben der Hauptnachrichtenschleife

Die Standard-Nachrichtenschleife einer MDI-Anwendung ähnelt der einer Nicht-MDI-Anwendung, die Zugriffstasten behandelt. Der Unterschied besteht darin, dass die MDI-Nachrichtenschleife die TranslateMDISysAccel-Funktion aufruft, bevor sie nach anwendungsdefinierte Tastenkombinationen oder vor dem Senden der Nachricht sucht.

Das folgende Beispiel zeigt die Nachrichtenschleife einer typischen MDI-Anwendung. Beachten Sie, dass GetMessage -1 zurückgeben kann, wenn ein Fehler vorliegt.

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

Die TranslateMDISysAccel-Funktion übersetzt WM_KEYDOWN Nachrichten in WM_SYSCOMMAND Nachrichten und sendet sie an das aktive untergeordnete MDI-Fenster. Wenn es sich bei der Nachricht nicht um eine MDI-Beschleunigermeldung handelt, gibt die Funktion FALSE zurück. In diesem Fall verwendet die Anwendung die TranslateAccelerator-Funktion , um zu bestimmen, ob eine der anwendungsdefinierten Tastenkombinationen gedrückt wurde. Andernfalls sendet die Schleife die Nachricht an die entsprechende Fensterprozedur.

Schreiben der Framefensterprozedur

Die Fensterprozedur für ein MDI-Rahmenfenster ähnelt der des Standard-Fensters einer Nicht-MDI-Anwendung. Der Unterschied besteht darin, dass eine Framefensterprozedur alle Nachrichten, die sie nicht verarbeitet, an die Funktion DefFrameProc und nicht an die DefWindowProc-Funktion übergibt. Darüber hinaus muss die Framefensterprozedur auch einige Meldungen übergeben, die sie verarbeitet, einschließlich der in der folgenden Tabelle aufgeführten.

Nachricht Antwort
WM_COMMAND Aktiviert das vom Benutzer ausgewählte untergeordnete MDI-Fenster. Diese Meldung wird gesendet, wenn der Benutzer im Fenstermenü des MDI-Rahmenfensters ein untergeordnetes MDI-Fenster auswäht. Der Dieser Meldung beigefügte Fensterbezeichner identifiziert das zu aktivierende untergeordnete MDI-Fenster.
WM_MENUCHAR Öffnet das Fenstermenü des aktiven untergeordneten MDI-Fensters, wenn der Benutzer die Tastenkombination ALT+ – (Minus) drückt.
WM_SETFOCUS Übergibt den Tastaturfokus an das MDI-Clientfenster, das ihn wiederum an das aktive untergeordnete MDI-Fenster übergibt.
WM_SIZE Ändert die Größe des MDI-Clientfensters so, dass es in den Clientbereich des neuen Rahmenfensters passt. Wenn die Framefensterprozedur das MDI-Clientfenster auf eine andere Größe vergrößert, sollte die Meldung nicht an die DefWindowProc-Funktion übergeben werden.

 

Die Framefensterprozedur in Multipad heißt MPFrameWndProc. Die Behandlung anderer Nachrichten durch MPFrameWndProc ähnelt der von Nicht-MDI-Anwendungen. WM_COMMAND Nachrichten in Multipad werden von der lokal definierten CommandHandler-Funktion verarbeitet. Für Befehlsmeldungen, die Multipad nicht verarbeitet, ruft CommandHandler die DefFrameProc-Funktion auf. Wenn Multipad DefFrameProc standardmäßig nicht verwendet, kann der Benutzer ein untergeordnetes Fenster nicht über das Fenstermenü aktivieren, da die durch Klicken auf das Menüelement des Fensters gesendete WM_COMMAND Nachricht verloren geht.

Schreiben der Prozedur für untergeordnete Fenster

Wie die Framefensterprozedur verwendet eine untergeordnete MDI-Fensterprozedur standardmäßig eine spezielle Funktion zum Verarbeiten von Nachrichten. Alle Meldungen, die von der untergeordneten Fensterprozedur nicht verarbeitet werden, müssen an die Funktion DefMDIChildProc und nicht an die Funktion DefWindowProc übergeben werden. Darüber hinaus müssen einige Fensterverwaltungsmeldungen an DefMDIChildProc übergeben werden, auch wenn die Anwendung die Nachricht verarbeitet, damit MDI ordnungsgemäß funktioniert. Es folgen die Meldungen, die die Anwendung an DefMDIChildProc übergeben muss.

Nachricht Antwort
WM_CHILDACTIVATE Führt die Aktivierungsverarbeitung aus, wenn untergeordnete MDI-Fenster dimensioniert, verschoben oder angezeigt werden. Diese Nachricht muss übergeben werden.
WM_GETMINMAXINFO Berechnet die Größe eines maximierten untergeordneten MDI-Fensters basierend auf der aktuellen Größe des MDI-Clientfensters.
WM_MENUCHAR Übergibt die Meldung an das MDI-Rahmenfenster.
WM_MOVE Berechnet MDI-Client-Bildlaufleisten neu, wenn sie vorhanden sind.
WM_SETFOCUS Aktiviert das untergeordnete Fenster, wenn es sich nicht um das aktive untergeordnete MDI-Fenster handelt.
WM_SIZE Führt Vorgänge aus, die zum Ändern der Größe eines Fensters erforderlich sind, insbesondere zum Maximieren oder Wiederherstellen eines untergeordneten MDI-Fensters. Wenn diese Nachricht nicht an die Funktion DefMDIChildProc übergeben wird, werden hochgradig unerwünschte Ergebnisse erzeugt.
WM_SYSCOMMAND Verarbeitet Fenstermenübefehle (früher als System bezeichnet): SC_NEXTWINDOW, SC_PREVWINDOW, SC_MOVE, SC_SIZE und SC_MAXIMIZE.

 

Erstellen eines untergeordneten Fensters

Um ein untergeordnetes MDI-Fenster zu erstellen, kann eine Anwendung entweder die CreateMDIWindow-Funktion aufrufen oder eine WM_MDICREATE Nachricht an das MDI-Clientfenster senden. (Die Anwendung kann die CreateWindowEx-Funktion mit dem WS_EX_MDICHILD Stil verwenden, um untergeordnete MDI-Fenster zu erstellen.) Eine Singlethread-MDI-Anwendung kann beide Methoden verwenden, um ein untergeordnetes Fenster zu erstellen. Ein Thread in einer Multithread-MDI-Anwendung muss die Funktion CreateMDIWindow oder CreateWindowEx verwenden, um ein untergeordnetes Fenster in einem anderen Thread zu erstellen.

Der lParam-Parameter einer WM_MDICREATE Nachricht ist ein weit entfernter Zeiger auf eine MDICREATESTRUCT-Struktur . Die Struktur enthält vier Dimensionselemente: x und y, die die horizontale und vertikale Position des Fensters angeben, und cx und cy, die die horizontalen und vertikalen Ausdehnungen des Fensters angeben. Jedes dieser Member kann von der Anwendung explizit zugewiesen werden, oder sie können auf CW_USEDEFAULT festgelegt werden. In diesem Fall wählt das System gemäß einem kaskadierenden Algorithmus eine Position, Größe oder beides aus. In jedem Fall müssen alle vier Member initialisiert werden. Multipad verwendet CW_USEDEFAULT für alle Dimensionen.

Das letzte Element der MDICREATESTRUCT-Struktur ist das Formatelement , das Formatvorlagenbits für das Fenster enthalten kann. Um ein untergeordnetes MDI-Fenster zu erstellen, das eine beliebige Kombination von Fensterformatvorlagen aufweisen kann, geben Sie die MDIS_ALLCHILDSTYLES Fensterstil an. Wenn diese Formatvorlage nicht angegeben ist, verfügt ein untergeordnetes MDI-Fenster über die Standardeinstellungen WS_MINIMIZE, WS_MAXIMIZE, WS_HSCROLL und WS_VSCROLL .

Multipad erstellt die untergeordneten MDI-Fenster mithilfe der lokal definierten AddFile-Funktion (in der Quelldatei MPFILE). C). Die AddFile-Funktion legt den Titel des untergeordneten Fensters fest, indem der member szTitle der MDICREATESTRUCT-Struktur des Fensters entweder dem Namen der zu bearbeitenden Datei oder "Unbenannt" zugewiesen wird. Das szClass-Element wird auf den Namen der untergeordneten MDI-Fensterklasse festgelegt, die in der InitializeApplication-Funktion von Multipad registriert ist. Das hOwner-Element wird auf das instance Handle der Anwendung festgelegt.

Das folgende Beispiel zeigt die AddFile-Funktion 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; 
} 

Der im lParam-Parameter der WM_MDICREATE Meldung übergebene Zeiger wird an die CreateWindow-Funktion übergeben und wird als erstes Element in der CREATESTRUCT-Struktur angezeigt, das in der WM_CREATE Meldung übergeben wird. In Multipad initialisiert sich das untergeordnete Fenster während WM_CREATE Nachrichtenverarbeitung selbst, indem Dokumentvariablen in den zusätzlichen Daten initialisiert und das untergeordnete Fenster des Bearbeitungssteuerelements erstellt wird.