Menüs

Kommen wir nun zu einer der wichtigsten Ressource, dem Menü. Damit es bei der folgenden Behandlung der Menüs zu keinen Missverständnissen kommt, zunächst die Erläuterung einiger wichtiger Begriffe:

Begriff

Bedeutung

Menü Menübalken, besteht in der Regel aus mehreren Popup-Menüs.
Popup-Menü Besteht aus einer Beschriftung (im Menübalken) sowie einem
aufklappbaren Menüfeld mit Menüeinträgen.
Menüeintrag Einzelner Eintrag innerhalb eines Popup-Menüs.

Menübegriffe

Und auch hier werden wir uns zunächst ansehen, wie ein Menü erstellt bzw. erweitert wird.

Erstellen Sie zunächst wieder ein neues SDI-Projekt mit dem Namen Menu. Nach dem der Anwendungs-Assistent seine Arbeit beendet hat, gehen Sie in die Ressourcen-Ansicht. Öffnen Sie dort den Ressource-Typ Menu. Sie werden feststellen, dass der Anwendungs-Assistent Ihrem Projekt schon ein Menü mit der ID IDR_MAINFRAME hinzugefügt hat. Führen Sie jetzt einen Doppelklick auf diese Menü-Ressource aus. Im Editorfenster wird dann das vom Anwendungs-Assistenten erstellte Menü dargestellt:

Das Standard-Menü

Dies ist das Standard-Menü, das bisher (fast) jedes im Kurs erstellte MFC-Fenster besessen hat. Erweitern wir jetzt das vorgegebene Menü. Unser Beispiel soll nachher an der Position im Fenster, die Sie mit der linken Maustaste anklicken, entweder eine Linie, einen Kreis oder ein Rechteck zeichnen. Die Auswahl des darzustellenden Objektes soll über ein Popup-Menü erfolgen. Klicken Sie zur Erstellung eines neuen Popup-Menüs den im Menü rechts dargestellten leeren Rahmen an. Der Rahmen erhält darauf eine Markierung. Ziehen Sie den markierten Rahmen nun mit gedrückter linker Maustaste zwischen die Popup-Menüs Bearbeiten und ?. Anschließend führen Sie einen Doppelklick auf den eingefügten, markierten Rahmen aus, worauf folgender Dialog eingeblendet wird:

Popup-Menü definieren

Hier geben Sie nun im Feld Titel den Text &Objekte ein. Dies ist die Überschrift unseres neuen Popup-Menüs. Die restlichen Einstellung lassen Sie unverändert. Drücken Sie die RETURN-Taste und dem Menü wird das soeben neu erstellte Popup-Menü hinzugefügt. Der Buchstabe, der dem Zeichen & folgt, wird unterstrichen ausgegeben und kann als Shortcut für den Zugriff auf das Popup-Menü verwendet werden. In unserem Fall erreichen Sie später das Popup-Menü Objekte also durch drücken der Tasten <ALT>-<O>.

Das erweiterte Standard-Menü

Gleichzeitig wurde durch den Menü-Editor dem Popup-Menü ein leerer Menüeintrag hinzugefügt (markierter Rahmen im Bild oben). Wir fügen dem Popup-Menü als nächstes den Menüeintrag hinzu, der für die Auswahl zum Zeichnen einer Linie dienen soll. Führen Sie dazu einen Doppelklick auf den markierten Rahmen aus und es wird wieder der gleiche Dialog wie vorhin eingeblendet, mit zwei wesentlichen Unterschieden: der Eintrag ID im Dialog ist nun nicht mehr gesperrt ist und die Auswahl Popup ist nicht mehr markiert:

Definition eines Menüeintrags

Jeder Menüeintrag wird durch eine ID eindeutig gekennzeichnet, die Sie im Feld ID angeben müssen. Wir verwenden für die Auswahl einer Linie die ID ID_LINE und beschriften den Menüeintrag mit &Linie. Die restlichen Elemente lassen wir unverändert und drücken die RETURN-Taste. Dem Popup-Menü wurde damit der Menüeintrag Linie hinzugefügt. Wenn später in der Anwendung dieser Menüeintrag ausgewählt wird, erhält die Anwendung eine Nachricht mit der angegebenen ID, hier also ID_LINE. Wie diese Nachricht verarbeitet wird, das erfahren Sie etwas später.

