TN021: Směrování příkazů a zpráv

Poznámka

Následující technická poznámka nebyla aktualizována, protože byla poprvé zahrnuta do online dokumentace. V důsledku toho můžou být některé postupy a témata zastaralé nebo nesprávné. Pro nejnovější informace doporučujeme vyhledat téma, které vás zajímá, v indexu online dokumentace.

Tato poznámka popisuje architekturu směrování a odesílání příkazů a také pokročilá témata v obecném směrování zpráv okna.

Obecné podrobnosti o zde popsaných architekturách, zejména rozdíl mezi zprávami systému Windows, oznámeními ovládacích prvků a příkazy, najdete v tématu Visual C++. Tato poznámka předpokládá, že jste velmi obeznámeni s problémy popsanými v tištěné dokumentaci a řeší pouze velmi pokročilá témata.

Funkce směrování a odesílání příkazů mfc 1.0 se mění na architekturu MFC 2.0

Systém Windows má WM_COMMAND zprávu, která je přetížená, aby poskytovala oznámení o příkazech nabídky, klávesách akcelerátoru a oznámeních o řízení dialogového okna.

MFC 1.0 na tom trochu staví tím, že povoluje obslužnou rutinu příkazu (například "OnFileNew") v odvozené CWnd třídě, aby byla volána v reakci na konkrétní WM_COMMAND. Ten je přilepený k datové struktuře označované jako mapa zpráv a výsledkem je velmi prostorově efektivní mechanismus příkazů.

MFC 1.0 také poskytl další funkce pro oddělení oznámení ovládacích prvků od zpráv příkazů. Příkazy jsou reprezentovány 16bitovým ID, které se někdy označuje jako ID příkazu. Příkazy obvykle začínají z CFrameWnd nabídky (výběr nabídky nebo přeložený akcelerátor) a směrují se do různých dalších oken.

MFC 1.0 používal směrování příkazů v omezeném smyslu pro implementaci rozhraní MDI (Multiple Document Interface). (Okno rámce MDI deleguje příkazy do aktivního podřízeného okna MDI.)

Tato funkce byla v MFC 2.0 zobecněna a rozšířena, aby umožňovala zpracování příkazů širším rozsahem objektů (nejen objektů oken). Poskytuje formálnější a rozšiřitelný architekturu pro směrování zpráv a opakovaně používá směrování cílů příkazů nejen pro zpracování příkazů, ale také pro aktualizaci objektů uživatelského rozhraní (jako jsou položky nabídek a tlačítka panelu nástrojů) tak, aby odrážely aktuální dostupnost příkazu.

Identifikátory příkazů

Vysvětlení směrování příkazů a procesu vazby najdete v tématu Visual C++. Technická poznámka 20 obsahuje informace o pojmenování ID.

Pro ID příkazů používáme obecnou předponu "ID_". ID příkazů jsou >= 0x8000. Na řádku zprávy nebo stavovém řádku se zobrazí řetězec popisu příkazu, pokud existuje prostředek STRINGTABLE se stejnými ID jako ID příkazu.

V prostředcích vaší aplikace se ID příkazu může zobrazit na několika místech:

  • V jednom prostředku STRINGTABLE, který má stejné ID jako výzva řádku zprávy.

  • V možná mnoha prostředcích MENU, které jsou připojeny k položkám nabídky, které vyvolávají stejný příkaz.

  • (ADVANCED) v dialogovém okně pro příkaz GOSUB.

Ve zdrojovém kódu aplikace se ID příkazu může zobrazit na několika místech:

  • Ve vašem PROSTŘEDKU. H (nebo jiný soubor hlaviček hlavního symbolu) k definování ID příkazů specifických pro aplikaci.

  • MOŽNÁ v poli ID použitém k vytvoření panelu nástrojů.

  • V makrech ON_COMMAND.

  • MOŽNÁ v makrech ON_UPDATE_COMMAND_UI.

V současné době je jedinou implementací v mfc, která vyžaduje ID >příkazů = 0x8000 je implementace dialogových oken/příkazů GOSUB.

Příkazy GOSUB, použití architektury příkazů v dialogových oknech

