Sdílet prostřednictvím


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

Poznámka

Následující technická poznámka se od prvního zahrnutí do online dokumentace neaktualizovala. V důsledku toho můžou být některé postupy a témata zastaralé nebo nesprávné. Nejnovější informace doporučujeme vyhledat v online indexu dokumentace, které vás zajímá.

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 najdete v jazyce Visual C++, zejména rozdílu mezi zprávami Windows, oznámeními o řízení a příkazy. 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 prostředí MFC 1.0 směrování příkazů a odesílání se vyvíjejí v architektuře MFC 2.0.

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

MFC 1.0 vychází z toho trochu tím, že povolí obslužnou rutinu příkazu (například "OnFileNew") v CWnd odvozené třídě, aby se volala v reakci na konkrétní WM_COMMAND. To je připevněno společně s datovou strukturou označovanou jako mapa zpráv a výsledkem je velmi prostorově efektivní mechanismus příkazů.

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

MFC 1.0 používá 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 zobecněna a rozšířena v prostředí MFC 2.0, aby příkazy byly zpracovány širším rozsahem objektů (nejen objekty okna). Poskytuje formální a rozšiřitelnější architekturu pro směrování zpráv a opakovaně používá směrování cíle příkazu nejen pro zpracování příkazů, ale také pro aktualizaci objektů uživatelského rozhraní (například položek nabídky a tlačítek panelu nástrojů) tak, aby odrážela aktuální dostupnost příkazu.

Identifikátory příkazů

Vysvětlení procesu směrování a vazby příkazů najdete v jazyce 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 mnoha prostředcích NABÍDKY, které jsou připojeny k položkám nabídky, které vyvolávají stejný příkaz.

  • (UPŘESNIT) 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 ON_COMMAND makre.

  • Možná v ON_UPDATE_COMMAND_UI makrech.

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

Příkazy GOSUB využívající architekturu příkazů v dialogových oknech

Architektura příkazů směrování a povolení příkazů funguje dobře s okny snímků, položkami nabídek, tlačítky panelu nástrojů, tlačítky na panelu dialogů, dalšími řídicími pruhy a dalšími prvky uživatelského rozhraní, které jsou navržené tak, aby aktualizovaly příkazy na vyžádání a směrují příkazy nebo id ovládacích prvků do hlavního cíle příkazu (obvykle okno hlavního rámce). Tento hlavní cíl příkazu může podle potřeby směrovat oznámení příkazu nebo řízení na jiné cílové objekty příkazu.

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, měly by být >ID příkazů = 0x8000. Vzhledem k tomu, že mnoho dialogů by mohlo být směrováno na stejný rámec, měly by být >sdílené příkazy = 0x8000, zatímco nesdílené id v konkrétním dialogovém okně by měly být <= 0x7FFF.

Normální modální dialog 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 tlačítko vybere, vlastník dialogového okna (obvykle hlavní okno rámce) získá příkaz stejně jako jakýkoli jiný příkaz. Tomu se říká příkaz 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é volat CWnd::UpdateDialogControls v 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 se volá automaticky pro řídicí pruhy ve smyčce nečinnosti vaší aplikace, ale musíte ji volat přímo pro normální dialogy, které chcete mít tuto funkci.

Při zavolání ON_UPDATE_COMMAND_UI

Udržování povoleného nebo zaškrtnutého stavu všech položek nabídky programu může být neustále náročným problémem. Běžnou technikou je povolit nebo zkontrolovat položky nabídky pouze tehdy, když uživatel vybere popUP. Implementace CFrameWnd MFC 2.0 zpracovává zprávu WM_INITMENUPOPUP a používá architekturu směrování příkazů k určení stavů nabídek prostřednictvím ON_UPDATE_COMMAND_UI obslužných rutin.

CFrameWnd zpracovává také 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++ se používá k reprezentaci potenciálních příkazů dostupných v WM_INITMENUPOPUP čase. ON_UPDATE_COMMAND_UI obslužné rutiny mohou změnit stav nebo text nabídky nebo pro rozšířené použití (například seznam MRU souboru nebo místní nabídka OLE Sloves), ve skutečnosti změnit strukturu nabídky před vykreslením nabídky.

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

Vnořené místní nabídky

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

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

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

To umožňuje povolit místní nabídku odlišnou od položek nabídky, ale vyžaduje, abyste napsali kód s podporou nabídky. Například v 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 lze nezávisle povolit nebo zakázat. Pokud je některý z těchto dvou povolených, měla by být povolena místní nabídka Nový .

