Share via


TN006: Berichtenkaarten

In deze opmerking wordt de MFC-berichtkaartfaciliteit beschreven.

Het probleem

Microsoft Windows implementeert virtuele functies in vensterklassen die gebruikmaken van de berichtenfaciliteit. Vanwege het grote aantal betrokken berichten zou het voorzien van een aparte virtuele functie voor elk Windows-bericht een onpraktisch grote vtable creëren.

Omdat het aantal door het systeem gedefinieerde Windows-berichten na verloop van tijd verandert en omdat toepassingen hun eigen Windows-berichten kunnen definiëren, bieden berichttoewijzingen een niveau van indirectie waardoor interfacewijzigingen bestaande code niet kunnen breken.

Overzicht

MFC biedt een alternatief voor de switch-instructie die is gebruikt in traditionele Windows-programma's voor het verwerken van berichten die naar een venster worden verzonden. Een toewijzing van berichten aan methoden kan zo worden gedefinieerd dat wanneer een bericht wordt ontvangen door een venster, de juiste methode automatisch wordt aangeroepen. Deze message-mapfaciliteit is ontworpen om op virtuele functies te lijken, maar heeft extra voordelen die niet mogelijk zijn met virtuele C++-functies.

Een berichtoverzicht definiëren

De DECLARE_MESSAGE_MAP macro declareert drie leden voor een klas.

  • Een privématrix van AFX_MSGMAP_ENTRY vermeldingen met de naam _messageEntries.

  • Een beveiligde AFX_MSGMAP structuur met de naam messageMap die verwijst naar de _messageEntries matrix.

  • Een beschermde virtuele functie genaamd GetMessageMap die het adres van messageMap retourneert.

Deze macro moet worden geplaatst in de declaratie van elke klasse die gebruikmaakt van berichtentoewijzingen. Volgens de conventie staat deze aan het einde van de klassedeclaratie. Voorbeeld:

class CMyWnd : public CMyParentWndClass
{
    // my stuff...

protected:
    //{{AFX_MSG(CMyWnd)
    afx_msg void OnPaint();
    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()
};

Dit is de indeling die wordt gegenereerd door AppWizard en ClassWizard wanneer ze nieuwe klassen maken. De haakjes //{{ en //}} zijn nodig voor ClassWizard.

De tabel van de berichtkaart wordt gedefinieerd met behulp van een set macro's die worden uitgevouwen tot berichttoewijzingsvermeldingen. Een tabel begint met een BEGIN_MESSAGE_MAP macro-aanroep, waarmee de klasse wordt gedefinieerd die wordt verwerkt door deze berichttoewijzing en de bovenliggende klasse waaraan niet-verwerkte berichten worden doorgegeven. De tabel eindigt met de END_MESSAGE_MAP macrooproep.

Tussen deze twee macroaanroepen bevindt zich een invoeging voor elk bericht dat door deze boodschappenlijst moet worden verwerkt. Elk standaardbericht van Windows heeft een macro van de vorm ON_WM_MESSAGE_NAME waarmee een invoer voor dat bericht wordt gegenereerd.

Er is een standaardfunctiehandtekening gedefinieerd voor het uitpakken van de parameters van elk Windows-bericht en het bieden van typeveiligheid. Deze handtekeningen zijn te vinden in het bestand Afxwin.h in de verklaring van CWnd. Elke is gemarkeerd met het trefwoord afx_msg voor eenvoudige identificatie.

Opmerking

ClassWizard vereist dat u het afx_msg trefwoord in de declaraties van de berichtafhandelaar gebruikt.

Deze functiehandtekeningen zijn afgeleid met behulp van een eenvoudige conventie. De naam van de functie begint altijd met "On'. Dit wordt gevolgd door de naam van het Windows-bericht met de 'WM_' verwijderd en de eerste letter van elk woord met hoofdletters. De volgorde van de parameters is wParam gevolgd door LOWORD(lParam) en vervolgens HIWORD(lParam). Ongebruikte parameters worden niet doorgegeven. Grepen die door MFC-klassen worden verpakt, worden geconverteerd naar aanwijzers naar de juiste MFC-objecten. In het volgende voorbeeld ziet u hoe u het WM_PAINT bericht verwerkt en ervoor zorgt dat de CMyWnd::OnPaint functie wordt aangeroepen:

BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
    //{{AFX_MSG_MAP(CMyWnd)
    ON_WM_PAINT()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

De berichtenkaarttabel moet worden gedefinieerd buiten het bereik van een functie of klassedefinitie. Het mag niet in een extern C-blok worden geplaatst.

Opmerking

ClassWizard wijzigt de berichtkaartenvermeldingen die voorkomen tussen de //{{ en //}} commentaarbeugel.

Door de gebruiker gedefinieerde Windows-berichten

Door de gebruiker gedefinieerde berichten kunnen worden opgenomen in een berichtoverzicht met behulp van de ON_MESSAGE macro. Deze macro accepteert een berichtnummer en een methode van het formulier:

    // inside the class declaration
    afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);

    #define WM_MYMESSAGE (WM_USER + 100)

BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
    ON_MESSAGE(WM_MYMESSAGE, OnMyMessage)
END_MESSAGE_MAP()

In dit voorbeeld maken we een handler voor een aangepast bericht met een Windows-bericht-id die is afgeleid van de standaard-WM_USER basis voor door de gebruiker gedefinieerde berichten. In het volgende voorbeeld ziet u hoe u deze handler aanroept:

CWnd* pWnd = ...;
pWnd->SendMessage(WM_MYMESSAGE);

Het bereik van door de gebruiker gedefinieerde berichten die deze methode gebruiken, moet liggen in de range WM_USER tot 0x7fff.

Opmerking

ClassWizard biedt geen ondersteuning voor het invoeren van ON_MESSAGE handlerroutines vanuit de gebruikersinterface van ClassWizard. U moet deze handmatig invoeren vanuit de Visual C++-editor. ClassWizard parseert deze invoeren en u kunt deze items bekijken, net zoals alle andere berichtenoverzichtitems.

Geregistreerde Windows-berichten

De functie RegisterWindowMessage wordt gebruikt om een nieuw vensterbericht te definiëren dat gegarandeerd uniek is in het hele systeem. De macro-ON_REGISTERED_MESSAGE wordt gebruikt om deze berichten te verwerken. Deze macro accepteert een naam van een UINT NEAR-variabele die de geregistreerde windows-bericht-id bevat. Bijvoorbeeld

class CMyWnd : public CMyParentWndClass
{
public:
    CMyWnd();

    //{{AFX_MSG(CMyWnd)
    afx_msg LRESULT OnFind(WPARAM wParam, LPARAM lParam);
    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()
};

static UINT NEAR WM_FIND = RegisterWindowMessage("COMMDLG_FIND");

BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
    //{{AFX_MSG_MAP(CMyWnd)
    ON_REGISTERED_MESSAGE(WM_FIND, OnFind)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

De variabele Windows-bericht-id (WM_FIND in dit voorbeeld) moet een NEAR-variabele zijn vanwege de manier waarop ON_REGISTERED_MESSAGE wordt geïmplementeerd.

Het bereik van door de gebruiker gedefinieerde berichten die deze methode gebruiken, ligt binnen het bereik 0xC000 voor 0xFFFF.

Opmerking

ClassWizard biedt geen ondersteuning voor het invoeren van ON_REGISTERED_MESSAGE handlerroutines vanuit de gebruikersinterface van ClassWizard. U moet deze handmatig invoeren vanuit de teksteditor. ClassWizard parseert deze invoeren en u kunt deze items bekijken, net zoals alle andere berichtenoverzichtitems.

Opdrachtberichten

Opdrachtberichten van menu's en sneltoetsen worden verwerkt in berichten met de ON_COMMAND macro. Deze macro accepteert een opdracht-id en een methode. Alleen het specifieke WM_COMMAND bericht met een wParam dat gelijk is aan de opgegeven opdracht-id wordt verwerkt door de methode die is opgegeven in de berichtkaartvermelding. Opdrachthandlerlidfuncties nemen geen parameters en retourneren void. De macro heeft het volgende formulier:

ON_COMMAND(id, memberFxn)

Berichten over het bijwerken van opdrachten worden gerouteerd via hetzelfde mechanisme, maar gebruiken in plaats daarvan de ON_UPDATE_COMMAND_UI macro. Lidfuncties van de opdracht-updatehandler nemen één parameter, een verwijzing naar een CCmdUI-object, en retourneren void. De macro heeft het formulier