Architektura příkazů pro směrování a povolení příkazů funguje dobře s okny rámečků, položkami nabídek, tlačítky panelu nástrojů, tlačítky dialogového panelu, dalšími ovládacími panely a dalšími prvky uživatelského rozhraní určenými k aktualizaci na vyžádání a směrování příkazů nebo ID ovládacích prvků do hlavního cíle příkazů (obvykle hlavního okna rámce). Tento hlavní cíl příkazu může podle potřeby směrovat oznámení příkazů nebo ovládacích prvků na jiné cílové objekty příkazů.

Dialogové okno (modální nebo bez režimu) může těžit z některých funkcí architektury příkazů, pokud přiřadíte ID ovládacího prvku dialogového okna příslušnému ID příkazu. Podpora dialogových oken není automatická, takže možná budete muset napsat nějaký další kód.

Všimněte si, že aby všechny tyto funkce správně fungovaly, id příkazů by mělo být >= 0x8000. Vzhledem k tomu, že mnoho dialogových oken může být směrováno na stejný rámec, sdílené příkazy by měly být >= 0x8000, zatímco nesdílené idc v konkrétním dialogovém okně by měly být <= 0x7FFF.

Normální tlačítko můžete umístit do normálního modálního dialogového okna s IDC tlačítka nastaveným na příslušné ID příkazu. Když uživatel vybere tlačítko, vlastník dialogového okna (obvykle hlavního okna rámce) získá příkaz stejně jako jakýkoli jiný příkaz. Tento příkaz se nazývá GOSUB, protože se obvykle používá k vyvolání dalšího dialogového okna (gosub prvního dialogového okna).

Funkci můžete také zavolat v CWnd::UpdateDialogControls dialogovém okně a předat jí adresu okna hlavního rámce. Tato funkce povolí nebo zakáže ovládací prvky dialogového okna na základě toho, jestli mají v rámci obslužné rutiny příkazů. Tato funkce je volána automaticky pro řídicí pruhy v nečinné smyčce vaší aplikace, ale musíte ji volat přímo pro normální dialogová okna, u kterých chcete mít tuto funkci.

Při ON_UPDATE_COMMAND_UI je volána

Udržování povoleného nebo zaškrtnutého stavu všech položek nabídky programu po celou dobu může být výpočetně náročný problém. Běžnou technikou je povolit nebo zkontrolovat položky nabídky, pouze když uživatel vybere POPUP. Mfc 2.0 implementace CFrameWnd zpracovává WM_INITMENUPOPUP zprávy a používá architekturu směrování příkazů k určení stavů nabídek prostřednictvím ON_UPDATE_COMMAND_UI obslužné rutiny.

CFrameWnd také zpracovává WM_ENTERIDLE zprávu, která popisuje aktuální položku nabídky vybranou na stavovém řádku (označuje se také jako řádek zprávy).

Struktura nabídek aplikace upravená jazykem Visual C++ slouží k reprezentaci potenciálních příkazů dostupných v WM_INITMENUPOPUP čase. ON_UPDATE_COMMAND_UI obslužné rutiny mohou upravit stav nebo text nabídky nebo pro pokročilé použití (jako je seznam MRU souboru nebo místní nabídka OLE Sloves) ve skutečnosti upravit strukturu nabídek před vykreslení nabídky.

Stejný druh zpracování ON_UPDATE_COMMAND_UI se provádí pro panely nástrojů (a další řídicí panely), když aplikace vstoupí do své nečinné smyčky. Další informace o ovládacích panelech najdete v referenčních informacích ke knihovně tříd a technické poznámce 31 .

Vnořené místní nabídky

Pokud používáte vnořenou strukturu nabídek, všimněte si, že obslužná rutina ON_UPDATE_COMMAND_UI pro první položku nabídky v místní nabídce je volána ve dvou různých případech.

Nejprve je volána pro samotnou místní nabídku. To je nezbytné, protože místní nabídky nemají ID a k odkazování na celou místní nabídku používáme ID první položky místní nabídky. V tomto případě bude proměnná m_pSubMenu člena objektu CCmdUI bez hodnoty NULL a bude odkazovat na místní nabídku.

Za druhé je volána těsně před tím, než se mají vykreslit položky nabídky v místní nabídce. V tomto případě ID odkazuje pouze na první položku nabídky a proměnná m_pSubMenu člena objektu CCmdUI bude null.

