TN021: Befehls- und Meldungsrouting

Hinweis

Der folgende technische Hinweis wurde seit dem ersten Erscheinen in der Onlinedokumentation nicht aktualisiert. Daher können einige Verfahren und Themen veraltet oder falsch sein. Um aktuelle Informationen zu erhalten, wird empfohlen, das gewünschte Thema im Index der Onlinedokumentation zu suchen.

In diesem Hinweis werden die Befehlsrouting- und Verteilerarchitektur sowie erweiterte Themen im allgemeinen Nachrichtenweiterleitungsfenster beschrieben.

Allgemeine Informationen zu den hier beschriebenen Architekturen finden Sie unter Visual C++, insbesondere bei der Unterscheidung zwischen Windows-Nachrichten, Steuerelementbenachrichtigungen und Befehlen. In diesem Hinweis wird davon ausgegangen, dass Sie mit den in der gedruckten Dokumentation beschriebenen Problemen vertraut sind und nur sehr fortgeschrittene Themen behandelt werden.

Command Routing and Dispatch MFC 1.0 Functionality Evolves to MFC 2.0 Architecture

Windows verfügt über die WM_COMMAND Nachricht, die überladen ist, um Benachrichtigungen über Menübefehle, Tastenkombinationen und Dialogfeldsteuerungsbenachrichtigungen bereitzustellen.

MFC 1.0 basiert darauf, indem ein Befehlshandler (z. B. "OnFileNew") in einer CWnd abgeleiteten Klasse als Reaktion auf eine bestimmte WM_COMMAND aufgerufen wird. Dies wird zusammen mit einer Datenstruktur, die als Nachrichtenzuordnung bezeichnet wird, geklebt und führt zu einem sehr raumeffizienten Befehlsmechanismus.

MFC 1.0 bietet auch zusätzliche Funktionen zum Trennen von Steuerungsbenachrichtigungen von Befehlsmeldungen. Befehle werden durch eine 16-Bit-ID dargestellt, manchmal auch als Befehls-ID bezeichnet. Befehle beginnen normalerweise von einem CFrameWnd (d. h. einer Menüauswahl oder einer übersetzten Zugriffstaste) und werden an eine Vielzahl anderer Fenster weitergeleitet.

MFC 1.0 verwendete Befehlsrouting in begrenztem Sinne für die Implementierung von Multiple Document Interface (MDI). (Ein MDI-Framefensterdelegatbefehle an das aktive untergeordnete MDI-Fenster.)

Diese Funktionalität wurde in MFC 2.0 generalisiert und erweitert, um die Verarbeitung von Befehlen durch einen größeren Objektbereich zu ermöglichen (nicht nur Fensterobjekte). Es bietet eine formellere und erweiterbarere Architektur zum Weiterleiten von Nachrichten und verwendet das Befehlszielrouting nicht nur für die Behandlung von Befehlen, sondern auch zum Aktualisieren von UI-Objekten (z. B. Menüelemente und Symbolleistenschaltflächen), um die aktuelle Verfügbarkeit eines Befehls widerzuspiegeln.

Befehls-IDs

Eine Erläuterung des Befehlsroutings und des Bindungsprozesses finden Sie unter Visual C++. Technisches Hinweis 20 enthält Informationen zur ID-Benennung.

Wir verwenden das generische Präfix "ID_" für Befehls-IDs. Befehls-IDs sind >= 0x8000. Die Meldungszeile oder Statusleiste zeigt die Befehlsbeschreibungszeichenfolge an, wenn eine STRINGTABLE-Ressource mit denselben IDs wie die Befehls-ID vorhanden ist.

In den Ressourcen Ihrer Anwendung kann eine Befehls-ID an mehreren Stellen angezeigt werden:

  • In einer STRINGTABLE-Ressource mit derselben ID wie die Meldungszeile.

  • In möglicherweise vielen MENÜressourcen, die an Menüelemente angefügt sind, die denselben Befehl aufrufen.

  • (ERWEITERT) in einer Dialogfeldschaltfläche für einen GOSUB-Befehl.