Als nächstes wollen wir dem Popup-Menü eine Trennlinie hinzufügen. Führen Sie dazu wieder einen Doppelklick auf den markierten Rahmen aus und es wird der der bekannte Dialog erneut eingeblendet. Markieren Sie nun die Auswahl Trennlinie im Dialog und drücken dann die RETURN-Taste. Ihr Popup-Menü hat soeben eine Trennlinie erhalten. Ganz einfach, oder? Vervollständigen Sie das Popup-Menü jetzt bis es folgendes Aussehen besitzt:

Das endgültige Objekt-Menü

Fügen Sie anschließend dem Menü ein weiteres Popup-Menü mit den folgenden Menüeinträgen hinzu:

Ein weiteres Popup-Menü

Übersetzen und starten Sie das Beispiel.

Menü-IDs beginnen in der Regel alle mit dem Präfix ID_xxx.

Und noch etwas Wichtiges. Vielleicht ist Ihnen schon einmal aufgefallen, dass einige Menüeinträge mit '...' aufhören (siehe z.B. im Popup-Menü Datei die Einträge Öffnen... und Speichern unter...). Menüeinträge die mit '...' enden führen keine unmittelbaren Aktionen aus, sondern sie blenden immer einen zusätzlichen Dialog ein. An diese Konvention sollten Sie sich halten, sie ist sozusagen ungeschriebenes Gesetz!

Wenn Sie eines der hinzugefügten Popup-Menüs einmal anklicken werden Sie feststellen, dass die Menüeinträge alle gesperrt sind. Das Rahmenprogramm der MFC sperrt automatisch alle Menüeinträge für die keine entsprechenden Nachrichtenbearbeiter definiert sind.

Fügen wir als nächstes die Nachrichtenbearbeiter für die Menüeinträge hinzu. Nachrichtenbearbeiter für Menüeinträge können an mehreren Stellen definiert werden: in der Klasse des Rahmenfensters, in der Ansichtsklasse, in der Dokumentenklasse oder auch in der Anwendungsklasse. Das Menü selbst gehört jedoch immer zum Rahmenfenster. Alle Methoden, die sich mit der Manipulation von Menüs befassen, sind aus diesem Grunde stets Methoden der Klasse CFrameWnd. Das Rahmenprogramm der MFC leitet jedoch Menü-Nachrichten, die nicht vom Rahmenfenster bearbeitet werden, entsprechend weiter. Wo Sie die Nachrichten der Menüeinträge letztendlich verarbeiten hängt von der Aktion ab, die Sie mit dem Menüeintrag durchführen wollen. Da sich unsere neu hinzugefügten Menüeinträge direkt mit der Darstellung von entsprechenden Grafiken innerhalb des Ansichtsobjekts befassen, bietet es sich an, die Nachrichtenbearbeiter auch innerhalb der Ansichtsklasse zu definieren. Selbstverständlich hätten wir auch die Nachrichtenbearbeiter z.B. im Rahmenfenster definieren können und dann das Ansichtsobjekt durch Aufruf von Methoden entsprechend steuern können.

Damit das Ansichtsobjekt immer darüber informiert ist, welche Grafik gerade aktiv, d.h. ausgewählt ist, werden zunächst folgende privat Membervariablen in der Klasse CMenuView definiert:
CPoint   m_CMousePos;
int      m_nLThickness;
COLORREF m_Color;
enum {NOTHING, LINE, CIRCLE, RECT} m_eObject;

m_CMousePos enthält die Position, an der die Grafik zu zeichnen ist. Wie am Anfang der Lektion erwähnt, soll diese Position später mit der linken Maustaste festgelegt werden. m_nLThickness definiert die aktuelle Stiftstärke mit der die Linie gezeichnet werden soll und m_Color enthält die Farbe der zu zeichnenden Grafik. m_eObject ist eine enum-Variable, die die zu zeichnende Grafik bestimmt. Und da es immer eine gute Idee ist, Variablen zu initialisieren, fügen wir der Ansichtsklasse noch die Methode OnInitialUpdate(...) hinzu und initialisieren dort die Membervariablen entsprechend:

void CMenuView::OnInitialUpdate()
{
    CView::OnInitialUpdate();
   
    // TODO: Speziellen Code hier einfügen und/oder Basisklasse aufrufen
    // Membervariablen initialisieren
    m_eObject = NOTHING;
    m_Color = RGB(255,0,0);
    m_nLThickness = 1;   
}

Nach dem Starten des Programms ist zunächst keine Grafik aktiv, die aktuelle Zeichenfarbe wird auf rot und die Linienstärke auf 1 Pixel gesetzt.

So, fügen wir nun die Nachrichtenbearbeiter für die Menüeinträge im Popup-Menü Objekte hinzu. Öffnen Sie den Klassen-Assistenten (über das Menü Ansicht im Visual Studio) um folgenden Dialog eingeblendet zu bekommen:

Nachrichtenbearbeiter hinzufügen

Wie Sie sehen, sind die IDs der neu hinzugefügten Menüeinträge bereits im Auswahlfeld Objekt-IDs aufgeführt. Da wir die Nachrichtenbearbeiter für die Menüeinträge in der Klasse CMenuView definieren wollen, wählen Sie als erstes diese Klasse im Auswahlfeld Klassenname aus. Über diese Auswahl bestimmen Sie, in welcher Klasse der Nachrichtenbearbeiter definiert wird. Als nächstes suchen Sie im Auswahlfeld Objekt-IDs die ID ID_LINE und klicken diese an. Im rechts davon befindlichen Auswahlfeld Nachrichten werden dann alle Nachrichten anzeigt, die Sie mit der ausgewählten ID verknüpfen können. Führen Sie einen Doppelklick auf den Eintrag COMMAND im Auswahlfeld Nachrichten aus. Der Klassen-Assistent öffnet daraufhin einen weiteren Dialog, in dem Sie den Namen der Methode die mit der Nachricht verknüpft werden soll eingeben können. Belassen Sie es bei dem Vorschlag OnLine(...) und klicken Sie den Button OK an. Der Klassen-Assistent erstellt dann die Methode OnLine(...) und verknüpft sie gleichzeitig (über die Nachrichtentabelle) mit der Nachricht vom Menüeintrag Linie. Das war's auch schon. So einfach können Sie mit Hilfe des Klassen-Assistenten Nachrichten von Menüeinträgen mit Methoden verknüpfen. Führen Sie die gleich Aktion für die restlichen Menüeinträge ID_CIRCLE, ID_RECT, ID_RED, ID_BLUE und ID_GREEN durch. Achten Sie aber immer darauf, das im Auswahlfeld Klassenname auch die Klasse CMenuView spezifiziert ist!

Welche Bedeutung die Nachricht UPDATE_COMMAND_UI im Nachrichtenfeld besitzt das erfahren Sie später noch.

Damit hätten wir für alle hinzugefügten Menüeinträge entsprechende Nachrichtenbearbeiter definiert. Im nächsten Schritt füllen wir die Nachrichtenarbeiter mit Leben.

In den Nachrichtenbearbeiter setzen wir die vorhin definierten Membervariablen m_Color und m_eObject einfach auf die entsprechenden Werte.
void CMenuView::OnBlue()
{
    // TODO: Code für Befehlsbehandlungsroutine hier einfügen
    m_Color = RGB(0,0,255);
}
void CMenuView::OnCircle()
{
    // TODO: Code für Befehlsbehandlungsroutine hier einfügen
    m_eObject = CIRCLE;
}
void CMenuView::OnGreen()
{
    // TODO: Code für Befehlsbehandlungsroutine hier einfügen
    m_Color = RGB(0,255,0);
}
void CMenuView::OnLine()
{
    // TODO: Code für Befehlsbehandlungsroutine hier einfügen
    m_eObject = LINE;
}
void CMenuView::OnRect()
{
    // TODO: Code für Befehlsbehandlungsroutine hier einfügen
    m_eObject = RECT;
}
void CMenuView::OnRed()
{
    // TODO: Code für Befehlsbehandlungsroutine hier einfügen
    m_Color = RGB(255,0,0);
}

