Delen via


TN021: Opdracht- en berichtroutering

Opmerking

De volgende technische opmerking is niet bijgewerkt sinds deze voor het eerst is opgenomen in de onlinedocumentatie. Als gevolg hiervan zijn sommige procedures en onderwerpen mogelijk verouderd of onjuist. Voor de meest recente informatie is het raadzaam om te zoeken naar het onderwerp waarin u geïnteresseerd bent in de onlinedocumentatie-index.

In deze opmerking worden de architectuur voor opdrachtroutering en verzending en geavanceerde onderwerpen in algemene berichtroutering beschreven.

Raadpleeg Visual C++ voor algemene informatie over de architecturen die hier worden beschreven, met name het onderscheid tussen Windows-berichten, besturingsmeldingen en opdrachten. In deze opmerking wordt ervan uitgegaan dat u bekend bent met de problemen die in de afgedrukte documentatie worden beschreven en dat alleen zeer geavanceerde onderwerpen worden behandeld.

De functionaliteit van Command Routing en Dispatch MFC 1.0 ontwikkelt zich naar de MFC 2.0-architectuur.

Windows heeft het WM_COMMAND bericht dat overbelast is om meldingen te geven van menuopdrachten, acceleratortoetsen en meldingen voor dialoogvensterbeheer.

MFC 1.0 is hierop gebouwd door een opdrachthandler (bijvoorbeeld 'OnFileNew') toe te staan in een CWnd afgeleide klasse om aangeroepen te worden als reactie op een specifieke WM_COMMAND. Dit wordt gelijmd met een gegevensstructuur die de berichtkaart wordt genoemd en resulteert in een zeer ruimte-efficiënt opdrachtmechanisme.

MFC 1.0 biedt ook extra functionaliteit voor het scheiden van besturingsmeldingen uit opdrachtberichten. Opdrachten worden vertegenwoordigd door een 16-bits id, ook wel een opdracht-id genoemd. Opdrachten beginnen normaal gesproken vanaf een CFrameWnd (dat wil gezegd, een menu selecteren of een vertaalde accelerator) en worden doorgestuurd naar een verscheidenheid aan andere vensters.

MFC 1.0 gebruikte opdrachtroutering in beperkte zin voor de implementatie van Multiple Document Interface (MDI). (Een MDI-hoofdvenster delegeert opdrachten aan het actieve MDI-kindvenster.)

Deze functionaliteit is gegeneraliseerd en uitgebreid in MFC 2.0, zodat opdrachten kunnen worden verwerkt door een breder scala aan objecten (niet alleen vensterobjecten). Het biedt een formelere en uitbreidbare architectuur voor het routeren van berichten en hergebruikt de doelroutering van de opdracht, niet alleen voor het verwerken van opdrachten, maar ook voor het bijwerken van UI-objecten (zoals menu-items en werkbalkknoppen) om de huidige beschikbaarheid van een opdracht weer te geven.

Opdracht-ID's

Zie Visual C++ voor een uitleg van het opdrachtrouterings- en bindingsproces. Technische opmerking 20 bevat informatie over id-naamgeving.

We gebruiken het algemene voorvoegsel 'ID_' voor opdracht-id's. Opdracht-id's zijn >= 0x8000. Op de berichtenregel of statusbalk wordt de tekenreeks voor de opdrachtbeschrijving weergegeven als er een STRINGTABLE-resource is met dezelfde id's als de opdracht-id.

In de resources van uw toepassing kan een opdracht-id op verschillende plaatsen worden weergegeven:

  • In één STRINGTABLE-resource met dezelfde id als de berichtregelprompt.

  • In mogelijk veel MENU-resources die zijn gekoppeld aan menu-items die dezelfde opdracht aanroepen.

  • (GEAVANCEERD) in een dialoogvensterknop voor een GOSUB-opdracht.

In de broncode van uw toepassing kan een opdracht-id op verschillende plaatsen worden weergegeven:

  • In uw RESOURCE. H (of een ander headerbestand met hoofdsymbolen) om toepassingsspecifieke opdracht-id's te definiëren.

  • Misschien in een ID-array die wordt gebruikt om een werkbalk te maken.

  • In een ON_COMMAND macro.

  • Misschien in een ON_UPDATE_COMMAND_UI-macro.

Momenteel is de enige implementatie in MFC waarvoor opdracht-id's die gelijk moeten zijn aan > 0x8000 vereist zijn, de implementatie van GOSUB-dialoogvensters/opdrachten.

GOSUB-opdrachten, met behulp van opdrachtarchitectuur in dialoogvensters