Im Quellcode Ihrer Anwendung kann an mehreren Stellen eine Befehls-ID angezeigt werden:

  • In Ihrer RESSOURCE. H (oder eine andere Standard Symbolkopfdatei), um anwendungsspezifische Befehls-IDs zu definieren.

  • VIELLEICHT in einem ID-Array, das zum Erstellen einer Symbolleiste verwendet wird.

  • In einem ON_COMMAND Makro.

  • VIELLEICHT in einem ON_UPDATE_COMMAND_UI-Makro.

Derzeit ist die einzige Implementierung in MFC, die Befehls-IDs erfordert, = >0x8000 ist die Implementierung von GOSUB-Dialogfeldern/-Befehlen.

GOSUB-Befehle unter Verwendung der Befehlsarchitektur in Dialogfeldern

Die Befehlsarchitektur des Routings und der Aktivierung von Befehlen funktioniert gut mit Framefenstern, Menüelementen, Symbolleistenschaltflächen, Dialogfelderschaltflächen, anderen Steuerelementleisten und anderen Elementen der Benutzeroberfläche, die zum Aktualisieren von Bedarfs- und Routingbefehlen oder Steuerelement-IDs an ein Standard Befehlsziel (in der Regel das Standard Framefenster) entwickelt wurden. Dies Standard Befehlsziel kann die Befehls- oder Steuerelementbenachrichtigungen ggf. an andere Befehlszielobjekte weiterleiten.

Ein Dialogfeld (modal oder moduslos) kann von einigen Der Features der Befehlsarchitektur profitieren, wenn Sie die Steuerelement-ID des Dialogfeld-Steuerelements der entsprechenden Befehls-ID zuweisen. Die Unterstützung für Dialogfelder ist nicht automatisch, daher müssen Sie möglicherweise zusätzlichen Code schreiben.

Beachten Sie, dass ihre Befehls-IDs = 0x8000 sein >sollten, damit alle diese Features ordnungsgemäß funktionieren. Da viele Dialogfelder an denselben Frame weitergeleitet werden können, sollten freigegebene Befehle = 0x8000 sein >, während die nicht freigegebenen IDCs in einem bestimmten Dialogfeld = 0x7FFF sein <sollten.

Sie können eine normale Schaltfläche in einem normalen modalen Dialogfeld platzieren, wobei die IDC der Schaltfläche auf die entsprechende Befehls-ID festgelegt ist. Wenn der Benutzer die Schaltfläche auswählt, erhält der Besitzer des Dialogfelds (in der Regel das Standard Framefenster) den Befehl genau wie jeder andere Befehl. Dies wird als GOSUB-Befehl bezeichnet, da in der Regel ein anderes Dialogfeld (ein GOSUB des ersten Dialogfelds) angezeigt wird.

Sie können die Funktion CWnd::UpdateDialogControls auch in Ihrem Dialogfeld aufrufen und sie an die Adresse Ihres Standard Framefensters übergeben. Diese Funktion aktiviert oder deaktiviert Ihre Dialogfeldsteuerelemente basierend darauf, ob sie Befehlshandler im Frame haben. Diese Funktion wird automatisch für Steuerleisten in der Leerlaufschleife Ihrer Anwendung aufgerufen, sie muss jedoch direkt für normale Dialogfelder aufgerufen werden, für die Sie dieses Feature verwenden möchten.

Wenn ON_UPDATE_COMMAND_UI aufgerufen wird

Die Beibehaltung des aktivierten/überprüften Zustands aller Menüelemente eines Programms kann immer ein rechenintensives Problem sein. Eine gängige Technik besteht darin, Menüelemente nur zu aktivieren/zu überprüfen, wenn der Benutzer das POPUP auswählt. Die MFC 2.0-Implementierung behandelt CFrameWnd die WM_INITMENUPOPUP Nachricht und verwendet die Befehlsroutingarchitektur, um die Zustände von Menüs über ON_UPDATE_COMMAND_UI Handlern zu bestimmen.

CFrameWnd behandelt auch die WM_ENTERIDLE Nachricht, um das aktuelle Menüelement zu beschreiben, das auf der Statusleiste ausgewählt ist (auch als Nachrichtenzeile bezeichnet).

Die Menüstruktur einer Anwendung, die von Visual C++ bearbeitet wird, wird verwendet, um die potenziellen Befehle darzustellen, die zu WM_INITMENUPOPUP Zeit verfügbar sind. ON_UPDATE_COMMAND_UI-Handler können den Zustand oder den Text eines Menüs ändern oder bei erweiterten Verwendungen (z. B. der Datei-MRU-Liste oder dem Popupmenü "OLE-Verben") die Menüstruktur ändern, bevor das Menü gezeichnet wird.

