Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Anmärkning
Följande tekniska anmärkning har inte uppdaterats sedan den först inkluderades i onlinedokumentationen. Därför kan vissa procedurer och ämnen vara inaktuella eller felaktiga. För den senaste informationen rekommenderar vi att du söker efter det intressanta ämnet i onlinedokumentationsindexet.
Den här anteckningen beskriver kommandodirigerings- och sändningsarkitekturen samt avancerade ämnen i allmän fönsterdirigering.
Se Visual C++ för allmän information om arkitekturerna som beskrivs här, särskilt skillnaden mellan Windows-meddelanden, kontrollmeddelanden och kommandon. Den här anteckningen förutsätter att du är mycket bekant med de problem som beskrivs i den tryckta dokumentationen och endast tar upp mycket avancerade ämnen.
Kommandodirigering och dispatch MFC 1.0-funktioner utvecklas till MFC 2.0-arkitektur
Windows har WM_COMMAND meddelande som är överbelastat för att ge meddelanden om menykommandon, acceleratornycklar och dialogkontrollmeddelanden.
MFC 1.0 byggde på det lite genom att tillåta att en kommandohanterare (till exempel "OnFileNew") i en CWnd härledd klass anropas som svar på en specifik WM_COMMAND. Detta limmas ihop med en datastruktur som kallas meddelandekartan och resulterar i en mycket utrymmeseffektiv kommandomekanism.
MFC 1.0 har även ytterligare funktioner för att separera kontrollmeddelanden från kommandomeddelanden. Kommandon representeras av ett 16-bitars ID, ibland kallat kommando-ID. Kommandon börjar normalt från en CFrameWnd (dvs. ett menyval eller en översatt accelerator) och dirigeras till en rad andra fönster.
MFC 1.0 använde kommandodirigering i begränsad mening för implementering av MDI (Multiple Document Interface). (Ett MDI-ramfönster delegerar kommandon till sitt aktiva MDI-underfönster.)
Den här funktionen har generaliserats och utökats i MFC 2.0 för att tillåta att kommandon hanteras av ett bredare antal objekt (inte bara fönsterobjekt). Den ger en mer formell och utökningsbar arkitektur för routning av meddelanden och återanvänder kommandomåldirigering för inte bara hantering av kommandon, utan även för uppdatering av gränssnittsobjekt (t.ex. menyobjekt och verktygsfältsknappar) för att återspegla den aktuella tillgängligheten för ett kommando.
Kommando-ID
Se Visual C++ för en förklaring av kommandodirigerings- och bindningsprocessen. Technical Note 20 innehåller information om ID-namngivning.
Vi använder det allmänna prefixet "ID_" för kommando-ID:er. Kommando-ID:n är >= 0x8000. Meddelanderaden eller statusfältet visar kommandobeskrivningssträngen om det finns en STRINGTABLE-resurs med samma ID:t som kommando-ID:t.
I programmets resurser kan ett kommando-ID visas på flera platser:
I en STRINGTABLE-resurs som har samma ID som meddelanderadsprompten.
I eventuellt många MENY-resurser som är anslutna till menyalternativ som anropar samma kommando.
(AVANCERAT) i en dialogruta för kommandot GOSUB.
I källkoden för ditt program kan ett kommando-ID visas på flera platser:
I resursen. H (eller annan huvudsymbolhuvudfil) för att definiera programspecifika kommando-ID:er.
KANSKE I en ID-matris som används för att skapa ett verktygsfält.
I ett ON_COMMAND makro.
KANSKE i ett ON_UPDATE_COMMAND_UI-makro.
För närvarande är den enda implementeringen i MFC som kräver att kommando-ID är >= 0x8000, implementeringen av GOSUB-dialogrutor och -kommandon.
GOSUB-kommandon, använda kommandoarkitektur i dialogrutor
Kommandoarkitekturen för routning och aktivering av kommandon fungerar bra med ramfönster, menyalternativ, verktygsfältsknappar, dialogfältsknappar, andra kontrollstaplar och andra användargränssnittselement som är utformade för att uppdatera kommandon på begäran och dirigera kommandon eller kontroll-ID:n till ett huvudkommandomål (vanligtvis huvudramsfönstret). Huvudkommandomålet kan dirigera kommando- eller kontrollmeddelanden till andra kommandomålobjekt efter behov.
En dialogruta (modal eller modelös) kan dra nytta av några av funktionerna i kommandoarkitekturen om du tilldelar kontroll-ID:t för dialogkontrollen till lämpligt kommando-ID. Stöd för dialogrutor är inte automatiskt, så du kan behöva skriva ytterligare kod.
Observera att för att alla dessa funktioner ska fungera korrekt bör dina kommando-ID:n vara >= 0x8000. Eftersom många dialogrutor kan dirigeras till samma ram bör delade kommandon vara >= 0x8000, medan de icke-delade IDC:erna i en specifik dialogruta ska vara <= 0x7FFF.
Du kan placera en normal knapp i en normal modal dialogruta med knappens IDC inställt på lämpligt kommando-ID. När användaren väljer knappen får dialogrutans ägare (vanligtvis huvudramsfönstret) kommandot precis som andra kommandon. Detta kallas för ett GOSUB-kommando eftersom det vanligtvis används för att ta upp en annan dialogruta (en GOSUB i den första dialogrutan).
Du kan också anropa funktionen CWnd::UpdateDialogControls i dialogrutan och skicka den till huvudramsfönstrets adress. Den här funktionen aktiverar eller inaktiverar dina dialogkontroller baserat på om de har kommandohanterare i ramen. Den här funktionen anropas automatiskt för kontrollstaplar i programmets inaktiva loop, men du måste anropa den direkt för normala dialogrutor som du vill ha den här funktionen.
När ON_UPDATE_COMMAND_UI anropas
Att upprätthålla det aktiverade/kontrollerade tillståndet för alla menyalternativ för ett program hela tiden kan vara ett beräkningsmässigt dyrt problem. En vanlig teknik är att endast aktivera/kontrollera menyalternativ när användaren väljer popup-fönstret. MFC 2.0-implementeringen av CFrameWnd hanterar WM_INITMENUPOPUP-meddelandet och använder kommandodirigeringsarkitekturen för att fastställa status för menyer via ON_UPDATE_COMMAND_UI-hanterare.
CFrameWnd hanterar också det WM_ENTERIDLE meddelandet för att beskriva det aktuella menyalternativet som valts i statusfältet (kallas även meddelanderaden).
Ett programs menystruktur, redigerad av Visual C++, används för att representera de potentiella kommandon som är tillgängliga vid WM_INITMENUPOPUP tidpunkt. ON_UPDATE_COMMAND_UI hanterare kan ändra status eller text för en meny, eller för avancerade användningsområden (t.ex. listrutan Fil-MRU eller popup-menyn OLE Verbs) ändrar faktiskt menystrukturen innan menyn ritas.
Samma typ av bearbetning av ON_UPDATE_COMMAND_UI görs för verktygsfält (och andra kontrollfält) när programmet går in i sin tomgångsloop. Mer information om kontrollstaplar finns i Referens för klassbibliotek och Teknisk anmärkning 31 .
Kapslade popup-menyer
Om du använder en kapslad menystruktur ser du att ON_UPDATE_COMMAND_UI-hanteraren för det första menyalternativet i popup-menyn anropas i två olika fall.
Först anropas den för själva popup-menyn. Detta är nödvändigt eftersom popup-menyer inte har ID:t och vi använder ID:t för det första menyalternativet i popup-menyn för att referera till hela popup-menyn. I det här fallet kommer m_pSubMenu medlemsvariabeln för CCmdUI objektet att vara icke-NULL och pekar på popup-menyn.
För det andra anropas den precis innan menyalternativen i popup-menyn ska ritas. I det här fallet refererar ID:t bara till det första menyalternativet och objektets m_pSubMenu medlemsvariabel CCmdUI är NULL.
Detta gör att du kan aktivera popup-menyn skild från dess menyalternativ, men kräver att du skriver viss menymedveten kod. Till exempel i en kapslad meny med följande struktur:
File>
New>
Sheet (ID_NEW_SHEET)
Chart (ID_NEW_CHART)
Kommandona ID_NEW_SHEET och ID_NEW_CHART kan aktiveras eller inaktiveras separat. Popup-menyn Nytt bör aktiveras om någon av de två är aktiverad.
Kommandohanteraren för ID_NEW_SHEET (det första kommandot i popup-fönstret) skulle se ut ungefär så här:
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);
}
Kommandohanteraren för ID_NEW_CHART skulle vara en vanlig uppdateringskommandohanterare och se ut ungefär så här:
void CMyApp::OnUpdateNewChart(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bCanCreateChart);
}
ON_COMMAND och ON_BN_CLICKED
Meddelandemappningsmakronen för ON_COMMAND och ON_BN_CLICKED är desamma. MFC-kommando- och kontrollmeddelandedirigeringsmekanismen använder bara kommando-ID:t för att bestämma vart du ska dirigera till. Kontrollmeddelanden med kontrollmeddelandekoden noll (BN_CLICKED) tolkas som kommandon.
Anmärkning
I själva verket går alla kontrollmeddelanden via kommandohanterarkedjan. Det är till exempel tekniskt möjligt för dig att skriva en kontrollmeddelandehanterare för EN_CHANGE i dokumentklassen. Detta är vanligtvis inte tillrådligt eftersom de praktiska tillämpningarna av den här funktionen är få, funktionen stöds inte av ClassWizard, och användningen av funktionen kan resultera i bräcklig kod.
Inaktivera automatisk inaktivering av knappkontroller
Om du placerar en knappkontroll i ett dialogfält, eller i en dialogruta där du anropar CWnd::UpdateDialogControls på egen hand, kommer du att märka att knappar som inte har ON_COMMAND eller ON_UPDATE_COMMAND_UI hanterare inaktiveras automatiskt för dig av ramverket. I vissa fall behöver du inte ha någon hanterare, men du vill att knappen ska vara aktiverad. Det enklaste sättet att uppnå detta är att lägga till en dummy-kommandohanterare (lätt att göra med ClassWizard) och inte göra något i den.
Routning av fönstermeddelande
Följande beskriver några mer avancerade ämnen om MFC-klasserna och hur Routning av Windows-meddelanden och andra ämnen påverkar dem. Informationen här beskrivs bara kortfattat. Mer information om offentliga API:er finns i klassbiblioteksreferensen . Mer information om implementeringsinformation finns i MFC-bibliotekets källkod.
Mer information om fönsterrensning finns i Technical Note 17 , ett mycket viktigt ämne för alla CWnd-härledda klasser.
Problem med CWnd
Implementeringsmedlemsfunktionen CWnd::OnChildNotify tillhandahåller en kraftfull och utökningsbar arkitektur för underordnade fönster (kallas även kontroller) att ansluta till eller på annat sätt informeras om meddelanden, kommandon och kontrollaviseringar som går till deras överordnade (eller "ägare"). Om det underordnade fönstret (/kontrollen) är ett C++ CWnd-objekt i sig själv, anropas den virtuella funktionen OnChildNotify först med parametrarna från det ursprungliga meddelandet (d.v.s. en MSG-struktur). Det underordnade fönstret kan lämna meddelandet ensamt, äta upp det eller ändra meddelandet för den överordnade (sällsynta).
CWnd-standardimplementeringen hanterar följande meddelanden och använder OnChildNotify-kroken för att tillåta underordnade fönster (kontroller) att först komma åt meddelandet:
WM_MEASUREITEM och WM_DRAWITEM (för självdragning)
WM_COMPAREITEM och WM_DELETEITEM (för självdragning)
WM_HSCROLL och WM_VSCROLL
WM_CTLCOLOR
WM_PARENTNOTIFY
Du kommer att märka att OnChildNotify-kroken används för att ändra ägarsritningsmeddelanden till självritningsmeddelanden.
Förutom OnChildNotify-kroken har rullningsmeddelanden ytterligare routningsbeteende. Se nedan för mer information om rullningslister och källor för WM_HSCROLL och WM_VSCROLL meddelanden.
Problem med CFrameWnd
Klassen CFrameWnd innehåller det mesta av implementeringen av kommandodirigering och användargränssnittsuppdatering. Detta används främst för programmets huvudramfönster (CWinApp::m_pMainWnd) men gäller för alla ramfönster.
Huvudramsfönstret är fönstret med menyraden och är överordnat för statusfältet eller meddelanderaden. Se ovanstående diskussion om kommandodirigering och WM_INITMENUPOPUP.
Klassen CFrameWnd tillhandahåller hantering av den aktiva vyn. Följande meddelanden går via den aktiva vyn:
Alla kommandomeddelanden (den aktiva vyn får första åtkomst till dem).
WM_HSCROLL och WM_VSCROLL meddelanden från syskon-rullningslister (se nedan).
WM_ACTIVATE (och WM_MDIACTIVATE för MDI) omvandlas till anrop till den virtuella funktionen CView::OnActivateView.
Problem med CMDIFrameWnd/CMDIChildWnd
Båda MDI-ramfönsterklasserna härleds från CFrameWnd och är därför båda aktiverade för samma typ av kommandodirigering och uppdatering av användargränssnittet som tillhandahålls i CFrameWnd. I ett typiskt MDI-program innehåller endast huvudramsfönstret (dvs . CMDIFrameWnd-objektet ) menyraden och statusfältet och är därför huvudkällan för implementeringen av kommandodirigering.
Det allmänna routningsschemat är att det aktiva, underordnade MDI-fönstret får först tillgång till kommandon. Standardfunktionerna PreTranslateMessage hanterar acceleratortabeller för både MDI-underordnade fönster (först) och MDI-ramen (andra) samt standardacceleratorerna för MDI-systemkommandon som normalt hanteras av TranslateMDISysAccel (sist).
Problem med rullningslist
När du hanterar rullningsmeddelanden (WM_HSCROLL/OnHScroll och/eller WM_VSCROLL/OnVScroll) bör du försöka skriva hanteringskoden så att den inte förlitar sig på var rullningslistmeddelandet kom ifrån. Det här är inte bara ett allmänt Windows-problem eftersom rullningsmeddelanden kan komma från sanna rullningslistkontroller eller från WS_HSCROLL/WS_VSCROLL rullningslister som inte är rullningslistkontroller.
MFC utökar detta så att rullkontroller kan vara antingen underordnade eller syskon till fönstret som rullas (faktiskt kan förhållandet mellan rullkontrollen och fönstret som rullas vara vilket som helst). Detta är särskilt viktigt för delade rullningslister med splitterfönster. Mer information om implementeringen av CSplitterWnd finns i Technical Note 29, inklusive mer information om problem med delade rullningslister.
För övrigt finns det två CWnd-härledda klasser där rullningsliststilarna som anges vid skapandetillfället är inte vidarebefordrade till Windows. När de skickas till en skapanderutin kan WS_HSCROLL och WS_VSCROLL anges separat, men när de har skapats kan de inte ändras. Du bör naturligtvis inte direkt testa eller ställa in WS_SCROLL stilbitar i fönstret som de skapade.
För CMDIFrameWnd används rullningslistformaten som du skickar till Skapa eller LoadFrame för att skapa MDICLIENT. Om du vill ha ett rullningsbart MDICLIENT-område (t.ex. Windows Program Manager) måste du ange båda rullningslistformaten (WS_HSCROLL | WS_VSCROLL) för det format som används för att skapa CMDIFrameWnd.
För CSplitterWnd gäller rullningslistformaten för de särskilda delade rullningslisterna för delningsregionerna. För statiska splitterfönster anger du normalt inte något rullningslistformat. För dynamiska splitterfönster har du vanligtvis formatmallen för rullningslisten inställd för den riktning du ska dela, det vill WS_HSCROLL om du kan dela rader WS_VSCROLL om du kan dela kolumner.
Se även
tekniska anteckningar efter nummer
tekniska anteckningar efter kategori