Obslužná rutina příkazu pro ID_NEW_SHEET (první příkaz v automaticky otevírané nabídce) by vypadala nějak 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žnou rutinou příkazu aktualizace a vypadala by nějak 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. Řízení oznámení s kódem oznámení ovládacího prvku nula (BN_CLICKED) jsou interpretovány jako příkazy.

Poznámka

Ve skutečnosti všechny zprávy s oznámením ovládacích prvků procházejí řetězem obslužných rutin příkazů. Technicky možné je například napsat obslužnou rutinu oznámení ovládacího prvku pro EN_CHANGE ve třídě dokumentu. To obecně nedoporučujeme, protože praktické aplikace této funkce jsou jen málo, funkce není podporována ClassWizard a použití této 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 panel dialogového okna nebo do dialogového okna, pomocí kterého voláte CWnd::UpdateDialogControls sami, všimnete si, že tlačítka, která nemají ON_COMMAND nebo ON_UPDATE_COMMAND_UI obslužné rutiny, budou automaticky zakázány 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šší způsob, jak toho dosáhnout, je přidat fiktivní obslužnou rutinu příkazů (snadno dělat s ClassWizard) a dělat v ní nic.

Směrování zpráv okna

Následuje popis některých pokročilejších témat o třídách MFC a o tom, jak je ovlivňuje 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 vyčištění okna, velmi důležité téma pro všechny třídy odvozené od CWnd, naleznete 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) pro připojení nebo jiné informace o zprávách, příkazech a řídicích oznámeních, která přejdou na nadřazené (nebo "vlastník"). Pokud je podřízené okno (/control) objekt CWnd jazyka C++, virtuální funkce OnChildNotify je volána jako první s parametry z původní zprávy (to znamená struktura MSG). Podřízené okno může nechat zprávu samotnou, jíst ji nebo upravovat zprávu nadřazeného objektu (vzácné).

Výchozí implementace CWnd zpracovává následující zprávy a pomocí háku OnChildNotify umožňuje podřízeným oknům (ovládacím prvkům) přístup k této zprávě:

  • WM_MEASUREITEM a WM_DRAWITEM (pro samokreslení)

  • WM_COMPAREITEM a WM_DELETEITEM (pro samokreslení)

  • WM_HSCROLL a WM_VSCROLL

  • WM_CTLCOLOR

  • WM_PARENTNOTIFY

Všimněte si, že háček OnChildNotify se používá ke změně zpráv nakreslených vlastníkem do samokreslených zpráv.

Kromě háku OnChildNotify mají zprávy posouvání 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 uživatelské rozhraní aktualizace implementace. Používá se primárně pro okno hlavního rámce aplikace (CWinApp::m_pMainWnd), ale platí pro všechna okna rámečku.

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

CFrameWnd třída poskytuje správu aktivního zobrazení. Prostřednictvím aktivního zobrazení se směrují následující zprávy:

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

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

  • WM_ACTIVATE (a WM_MDIACTIVATE pro MDI) se převést na volání virtuální funkce CView::OnActivateView.

CMDIFrameWnd/CMDIChildWnd – problémy

Obě třídy oken oken MDI jsou odvozeny od CFrameWnd , a proto jsou oba 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í je, ž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ý) i standardní akcelerátory systémových příkazů MDI, které obvykle zpracovává TranslateMDISysAccel (poslední).

Problémy s posuvníkem

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

MFC rozšiřuje, aby ovládací prvky posuvníku byly podřízené nebo na stejné úrovni okna, které se posouvají (ve skutečnosti může být vztah nadřazenosti a podřízenosti mezi posuvníkem a posunovaným oknem cokoli). To je zvlášť důležité pro sdílené posuvníky s rozdělovači oken. Podrobnosti o implementaci CSplitterWnd najdete v technické poznámce 29, včetně dalších informací o problémech se sdíleným posuvníkem.

Na vedlejší poznámce existují dvě odvozené třídy CWnd , kde styly posuvníku určené při vytvoření jsou zachycené a nepředané do Windows. Při předání rutině vytváření je možné nezávisle nastavit WS_HSCROLL a WS_VSCROLL , ale po vytvoření nelze změnit. Samozřejmě byste neměli přímo testovat ani nastavovat WS_SCROLL bity okna, které vytvořily.

Pro CMDIFrameWnd styly posuvníku, které předáte create nebo LoadFrame, se používají k vytvoření MDICLIENT. Pokud chcete mít posuvnou oblast MDICLIENT (například Správce programu Windows), nezapomeňte nastavit oba styly posuvníku (WS_HSCROLL | WS_VSCROLL) pro styl použitý k vytvoření CMDIFrameWnd.

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

Viz také

Technické poznámky podle čísel
Technické poznámky podle kategorií