Dieselbe Art von ON_UPDATE_COMMAND_UI Verarbeitung erfolgt für Symbolleisten (und andere Steuerleisten), wenn die Anwendung in die Leerlaufschleife wechselt. Weitere Informationen zu Steuerleisten finden Sie in der Klassenbibliotheksreferenz und im technischen Hinweis 31 .

Geschachtelte Popupmenüs

Wenn Sie eine geschachtelte Menüstruktur verwenden, werden Sie feststellen, dass der ON_UPDATE_COMMAND_UI-Handler für das erste Menüelement im Popupmenü in zwei verschiedenen Fällen aufgerufen wird.

Zuerst wird es für das Popupmenü selbst aufgerufen. Dies ist erforderlich, da Popupmenüs keine IDs enthalten und wir die ID des ersten Menüelements des Popupmenüs verwenden, um auf das gesamte Popupmenü zu verweisen. In diesem Fall ist die m_pSubMenu Membervariable des CCmdUI Objekts nicht NULL und zeigt auf das Popupmenü.

Zweitens wird sie kurz vor dem Zeichnen der Menüelemente im Popupmenü aufgerufen. In diesem Fall bezieht sich die ID nur auf das erste Menüelement, und die m_pSubMenu Membervariable des CCmdUI Objekts ist NULL.

Auf diese Weise können Sie das Popupmenü aktivieren, das sich von seinen Menüelementen unterscheidet, erfordert jedoch, dass Sie codebasierte Menüfunktionen schreiben. Beispiel: In einem geschachtelten Menü mit der folgenden Struktur:

File>
    New>
    Sheet (ID_NEW_SHEET)
    Chart (ID_NEW_CHART)

Die Befehle ID_NEW_SHEET und ID_NEW_CHART können unabhängig oder deaktiviert werden. Das Popupmenü "Neu " sollte aktiviert werden, wenn eine der beiden aktiviert ist.

Der Befehlshandler für ID_NEW_SHEET (der erste Befehl im Popup) würde etwa wie folgt aussehen:

void CMyApp::OnUpdateNewSheet(CCmdUI* pCmdUI)
{
    if (pCmdUI->m_pSubMenu != NULL)
    {
        // enable entire pop-up for "New" sheet and chart
        BOOL bEnable = m_bCanCreateSheet || m_bCanCreateChart;
        // CCmdUI::Enable is a no-op for this case, so we
        // must do what it would have done.
        pCmdUI->m_pMenu->EnableMenuItem(pCmdUI->m_nIndex,
            MF_BYPOSITION |
            (bEnable  MF_ENABLED : (MF_DISABLED | MF_GRAYED)));

        return;
    }
    // otherwise just the New Sheet command
    pCmdUI->Enable(m_bCanCreateSheet);
}

Der Befehlshandler für ID_NEW_CHART wäre ein normaler Updatebefehlshandler und sieht ungefähr wie folgt aus:

void CMyApp::OnUpdateNewChart(CCmdUI* pCmdUI)
{
    pCmdUI->Enable(m_bCanCreateChart);
}

ON_COMMAND und ON_BN_CLICKED

Die Nachrichtenzuordnungsmakros für ON_COMMAND und ON_BN_CLICKED sind identisch. Der MFC-Befehls- und Steuerungsbenachrichtigungsroutingmechanismus verwendet nur die Befehls-ID, um zu entscheiden, an wen sie weitergeleitet werden soll. Steuerelementbenachrichtigungen mit Steuerelementbenachrichtigungscode null (BN_CLICKED) werden als Befehle interpretiert.

Hinweis

Tatsächlich durchlaufen alle Steuerelementbenachrichtigungen die Befehlshandlerkette. Beispielsweise ist es technisch möglich, einen Steuerelementbenachrichtigungshandler für EN_CHANGE in Ihrer Dokumentklasse zu schreiben. Dies ist im Allgemeinen nicht ratsam, da die praktischen Anwendungen dieses Features nur wenige sind, das Feature von ClassWizard nicht unterstützt wird und die Verwendung des Features zu fragilem Code führen kann.