De architectuur voor het routeren en inschakelen van opdrachten werkt goed met framevensters, menu-items, werkbalkknoppen, knoppen van dialoogbalken, andere besturingsbalken en gebruikersinterface-elementen die zijn ontworpen om opdrachten op verzoek bij te werken en opdrachten of ID's door te sturen naar een hoofdopdrachtdoel (meestal het hoofdframevenster). Dit hoofdopdrachtdoel kan de opdracht- of besturingsmeldingen naar andere opdrachtdoelobjecten routeren, indien van toepassing.

Een dialoogvenster (modaal of modusloos) kan profiteren van een aantal functies van de opdrachtarchitectuur als u de besturings-id van het dialoogvenster aan de juiste opdracht-id toewijst. Ondersteuning voor dialoogvensters is niet automatisch, dus mogelijk moet u extra code schrijven.

Houd er rekening mee dat uw opdracht-id's = 0x8000 moeten zijn >om al deze functies goed te laten werken. Omdat veel dialoogvensters naar hetzelfde frame kunnen worden doorgestuurd, moeten gedeelde opdrachten = 0x8000 zijn >, terwijl de niet-gedeelde IDC's in een specifiek dialoogvenster = 0x7FFF moeten zijn <.

U kunt een normale knop in een normaal modaal dialoogvenster plaatsen met de IDC van de knop ingesteld voor de juiste opdracht-ID. Wanneer de gebruiker de knop selecteert, krijgt de eigenaar van het dialoogvenster (meestal het hoofdframevenster) de opdracht net als elke andere opdracht. Dit wordt een GOSUB-opdracht genoemd, omdat deze meestal wordt gebruikt om een ander dialoogvenster weer te geven (een GOSUB van het eerste dialoogvenster).

U kunt de functie CWnd::UpdateDialogControls ook aanroepen in het dialoogvenster en het adres van het hoofdframevenster doorgeven. Met deze functie worden dialoogvensterbesturingselementen in- of uitgeschakeld op basis van of ze opdrachthandlers in het frame hebben. Deze functie wordt automatisch aangeroepen voor besturingsbalken in de niet-actieve lus van uw toepassing, maar u moet deze rechtstreeks aanroepen voor normale dialoogvensters waarvoor u deze functie wilt gebruiken.

Wanneer ON_UPDATE_COMMAND_UI wordt aangeroepen

Het onderhouden van de ingeschakelde/gecontroleerde status van alle menu-items van een programma kan een rekenkundig duur probleem zijn. Een veelgebruikte techniek is om menu-items alleen in te schakelen/te controleren wanneer de gebruiker de POP-UP selecteert. De implementatie van CFrameWnd MFC 2.0 verwerkt het WM_INITMENUPOPUP bericht en gebruikt de architectuur voor opdrachtroutering om de statussen van menu's te bepalen via ON_UPDATE_COMMAND_UI handlers.

CFrameWnd verwerkt ook het WM_ENTERIDLE bericht om het huidige menu-item te beschrijven dat is geselecteerd op de statusbalk (ook wel de berichtregel genoemd).

De menustructuur van een toepassing, bewerkt door Visual C++, wordt gebruikt om de mogelijke opdrachten weer te geven die op WM_INITMENUPOPUP tijdstip beschikbaar zijn. ON_UPDATE_COMMAND_UI handlers kunnen de status of tekst van een menu wijzigen of voor geavanceerd gebruik (zoals de MRU-lijst met bestanden of het pop-upmenu met OLE-werkwoorden), de menustructuur echt wijzigen voordat het menu wordt getekend.

Hetzelfde soort ON_UPDATE_COMMAND_UI verwerking wordt uitgevoerd voor werkbalken (en andere besturingsbalken) wanneer de toepassing in de inactieve modus gaat. Zie de naslaginformatie voor klassenbibliotheek en technische opmerking 31 voor meer informatie over besturingsbalken.

Geneste pop-upmenu's

Als u een geneste menustructuur gebruikt, ziet u dat de ON_UPDATE_COMMAND_UI handler voor het eerste menu-item in het snelmenu in twee verschillende gevallen wordt aangeroepen.

Eerst wordt het aangeroepen voor het menu dat in een pop-up verschijnt. Dit is nodig omdat pop-upmenu's geen id's hebben en we de id van de eerste menuopdracht van het snelmenu gebruiken om te verwijzen naar het hele pop-upmenu. In dit geval is de m_pSubMenu lidvariabele van het CCmdUI object niet null en verwijst deze naar het snelmenu.

Ten tweede wordt het net voordat het aangeroepen moet worden, wanneer de menu-items in het snelmenu moeten worden getekend. In dit geval verwijst de id alleen naar het eerste menu-item en de m_pSubMenu lidvariabele van het CCmdUI object is NULL.