To vám umožní povolit místní nabídku odlišnou od jejích položek nabídky, ale vyžaduje, abyste napsali nějaký kód, který bude znát nabídku. Například ve vnořené nabídce s následující strukturou:

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

Příkazy ID_NEW_SHEET a ID_NEW_CHART je možné nezávisle povolit nebo zakázat. Pokud je některý z těchto dvou povolených, místní nabídka Nový by měla být povolená.

Obslužná rutina příkazu pro ID_NEW_SHEET (první příkaz v automaticky otevírané aplikaci) by vypadala přibližně takto:

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

Obslužná rutina příkazu pro ID_NEW_CHART by byla normální obslužná rutina příkazu aktualizace a vypadala přibližně takto:

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

ON_COMMAND a ON_BN_CLICKED

Makra mapy zpráv pro ON_COMMAND a ON_BN_CLICKED jsou stejná. Mechanismus směrování oznámení a příkazů mfc používá pouze ID příkazu k rozhodnutí, kam se má směrovat. Oznámení ovládacích prvků s kódem řídicího oznámení nula (BN_CLICKED) se interpretují jako příkazy.

Poznámka

Ve skutečnosti všechny zprávy oznámení ovládacích prvků procházejí řetězem obslužných rutin příkazů. Technicky je například možné napsat obslužnou rutinu oznámení ovládacího prvku pro EN_CHANGE ve třídě dokumentu. To se obecně nedoporučuje, protože praktických aplikací této funkce je málo, funkce není podporována ClassWizard a použití funkce může vést k křehkému kódu.

Zakázání automatického zakázání ovládacích prvků tlačítek

Pokud umístíte ovládací prvek tlačítka na dialogový panel nebo do dialogového okna, ve kterém sami voláte CWnd::UpdateDialogControls , všimněte si, že tlačítka, která nemají ON_COMMAND nebo ON_UPDATE_COMMAND_UI obslužné rutiny, budou automaticky zakázána rozhraním. V některých případech nebudete muset mít obslužnou rutinu, ale budete chtít, aby tlačítko zůstalo povolené. Nejjednodušším způsobem, jak toho dosáhnout, je přidat fiktivní obslužnou rutinu příkazu (snadno se dá dělat s ClassWizard) a nic v ní nedělejte.

Směrování zpráv okna

Následující článek popisuje některá pokročilejší témata o třídách MFC a o tom, jak na ně mají vliv směrování zpráv systému Windows a další témata. Zde uvedené informace jsou popsány pouze stručně. Podrobnosti o veřejných rozhraních API najdete v referenčních informacích ke knihovně tříd. Další informace o implementaci najdete ve zdrojovém kódu knihovny MFC.

Podrobnosti o čištění oken, které je velmi důležité pro všechny třídy odvozené od CWnd, najdete v technické poznámce 17.

Problémy s CWnd

Funkce člena implementace CWnd::OnChildNotify poskytuje výkonnou a rozšiřitelnou architekturu pro podřízená okna (označovaná také jako ovládací prvky), která umožňuje připojit nebo jinak informovat o zprávách, příkazech a řídicích oznámeních, která odesílají jejich nadřazená (neboli "vlastník"). Pokud je podřízené okno (/ovládací prvek) objektem CWnd jazyka C++, je virtuální funkce OnChildNotify volána jako první s parametry z původní zprávy (to je struktura MSG ). Podřízené okno může zprávu nechat samotnou, jíst ji nebo upravit zprávu pro rodiče (vzácné).

Výchozí implementace CWnd zpracovává následující zprávy a pomocí háku OnChildNotify povolí podřízeným oknám (ovládacím prvkům) první přístup ke zprávě:

  • WM_MEASUREITEM a WM_DRAWITEM (pro vlastní kreslení)

  • WM_COMPAREITEM a WM_DELETEITEM (pro vlastní kreslení)

  • WM_HSCROLL a WM_VSCROLL

  • WM_CTLCOLOR

  • WM_PARENTNOTIFY

Všimněte si, že pro změnu zpráv o kreslení vlastníkem na zprávy s vlastním kreslením se používá háček OnChildNotify .

Kromě háku OnChildNotify mají posunovací zprávy další chování směrování. Další podrobnosti o posuvníkech a zdrojích WM_HSCROLL a WM_VSCROLL zpráv najdete níže.