Und damit sind wir fast fertig. Nur das Zeichnen der Grafik selbst fehlt uns noch. Die Grafik soll, wie bereits erwähnt, immer an der Position gezeichnet werden, an der die linke Maustaste gedrückt wurde. Diese Position ist zunächst in der Membervariable m_CMousePos abzulegen und dann der Fensterinhalt als ungültig zu kennzeichnen.

Fügen Sie der Klasse CMenuView die Methode OnLButtonDown(...) hinzu. Legen Sie dort die aktuelle Mausposition in der Membervariable m_CMousePos ab und lassen dann das Fenster neu zeichnen. Zum Schluss ist noch die Methode OnDraw(...) wie nachfolgend angegeben zu implementieren:
void CMenuView::OnLButtonDown(UINT nFlags, CPoint point)
{
    // TODO: Code für die Behandlungsroutine für Nachrichten hier einfügen...
    m_CMousePos = point;
    Invalidate();
}
void CMenuView::OnDraw(CDC* pDC)
{
    CMenuDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    // ZU ERLEDIGEN: Hier Code zum Zeichnen der ursprünglichen Daten hinzufügen

    switch (m_eObject)
    {
    case LINE:
        {
            CPen CLinePen(PS_SOLID,m_nLThickness,m_Color);
            CPen *pCOldPen = pDC->SelectObject(&CLinePen);
            pDC->MoveTo(m_CMousePos.x-20,m_CMousePos.y-20);
            pDC->LineTo(m_CMousePos.x+20,m_CMousePos.y+20);
            pDC->SelectObject(pCOldPen);
        }
        break;
    case CIRCLE:
        {
            CPen CFramePen(PS_SOLID,m_nLThickness,(~m_Color)&0x00FFFFFFUL);
            CPen *pCOldPen = pDC->SelectObject(&CFramePen);
            CBrush CFillBrush(m_Color);
            CBrush *pCOldBrush = pDC->SelectObject(&CFillBrush);
            pDC->Ellipse(m_CMousePos.x-20,m_CMousePos.y-20,
            m_CMousePos.x+20,m_CMousePos.y+20);
            pDC->SelectObject(pCOldBrush);
            pDC->SelectObject(pCOldPen);
        }
        break;
    case RECT:
        {
            CPen CFramePen(PS_SOLID,m_nLThickness,(~m_Color)&0x00FFFFFFUL);
            CPen *pCOldPen = pDC->SelectObject(&CFramePen);
            CBrush CFillBrush(m_Color);
            CBrush *pCOldBrush = pDC->SelectObject(&CFillBrush);
            pDC->Rectangle(m_CMousePos.x-20,m_CMousePos.y-20,
            m_CMousePos.x+20,m_CMousePos.y+20);
            pDC->SelectObject(pCOldBrush);
            pDC->SelectObject(pCOldPen);
        }
        break;
    }

}

Übersetzen und starten Sie das Programm. Wenn Sie jetzt die linke Maustaste drücken wird eine entsprechende Grafik in der ausgewählten Farbe dargestellt.

Im Prinzip könnten wir damit diese Lektion beenden. Sie wissen nun, wie man Popup-Menüs mit Menüeinträgen erstellt und die Menüeinträge mit entsprechenden Nachrichtenbearbeitern verknüpft. Wir werden nun aber noch etwas mit den Menüs experimentieren. Vielleicht ist Ihnen schon aufgefallen, dass in manchen Anwendungen die zuletzt ausgewählten Menüeinträge mit einem kleinen Haken, dem so genannten Checkmark, versehen sind. Und genau diese Eigenschaft werden wir jetzt dem Farbe-Menü hinzufügen. Um für einen Menüeintrag ein Checkmark zu setzen oder zu löschen, wird die CMenu Methode CheckMenuItem(...) aufgerufen. Der erste Parameter der Methode spezifiziert, welcher Menüeintrag beeinflusst werden soll. Hier kann entweder die Position innerhalb des Popup-Menüs angeben werden oder aber die entsprechende ID, je nach dem welches Flag im zweiten Parameter gesetzt ist. Der zweite Parameter gibt an, ob ein Checkmark gesetzt oder gelöscht werden soll und wie der erste Parameter zu interpretieren ist. Für diesen Parameter sind folgenden Werte zugelassen:

Wert

Bedeutung

MF_BYPOSITION Der erste Parameter enthält die Position des zu verändernden Menüeintrags, wobei der erste Menüeintrag die Position 0 besitzt.
MF_BYCOMMAND Der erste Parameter enthält die ID des zu verändernden Menüeintrags.
MF_CHECKED Setzt das Checkmark.
MF_UNCHECKED Löscht das Checkmark.

Eigentlich doch ganz einfach? Doch das Problem das sich dabei nun stellt ist folgendes: wie erhält man ein CMenu-Objekt zu einem bestehenden Popup-Menü? Die Methode CheckMenuItem(...) ist ja eine Methode der Klasse CMenu. Hierbei muss in der Regel zweistufig vorgegangen werden. Als erstes müssen Sie das CMenü Objekt des Menüs (das ist der Menübalken!) ermitteln. Rufen Sie dazu die CWnd Methode GetMenu(...) des Rahmenfensters auf. Sie liefert als Returnwert einen Zeiger auf das CMenu-Objekt des Fensters. Danach kann durch den Aufruf der CMenu Methode GetSubMenu(...) des Menüs der CMenu-Zeiger auf das gewünschte Popup-Menü ermittelt werden. Die Methode erhält als Parameter die Position des Popup-Menüs, für das das CMenu-Objekt geliefert werden soll. Beachten Sie dabei bitte, dass das erste Popup-Menü (in der Regel das Popup-Menü Datei) die Position '0' besitzt. Über diesen CMenu-Zeiger auf das Popup-Menü können Sie nun die oben aufgeführte CheckMenuItem(...) Methode aufrufen, um z.B. einen Menüeintrag mit einem Checkmark zu versehen.

Beide Methoden GetMenu(...) wie auch GetSubMenu(...) liefern Zeiger auf temporäre CMenu-Objekte zurück. Diese Zeiger sind nur solange gültig, bis die Bearbeitung der aktuellen Nachricht abgeschlossen ist!
So, uns jetzt sind Sie endlich mal wieder dran. Versehen Sie jetzt den Menüeintrag im Popup-Menü Farbe mit einem Checkmark, dessen Einstellung gerade gültig ist. Denken Sie auch daran, den aktuellen Menüeintrag beim Starten des Programms mit einem Checkmark zu versehen!

Lösung zur Ausgabe von Checkmarks

Als erstes müssen Sie in der Methode OnInitialUpdate(...) für die Farbe rot das Checkmark setzen.

void CMenuView::OnInitialUpdate()
{
    ....
    m_nLThickness = 1;   
    // ausgewaehlte Farbe mit Checkmark versehen
    // CMenu-Zeiger auf Menue (Fenstermenue) holen
    CMenu *pMenuBar = GetParent()->GetMenu();
    // CMenu-Zeiger auf Popup-Menue 'Farbe' holen,
    // ist 4. Popup-Menue im Fenstermenue
    CMenu *pColorMenu = pMenuBar->GetSubMenu(3);
    // Menue-Eintrag ID_RED mit Checkmark versehen
    pColorMenu->CheckMenuItem(ID_RED,MF_CHECKED);
}

Anschließend können dann die Nachrichtenbearbeiter für die Farbauswahl wie folgt erweitert werden:

void CMenuView::OnBlue()
{
    // TODO: Code für Befehlsbehandlungsroutine hier einfügen
    m_Color = RGB(0,0,255);

    // Checkmarks setzen
    CMenu *pMenuBar = GetParent()->GetMenu();
    CMenu *pColorMenu = pMenuBar->GetSubMenu(3);
    pColorMenu->CheckMenuItem(ID_BLUE,MF_CHECKED);
    pColorMenu->CheckMenuItem(ID_GREEN,MF_UNCHECKED);
    pColorMenu->CheckMenuItem(ID_RED,MF_UNCHECKED);
}
void CMenuView::OnGreen()
{
    // TODO: Code für Befehlsbehandlungsroutine hier einfügen
    m_Color = RGB(0,255,0);
    // Checkmarks setzen
    CMenu *pMenuBar = GetParent()->GetMenu();
    CMenu *pColorMenu = pMenuBar->GetSubMenu(3);
    pColorMenu->CheckMenuItem(ID_BLUE,MF_UNCHECKED);
    pColorMenu->CheckMenuItem(ID_GREEN,MF_CHECKED);
    pColorMenu->CheckMenuItem(ID_RED,MF_UNCHECKED);
}
void CMenuView::OnRed()
{
    // TODO: Code für Befehlsbehandlungsroutine hier einfügen
    m_Color = RGB(255,0,0);
    // Checkmarks setzen
    CMenu *pMenuBar = GetParent()->GetMenu();
    CMenu *pColorMenu = pMenuBar->GetSubMenu(3);
    pColorMenu->CheckMenuItem(ID_BLUE,MF_UNCHECKED);
    pColorMenu->CheckMenuItem(ID_GREEN,MF_UNCHECKED);
    pColorMenu->CheckMenuItem(ID_RED,MF_CHECKED);
}