Hiermee kunt u het pop-upmenu inschakelen dat verschilt van de menu-items, maar u moet wel wat menubewuste code schrijven. Bijvoorbeeld, in een genest menu met de volgende structuur:

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

De opdrachten ID_NEW_SHEET en ID_NEW_CHART kunnen onafhankelijk worden ingeschakeld of uitgeschakeld. Het pop-upmenu Nieuw moet ingeschakeld zijn als een van de twee is ingeschakeld.

De opdrachthandler voor ID_NEW_SHEET (de eerste opdracht in het pop-upvenster) ziet er ongeveer als volgt uit:

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

De opdrachthandler voor ID_NEW_CHART is een normale updateopdrachthandler en ziet er ongeveer als volgt uit:

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

ON_COMMAND en ON_BN_CLICKED

De berichtkaartmacro's voor ON_COMMAND en ON_BN_CLICKED zijn hetzelfde. Het MFC-opdracht- en controlemechanisme voor meldingsroutering gebruikt alleen de opdracht-id om te bepalen waarheen moet worden gerouteerd. Besturingsmeldingen met meldingscode voor besturingselementen van nul (BN_CLICKED) worden geïnterpreteerd als opdrachten.

Opmerking

In feite doorlopen alle meldingsberichten van besturingselementen de opdrachthandlerketen. Het is bijvoorbeeld technisch mogelijk om een meldingshandler voor besturingselementen te schrijven voor EN_CHANGE in uw documentklasse. Dit is over het algemeen niet raadzaam omdat de praktische toepassingen van deze functie weinig zijn, de functie niet wordt ondersteund door ClassWizard en het gebruik van de functie kan leiden tot kwetsbare code.

Het automatisch uitschakelen van knopbesturingselementen uitschakelen

Als u een knopcontrole op een dialoogvensterbalk plaatst of in een dialoog wanneer u op eigen houtje CWnd::UpdateDialogControls aanroept, zult u merken dat knoppen die niet over ON_COMMAND of ON_UPDATE_COMMAND_UI handlers beschikken, automatisch door het framework worden uitgeschakeld. In sommige gevallen hoeft u geen handler te hebben, maar u wilt dat de knop ingeschakeld blijft. De eenvoudigste manier om dit te bereiken is door een dummy-opdrachthandler toe te voegen (eenvoudig te doen met ClassWizard) en er niets in te doen.

Berichtroutering van venster

Hieronder worden enkele geavanceerdere onderwerpen beschreven over de MFC-klassen en hoe windows-berichtroutering en andere onderwerpen van invloed zijn op deze onderwerpen. De informatie hier wordt slechts kort beschreven. Raadpleeg de naslaginformatie over klassenbibliotheek voor meer informatie over openbare API's. Raadpleeg de broncode van de MFC-bibliotheek voor meer informatie over implementatiedetails.

Raadpleeg technische opmerking 17 voor meer informatie over het opschonen van vensters, een zeer belangrijk onderwerp voor alle CWnd-afgeleide klassen.

Problemen met CWnd

De implementatielidfunctie CWnd::OnChildNotify biedt een krachtige en uitbreidbare architectuur voor onderliggende vensters (ook wel besturingselementen genoemd) om berichten, opdrachten en controlemeldingen te koppelen of anderszins op de hoogte te stellen van berichten, opdrachten en controlemeldingen die naar hun bovenliggende (of 'eigenaar' gaan). Als het onderliggende venster (/control/) zelf een C++ CWnd-object is, wordt de virtuele functie OnChildNotify eerst aangeroepen met de parameters van het oorspronkelijke bericht, dat wil zeggen een MSG-structuur. Het onderliggende venster kan het bericht alleen laten, het opeten of wijzigen van het bericht voor het bovenliggende item (zeldzaam).

De standaard-CWnd-implementatie verwerkt de volgende berichten en gebruikt de OnChildNotify-hook om onderliggende vensters (besturingselementen) eerst toegang te geven tot het bericht:

  • WM_MEASUREITEM en WM_DRAWITEM (voor zelf tekenen)

  • WM_COMPAREITEM en WM_DELETEITEM (voor zelf tekenen)

  • WM_HSCROLL en WM_VSCROLL

  • WM_CTLCOLOR

  • WM_PARENTNOTIFY

U merkt dat de OnChildNotify-hook wordt gebruikt om eigenaar-gestuurde berichten om te zetten in zelf-gestuurde berichten.

Naast de OnChildNotify-hook hebben schuifberichten daarnaast nog een specifiek routeringsgedrag. Zie hieronder voor meer informatie over schuifbalken en bronnen van WM_HSCROLL en WM_VSCROLL berichten.