Problémy s CFrameWnd

CFrameWnd Třída poskytuje většinu směrování příkazů a aktualizace uživatelského rozhraní implementace. Používá se primárně pro okno hlavního rámce aplikace (CWinApp::m_pMainWnd), ale platí pro všechna okna rámců.

Okno hlavního rámce je okno s řádkem nabídek a je nadřazeným stavovým řádkem nebo řádkem zprávy. Projděte si výše uvedenou diskuzi o směrování příkazů a WM_INITMENUPOPUP.

Třída CFrameWnd poskytuje správu aktivního zobrazení. Následující zprávy se směrují přes aktivní zobrazení:

  • Všechny příkazové zprávy (aktivní zobrazení k nim získá první přístup).

  • WM_HSCROLL a WM_VSCROLL zprávy z posuvníků na stejné posádce (viz níže).

  • WM_ACTIVATE (a WM_MDIACTIVATE pro MDI) se změní na volání virtuální funkce CView::OnActivateView.

Problémy s CMDIFrameWnd/CMDIChildWnd

Obě třídy oken rámců MDI jsou odvozeny od CFrameWnd , a proto jsou obě povoleny pro stejný druh směrování příkazů a aktualizace uživatelského rozhraní poskytované v CFrameWnd. V typické aplikaci MDI obsahuje řádek nabídek a stavový řádek pouze okno hlavního rámce (tj. objekt CMDIFrameWnd ), a proto je hlavním zdrojem implementace směrování příkazů.

Obecné schéma směrování spočívá v tom, že aktivní podřízené okno MDI získá první přístup k příkazům. Výchozí funkce PreTranslateMessage zpracovávají tabulky akcelerátorů pro podřízená okna MDI (první) i rámec MDI (druhé) a také standardní akcelerátory systémových příkazů MDI, které obvykle zpracovává TranslateMDISysAccel (poslední).

Problémy s posuvníkem

Při zpracování scroll-message (WM_HSCROLL/OnHScroll nebo WM_VSCROLL/OnVScroll) byste se měli pokusit napsat kód obslužné rutiny, aby nespoléhal na to, odkud zpráva posuvníku pochází. Nejedná se jen o obecný problém windows, protože zprávy o posouvání můžou pocházet z skutečných ovládacích prvků posuvníku nebo z WS_HSCROLL/WS_VSCROLL posuvníků, které nejsou ovládacími prvky posuvníku.

MFC to rozšiřuje, aby ovládací prvky posuvníku byly buď podřízené, nebo sourozené pro posouvání okna (ve skutečnosti může být vztah nadřazenosti a podřízenosti mezi posuvníkem a posouváním okna jakýkoli). To je zvlášť důležité pro sdílené posuvníky s dělenými okny. Podrobnosti o implementaci CSplitterWnd včetně dalších informací o problémech se sdíleným posuvníkem najdete v technické poznámce 29.

Na boční poznámce jsou dvě odvozené třídy CWnd , kde styly posuvníku zadané v době vytvoření jsou zachycené a nepředávají se do systému Windows. Při předání do rutiny vytváření lze nezávisle nastavit WS_HSCROLL a WS_VSCROLL , ale po vytvoření nelze změnit. Samozřejmě byste neměli přímo testovat nebo nastavovat WS_SCROLL styl bitů okna, které vytvořili.

Pro CMDIFrameWnd styly posuvníku, které předáte do Create nebo LoadFrame se používají k vytvoření MDICLIENT. Pokud chcete mít oblast MDICLIENT s možností posouvání (například Správce programů Systému Windows), nezapomeňte nastavit oba styly posuvníku (WS_HSCROLL | WS_VSCROLL) pro styl použitý k vytvoření prvku CMDIFrameWnd.

Pro CSplitterWnd se styly posuvníku vztahují na speciální sdílené posuvníky pro oblasti rozdělovačů. U statických oken rozdělovače obvykle nenastavíte ani styl posuvníku. U dynamických oken rozdělovače budete mít obvykle styl posuvníku nastavený pro směr rozdělení, to znamená , že WS_HSCROLL pokud můžete rozdělit řádky , WS_VSCROLL pokud můžete rozdělit sloupce.

Viz také

Technické poznámky podle čísla
Technické poznámky podle kategorie