Sie sehen, relativ viel Aufwand für eine 'nette' Kleinigkeit. Aber so ist halt das Leben. Haben Sie auch nicht vergessen, das vorherige Checkmark auch wieder wegzunehmen?

Ende der Lösung

Wenn Sie das Programm übersetzen und starten, wird die aktuelle Farbauswahl im Popup-Menü Farbe mit einem Checkmark versehen werden.

Aber gehen wir noch einen Schritt weiter. Wenn Sie sich das Menü im Visual Studio einmal genauer angesehen haben, werden Sie vielleicht bemerken, dass je nach Aktion manchmal zusätzliche Popup-Menüs eingeblendet werden. Wie Sie so etwas auch erreichen können, das erfahren Sie jetzt.

Im Beispiel soll die Linienstärke des Linienobjektes über ein zusätzliches Popup-Menü eingestellt werden können. Dieses Popup-Menü soll aber auch nur dann erscheinen, wenn das aktuelle Objekt auch ein Linienobjekt ist. Dazu muss zuerst für das wahlweise einzublendende Popup-Menü ein gesondertes Menü erstellt werden.

Gehen Sie nun wieder in die Ressourcen-Ansicht, klicken dort den Ressource-Typ Menu mit der rechten Maustaste an und wählen im eingeblendeten Kontext-Menü den Eintrag Menü einfügen aus. Daraufhin wird ein neues Menü mit der ID IDR_MENU1 erstellt das einen noch leeren Menübar besitzt. Klicken Sie jetzt mit der rechten Maustaste ins Editorfenster des Menü-Editors und wählen Sie aus dem dann eingeblendeten Kontext-Menü den Eintrag Als Kontextmenü anzeigen aus. Legen Sie danach ein Menü mit den unten angegebenen Menüeinträgen und den entsprechenden IDs an:

Zusätzliches dyn. Popup-Menü

Zum Schluss ändern Sie noch die ID des Menüs von IDR_MENU1 auf IDR_LTHICKNESS

Da dieses Menü nicht automatisch, wie das Fenstermenü, durch das Rahmenprogramm geladen wird, muss dies explizit erfolgen. Das Laden erfolgt durch den Aufruf der CMenu Methode LoadMenu(...). Nach dem Laden des Menüs kann das Popup-Menü dann bei Bedarf in das bestehende Menü eingefügt werden. Hierfür besitzt die Klasse CMenu die Methode InsertMenu(...), mit deren Hilfe einem bestehenden Menü weitere Popup-Menüs hinzugefügt werden können oder aber auch einem Popup-Menü neue Menüeinträge. Die Methode erhält u.a. als Parameter die Position im Menü, vor der das neue Popup-Menü bzw. der neue Menüeintrag eingefügt werden soll. Beachten Sie bitte bei der Angabe des Menütextes, dass das Zeichen '&' dazu verwendet wird die Auswahltaste zu bestimmen.

Um ein Popup-Menü oder einen Menüeintrag zu entfernen dient die CMenu Methode RemoveMenu(...).

Das solchermaßen modifizierte Menü wird aber nicht automatisch neu dargestellt sondern erst durch den Aufruf der CMenu Methode DrawMenuBar(...).

Bauen wir jetzt das zusätzliche Popup-Menü in unser Beispiel ein.