Deaktivieren der automatischen Deaktivierung von Schaltflächensteuerelementen

Wenn Sie ein Schaltflächensteuerelement auf einer Dialogfeldleiste oder in einem Dialogfeld platzieren, in dem Sie CWnd::UpdateDialogControls selbst aufrufen, werden Sie feststellen, dass Schaltflächen, die nicht über ON_COMMAND oder ON_UPDATE_COMMAND_UI Handler verfügen, automatisch vom Framework deaktiviert werden. In einigen Fällen müssen Sie keinen Handler haben, aber Sie möchten, dass die Schaltfläche erneut aktiviert ist Standard. Die einfachste Möglichkeit, dies zu erreichen, ist das Hinzufügen eines Dummy-Befehlshandlers (einfach zu tun mit ClassWizard) und nichts in ihr.

Nachrichtenweiterleitung im Fenster

Im Folgenden werden einige komplexere Themen zu den MFC-Klassen beschrieben und erläutert, wie sich windows-Nachrichtenrouting und andere Themen darauf auswirken. Die hier beschriebenen Informationen werden nur kurz beschrieben. Weitere Informationen zu öffentlichen APIs finden Sie in der Klassenbibliotheksreferenz . Weitere Informationen zu Implementierungsdetails finden Sie im Quellcode der MFC-Bibliothek.

Weitere Informationen zu Window sauber up, einem sehr wichtigen Thema für alle von CWnd abgeleiteten Klassen, finden Sie im Technischen Hinweis 17.

CWnd-Probleme

Die Implementierungselementfunktion CWnd::OnChildNotify bietet eine leistungsstarke und erweiterbare Architektur für untergeordnete Fenster (auch als Steuerelemente bezeichnet), um Nachrichten, Befehle und Steuerelementbenachrichtigungen zu verknüpfen oder anderweitig zu informieren, die zu ihrem übergeordneten Element (oder "Besitzer") wechseln. Wenn das untergeordnete Fenster (/Steuerelement) ein C++ -CWnd-Objekt selbst ist, wird die virtuelle Funktion OnChildNotify zuerst mit den Parametern aus der ursprünglichen Nachricht (d. h. einer MSG-Struktur ) aufgerufen. Das untergeordnete Fenster kann die Nachricht allein lassen, sie essen oder die Nachricht für das übergeordnete Element ändern (selten).

Die standardmäßige CWnd-Implementierung behandelt die folgenden Meldungen und verwendet den OnChildNotify-Hook , um untergeordnete Fenster (Steuerelemente) den ersten Zugriff auf die Nachricht zu ermöglichen:

  • WM_MEASUREITEM und WM_DRAWITEM (für selbst zeichnen)

  • WM_COMPAREITEM und WM_DELETEITEM (für selbst zeichnen)

  • WM_HSCROLL und WM_VSCROLL

  • WM_CTLCOLOR

  • WM_PARENTNOTIFY

Sie werden feststellen, dass der OnChildNotify-Hook zum Ändern von Besitzer-Zeichnen-Nachrichten in selbst zeichnende Nachrichten verwendet wird.

Zusätzlich zum OnChildNotify-Hook weisen Bildlaufnachrichten ein weiteres Routingverhalten auf. Weitere Informationen zu Bildlaufleisten und Quellen von WM_HSCROLL und WM_VSCROLL Nachrichten finden Sie unten.

CFrameWnd-Probleme

Die CFrameWnd-Klasse bietet den größten Teil der Implementierung des Befehlsroutings und der Benutzeroberflächenaktualisierung. Dies wird in erster Linie für das Standard Rahmenfenster der Anwendung (CWinApp::m_pMainWnd) verwendet, gilt jedoch für alle Rahmenfenster.

Das Standard Rahmenfensters ist das Fenster mit der Menüleiste und das übergeordnete Element der Statusleiste oder Nachrichtenzeile. Weitere Informationen finden Sie in der obigen Diskussion zu Befehlsrouting und WM_INITMENUPOPUP.

