Udostępnij za pośrednictwem


TN021: routing poleceń i komunikatów

Uwaga

Następująca uwaga techniczna nie została zaktualizowana, ponieważ została po raz pierwszy uwzględniona w dokumentacji online. W związku z tym niektóre procedury i tematy mogą być nieaktualne lub nieprawidłowe. Aby uzyskać najnowsze informacje, zaleca się wyszukanie interesującego tematu w indeksie dokumentacji online.

W tej notatce opisano architekturę routingu poleceń i wysyłania, a także zaawansowane tematy ogólne routingu komunikatów okien.

Zapoznaj się z językiem Visual C++, aby uzyskać ogólne informacje na temat architektur opisanych tutaj, szczególnie różnice między komunikatami systemu Windows, powiadomieniami sterowania i poleceniami. W tej notatce założono, że bardzo dobrze znasz problemy opisane w drukowanej dokumentacji i zajmujesz się tylko bardzo zaawansowanymi tematami.

Routing poleceń i funkcja wysyłania MFC 1.0 ewoluuje do architektury MFC 2.0

System Windows ma komunikat WM_COMMAND, który jest przeciążony w celu dostarczania powiadomień o poleceniach menu, klawiszach akceleratora i powiadomieniach sterowania oknami dialogowymi.

MFC 1.0 opiera się na tym nieco, pozwalając programowi obsługi poleceń (na przykład "OnFileNew") w klasie pochodnej CWnd wywołać wywołanie w odpowiedzi na określony WM_COMMAND. Jest ona przyklejona wraz ze strukturą danych nazywaną mapą komunikatów i skutkuje bardzo wydajnym mechanizmem poleceń.

MFC 1.0 udostępniał również dodatkowe funkcje oddzielania powiadomień sterujących od komunikatów poleceń. Polecenia są reprezentowane przez 16-bitowy identyfikator, czasami znany jako identyfikator polecenia. Polecenia zwykle zaczynają się od CFrameWnd elementu (czyli wyboru menu lub przetłumaczonego akceleratora) i są kierowane do różnych innych okien.

Usługa MFC 1.0 używała routingu poleceń w ograniczonym sensie do implementacji interfejsu wielu dokumentów (MDI). (Okno ramki MDI deleguje polecenia do aktywnego okna podrzędnego MDI).

Ta funkcja została uogólniona i rozszerzona w MFC 2.0, aby umożliwić obsługę poleceń przez szerszy zakres obiektów (nie tylko obiektów okien). Zapewnia bardziej formalną i rozszerzalną architekturę do routingu komunikatów i ponownie używa routingu docelowego poleceń, aby nie tylko obsługiwać polecenia, ale także aktualizować obiekty interfejsu użytkownika (takie jak elementy menu i przyciski paska narzędzi), aby odzwierciedlić bieżącą dostępność polecenia.

Identyfikatory poleceń

Aby zapoznać się z opisem routingu poleceń i procesu powiązania, zobacz Visual C++. Uwaga techniczna 20 zawiera informacje dotyczące nazewnictwa identyfikatorów.

Używamy ogólnego prefiksu "ID_" dla identyfikatorów poleceń. Identyfikatory poleceń to >= 0x8000. Wiersz komunikatu lub pasek stanu pokaże ciąg opisu polecenia, jeśli istnieje zasób STRINGTABLE z tymi samymi identyfikatorami co identyfikator polecenia.

W zasobach aplikacji identyfikator polecenia może pojawić się w kilku miejscach:

  • W jednym zasobie STRINGTABLE, który ma ten sam identyfikator co wiersz komunikatu.

  • W prawdopodobnie wielu zasobach menu dołączonych do elementów menu, które wywołują to samo polecenie.

  • (ZAAWANSOWANE) w przycisku okna dialogowego dla polecenia GOSUB.

W kodzie źródłowym aplikacji identyfikator polecenia może pojawić się w kilku miejscach:

  • W zasobie. H (lub inny główny plik nagłówka symboli) do definiowania identyfikatorów poleceń specyficznych dla aplikacji.

  • BYĆ MOŻE w tablicy identyfikatorów użytej do utworzenia paska narzędzi.

  • W makrze ON_COMMAND.

  • BYĆ MOŻE w makrze ON_UPDATE_COMMAND_UI.

Obecnie jedyną implementacją w MFC, która wymaga identyfikatorów poleceń musi być >= 0x8000 jest implementacja okien dialogowych/poleceń GOSUB.

Polecenia GOSUB korzystające z architektury poleceń w oknach dialogowych