Nachdem im vorherigen Schritt das neue Popup-Menü erstellt wurde, laden wir dieses Menü in der Methode OnInitialUpdate(...). Fügen Sie dazu der Klasse CMenuView zunächst die Membervariable m_CLineMenu vom Typ CMenu hinzu. Anschließend erweitern Sie die Methode OnInitialUpdate(...) wie folgt:
void CMenuView::OnInitialUpdate()
{
    CView::OnInitialUpdate();
   
    // TODO: Speziellen Code hier einfügen und/oder Basisklasse aufrufen
    ....
    // Menue-Eintrag ID_RED mit Checkmark versehen
    pColorMenu->CheckMenuItem(ID_RED,MF_CHECKED);
    // Popup-Menue fuer Linienstaerke laden
    // wird noch nicht dargestellt!
    m_CLineMenu.LoadMenu(IDR_LTHICKNESS);
}

Anschließend sind die Methoden OnLine(...), OnCircle(...) und OnRect(...) noch wie folgt anzupassen:

void CMenuView::OnLine()
{
    // TODO: Code für Befehlsbehandlungsroutine hier einfügen
    // Falls Linienobjekt noch nicht aktiv ist
    if (m_eObject != LINE)
    {
        // Linienobjekt aktivieren
        m_eObject = LINE;
        // Popup-Menue fuer Linienstaerke hinzufuegen
        CMenu *pMenuBar = GetParent()->GetMenu();
        pMenuBar->InsertMenu(4,MF_BYPOSITION|MF_POPUP,
                            (UINT)m_CLineMenu.m_hMenu,"&LStärke");
        GetParent()->DrawMenuBar();
    }
}
void CMenuView::OnCircle()
{
    // TODO: Code für Befehlsbehandlungsroutine hier einfügen
    // Falls Linienobjekt aktiv war
    if (m_eObject == LINE)
    {
        // Popup-Menu fuer Linienstaerke entfernen
        CMenu *pMenuBar = GetParent()->GetMenu();
        pMenuBar->RemoveMenu(4,MF_BYPOSITION);
        GetParent()->DrawMenuBar();
    }
    m_eObject = CIRCLE;
}
void CMenuView::OnRect()
{
    // TODO: Code für Befehlsbehandlungsroutine hier einfügen
    // Falls Linienobjekt aktiv war
    if (m_eObject == LINE)
    {
        // Popup-Menu fuer Linienstaerke entfernen
        CMenu *pMenuBar = GetParent()->GetMenu();
        pMenuBar->RemoveMenu(4,MF_BYPOSITION);
        GetParent()->DrawMenuBar();
    }
    m_eObject = RECT;
}

Damit sind wir fast fertig. Was jetzt nur noch fehlt, sind die entsprechenden Nachrichtenbearbeiter für die neuen Menüeinträge. Fügen Sie, wie am Anfang der Lektion beschrieben, die Nachrichtenbearbeiter über den Klassen-Assistenten hinzu und erweitern Sie die neuen Methoden wie nachfolgenden angegeben:

void CMenuView::On1pix()
{
    // TODO: Code für Befehlsbehandlungsroutine hier einfügen
    m_nLThickness = 1;
}
void CMenuView::On2pix()
{
    // TODO: Code für Befehlsbehandlungsroutine hier einfügen
    m_nLThickness = 2;
}
void CMenuView::On4pix()
{
    // TODO: Code für Befehlsbehandlungsroutine hier einfügen
    m_nLThickness = 4;
}

Das war's! Übersetzen und starten Sie das Programm. Wenn das Linienobjekt aktiv ist wird nun zusätzlich das neu erstellte Popup-Menü zum Menü hinzugefügt

Zum Schluss werden wir dem Beispiel noch den 'letzten Schliff' geben. Das Popup-Menü zum Einstellen der Linienstärke soll nun auch als Kontext-Menü angezeigt werden wenn die rechte Maustaste gedrückt wird und das Linienobjekt aktiv ist. Zur Darstellung eines Kontext-Menüs stellt die Klasse CMenu die Methode TrackPopupMenu(...) bereit. Die Parameter zur Methode entnehmen Sie bitte der Online-Hilfe zur gleichnamigen API-Funktion. Die Online-Hilfe zur CMenu-Methode ist nicht ganz auf dem aktuellen Stand. Beachten Sie bitte bei der Angabe der Menüposition beim Aufruf der Methode, dass diese in Bildschirmkoordinaten angegeben werden muss!