Die CFrameWnd-Klasse stellt die Verwaltung der aktiven Ansicht bereit. Die folgenden Nachrichten werden über die aktive Ansicht weitergeleitet:

  • Alle Befehlsmeldungen (die aktive Ansicht erhält zuerst Zugriff darauf).

  • WM_HSCROLL und WM_VSCROLL Nachrichten aus gleichgeordneten Bildlaufleisten (siehe unten).

  • WM_ACTIVATE (und WM_MDIACTIVATE für MDI) werden in Aufrufe der virtuellen Funktion CView::OnActivateView umgewandelt.

CMDIFrameWnd/CMDIChildWnd-Probleme

Beide MDI-Framefensterklassen werden von CFrameWnd abgeleitet und sind daher für dieselbe Art von Befehlsrouting und Aktualisierung der Benutzeroberfläche in CFrameWnd aktiviert. In einer typischen MDI-Anwendung enthält nur das Standard Framefenster (d. h. das CMDIFrameWnd-Objekt) die Menüleiste und die Statusleiste und daher die Standard Quelle der Befehlsroutingimplementierung.

Das allgemeine Routingschema besteht darin, dass das aktive untergeordnete MDI-Fenster zuerst Zugriff auf Befehle erhält. Die Standardmäßigen PreTranslateMessage-Funktionen behandeln Zugriffstastentabellen sowohl für untergeordnete MDI-Fenster (zuerst) als auch für den MDI-Frame (sekunde) sowie die standardmäßigen MDI-Systembefehlstasten, die normalerweise von TranslateMDISysAccel (zuletzt) behandelt werden.

Probleme mit der Bildlaufleiste

Beim Behandeln von Bildlaufnachrichten (WM_HSCROLL/OnHScroll und/oder WM_VSCROLL/OnVScroll) sollten Sie versuchen, den Handlercode zu schreiben, sodass er nicht darauf basiert, wo die Bildlaufleistennachricht stammt. Dies ist nicht nur ein allgemeines Windows-Problem, da Bildlaufmeldungen von steuerelementen true Scrollleisten oder von WS_HSCROLL/WS_VSCROLL Bildlaufleisten stammen können, die keine Bildlaufleisten-Steuerelemente sind.

MFC erweitert dies, damit Bildlaufleisten-Steuerelemente entweder untergeordnete oder gleichgeordnete Elemente des Fensters sein können, das bildlaufbar ist (tatsächlich kann die Beziehung zwischen der Bildlaufleiste und dem Fenster, das gescrollt wird, alles sein). Dies ist besonders wichtig für freigegebene Bildlaufleisten mit Geteilten Fenstern. Weitere Informationen zur Implementierung von CSplitterWnd finden Sie in technischem Hinweis 29. Weitere Informationen zu Problemen mit freigegebenen Bildlaufleisten.

In einer Nebennotiz gibt es zwei von CWnd abgeleitete Klassen, in denen die beim Erstellen angegebenen Bildlaufleistenarten abgefangen und nicht an Windows übergeben werden. Wenn sie an eine Erstellungsroutine übergeben wird, können WS_HSCROLL und WS_VSCROLL unabhängig festgelegt werden, aber nach der Erstellung können sie nicht mehr geändert werden. Natürlich sollten Sie die WS_SCROLL Formatvorlagenbits des von ihnen erstellten Fensters nicht direkt testen oder festlegen.

Für CMDIFrameWnd werden die Formatvorlagen der Bildlaufleiste, die Sie an Create oder LoadFrame übergeben, verwendet, um den MDICLIENT zu erstellen. Wenn Sie einen bildlauffähigen MDICLIENT-Bereich (z. B. den Windows Program Manager) verwenden möchten, müssen Sie beide Bildlaufleistenarten (WS_HSCROLL | WS_VSCROLL) für die Zum Erstellen des CMDIFrameWnd verwendete Formatvorlage festlegen.

Bei CSplitterWnd gelten die Bildlaufleistenformatvorlagen für die speziellen freigegebenen Bildlaufleisten für die Teilerbereiche. Bei statischen Teilerfenstern legen Sie normalerweise keine Bildlaufleistenart fest. Bei dynamischen Teilerfenstern haben Sie in der Regel den Stil der Bildlaufleiste für die Richtung festgelegt, in der Sie geteilt werden, d. h. WS_HSCROLL , wenn Sie Zeilen teilen können, WS_VSCROLL , wenn Sie Spalten teilen können.

Siehe auch

Technische Hinweise – nach Nummern geordnet
Technische Hinweise – nach Kategorien geordnet