Architektura poleceń routingu i włączania poleceń działa dobrze w oknach ramowych, elementach menu, przyciskach paska narzędzi, przyciskach paska dialogowego, innych paskach sterowania i innych elementach interfejsu użytkownika zaprojektowanych do aktualizowania poleceń na żądanie i kierowania poleceń lub identyfikatorów sterowania do głównego celu polecenia (zazwyczaj główne okno ramki). Ten główny element docelowy polecenia może odpowiednio kierować powiadomienia polecenia lub sterowania do innych obiektów docelowych poleceń.

Okno dialogowe (modalne lub moderacyjne) może korzystać z niektórych funkcji architektury poleceń, jeśli przypiszesz identyfikator sterowania kontrolki okna dialogowego do odpowiedniego identyfikatora polecenia. Obsługa okien dialogowych nie jest automatyczna, więc może być konieczne napisanie dodatkowego kodu.

Należy pamiętać, że aby wszystkie te funkcje działały prawidłowo, identyfikatory poleceń powinny mieć >wartość = 0x8000. Ponieważ wiele okien dialogowych może być kierowanych do tej samej ramki, współużytkowane polecenia powinny być >= 0x8000, podczas gdy identyfikatory innych niż udostępnione w określonym oknie dialogowym powinny być <= 0x7FFF.

Można umieścić normalny przycisk w normalnym modalnym oknie dialogowym z centrum danych IDC przycisku ustawionego na odpowiedni identyfikator polecenia. Gdy użytkownik wybierze przycisk, właściciel okna dialogowego (zazwyczaj główne okno ramki) pobiera polecenie tak samo jak każde inne polecenie. Jest to nazywane poleceniem GOSUB, ponieważ zwykle służy do uruchamiania innego okna dialogowego (GOSUB pierwszego okna dialogowego).

Możesz również wywołać funkcję CWnd::UpdateDialogControls w oknie dialogowym i przekazać jej adres głównego okna ramki. Ta funkcja włącza lub wyłącza kontrolki okna dialogowego na podstawie tego, czy mają programy obsługi poleceń w ramce. Ta funkcja jest wywoływana automatycznie dla pasków sterowania w pętli bezczynności aplikacji, ale należy wywołać ją bezpośrednio dla normalnych okien dialogowych, które chcesz mieć tę funkcję.

Po wywołaniu ON_UPDATE_COMMAND_UI

Utrzymywanie włączonego/sprawdzonego stanu wszystkich elementów menu programu przez cały czas może być kosztowny obliczeniowo. Typową techniką jest włączenie/sprawdzenie elementów menu tylko wtedy, gdy użytkownik wybierze popUP. Implementacja CFrameWnd MFC 2.0 obsługuje komunikat WM_INITMENUPOPUP i używa architektury routingu poleceń do określania stanów menu za pośrednictwem programów obsługi ON_UPDATE_COMMAND_UI.

CFrameWnd Obsługuje również komunikat WM_ENTERIDLE opisujący bieżący element menu wybrany na pasku stanu (nazywany również wierszem komunikatu).

Struktura menu aplikacji, edytowana przez program Visual C++, służy do reprezentowania potencjalnych poleceń dostępnych w WM_INITMENUPOPUP czasie. ON_UPDATE_COMMAND_UI programy obsługi mogą modyfikować stan lub tekst menu albo w przypadku zastosowań zaawansowanych (takich jak lista plików MRU lub menu podręczne czasowniki OLE), faktycznie zmodyfikować strukturę menu przed narysowanym menu.

Takie samo przetwarzanie ON_UPDATE_COMMAND_UI jest wykonywane dla pasków narzędzi (i innych pasków sterowania), gdy aplikacja wchodzi w pętlę bezczynności. Aby uzyskać więcej informacji na temat pasków sterowania, zobacz Dokumentację biblioteki klas i Uwagi techniczne 31.

Menu podręczne zagnieżdżone

Jeśli używasz struktury menu zagnieżdżonego, zauważysz, że program obsługi ON_UPDATE_COMMAND_UI dla pierwszego elementu menu w menu podręcznym jest wywoływany w dwóch różnych przypadkach.

Najpierw jest on wywoływany dla samego menu podręcznego. Jest to konieczne, ponieważ wyskakujące menu nie mają identyfikatorów i używamy identyfikatora pierwszego elementu menu menu podręcznego, aby odwoływać się do całego menu podręcznego. W takim przypadku zmienna CCmdUI składowa m_pSubMenu obiektu będzie różna od wartości NULL i będzie wskazywać menu podręczne.

Po drugie, jest wywoływana tuż przed narysem elementów menu w menu podręcznym. W takim przypadku identyfikator odwołuje się tylko do pierwszego elementu menu, a zmienna składowa m_pSubMenu obiektu będzie mieć wartość CCmdUI NULL.