ON_UPDATE_COMMAND_UI(id, memberFxn)

Geavanceerde gebruikers kunnen de ON_COMMAND_EX macro gebruiken. Dit is een uitgebreide vorm van berichthandlers voor opdrachten. De macro biedt een superset van de ON_COMMAND functionaliteit. Uitgebreide opdrachthandlerlidfuncties nemen één parameter, een UINT die de opdracht-id bevat en retourneert een BOOL. De retourwaarde moet TRUE zijn om aan te geven dat de opdracht is verwerkt. Anders wordt routering voortgezet naar andere opdrachtdoelobjecten.

Voorbeelden van deze formulieren:

  • Binnen Resource.h (meestal gegenereerd door Visual C++)

    #define    ID_MYCMD      100
    #define    ID_COMPLEX    101
    
  • Binnen de klassedefinitie

    afx_msg void OnMyCommand();
    afx_msg void OnUpdateMyCommand(CCmdUI* pCmdUI);
    afx_msg BOOL OnComplexCommand(UINT nID);
    
  • Binnen de definitie van de berichtenkaart

    ON_COMMAND(ID_MYCMD, OnMyCommand)
    ON_UPDATE_COMMAND_UI(ID_MYCMD, OnUpdateMyCommand)
    ON_COMMAND_EX(ID_MYCMD, OnComplexCommand)
    
  • In het implementatiebestand

    void CMyClass::OnMyCommand()
    {
        // handle the command
    }
    
    void CMyClass::OnUpdateMyCommand(CCmdUI* pCmdUI)
    {
        // set the UI state with pCmdUI
    }
    
    BOOL CMyClass::OnComplexCommand(UINT nID)
    {
        // handle the command
        return TRUE;
    }
    

Geavanceerde gebruikers kunnen een reeks opdrachten afhandelen met behulp van één opdrachthandler: ON_COMMAND_RANGE of ON_COMMAND_RANGE_EX. Zie de productdocumentatie voor meer informatie over deze macro's.

Opmerking

ClassWizard ondersteunt het maken van ON_COMMAND en ON_UPDATE_COMMAND_UI handlers, maar biedt geen ondersteuning voor het maken van ON_COMMAND_EX of ON_COMMAND_RANGE handlers. De Klassewizard parseert echter en stelt u in staat om door alle vier de varianten van opdrachthandlers te bladeren.

Meldingsberichten beheren

Berichten die van onderliggende besturingselementen naar een venster worden verzonden, bevatten extra informatie in de berichtkaartvermelding: de id van het besturingselement. De berichthandler die is opgegeven in een berichtkaartvermelding, wordt alleen aangeroepen als aan de volgende voorwaarden wordt voldaan:

  • De meldingscode voor besturingselementen (het hoge woord van lParam), zoals BN_CLICKED, komt overeen met de meldingscode die is opgegeven in de vermelding in de berichtkaart.

  • De besturingselement-id (wParam) komt overeen met de besturingselement-id die is opgegeven in de berichtkaartvermelding.

Aangepaste meldingen voor besturingselementen kunnen de ON_CONTROL macro gebruiken om een berichttoewijzingsvermelding met een aangepaste meldingscode te definiëren. Deze macro heeft de vorm

ON_CONTROL(wNotificationCode, id, memberFxn)

Voor geavanceerd gebruik kan ON_CONTROL_RANGE worden gebruikt om een specifieke notificatie van bedieningselementen binnen een reeks met eenzelfde handler af te handelen.

Opmerking

ClassWizard biedt geen ondersteuning voor het maken van een ON_CONTROL of ON_CONTROL_RANGE handler in de gebruikersinterface. U moet ze handmatig invoeren met de teksteditor. ClassWizard zal deze vermeldingen parseren zodat u er net als bij alle andere berichtkoppelingsitems door kunt bladeren.

De algemene besturingselementen van Windows maken gebruik van de krachtigere WM_NOTIFY voor complexe besturingsmeldingen. Deze versie van MFC biedt directe ondersteuning voor dit nieuwe bericht met behulp van de ON_NOTIFY en ON_NOTIFY_RANGE macro's. Zie de productdocumentatie voor meer informatie over deze macro's.

Zie ook

Technische notities per nummer
Technische Aantekeningen Per Categorie