Heute lernen Sie, wie man mit Visual C++ MDI-Anwendungen erstellt. MDI steht für Multiple Document Interface, zu deutsch Mehrfachdokumentschnittstelle. Damit lassen sich Anwendungen erstellen, bei denen der Benutzer an mehreren Dokumenten gleichzeitig arbeiten und zwischen den Fenstern der Anwendung umschalten kann. In diesem Kapitel erfahren Sie, ...
In bezug auf die Codierung mit Visual C++ gibt es kaum einen Unterschied zwischen SDI- und MDI-Anwendungen. Erst wenn man tiefer in die beiden Anwendungsstile einsteigt, treten die Unterschiede zutage. Eine SDI-Anwendung erlaubt dem Benutzer lediglich, an einem Dokument zu einem bestimmten Zeitpunkt zu arbeiten und läßt dabei nur einen einzigen Dokumenttyp zu. MDI-Anwendungen erlauben dagegen dem Benutzer nicht nur, an mehreren Dokumenten gleichzeitig zu arbeiten, sondern bieten auch die Möglichkeit, mehrere Arten von Dokumenten zu behandeln.
Eine MDI-Anwendung ist in einer Art Fenster-in-Fenster aufgebaut, wobei ein Rahmenfenster ein oder mehrere untergeordnete Fenster umschließt. Diesen Anwendungsstil findet man in vielen bekannten Softwarepaketen wie zum Beispiel Word und Excel.
Die Architektur einer MDI-Anwendung entspricht weitgehend der einer SDI-Anwendung. Praktisch besteht der einzige Unterschied bei einer einfachen MDI-Anwendung nur darin, daß der Anwendungs-Assistent neben den üblichen Klassen noch eine zweite Rahmenklasse erzeugt, wie es Abbildung 11.1 zeigt. Der Abbildung können Sie auch entnehmen, daß die Herangehensweise bei der Entwicklung von MDI-Anwendungen der für SDI-Anwendungen sehr ähnlich ist.
Abbildung 11.1:
Die Dokument/Ansicht-Architektur einer MDI-Anwendung
Für eine MDI-Anwendung erstellen Sie lediglich eine Klasse mehr als bei einer SDI- Anwendung. Zu diesen Klassen gehören:
CWinApp
abgeleitete Klasse
CMDIFrameWnd
abgeleitete Klasse
CMDIChildWnd
abgeleitete Klasse
CDocument
abgeleitete Klasse
CView
abgeleitete Klasse
Die beiden MDI-spezifischen Klassen CMDIFrameWnd
(die Klasse CMainFrame
in Ihrem
Projekt) und CMDIChildWnd
(die Klasse CChildFrame
in Ihrem Projekt) sind die beiden
einzigen Klassen, die sich von den bisher erstellten SDI-Anwendungen unterscheiden.
Die von CMDIFrameWnd
abgeleitete Klasse CMainFrame
stellt den Hauptrahmen der Anwendung
dar und liefert einen abgeschlossenen Bereich auf dem Desktop, in dem die
gesamte Interaktion der Anwendung stattfindet. Mit dem Rahmenfenster sind das
Menü und die Symbolleisten verbunden.
Die von CMDIChildWnd
abgeleitete Klasse CChildFrame
bildet den Rahmen, der die
CView
-Klassen aufnimmt. Dieser Rahmen leitet die Nachrichten und Ereignisse an die
Ansichtsklasse zur Verarbeitung oder Anzeige weiter.
Gewissermaßen ist die Funktionalität der Rahmenklasse einer SDI-Anwendung in die beiden MDI-spezifischen Klassen einer MDI-Anwendung aufgeteilt worden. Gleichzeitig gibt es eine zusätzliche Unterstützung für die Ausführung mehrerer untergeordneter Rahmen in deren eigenen Dokument/Ansicht-Klasseninstanzen.
Damit Sie sich selbst davon überzeugen können, wie ähnlich sich die Dokument/Ansicht-Architekturen von SDI- und MDI-Anwendungen sind, erstellen Sie heute die gleiche Zeichenanwendung wie gestern - nur eben als MDI-Anwendung.
Das Gerüst für die heutige Beispielanwendung erstellen Sie in folgenden Schritten:
1. Legen Sie mit dem Anwendungs-Assistenten ein neues Projekt namens Tag11
an.
2. Im ersten Dialogfeld des Anwendungs-Assistenten wählen Sie die Option Mehrere Dokumente (MDI), wie es Abbildung 11.2 zeigt.
Abbildung 11.2:
Eine MDI-Anwendung festlegen
3. Übernehmen Sie die Voreinstellungen im zweiten Schritt des Assistenten.
4. Im dritten Dialogfeld des Anwendungs-Assistenten schalten Sie die Unterstützung für ActiveX-Steuerelemente aus.
5. Im vierten Dialogfeld lassen Sie die Voreinstellungen unverändert und klicken auf die Schaltfläche Weitere Optionen.
6. Im Dialogfeld Weitere Optionen geben Sie eine aus drei Buchstaben bestehende
Erweiterung für die Dateien ein, die Ihre Anwendung generiert (beispielsweise dhc
oder dvp
). Klicken Sie auf die Schaltfläche Schliessen, um das Dialogfeld zu
schließen, und dann auf Weiter, um zum nächsten Schritt des Anwendungs-Assistenten
zu gelangen.
7. Übernehmen Sie die Standardeinstellungen im fünften Schritt des Assistenten.
8. Im sechsten und letzten Dialogfeld des Anwendungs-Assistenten übernehmen Sie
CView
als Basisklasse und klicken auf Fertigstellen. Der Anwendungs-Assistent
generiert nun das Gerüst der Anwendung.
Da Sie die gleiche Anwendung wie gestern, allerdings jetzt als MDI-Version, erstellen,
nehmen Sie auch die gleiche Funktionalität in die Anwendung auf, die Sie bereits aus
der vergangenen Lektion kennen. Um Zeit zu sparen und noch einmal die Ähnlichkeit
der beiden Anwendungsarchitekturen zu betonen, führen Sie die gleichen Schritte wie
gestern aus, um die Klasse CLine
zu erstellen und die Funktionalität - diesmal in die
Klassen CTag11Doc
und CTag11View
- hinzuzufügen. Bauen Sie auch die Unterstützung
für die Auswahl der Farben und der Stiftbreite (wie in der Übung zu Tag 10) in
die Klassen CTag11Doc
und CLine
ein, nehmen Sie aber noch keine Behandlungsfunktionen
für Menüereignisse auf, und lassen Sie auch noch das Farbmenü außen vor. Bei
diesem Entwicklungsstand haben Sie nun eine Anwendung vor sich, bei der Sie mehrere
Zeichnungen öffnen können, wobei aber nur die Zeichenfarbe Schwarz zur Verfügung
steht.
Nachdem nun die gesamte Funktionalität in die Anwendung eingebunden ist, möchten
Sie sicherlich das Farbmenü hinzufügen, damit sich alle Farben für die Zeichnungen
verwenden lassen. Wenn Sie den Baum auf der Registerkarte Ressourcen erweitern
und in den Ordner Menu
sehen, finden Sie nicht nur ein, sondern zwei definierte
Menüs. In welches nehmen Sie nun das Farbmenü auf?
Das Menü IDR_MAINFRAME
ist verfügbar, wenn keine untergeordneten Fenster geöffnet
sind. Wenn Sie Ihre Anwendung ausführen und alle untergeordneten Fenster schließen,
wechselt die Menüleiste, und es verschwinden alle Menüs, die sich auf untergeordnete
Fenster beziehen. Sobald Sie ein anderes Dokument - entweder ein neues
oder ein vorhandenes - öffnen, ändert sich die Menüleiste erneut und zeigt alle Menüs
an, die für die Dokumente maßgeblich sind.
Das Menü IDR_TAG11TYPE
erscheint, wenn ein untergeordnetes Fenster geöffnet ist.
Dieses Menü enthält alle Funktionen, die sich auf Dokumente beziehen. Demzufolge
fügen Sie das Farbmenü in dieses Menü ein. Dabei gehen Sie genauso vor, wie am
gestrigen Tag, und verwenden auch die gleichen Menüeigenschaften.
Für das Menü Farbe müssen Sie jetzt eine andere Zugriffstaste festlegen,
da der - gestern verwendete - Buchstabe |
Nachdem Sie alle Menübefehle hinzugefügt haben, sind noch die Behandlungsroutinen für die Menüereignisse zu realisieren. Heute implementieren wir die Behandlungsroutinen nach einem anderen Ansatz als gestern. Der Abschnitt »Fragen und Antworten« am Ende des gestrigen Kapitels hat die Frage nach einer einzigen Behandlungsroutine für alle Befehle des Farbmenüs aufgeworfen. Heute setzen Sie diesen Gedanken in die Tat um. Leider versteht der Klassen-Assistent nicht, wie sich mehrere Menünachrichten an dieselbe Funktion weiterleiten lassen, so daß Sie das in eigener Regie nach den folgenden Schritten realisieren müssen:
1. Öffnen Sie die Header-Datei Tag11Doc.h
.
2. Gehen Sie in der Header-Datei nach unten, bis Sie zum geschützten (protected
)
Abschnitt mit der Deklaration der Nachrichtenzuordnungstabelle AFX_MSG
gelangen
(suchen Sie nach //{{AFX_MSG(CTag11Doc)
).
3. Fügen Sie die Funktionsdeklarationen aus Listing 11.1 vor der gesuchten Zeile
hinzu. (Der Suchstring kennzeichnet den Beginn der vom Klassen-Assistenten verwalteten
Nachrichtenzuordnungstabelle. Alles, was Sie zwischen diese Markierung
und die Endemarkierung //}}AFX_MSG
schreiben, betrachtet der Klassen-Assistent
als sein »Eigentum« und wird es möglicherweise entfernen oder verstümmeln.)
Listing 11.1: Die Deklarationen der Behandlungsroutine in Tag11Doc.h
.
.
.
1: #ifdef _DEBUG
2: virtual void AssertValid() const;
3: virtual void Dump(CDumpContext& dc) const;
4: #endif
5:
6: protected:
7:
8: // Generierte Message-Map-Funktionen
9: protected:
10: afx_msg void OnColorCommand(UINT nID);
11: afx_msg void OnUpdateColorUI(CCmdUI* pCmdUI);
12: //{{AFX_MSG(CTag11Doc)
13: // HINWEIS - An dieser Stelle werden Member-Funktionen vom Klassen-
ÂAssistenten eingefügt und entfernt.
14: // Innerhalb dieser generierten Quelltextabschnitte NICHTS
ÂVERÄNDERN!
15: //}}AFX_MSG
16: DECLARE_MESSAGE_MAP()
17: private:
18: UINT m_nColor;
19: CObArray m_oaLines;
20: };
4. Öffnen Sie die Quellcodedatei Tag11Doc.cpp
.
5. Suchen Sie nach der Zeile BEGIN_MESSAGE_MAP
, und fügen Sie unmittelbar danach
die Zeilen aus Listing 11.2 ein. Es ist wichtig, daß dieser Code zwischen der Zeile
BEGIN_MESSAGE_MAP
und der Zeile //{{AFX_MSG_MAP
steht. Wenn Sie diese Befehle
zwischen die Zeilen //{{AFX_MSG_MAP
und //}}AFX_MSG_MAP
schreiben, entfernt
oder verstümmelt sie der Klassen-Assistent.
Listing 11.2: Die Einträge der Nachrichtenzuordnungstabelle in Tag11Doc.cpp
1: /////////////////////////////////////////////////////////////////////////////
2: // CTag11Doc
3:
4: IMPLEMENT_DYNCREATE(CTag11Doc, CDocument)
5:
6: BEGIN_MESSAGE_MAP(CTag11Doc, CDocument)
7: ON_COMMAND_RANGE(ID_COLOR_BLACK, ID_COLOR_WHITE, OnColorCommand)
8: ON_UPDATE_COMMAND_UI_RANGE(ID_COLOR_BLACK, ID_COLOR_WHITE,
ÂOnUpdateColorUI)
9: //{{AFX_MSG_MAP(CTag11Doc)
10: // HINWEIS - Hier werden Mapping-Makros vom Klassen-Assistenten
Âeingefügt und entfernt.
11: // Innerhalb dieser generierten Quelltextabschnitte NICHTS
ÂVERÄNDERN!
12: //}}AFX_MSG_MAP
13: END_MESSAGE_MAP()
14:
15: const COLORREF CTag11Doc::m_crColors[8] = {
16: RGB( 0, 0, 0), // Schwarz
17: RGB( 0, 0, 255), // Blau
18: .
19: .
20: .
6. Gehen Sie ans Ende der Datei, und fügen Sie die beiden Behandlungsroutinen gemäß Listing 11.3 hinzu.
Listing 11.3: Die Behandlungsroutinen für das Farbmenü
1: void CTag11Doc::OnColorCommand(UINT nID)
2: {
3: // Aktuelle Farbe setzen
4: m_nColor = nID - ID_COLOR_BLACK;
5: }
6:
7: void CTag11Doc::OnUpdateColorUI(CCmdUI* pCmdUI)
8: {
9: // Prüfen, ob Menübefehl mit Kontrollhäkchen zu versehen ist
10: pCmdUI->SetCheck(GetColor() == pCmdUI->m_nID ? 1 : 0);
11: }
In Listing 11.1 sind die beiden hinzugefügten Funktionsdeklarationen als Behandlungsroutinen
für Nachrichten durch die Deklaration des Funktionstyps afx_msg
spezifiziert.
Diese Art der Funktionsdeklarationen muß mit geschütztem Zugriff versehen
sein. Ansonsten sind diese Funktionen praktisch identisch mit jeder anderen Funktionsdeklaration
von Klassenelementen.
In Listing 11.2 handelt es sich bei den beiden Einträgen ON_COMMAND_RANGE
und
ON_UPDATE_COMMAND_UI_RANGE
in der Nachrichtenzuordnungstabelle um Standardeinträge,
die aber der Klassen-Assistent weder unterstützt noch versteht. Wenn Sie sich
die Einträge der Nachrichtenzuordnungstabelle in den Anwendungen der vergangenen
Tage ansehen, finden Sie die Einträge ON_COMMAND
und ON_UPDATE_COMMAND_UI
. Diese
Makros haben zwei Argumente, die Nachrichten-ID und der Name der Behandlungsfunktion,
die für die Nachricht aufzurufen ist. Die neuen Einträge in der Nachrichtenzuordnungstabelle
funktionieren genauso, weisen aber zwei Argumente für Nachrichten-IDs
statt nur ein Argument auf. Die beiden ID-Argumente markieren Anfang und
Ende eines Bereichs von Nachrichten-IDs, der an die spezifizierte Funktion zu übergeben
ist. Die beiden Nachrichten-IDs beziehen sich auf den ersten und letzten Menübefehl,
die Sie im Farbmenü erstellt haben.
Wenn Sie den Eintrag ON_COMMAND_RANGE
der Nachrichtenzuordnungstabelle verwenden,
wird die Nachrichten-ID automatisch als Argument an die Behandlungsfunktion
übergeben. Dadurch ist es möglich, die Funktion entsprechend Listing 11.3 zu erstellen,
um die Nachrichten der Farbauswahl zu behandeln. Wenn Sie die Anwendung
zum jetzigen Zeitpunkt kompilieren und ausführen, sollte die Farbauswahl genau wie
in der gestrigen Anwendung funktionieren (siehe Abbildung 11.3).
Abbildung 11.3:
Die MDI-Anwendung ausführen
In den meisten Windows-Anwendungen kann man mit der rechten Maustaste klicken, und es erscheint ein sogenanntes Kontextmenü oder Popup-Menü. In Lektion 6 haben Sie bereits ein einfaches Kontextmenü erstellt. Allerdings gibt es einen bestimmten Mechanismus für das Erstellen und Verwenden dieser Kontextmenüs, wenn Windows annimmt, daß das Menü geöffnet werden sollte. Nach diesem Verfahren können Sie Kontextmenüs hinzufügen, die sich genau wie bei jeder anderen Windows-Anwendung verhalten (und wenn Microsoft den Auslösemechanismus von Kontextmenüs bei einer neueren Version von Windows ändert, verhält sich Ihre Anwendung weiterhin dem Windows-Standard entsprechend).
Windows schreibt in die Ereigniswarteschlange die Nachricht WM_CONTEXTMENU
, wenn
der Benutzer die rechte Maustaste losläßt oder die Taste für das Kontextmenü (bei
neueren Windows-Tastaturen) betätigt. Wenn Sie eine Behandlungsroutine für die
Nachricht WM_CONTEXTMENU
vorsehen, können Sie ein Kontextmenü anzeigen und darauf
vertrauen, daß es genau zum richtigen Zeitpunkt erscheint.
Um die Anwendung mit einem Kontextmenü auszustatten, erstellen Sie ein neues Menü, das als Kontextmenü dient. Führen Sie dazu die folgenden Schritte aus:
1. Klicken Sie im Arbeitsbereich auf der Registerkarte Ressourcen mit der rechten
Maustaste auf den Ordner Menu
.
2. Wählen Sie den Befehl Menu einfügen aus dem Kontextmenü.
3. Markieren Sie (immer noch im Arbeitsbereich) das neue Menü, öffnen Sie das zugehörige
Eigenschaftsdialogfeld, und nennen Sie das Menü IDR_CONTEXTMENU
.
4. Im Menü-Editor geben Sie als Titel für die Hauptmenüebene ein einzelnes Leerzeichen an. Das bewirkt, daß Visual C++ den ersten Menübefehl in den Dropdown- Bereich des Menüs hinzufügt.
5. Legen Sie für den ersten Menübefehl den Titel Brei&te
fest, und schalten Sie das
Kontrollkästchen Popup ein. (Daraufhin wird das Kombinationsfeld ID deaktiviert,
der Titel des gerade modifizierten Menübefehls zeigt einen Pfeil, und rechts daneben
erscheint ein weiterer Menübefehl.)
6. Fügen Sie zu diesem Zeitpunkt noch keine Menübefehle in das überlappende Menü Breite hinzu (das heben wir uns für eine Übung am Ende des Kapitels auf). Markieren Sie statt dessen den Menübefehl unterhalb des Befehls Breite, und öffnen Sie dessen Eigenschaftsdialogfeld. Legen Sie die Beschriftung mit Fa&rben fest, und schalten Sie das Kontrollkästchen Popup ein.
7. Nehmen Sie in das überlappende Farbmenü die Menübefehle für die Farben auf,
wie Sie es bereits für das Menü IDR_TAG11TYPE
vorgenommen haben. Verwenden
Sie auch die gleichen Eigenschaftseinstellungen. Wenn Sie die ID lieber suchen,
statt per Hand einzutippen, können Sie sie auch aus der Dropdown-Liste ID auswählen.
Wenn Sie damit fertig sind, sollte das Menü wie in Abbildung 11.4 aussehen.
Abbildung 11.4:
Der Entwurf des Kontextmenüs
8. Gehen Sie im Arbeitsbereich auf die Registerkarte Klassen.
9. Markieren Sie die Klasse CTag11View
. Öffnen Sie den Klassen-Assistenten über
Ansicht / Klassen-Assistent.
10. Fügen Sie eine Funktion für die Nachricht WM_CONTEXTMENU
in die Klasse
CTag11View
ein.
11. Schreiben Sie in die Funktion den Code aus Listing 11.4.
Listing 11.4: Die Funktion OnContextMenu der Klasse CTag11View
1: void CTag11View::OnContextMenu(CWnd* pWnd, CPoint point)
2: {
3: // TODO: Code für die Behandlungsroutine für Nachrichten hier einfügen
4:
5: ///////////////////////
6: // EIGENER CODE, ANFANG
7: ///////////////////////
8:
9: CMenu menu;
10:
11: // Kontextmenü laden
12: menu.LoadMenu(IDR_CONTEXTMENU);
13: // Erstes Untermenü (das eigentliche Menü) holen
14: CMenu *pContextMenu = menu.GetSubMenu(0);
15:
16: // Kontextmenü anzeigen
17: pContextMenu->TrackPopupMenu(TPM_LEFTALIGN |
18: TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
19: point.x, point.y, AfxGetMainWnd());
20:
21: ///////////////////////
22: // EIGENER CODE, ENDE
23: ///////////////////////
24: }
Dieser Code sollte Ihnen aus Lektion 6 bekannt vorkommen. Wenn Sie die Anwendung jetzt kompilieren und ausführen, können Sie mit der rechten Maustaste auf das untergeordnete Fenster klicken und eine Zeichenfarbe aus dem daraufhin geöffneten Kontextmenü auswählen, wie es Abbildung 11.5 zeigt.
Abbildung 11.5:
Die Zeichenfarben über das Kontextmenü ändern
Das war nicht schlecht, oder? Nach dem anstrengenden gestrigen Tag haben Sie sich sicherlich eine kleine Verschnaufpause mit der heutigen Lektion verdient. Außerdem konnten Sie viele Dinge aus der gestrigen Lektion vertiefen. Es ist aber auch einiges Neues hinzugekommen. Sie haben gelernt, was MDI-Anwendungen sind und wie sie sich von SDI-Anwendungen unterscheiden. Es wurde gezeigt, wie man eine Reihe von Menübefehlen mit einer einzigen Behandlungsroutine bearbeitet. Weiterhin haben Sie gelernt, wie man ein Menü erstellt, das speziell als Kontextmenü vorgesehen ist, und wie man es in eine MDI-Anwendung integriert.
Frage:
Da es sich bei einer MDI-Anwendung grundsätzlich um den gleichen Code handelt
wie bei einer SDI-Anwendung, warum soll ich dann überhaupt eine SDI-Anwendung
erstellen? Warum soll ich nicht einfach nur noch MDI-Anwendungen schreiben?
Antwort:
Das hängt von der konkreten Anwendung und deren vorgesehenem Einsatz
ab. Wahrscheinlich werden beide Anwendungstypen in Ihrer Programmiertätigkeit
zur Routine gehören. Wenn Sie ein Memo schreiben oder an einem Tabellenblatt
arbeiten, werden Sie eine MDI-Anwendung bevorzugen. Wollen
Sie durch das World Wide Web browsen, wird Ihr Webbrowser wahrscheinlich
eine SDI-Anwendung sein. Ein einfacher Texteditor wie der Windows-Editor
wäre als MDI-Anwendung für den Benutzer sicherlich unnötig kompliziert.
Für die vorgesehenen Aufgaben ist eine SDI-Anwendung besser geeignet. Bei
bestimmten Anwendungen ist es von vornherein sinnvoller, sie als SDI- und
nicht als MDI-Anwendung zu konzipieren. Stellen Sie sich einfach den möglichen
Einsatzbereich der geplanten Anwendung vor, und entscheiden Sie sich
dann für das besser geeignete Modell.
Frage:
Einige Befehle in meinem Farbmenü führen zur Auswahl der falschen Farbe. Wie
kann ich das Problem ermitteln?
Antwort:
Die Ursache des Problems liegt wahrscheinlich darin, daß die Menü-IDs des
Farbmenüs nicht in der richtigen Reihenfolge aufeinanderfolgen. Das können
Sie nachprüfen, indem Sie mit der rechten Maustaste im Arbeitsbereich auf
der Registerkarte Ressourcen auf Tag11 Ressourcen klicken. Wählen Sie aus
dem Kontextmenü den Befehl Ressourcensymbole, um eine Liste der IDs mit
den zugeordneten Nummern in alphabetischer Ordnung anzuzeigen. Beginnen
Sie mit der ID für Schwarz, und stellen Sie sicher, daß die Nummern mit
einem Abstand von 1 aufeinanderfolgen, ohne Nummern zu überspringen.
Überprüfen Sie, ob diese IDs in der Reihenfolge stehen, wie sie im Farbmenü
(und in der Farbtabelle in der Datei Tag11Doc.cpp) eingetragen sind und
nicht in alphabetischer Reihenfolge, wie sie in der Liste erscheinen. Wenn Sie
auf Fehler stoßen sollen, müssen Sie Visual C++ schließen und die Datei Resource.h
in einem Texteditor öffnen, um die IDs in der richtigen Weise neu zu
numerieren. Nachdem Sie diese Korrekturen vorgenommen haben (achten Sie
darauf, Duplikate zu löschen), speichern Sie sie, starten Visual C++ erneut
und kompilieren die Anwendung. Das Farbmenü sollte nun ordnungsgemäß
funktionieren.
1. Welche fünf Basisklassen kommen in MDI-Anwendungen zum Einsatz?
2. Warum müssen Sie den Eintrag ON_COMMAND_RANGE
außerhalb des Abschnitts der
vom Klassen-Assistenten verwalteten Nachrichtenzuordnungstabelle unterbringen?
3. Welches Argument übergibt ON_COMMAND_RANGE
an die Behandlungsfunktion?
4. Welche Nachricht sollten Sie verwenden, um ein Kontextmenü anzuzeigen?
Fügen Sie die Pulldown- und Kontextmenüs für die Breite hinzu. Verwenden Sie die gleichen Stiftbreiten wie in der gestrigen Lektion.