Dzięki temu można włączyć menu podręczne odrębne od jego elementów menu, ale wymaga pisania kodu obsługującego menu. Na przykład w menu zagnieżdżonym z następującą strukturą:

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

Polecenia ID_NEW_SHEET i ID_NEW_CHART mogą być niezależnie włączone lub wyłączone. Menu podręczne Nowy powinno być włączone, jeśli jeden z tych dwóch jest włączony.

Procedura obsługi poleceń dla ID_NEW_SHEET (pierwsze polecenie w wyskakującym okienku) będzie wyglądać mniej więcej tak:

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

Procedura obsługi poleceń dla ID_NEW_CHART byłaby normalną procedurą obsługi poleceń aktualizacji i wyglądałaby następująco:

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

ON_COMMAND i ON_BN_CLICKED

Makra mapy komunikatów dla ON_COMMAND i ON_BN_CLICKED są takie same. Mechanizm routingu poleceń i sterowania powiadomieniami MFC używa tylko identyfikatora polecenia, aby zdecydować, gdzie należy kierować. Powiadomienia sterujące za pomocą kodu powiadomienia sterującego o wartości zero (BN_CLICKED) są interpretowane jako polecenia.

Uwaga

W rzeczywistości wszystkie komunikaty powiadomień sterowania przechodzą przez łańcuch obsługi poleceń. Na przykład technicznie można napisać program obsługi powiadomień sterujących dla EN_CHANGE w klasie dokumentów. Nie jest to ogólnie zalecane, ponieważ praktyczne zastosowania tej funkcji są nieliczne, funkcja nie jest obsługiwana przez klasę ClassWizard, a użycie tej funkcji może spowodować niestabilny kod.

Wyłączanie automatycznego wyłączania kontrolek przycisków

Jeśli umieścisz kontrolkę przycisku na pasku okna dialogowego lub w oknie dialogowym, w którym wywołujesz polecenia CWnd::UpdateDialogControls samodzielnie, zauważysz, że przyciski, które nie mają ON_COMMAND lub ON_UPDATE_COMMAND_UI programy obsługi zostaną automatycznie wyłączone przez platformę. W niektórych przypadkach nie trzeba mieć programu obsługi, ale chcesz, aby przycisk pozostał włączony. Najprostszym sposobem osiągnięcia tego celu jest dodanie fikcyjnej procedury obsługi poleceń (łatwej do zrobienia z klasą ClassWizard) i nic w nim zrobić.

Routing komunikatów okna

Poniżej opisano bardziej zaawansowane tematy dotyczące klas MFC oraz sposób ich wpływu na routing komunikatów systemu Windows i inne tematy. Informacje przedstawione w tym miejscu zostały opisane tylko krótko. Aby uzyskać szczegółowe informacje na temat publicznych interfejsów API, zapoznaj się z dokumentacją biblioteki klas. Aby uzyskać więcej informacji na temat szczegółów implementacji, zapoznaj się z kodem źródłowym biblioteki MFC.

Aby uzyskać szczegółowe informacje na temat oczyszczania okien, zapoznaj się z dokumentacją techniczną 17 , bardzo ważnym tematem dla wszystkich klas pochodnych CWnd.

Problemy z CWnd

Funkcja składowa implementacji CWnd::OnChildNotify zapewnia zaawansowaną i rozszerzalną architekturę dla okien podrzędnych (nazywanych również kontrolkami) w celu zaczepienia lub w inny sposób informowania o komunikatach, poleceniach i powiadomieniach sterujących, które przechodzą do elementu nadrzędnego (lub "właściciela"). Jeśli okno podrzędne (/kontrolka) jest obiektem C++ CWnd , funkcja wirtualna OnChildNotify jest wywoływana jako pierwsza z parametrami oryginalnego komunikatu (czyli strukturą MSG ). Okno podrzędne może pozostawić wiadomość samodzielnie, jeść lub zmodyfikować komunikat dla elementu nadrzędnego (rzadko).

Domyślna implementacja CWnd obsługuje następujące komunikaty i używa haka OnChildNotify , aby umożliwić podrzędnym windows (kontrolkom) pierwszy dostęp do komunikatu:

  • WM_MEASUREITEM i WM_DRAWITEM (do samodzielnego rysowania)

  • WM_COMPAREITEM i WM_DELETEITEM (do samodzielnego rysowania)

  • WM_HSCROLL i WM_VSCROLL

  • WM_CTLCOLOR

  • WM_PARENTNOTIFY

Zauważysz, że hak OnChildNotify jest używany do zmieniania komunikatów rysowania właściciela na wiadomości samodzielnie rysowane.

Oprócz haka OnChildNotify komunikaty przewijania mają dalsze zachowanie routingu. Zobacz poniżej, aby uzyskać więcej informacji na temat pasków przewijania i źródeł komunikatów WM_HSCROLL i WM_VSCROLL .