So, nun dürfen Sie wieder üben. Versuchen Sie nun einmal das vorher angelegt Menü für die Auswahl der Stiftdicke als Kontextmenü darzustellen. Das CMenu-Objekt für das Kontextmenü ist das vorhin angelegte Objekt m_CLineMenu.

Kontextmenüs werden immer dann eingeblendet, wenn die rechte Maustaste gedrückt wird. Beachten Sie dabei aber bitte, dass das Kontextmenü auch nur dann dargestellt werden soll, wenn als aktuelles Objekt das Linienobjekt eingestellt ist. Sie brauchen für die Lösung der Aufgabe 'nur' eine kleine Methode zum bisherigen Beispiel hinzuzufügen.

Lösung zur Ausgabe des Kontextmenüs

Das Kontextmenü wird, wie soll's auch anders sein, innerhalb der Methode OnRButtonDown(...) dargestellt.

void CMenuView::OnRButtonDown(UINT nFlags, CPoint point)
{
    // TODO: Code für die Behandlungsroutine für Nachrichten hier einfügen....
    // Falls nicht Linienobjekt aktiv ist, Peep!
    if (m_eObject != LINE)
        MessageBeep(-1);
    else
    {
        // Client-Koordinaten in Screen-Koordinaten umrechnen
        ClientToScreen(&point);
        // Kontext-Menue ausgeben
        m_CLineMenu.TrackPopupMenu(TPM_LEFTALIGN|TPM_LEFTBUTTON,
                                   point.x,point.y,this,NULL);
    }
}

Das wohl einzige größere Problem bei dieser Übung dürfte wohl die Positionierung des Kontextmenüs gewesen sein. In der obigen Lösung wir das Kontextmenü an der Position dargestellt, an der Sie die Maustaste gedrückt haben. Da die Methode TrackPopupMenu(...) die Position aber in Bildschirmkoordinaten benötigt, wird die Mauskoordinate (immer relativ zur Client-Area des Fensters) mittels ClientToScreen(...) entsprechend umgerechnet.

Ende der Lösung

Damit ist das Beispiel fertig. Sie finden das komplette Beispiel auch im Verzeichnis 07Ressourcen\Menu.

Bei den Tipps&Tricks zu den Ressourcen können Sie sich u.a. noch ansehen, wie Menüeinträge mit Icons versehen werden können.

Sehen wir uns zum Schluss noch an, was es mit der Nachrichten UPDATE_COMMAND_UI auf sich hat. Nun, bevor ein Menü dargestellt wird, ruft das Rahmenprogramm der MFC für jeden Menüpunkt dessen OnUpdateXxxx(...) Methode auf. Sie haben in dieser Methode dann u.a. die Möglichkeit, Menüpunkte explizit zu sperren. Wie so etwas vonstatten geht, können Sie sich im folgenden Listing noch ansehen:

Im Beispiel wollen wir die erneute Auswahl der Farbe blau nur dann zulassen, wenn das Linienobjekt aktiv ist. Dazu ist für die ID ID_BLUE der Nachrichtenbearbeiter UPDATE_COMMAND_UI mit Hilfe des Klassen-Assistenten zu erstellen. Modifizieren Sie die Methode wie folgt:
void CMenuView::OnUpdateBlue(CCmdUI* pCmdUI)
{
    // TODO: Code für die Befehlsbehandlungsroutine zum Aktualisieren ....
    pCmdUI->Enable(m_eObject==LINE);
}

Der Menüeintrag wird jetzt nur noch dann freigegeben, wenn das Linienobjekt aktiv ist.

So, in der nächsten Lektion geht's dann weiter mit der nächsten Ressource, den Schnellzugriffstasten.



Copyright © 2004

Senden Sie Emails mit Fragen oder Kommentaren zu dieser Website an: mailto:info@cpp-tutor.de
 Wolfgang Schröder, Lerchenweg 23, D-72805 Lichtenstein. Tel: +49 7129 6470