Problemen met CFrameWnd

De CFrameWnd-klasse biedt de meeste opdrachtroutering en de implementatie van het bijwerken van de gebruikersinterface. Dit wordt voornamelijk gebruikt voor het hoofdframevenster van de toepassing (CWinApp::m_pMainWnd), maar is van toepassing op alle framevensters.

Het hoofdframevenster is het venster met de menubalk en is het bovenliggende element van de statusbalk of berichtregel. Raadpleeg de bovenstaande discussie over opdrachtroutering en WM_INITMENUPOPUP.

De CFrameWnd-klasse biedt beheer van de actieve weergave. De volgende berichten worden doorgestuurd via de actieve weergave:

  • Alle opdrachtberichten (de actieve weergave krijgt er eerst toegang toe).

  • WM_HSCROLL en WM_VSCROLL berichten van verwante schuifbalken (zie hieronder).

  • WM_ACTIVATE (en WM_MDIACTIVATE voor MDI) worden omgezet in aanroepen naar de virtuele functie CView::OnActivateView.

Problemen met CMDIFrameWnd/CMDIChildWnd

Beide MDI-framevensterklassen zijn afgeleid van CFrameWnd en daarom zijn beide ingeschakeld voor hetzelfde type opdrachtroutering en het bijwerken van de gebruikersinterface die is geleverd in CFrameWnd. In een typische MDI-toepassing bevat alleen het hoofdframevenster (het CMDIFrameWnd-object ) de menubalk en de statusbalk en is daarom de belangrijkste bron van de implementatie van de opdrachtroutering.

Het algemene routeringsschema is dat het actieve onderliggende MDI-venster eerst toegang krijgt tot opdrachten. De standaard PreTranslateMessage-functies verwerken acceleratortabellen voor zowel MDI-kindvensters (eerste) als het MDI-frame (tweede) en de standaard MDI-systeemopdrachtversnellers die normaal gesproken worden verwerkt door TranslateMDISysAccel (laatste).

Problemen met schuifbalk

Bij het afhandelen van scroll-message (WM_HSCROLL/OnHScroll en/of WM_VSCROLL/OnVScroll), moet u proberen de handlercode te schrijven, zodat deze niet afhankelijk is van waar het schuifbalkbericht vandaan komt. Dit is niet alleen een algemeen Windows-probleem, omdat schuifberichten afkomstig kunnen zijn van echte besturingselementen voor schuifbalken of van WS_HSCROLL/WS_VSCROLL schuifbalken die geen schuifbalkbesturingselementen zijn.

MFC breidt uit dat besturingselementen voor schuifbalken kunnen worden gebruikt als onderliggende of broers of zussen van het venster dat wordt geschoven (in feite kan de bovenliggende/onderliggende relatie tussen de schuifbalk en het venster dat wordt gesrold, alles zijn). Dit is vooral belangrijk voor gedeelde schuifbalken met splitsvensters. Raadpleeg technische opmerking 29 voor meer informatie over de implementatie van CSplitterWnd , waaronder meer informatie over problemen met de gedeelde schuifbalk.

Terzijde, er zijn twee van CWnd afgeleide klassen waarbij de scrollbalkstijlen die bij het aanmaken zijn opgegeven, worden opgevangen en niet doorgegeven aan Windows. Wanneer deze wordt doorgegeven aan een aanmaakroutine, kunnen WS_HSCROLL en WS_VSCROLL onafhankelijk worden ingesteld, maar na het maken kan het niet worden gewijzigd. Natuurlijk moet u de WS_SCROLL stijl bits van het venster dat ze hebben gemaakt niet rechtstreeks testen of instellen.

Voor CMDIFrameWnd worden de schuifbalkstijlen die u doorgeeft aan Create of LoadFrame gebruikt om de MDICLIENT te maken. Als u een schuifbaar MDICLIENT-gebied (zoals Windows Program Manager) wilt hebben, moet u beide schuifbalkstijlen (WS_HSCROLL | WS_VSCROLL) instellen voor de stijl die wordt gebruikt om het CMDIFrameWnd te maken.

Voor CSplitterWnd zijn de schuifbalkstijlen van toepassing op de speciale gedeelde schuifbalken voor de splitsregio's. Voor statische splitsvensters stelt u normaal gesproken geen schuifbalkstijl in. Voor dynamische splitsvensters hebt u meestal de stijl van de schuifbalk ingesteld voor de richting waarin u splitst. Dat is WS_HSCROLL als u rijen kunt splitsen, WS_VSCROLL als u kolommen kunt splitsen.

Zie ook

Technische notities per nummer
Technische Aantekeningen Per Categorie