Problemy cFrameWnd

Klasa CFrameWnd zapewnia większość implementacji routingu poleceń i aktualizacji interfejsu użytkownika. Jest to używane głównie w głównym oknie ramowym aplikacji (CWinApp::m_pMainWnd), ale dotyczy wszystkich okien ramowych.

Główne okno ramki to okno z paskiem menu i jest elementem nadrzędnym paska stanu lub wiersza komunikatu. Zapoznaj się z powyższym omówieniem routingu poleceń i WM_INITMENUPOPUP.

Klasa CFrameWnd zapewnia zarządzanie aktywnym widokiem. Następujące komunikaty są kierowane przez aktywny widok:

  • Wszystkie komunikaty poleceń (aktywny widok uzyskuje do nich pierwszy dostęp).

  • WM_HSCROLL i WM_VSCROLL komunikaty z pasków przewijania elementów równorzędnych (patrz poniżej).

  • WM_ACTIVATE (i WM_MDIACTIVATE dla MDI) są przekształcane w wywołania funkcji wirtualnej CView::OnActivateView.

CmDIFrameWnd/CMDIChildWnd — problemy

Obie klasy okien ramek MDI pochodzą z CFrameWnd i dlatego są włączone dla tego samego rodzaju routingu poleceń i aktualizacji interfejsu użytkownika udostępnionego w CFrameWnd. W typowej aplikacji MDI tylko główne okno ramki (czyli obiekt CMDIFrameWnd ) przechowuje pasek menu i pasek stanu, a zatem jest głównym źródłem implementacji routingu poleceń.

Ogólny schemat routingu polega na tym, że aktywne okno podrzędne MDI uzyskuje pierwszy dostęp do poleceń. Domyślne funkcje PreTranslateMessage obsługują tabele akceleratora zarówno dla okien podrzędnych MDI (pierwszy), jak i ramki MDI (drugi), a także standardowych akceleratorów poleceń systemowych MDI obsługiwanych zwykle przez TranslateMDISysAccel (ostatni).

Problemy z paskiem przewijania

Podczas obsługi komunikatu przewijania (WM_HSCROLL/OnHScroll i/lub WM_VSCROLL/OnVScroll) należy spróbować napisać kod programu obsługi, aby nie polegał na tym, skąd pochodzi komunikat paska przewijania. Jest to nie tylko ogólny problem z systemem Windows, ponieważ komunikaty przewijania mogą pochodzić z prawdziwych kontrolek paska przewijania lub z WS_HSCROLL/WS_VSCROLL pasków przewijania, które nie są kontrolkami paska przewijania.

MFC rozszerza to, aby umożliwić kontrolki paska przewijania być elementami podrzędnymi lub równorzędnymi przewijania okna (w rzeczywistości relacja nadrzędna/podrzędna między paskiem przewijania i przewijaniem okna może być czymś innym). Jest to szczególnie ważne w przypadku udostępnionych pasków przewijania z oknami rozdzielanymi. Szczegółowe informacje na temat implementacji CSplitterWnd można znaleźć w notatce technicznej 29, w tym więcej informacji na temat problemów z udostępnionym paskiem przewijania.

W notatce bocznej istnieją dwie klasy pochodne CWnd , w których style paska przewijania określone w czasie tworzenia są uwięzione i nie są przekazywane do systemu Windows. Po przekazaniu do procedury tworzenia WS_HSCROLL i WS_VSCROLL można ustawić niezależnie, ale po utworzeniu nie można zmienić. Oczywiście nie należy bezpośrednio testować ani ustawiać bitów stylu WS_SCROLL utworzonego okna.

W przypadku elementu CMDIFrameWnd style paska przewijania przekazywane do polecenia Create lub LoadFrame są używane do tworzenia obiektu MDICLIENT. Jeśli chcesz mieć przewijany obszar MDICLIENT (na przykład Menedżer programów systemu Windows) należy ustawić oba style paska przewijania (WS_HSCROLL | WS_VSCROLL) dla stylu użytego do utworzenia CMDIFrameWnd.

W przypadku CSplitterWnd style paska przewijania dotyczą specjalnych udostępnionych pasków przewijania dla regionów podziału. W przypadku okien rozdzielanych statycznie zwykle nie można ustawić stylu paska przewijania. W przypadku dynamicznych okien rozdzielających zazwyczaj styl paska przewijania jest ustawiony dla kierunku podziału, czyli WS_HSCROLL , jeśli można podzielić wiersze, WS_VSCROLL , jeśli możesz podzielić kolumny.

Zobacz też

Uwagi techniczne według numerów
Uwagi techniczne według kategorii