Buttons
und Checkboxen
Nach dem Sie in der vorherigen Lektion die Grundlagen des Dialogs kennen gelernt
haben, werden wir uns in den folgenden Lektionen dieses Kapitels mit den Controls
(Steuerelemente) beschäftigen. Außerdem erfahren Sie nun, wie der Datenaustausch
zwischen der Anwendung und einem Dialog vonstatten geht.
Beginnen werden wir mit den einfachsten Controls, den Buttons. Folgender Dialog
wird im Verlaufe dieser Lektion entwickelt:

Checkboxen (links im Dialog dargestellt) dienen zur Einstellung von boolschen
Parametern. Die Anwendung initialisiert vor dem Anzeigen des Dialogs die mit den
Checkboxen verbundenen Variablen und kann dann nach dem Schließen des Dialogs die
neuen Einstellungen abfragen. Wie das alles geschieht, das erfahren Sie gleich.
Radiobuttons (rechts im Dialog dargestellt) hingegen werden zur Auswahl einer
Möglichkeit aus mehreren eingesetzt (1 aus n Auswahl). Auch diese Auswahl muss vor
dem Anzeigen des Dialogs initialisiert werden. Nach dem Schließen des Dialogs kann
die Anwendung dann die aktuelle Auswahl abfragen.
Das letzte, in dieser Lektion besprochene, Control ist der Button. Buttons veranlassen
in der Regel sofortige Aktionen, die entweder im Dialog selbst oder aber in der
Anwendung verarbeitet werden.
Die ebenfalls im Dialog vorhandenen Gruppenfelder sind im eigentliche Sinne keine
Controls, sondern dienen nur zur optischen Hervorhebung von Gruppen im Dialog.
Im obigen Dialog werden wir im Verlaufe dieser Lektion zwei 'nette Kleinigkeiten'
einbauen. Zum einen werden die Radiobuttons zur Farbauswahl über die Checkbox
Syntax-Coloring gesteuert. Ist das Syntax-Coloring aktiv, kann eine der
drei Farben ausgewählt werden. Wird das Syntax-Coloring dagegen gesperrt,
so werden auch die Radiobuttons gesperrt, d.h. es ist dann keine Farbauswahl mehr
möglich. Die zweite Besonderheit betrifft den Button Übernehmen. Wird dieser Button
anklickt, so werden die gerade aktuellen Einstellung an die Anwendung übermittelt,
die diese dann sofort auswerten kann ohne dass der Dialog geschlossen werden muss.
Aber fangen wir nun erst einmal mit der Erstellung des Dialogs an.
 |
Erstellen Sie wieder ein neues SDI-Projekt und geben Sie diesem
den Namen Buttons. Wechseln Sie anschließend zur Ressourcen-Ansicht und
fügen Sie, wie in der letzten Lektion besprochen, einen neuen Dialog hinzu.
Ändern Sie jetzt durch Ziehen am Dialograhmen zunächst die Größe des Dialogs,
bis dieser etwa 210x150 Einheiten groß ist. Die aktuelle Größe wird unten rechts
in der Statuszeile angezeigt. Im nächsten Schritt werden wir die beiden Buttons
Ok und Abbrechen an den unteren Dialogrand verschieben. Dazu
markieren Sie bitte die beiden Buttons und wählen dann den Menüeintrag Layout-Schaltflächen
anordnen-Unten aus. Danach sollte Dialog folgendes Aussehen besitzen:

Anschließend definieren wir die dem Dialog zugeordnete Klasse.
Führen Sie dazu einen Doppelklick auf den Dialog aus und wählen Sie bei dem
daraufhin eingeblendeten Dialog die Option Neue Klasse erstellen aus.
Geben Sie der Dialogklasse den Namen CButtonDlg.

Um den Dialog nachher in der Anwendung anzuzeigen, fügen Sie
dem Menü ein neues Popup-Menü Dialog mit dem Menüeintrag Anzeigen
hinzu. Geben Sie dem Menüeintrag die ID ID_SHOWDLG.

Zum Schluss der Vorbereitungen muss noch die entsprechende Methode
für die Bearbeitung des Menüeintrags definiert werden. Starten Sie den Klassen-Assistenten
und stellen Sie als Klassenname die Ansichtsklasse CButtonsView ein.
Suchen Sie anschließend im Feld Objekt-IDs die ID ID_SHOWDLG und führen
Sie dann einen Doppelklick auf den Eintrag COMMAND im Feld Nachrichten
aus.

Übernehmen Sie den daraufhin vorgeschlagenen Namen OnShowdlg
für die Methode zur Bearbeitung des Menüeintrags. Vom Klassen-Assistenten wird
nun die Methode OnShowdlg(...) zum Ansichtsobjekt hinzugefügt, die Sie wie folgt
noch erweitern um den Dialog modal anzuzeigen:
void CButtonsView::OnShowdlg()
{
// TODO: Code für Befehlsbehandlungsroutine hier einfügen
CButtonDlg
CMyDlg;
int
nRetValue;
// Dialog anzeigen
nRetValue = CMyDlg.DoModal();
// Returncode auswerten
if (nRetValue == IDOK)
{
TRACE("Dialogdaten übernommen\n");
}
else
TRACE("Dialog abgebrochen");
} |
Binden Sie nun noch die Header-Datei CButtonDlg.h ein und übersetzen
Sie dann das Projekt. Bei Auswahl des Menüeintrags Dialog-Anzeigen
sollte dann der neu erstellte Dialog angezeigt werden.
|
So, nun können dem Dialog die Controls hinzufügen werden. Beginnen werden wir
mit den Checkboxen. WINDOWS kennt vier Arten von Checkboxen: die 2-State Checkbox,
die den Zustand markiert und nicht-markiert annehmen kann und die 3-State Checkbox,
die zusätzlich noch den Zustand gesperrt besitzt. Außerdem kann jede dieser beiden
Checkbox-Arten noch als Auto-Checkbox definiert werden oder nicht. Bei Auto-Checkboxen
wird der Zustandswechsel (markiert, nicht markiert) von WINDOWS vorgenommen und
die Anwendung erhält eine Nachricht nach dem der Zustandswechsel durchgeführt
wurde. Bei Nicht-Auto-Checkboxen ist die Anwendung für den Zustandswechsel verantwortlich.
Daher erhält die Anwendung in diesem Fall nur eine Nachricht zugesandt, die den
angeforderten Zustandswechsel signalisiert. Die Checkbox selbst ist zu diesem Zeitpunkt
noch nicht verändert. Wir werden im Beispiel gleich beide Fälle einbauen.
Fügen wir jetzt die vier Checkboxen und das sie umschließende Gruppenfeld zum
Dialog hinzu.
 |
Zum Einfügen von Gruppenfeldern und Checkboxen werden folgende
Symbole innerhalb des Toolbars verwendet:

Fügen Sie nun vier Checkboxen in den Dialog ein, in dem Sie im Toolbar zunächst
das Checkbox-Symbol anklicken. Fahren Sie dann mit der Maus an die Stelle im
Dialog, an der die Checkbox platziert werden soll. Drücken Sie die linke Maustaste,
ziehen dann den Rahmen bei gedrückter Maustaste auf die Größe, die die Checkbox
erhalten soll, und lassen dann die Maustaste wieder los. Sie können die Position
und Größe eines Controls selbstverständlich zu jedem Zeitpunkt korrigieren,
in dem Sie es anklicken und dann an den Markern im Rahmen ziehen. Fügen Sie
nun die Checkboxen wie unten angegeben in den Dialog ein. Die endgültige Größe
und Position der Checkboxen werden wir nachher gleich noch festlegen.

Als nächstes werden die Eigenschaften der Checkboxen festgelegt. Dazu klicken
Sie zunächst die oberste Checkbox mit der rechten Maustaste an und wählen aus
dem dann eingeblendeten Kontextmenü den Eintrag Eigenschaften aus.
Folgender Dialog wird dann eingeblendet:

Wie Sie sehen, hat auch jede Checkbox wieder eine ID und einen Titel (Beschriftung).
IDs von Steuerelementen sollten laut ungeschriebenem Gesetz immer mit dem Präfix
IDC_xxx beginnen. Geben Sie den vier Checkboxen folgende IDs und Titel; die
anderen Eigenschaften übernehmen Sie bitte noch unverändert.

Und jetzt kommt die Kosmetik an die Reihe. Zuerst bringen wir alle Checkboxen
einmal auf die gleiche Größe. Dazu wird zunächst die Checkbox mit dem längsten
Titel ausgewählt, in unserem Beispiel also die Checkbox IDC_TABSTOP. Verkleinern/vergrößern
Sie die Checkbox nun so, dass der Titel darin gerade Platz findet. Anschließend
markieren Sie alle Checkboxen in dem Sie sie nacheinander bei gedrückter SHIFT-Taste
anklicken, wobei das zuletzt markierte Steuerelement als Referenzelement dient.
Sollen also alle Checkboxen die gleiche Größe wie die IDC_TABSTOP Checkbox besitzen,
so ist diese Checkbox als letztes zu markieren. Wenn Sie alle Checkboxen markiert
haben, wählen Sie den Menüeintrag Layout-Gleiche Größe-Beides aus und
alle markierten Checkboxen erhalten die gleiche Größe.
Als nächstes korrigieren wir die vertikalen Abstände der Checkboxen. Positionieren
Sie dazu die obere Checkbox IDC_HBAR so, dass deren Y-Koordinate ungefähr 30
Einheit beträgt. Die Position des markierten Elements können Sie unten rechts
neben der Größenangabe in der Statuszeile ablesen. Als nächstes legen Sie die
Y-Position der untere Checkbox IDC_SYNCOLOR auf etwa 75 Einheiten. Die X-Position
der Checkboxen brauchen Sie dabei nicht zu beachten. Anschließend markieren
Sie wieder alle Checkboxen und wählen dann den Menüeintrag Layout-Gleichmäßig
verteilen-Abwärts aus. Die Checkboxen werden jetzt zwischen der oberen
und unteren Checkbox vertikal gleichmäßig verteilt.
Nun richten wir die Checkboxen vertikal so aus, dass deren linke Kanten untereinander
liegen. Markieren Sie dazu wieder alle Checkboxen Auch hier dient das zuletzt
markierte Steuerelement wieder als Referenzelement. Wenn Sie alle Checkboxen
markiert haben, wählen Sie den Menüeintrag Layout-Ausrichten-Links
aus und die Checkboxen werden entsprechend neu platziert.
Zum Schluss umrahmen wir die Checkboxen noch mit einem Gruppenfeld. Klicken
Sie dazu das entsprechende Symbol im Toolbar an und setzen den Mauszeiger dann
an die Position, die der linken oberen Ecke des Gruppenfeldes entspricht. Drücken
Sie nun die linke Maustaste und ziehen Sie den Rahmen um die Checkboxen herum
auf, wobei Sie unterhalb der Checkboxen noch etwas Platz lassen sollten um später
einen Button einfügen zu können. Wenn der Rahmen die gewünschte Größe hat, lassen
Sie die Maustaste wieder los. Um den Text des Gruppenfeldes zu ändern, rufen
Sie über die rechte Maustaste dessen Eigenschaftsdialog auf und geben als Titel
den Text Edit-Optionen ein. Die vorgeschlagene ID IDC_STATIC können
Sie unverändert übernehmen, da wir während der Programmlaufes das Gruppenfeld
nicht verändern wollen.
Nun sollte der Dialog etwa folgendes Layout besitzen:

Alle bis jetzt eingefügten Checkboxen sind 2-State Auto-Checkboxen, d.h.
sie können nur die beiden Zustände markiert/nicht-markiert annehmen und der
Zustandswechsel wird von WINDOWS übernommen. Damit aber noch 'etwas Leben' ins
Beispiel kommt, wollen wir den Zustandswechsel der Checkbox IDC_SYNCOLOR selbst
in die Hand nehmen. Dazu ist zunächst der Stil Auto-Checkbox zu entfernen.
Öffnen Sie Eigenschaftsdialog der Checkbox IDC_SYNCOLOR und wählen dort den
Tabulator Formate aus. Entfernen Sie die Markierung bei Auto.

 |
Um eine 3-State Checkbox zu erstellen, markieren Sie die Option Tri-State
im Eigenschaftsdialog. Diese Option wird im Beispiel aber nicht verwendet. |
Übersetzen und starten Sie das Programm jetzt wieder.
|
Wenn Sie das Programm gestartet haben, so haben Sie sicher bemerkt, dass lediglich
die Auto-Checkboxen ihren Zustand wechseln. Bei der 'normalen' Checkbox Syntax
Coloring muss der Zustandswechsel durch die Anwendung erfolgen. Bauen wir diese
Funktionalität nun ein. Um eine Checkbox auf einen bestimmten Zustand zu setzen,
wird die CButton-Methode SetCheck(...) aufgerufen. Sie erhält als
Parameter den zu setzenden Zustand laut nachfolgender Tabelle:
|
Wert
|
Zustand
|
| 0 |
Checkbox nicht markiert |
| 1 |
Checkbox markiert |
| 2 |
Checkbox gesperrt |
Beachten Sie bitte, dass der Wert '2' nur für 3-State Checkboxen erlaubt ist.
Genauso wie Sie den Zustand einer Checkbox setzen können, können Sie auch deren
aktuellen Zustand auslesen. Rufen Sie dazu die CButton-Methode GetCheck(...)
auf. Als Returnwert liefert die Methode je nach Zustand einen der oben angegebenen
Werte.
Erweitern wir das Beispiel jetzt um die Steuerung der Zustandswechsel für die
'normale' Checkbox. Wie schon angesprochen, versenden Checkboxen beim Anklicken
Nachrichten. Diese Nachrichten sind vom Typ WM_COMMAND mit dem Benachrichtigungscode
BN_CLICKED. Sehen wir uns jetzt an, wie Sie für diese Nachricht mit dem Klassen-Assistenten
einen Nachrichtenbearbeiter definieren.
 |
Öffnen Sie zunächst den Klassen-Assistenten. Stellen Sie dann
im Feld Klassenname die Dialogklasse CButtonDlg ein, da der Nachrichtenbearbeiter
für die Checkbox innerhalb der Dialogklasse platziert werden muss. Anschließend
suchen in der Liste Objekt-IDs die ID IDC_SYNCOLOR und klicken diese
an. Im Feld Nachrichten wird daraufhin u.a. die Nachricht BN_CLICKED aufgelistet.

Führen Sie einen Doppelklick auf diese Nachricht aus und übernehmen Sie den
vom Klassen-Assistenten vorgeschlagenen Namen OnSyncolor für den Nachrichtenbearbeiter.
|
Damit wäre zunächst der Nachrichtenbearbeiter definiert. Um in der Anwendung
den Zustandswechsel der Checkbox durchführen zu können, muss zuerst der aktuelle
Zustand ausgelesen und abhängig davon (und eventl. weiteren Bedingungen) der neue
Zustand gesetzt werden. Die beiden hierfür notwendigen Methoden GetCheck(...)
und SetCheck(...) der Klasse CButton haben Sie ja bereits kennen gelernt.
Doch damit bleibt eine wichtige Frage noch offen: wie erhält man zu einem Control
das dazugehörige C++ Objekt um das Control beeinflussen zu können? Die Lösung der
Problems naht in Form der CWnd Methode GetDlgItem(...). Als Parameter
erhält die Methode die ID, für die ein entsprechendes C++ Objekt erstellt werden
soll. Und als Returnwert liefert die Methode einen CWnd Zeiger auf das mit
dem Control verbunden Objekt. Da alle Controls von CWnd abgeleitet sind,
erhalten Sie einen Basisklassen auf das Control, den Sie dann in der Regel noch
in den 'richtigen' Datentyp konvertieren müssen. In unserem Fall also in einen
CButton Zeiger.
 |
So, jetzt dürfen Sie wieder rann! Versuchen Sie einmal die
Methode OnSyncolor(...) so zu vervollständigen, dass die Checkbox bei
jedem Anklicken ihren Zustand wechselt.
 |
Aber wenn Sie wollen kann ich Ihnen auch meine Lösung anzeigen. |
Lösung zur Steuerung einer Nicht-Auto-Checkbox
void CButtonDlg::OnSyncolor()
{
// TODO: Code für die Behandlungsroutine der Steuerelement-....
// CButton Zeiger
auf Dialogobjekt holen
CButton *pCBtn = static_cast<CButton*>(GetDlgItem(IDC_SYNCOLOR));
ASSERT(pCBtn);
// Falls Checkbox nicht
markiert
if (pCBtn->GetCheck()
== 0)
{
// Checkbox markieren
pCBtn->SetCheck(1);
}
else
{
// Checkbox nicht markieren
pCBtn->SetCheck(0);
}
} |
Dürfte eigentlich nicht besonders schwierig gewesen sein, oder? Sollten
Sie bei der IF-Abfrage keine geschweiften Klammern für die einzelnen Zweige
stehen haben, so fügen Sie diese nun ein. Wir werden später die Methode
noch etwas erweitern.
Ende der Lösung
|
Übersetzen und starten Sie das Programm nun erneut. Die Checkbox IDC_SYNCOLOR
sollte dann, dank Ihrer eingefügten Routine, gleich funktionieren wie die Auto-Checkboxen.
|
Damit hätten wir die Checkboxen fast fertig behandelt. Was jetzt nur noch fehlt
ist der Datenaustausch zwischen der Anwendung und den Checkboxen. Während Buttons
in der Regel nur direkte Aktionen auslösen, so dienen die meisten der anderen Controls
hauptsächlich dazu, irgend welche Einstellungen bzw. Vorgaben vom Anwender abzufragen
und dann in der Anwendung zu verarbeiten. Hierzu muss zum einen die Möglichkeit
bestehen, die Controls mit definierten Werten zu initialisieren und zum anderen
beim Schließen des Dialogs, natürlich nur wenn der OK-Button angeklickt wurde, auch
die aktuell eingestellten Werte in der Anwendung wieder auszulesen. Die MFC verwendet
für diesen Datenaustausch den DDX- (dialog data exchange) und DDV-(dialog data validation)
Mechanismus. Dieser Mechanismus verwendet für den Datenaustausch zwischen der Anwendung
und dem Dialog public Membervariablen in der Dialogklasse. Vor der Darstellung
des Dialogs werden die den einzelnen Controls zugeordneten Membervariablen einfach
auf ihren Initialwert gesetzt. Die Controls selbst werden dann über den DDX-Mechanismus
entsprechend dieser Vorgabe dargestellt. Nach dem Schließen des Dialogs können die
public Membervariablen wieder ausgelesen werden um die aktuellen Einstellungen
abzufragen. Um diesen Datenaustausch zu implementieren wird wiederum der Klassen-Assistent
eingesetzt. Bauen wird jetzt den DDX-Mechanismus für die Checkboxen in unser Beispiel
ein.
 |
Öffnen Sie den Klassen-Assistent und wählen dort den Tabulator
Membervariablen aus. Falls noch nicht automatisch erfolgt, wählen Sie
in der Listbox Klassenname die Klasse CButtonsDlg aus. Die
anschließend zu definierende Membervariable wird damit der Dialogklasse hinzugefügt.

Im Feld Steuerelement-IDs sehen Sie die IDs, die Sie Ihren Controls
zugewiesen haben. Um jetzt für die Checkbox IDC_HBAR eine DDX-Variable zu definieren,
klicken Sie zunächst die ID IDC_HBAR an und anschließend den Button Variable
hinzufügen.... Daraufhin wird folgender Dialog geöffnet:

Über diesen Dialog legen Sie zunächst den Namen der DDX-Variablen fest. Geben
Sie hier bitte den im Bild angegebenen Namen m_bHBar ein. Im darunter
stehenden Feld Kategorie können Sie noch spezifizieren, ob die Membervariable
zum Datenaustausch verwendet wird (Einstellung: Wert) oder ob ein dem Control
entsprechendes C++ Objekt (Einstellung: Control) erstellt werden soll. Mit dem
letzteren Fall werden wir uns in der nächsten Lektion befassen. Das letzte Feld
Variablentyp legt den Datentyp der Membervariable fest. In unserem
Fall ist hier nur der Typ BOOL einstellbar, da eine 2-State Checkbox Variable
nur die Zustände markiert oder nicht-markiert annehmen kann. Klicken Sie den
OK-Button an und es wird der Dialogklasse die public Membervariable hinzugefügt.
 |
Wenn Sie eine Membervariable für eine 3-State Checkbox hinzufügen, so
wird anstelle des Variablentyps BOOL der Typ int verwendet, da hier zusätzlich
der Zustand gesperrt noch auftreten kann. |
Fügen Sie jetzt auf die gleiche Art und Weise Membervariablen für die restlichen
Checkboxen laut nachfolgender Tabelle hinzu:

|
Hiermit haben wir der Dialogklasse die public Membervariablen für die
Checkboxen zugefügt. Um nun eine Voreinstellung für die Checkboxen vorzunehmen,
müssen die Membervariablen vor dem Darstellen des Dialogs nur noch entsprechend
gesetzt werden. Wird der Dialog dann geschlossen, so enthalten die Membervariablen
den aktuellen Zustand der Checkboxen beim Verlassen des Dialogs. So einfach geht's.
Bauen wir dies nun auch gleich ins Beispiel ein.
 |
Da die aktuellen Einstellungen auch dann noch gültig sein sollen,
wenn der Dialog nicht mehr angezeigt wird, müssen Sie in der Regel innerhalb
der Anwendung ebenfalls entsprechende Membervariablen bereitstellen um die aktuellen
Einstellung für die Anwendung abzuspeichern. Fügen Sie daher zur Klasse des
Ansichtsobjektes die Membervariablen m_bHBar, m_bVBar, m_bTabStops
und m_bSynColor, alle vom Typ BOOL, hinzu. Die Namen der Membervariablen
wurden hier gleich gewählt wie die innerhalb des Dialogs. Sie könnten, wenn
Sie wollen, aber auch ganz andere Namen verwenden. Sie müssen dann nur immer
die richtige Zuordnung der Variablen beachten. Diese neu hinzugefügten Membervariablen
werden im Konstruktor des Ansichtsobjektes initialisiert. Die hier angegebenen
Werte erscheinen dann nachher beim ersten Anzeigen des Dialogs als Vorgaben.
Erweitern Sie den Konstruktor der Ansichtsklasse wie folgt:
CButtonsView::CButtonsView()
{
// ZU ERLEDIGEN: Hier Code zur Konstruktion einfügen,
m_bHBar = m_bVBar =
TRUE;
m_bTabStops = m_bSynColor = FALSE;
} |
Anschließend müssen in der Methode OnShowdlg(...) vor dem Anzeigen
des Dialogs die public Membervariablen des Dialogs mit den Membervariablen
der Anwendung initialisiert werden. Wurde der Dialog mit OK geschlossen,
so muss der umgekehrte Vorgang erfolgen, d.h. die Werte der Membervariablen
des Dialogs werden in die Membervariablen der Anwendung umkopiert.
Versuchen Sie jetzt wieder einmal selbst, den entsprechenden Datenaustausch
in der Methode OnShowdlg(...) des Ansichtsobjektes zu implementieren.
Beachten Sie dabei aber, dass die aktuellen Dialogdaten nur dann übernommen
werden dürfen, wenn der Dialog mit dem OK Button geschlossen wurde.
 |
Bevor Sie verzweifeln kann ich Ihnen auch meine Lösung anzeigen. |
Lösung zum Datenaustausch mit dem Dialog
void CButtonsView::OnShowdlg()
{
// TODO: Code für Befehlsbehandlungsroutine hier einfügen
CButtonDlg CMyDlg;
int nRetValue;
// Dialog-Controls
initialisieren
CMyDlg.m_bHBar = m_bHBar;
CMyDlg.m_bVBar = m_bVBar;
CMyDlg.m_bTabStops = m_bTabStops;
CMyDlg.m_bSynColor = m_bSynColor;
// Dialog anzeigen
nRetValue = CMyDlg.DoModal();
// Returncode auswerten
if (nRetValue == IDOK)
{
TRACE("Dialogdaten übernommen\n");
// Daten aus Dialog uebernehmen
m_bHBar = CMyDlg.m_bHBar;
m_bVBar = CMyDlg.m_bVBar;
m_bTabStops = CMyDlg.m_bTabStops;
m_bSynColor = CMyDlg.m_bSynColor;
}
else
TRACE("Dialog abgebrochen");
} |
Dürfte eigentlich nicht besonders schwierig gewesen sein, oder?
Ende der Lösung
|
Übersetzen und starten Sie das Programm nun.
|
Die Checkboxen im Dialog sollten nun entsprechend den Vorgaben anzeigt werden.
Wenn Sie diese Einstellungen verändern und den Dialog mit OK schließen und dann
wieder öffnen, so werden die zuletzt vorgenommen Einstellungen angezeigt. Beim Abbruch
des Dialogs sollten alle Einstellung erhalten bleiben.
Verlassen wir damit vorerst die Checkboxen und wenden uns den Radiobuttons zu.
Während Checkboxen in der Regel dazu dienen, eine beliebige Anzahl von Optionen
gleichzeitig auszuwählen, werden Radiobuttons immer dann eingesetzt, wenn von einer
Anzahl von Optionen immer nur eine gültig sein kann. Da ein Dialog verschiedene
logisch zusammengehörige Radiobuttons besitzen kann, ist beim Anlegen der Radiobuttons
einiges zu beachten.
Aber gehen wir auch hier wieder Schritt für Schritt vor. Fügen wir unserem Beispiel
jetzt zuerst ein paar Radiobuttons hinzu.
 |
Um einem Dialog einen Radiobutton hinzuzufügen, ist das unten
markierte Symbol im Toolbar des Dialogeditors anzuklicken und danach der Radiobutton
entsprechend zu positionieren.

Legen Sie nun rechts im Dialog (siehe auch Bild am Anfang der Lektion) drei
Radiobuttons mit den angegebenen Beschriftungen und IDs an. Fügen Sie dann noch
ein Textfeld unterhalb der Radiobuttons hinzu und umrahmen Sie die neuen Controls
mit einem Gruppenfeld. Beachten Sie bitte, dass auch das Textfeld hier eine
eindeutige ID erhält da wir nachher dessen Text per Programm verändern werden.

|
Durch das Gruppenfeld wird optisch hervorgehoben, welche Radiobuttons logisch
zusammengehören. Doch woher weiß WINDOWS wenn mehrere Radiobutton-Gruppen im Dialog
vorhanden sind, welche Radiobuttons logisch zusammengehören und somit sich gegenseitig
ausschließen. Dazu müssen wir uns zunächst mit einer neuen Eigenschaft der Dialoge
befassen, der Tabulator-Reihenfolge. Bestimmt ist Ihnen auch schon einmal aufgefallen,
dass Sie in einem Dialog mit der TAB-Taste von einem Control zum anderen springen
können. Die Reihenfolge, in der die Controls dabei durchlaufen werden, wird beim
Entwurf des Dialogs festgelegt.
 |
Wählen Sie nun einmal den Menüeintrag Layout-Tabulator
Reihenfolge aus. Die Anzeige des Dialogs wird sich daraufhin wie folgt
ändern:

Sollten bei Ihnen anderen Zahlen an den einzelnen Controls stehen so macht
dies im Augenblick nichts aus. Wir werden nachher gleich noch die notwendigen
Anpassungen vornehmen.
|
Die Zahlen an den Controls geben die Reihenfolge an, in der die Controls durchlaufen
werden wenn die TAB-Taste gedrückt wird und das Control die Eigenschaft
Tabstopp besitzt. Um für ein Control die Eigenschaft Tabstopp
zu setzen, markieren im Eigenschaftsdialog einfach diese Eigenschaft.

 |
Setzen Sie die Eigenschaft Tabstopp auch nur bei den Controls,
die auch tatsächlich Benutzereingaben verarbeiten können. So macht es wenig
Sinn, einen Gruppenfeld oder ein Textfeld mit dieser Eigenschaft auszustatten. |
Um die Tab-Reihenfolge festzulegen, klicken Sie (nachher in der Übung) die Controls
in der Reihenfolge an, in der sie durchlaufen werden sollen. Haben Sie die Reihenfolge
fertig spezifiziert, klicken Sie einfach mit der Maus neben den Dialog und die Dialogdarstellung
erfolgt wieder in der gewohnten Weise. Wollen Sie eine bereits bestehende Reihenfolge
nicht komplett neu aufbauen sondern nur leicht abändern, so können Sie auch dies
tun. Halten Sie dazu die SHIFT-Taste gedrückt während Sie das Control anklicken,
von dem ab Sie die Reihenfolge ändern wollen. Der Dialog-Editor nimmt dann die aktuelle
Position in der Reihenfolge als Ausgangspunkt. Wollten Sie also z.B. im obigen Bild
die Tab-Reihenfolge 5 und 6 vertauschen, so klicken Sie zuerst bei gedrückter SHIFT-Taste
die Position 4 an und dann nacheinander die Controls Syntax Coloring und
Tabs verwenden.
Doch die Tabulator-Reihenfolge alleine reicht noch nicht aus um verschiedene
Radiobuttons in logischen Gruppen zusammenzufassen. Dies wird erst erst dadurch
erreicht, dass der erste Radiobutton in der Gruppe, das ist der mit der niedrigsten
Tab-Position, zusätzlich zur Tabstopp Eigenschaft noch die Eigenschaft
Gruppe erhält. Hiermit wird der erste Button in einer Gruppe festgelegt.
Alle weiteren Radiobuttons der Gruppe müssen unmittelbar aufeinanderfolgende Tab-Positionen
besitzen. Um die Buttongruppe abzuschließen muss dass nächste, auf den letzten Button
in der Gruppe folgende Control ebenfalls die Eigenschaft Gruppe erhalten.
 |
Gruppieren wir nun unsere Radiobuttons. Legen Sie dazu zunächst
wie oben dargestellt die Tabulator-Reihenfolge fest, in dem Sie im Menü
Layout den Menüpunkt Tabulator-Reihenfolge auswählen. Klicken
Sie dann die Controls in der angegebenen Reihenfolge an. Fügen dann noch den
Controls mit den Tab-Positionen 1-6 und 8 die Eigenschaft Tabstopp
hinzu.
Als nächstes müssen die Radiobuttons zu einer logischen Gruppen zusammenfassen.
Setzen Sie zunächst beim Button IDC_RED (Tab-Position 8) die Eigenschaft
Gruppe. Damit haben wir den Beginn der Gruppe festgelegt. Die Buttons an
der Tab-Position 9 und 10 sollen nun zu dieser Gruppe hinzugefügt werden. Um
die Gruppe abzuschließen, muss dem Control an der nachfolgenden Position, also
der Position 11, ebenfalls die Eigenschaft Gruppe hinzugefügt werden.
Und damit ist die Gruppe fertig definiert. Ganz einfach, wenn man's weiß.
Sie können nun Ihren Dialog testen, in dem Sie im Menü Layout den
Menüpunkt Testen auswählen. Der Dialog wird daraufhin dargestellt und
Sie könnten nun z.B. auch die Tab-Reihenfolge verfolgen. Wenn Sie einen der
Radiobuttons in der Gruppe anklicken, so sollte der andere nicht mehr markiert
sein.
|
Nachdem die Buttongruppe festgelegt ist, kann es an die Implementierung der Button-Funktionen
gehen.
 |
Die nachfolgenden Schritte sind nur dann notwendig, wenn Sie
innerhalb eines Dialog unmittelbar auf die Auswahl eines Radiobuttons reagieren
müssen. Für den Datenaustausch der Anwendung mit dem Dialog sind keine Button-Funktionen
notwendig. Wie der Datenaustausch bei Radiobuttons funktioniert wird nachher
gleich beschrieben. |
Radiobuttons senden, genauso wie die bereits behandelten Checkboxen, beim Anklicken
eine BN_CLICKED Nachricht an den übergeordneten Dialog. Über den Klassen-Assistent
könnten jetzt die entsprechenden Nachrichtenbearbeiter für die einzelnen Radiobuttons
zur Dialogklasse hinzugefügt werden. Wir werden aber eine neue Variante für die
Nachrichtenbearbeitung uns ansehen. Da alle drei Radiobuttons beim Anklicken die
aktuelle Farbauswahl im Textfeld IDC_COLOR ausgeben sollen, werden wir einen
Nachrichtenbearbeiter für alle Radiobuttons definieren. Dazu ist es aber notwendig,
dass die IDs der Radiobuttons unmittelbar aufeinander folgen.
 |
Um den Wert einer Control-ID explizit vorzugeben, öffnen Sie
dessen Eigenschaftsdialog. Im Feld ID geben dann nach der ID ein Gleichheitszeichen
folgt vom nummerischen Wert ein. Das war's auch schon. Im Bild unten besitzt
die ID IDC_RED damit den Wert 2000.

Ändern Sie jetzt die Werte der IDs IDC_RED, IDC_GREEN und IDC_BLUE so ab,
dass diese die Werte 2000, 2001 und 2002 besitzen.
|
Nachdem Sie die IDs geordnet haben, kann's ans Definieren des Nachrichtenbearbeiters
gehen. Um für mehrere IDs einen Nachrichtenbearbeiter festzulegen, wird das Makro
ON_COMMAND_RANGE(...) eingesetzt. Als Parameter erhält das Makro die Start- und
End-ID sowie den Namen des Nachrichtenbearbeiters. Der Nachrichtenbearbeiter selbst
muss vom Typ afx_msg void DoSomething(UINT wID) sein. Im Parameter
wID erhält die Methode die ID des Controls, das die Nachricht ausgelöst hat.
 |
Da der Klassen-Assistent keine Nachrichtenbereiche unterstützt,
muss das Makro ON_COMMAND_RANGE(...) von Hand in die Nachrichtentabelle eingefügt
werden. Erweitern Sie die Nachrichtentabelle wie folgt:
BEGIN_MESSAGE_MAP(CButtonDlg,
CDialog)
//{{AFX_MSG_MAP(CButtonDlg)
ON_BN_CLICKED(IDC_SYNCOLOR, OnSyncolor)
//}}AFX_MSG_MAP
ON_COMMAND_RANGE(IDC_RED,
IDC_BLUE, OnColorChange)
END_MESSAGE_MAP() |
Fügen Sie anschließend der Dialogklasse die Methode OnColorChange(...) wie
folgt hinzu.
void CButtonDlg::OnColorChange(UINT
wID)
{
CStatic *pCText = static_cast<CStatic*>(GetDlgItem(IDC_COLOR));
switch (wID)
{
case IDC_RED:
pCText->SetWindowText("Farbe: rot");
break;
case IDC_GREEN:
pCText->SetWindowText("Farbe: grün");
break;
case IDC_BLUE:
pCText->SetWindowText("Farbe: blau");
break;
}
} |
Übersetzen und starten Sie das Programm nun. Wenn Sie nun einen der Radiobuttons
anklicken, so wird im Textfeld IDC_COLOR die aktuell eingestellte Farbe ausgegeben.
|
Einen kleinen Schönheitsfehler hat unser Dialog noch. Wenn Sie den Dialog neu
anzeigen, so wird keiner der Radiobuttons eine Markierung aufweisen. Dies ist aber
nicht zulässig, da ein Radiobutton innerhalb einer logischen Gruppe immer
selektiert sein muss. Wir müssen nun noch einen Weg finden, die Radiobuttons zu
initialisieren. Dazu wird dem ersten Button der Gruppe über den Klassen-Assistenten
wieder eine entsprechende public Membervariable der Dialogklasse zugeordnet.
Wird diese Membervariable vor dem Aufruf des Dialogs mit dem Wert '0' initialisiert,
so wird der erste Radiobutton der Gruppe markiert. Entsprechend wird der zweite
Radiobutton markiert, wenn der Membervariable der Wert '1' zugewiesen wird, usw..
Beachten Sie bitte, dass Sie nur dem ersten Button innerhalb einer Gruppe eine Variable
zuordnen müssen. Über den DDX-Mechanismus wird dann je nach Wert der entsprechende
Radiobutton in der Gruppe gesetzt.
 |
Initialisieren wir die Radiobuttons so, dass der erste Radiobutton
markiert ist. Weisen Sie, wie nachfolgend angegeben, dem Radiobutton IDC_RED
die Membervariable m_nSynColor zu.

Zum Schluss muss die Membervariable noch vor der Anzeige des Dialogs in der
Methode OnShowdlg(...) des Ansichtsobjektes entsprechend initialisiert
werden. Nachdem der Dialog geschlossen wurde können Sie durch auslesen des Wertes
abprüfen, welcher Radiobutton markiert war. Fügen Sie zur Ansichtsklasse die
Membervariable m_nSynColor hinzu und initialisieren Sie diese im Dialog wie
folgt:
CButtonsView::CButtonsView()
{
// ZU ERLEDIGEN: Hier Code zur Konstruktion einfügen,
m_bHBar = m_bVBar = TRUE;
m_bTabStops = m_bSynColor = FALSE;
m_nSynColor = 0;
} |
Anschließend erweitern Sie den folgenden Code der Methode OnShowdlg(...)
noch.
void CButtonsView::OnShowdlg()
{
// Dialog-Controls initialisieren
....
CMyDlg.m_bSynColor = m_bSynColor;
CMyDlg.m_nSynColor
= m_nSynColor;
// Dialog anzeigen
nRetValue = CMyDlg.DoModal();
// Returncode auswerten
if (nRetValue == IDOK)
{
....
m_bSynColor = CMyDlg.m_bSynColor;
m_nSynColor = CMyDlg.m_nSynColor;
}
else
TRACE("Dialog abgebrochen");
} |
Übersetzen und starten Sie das Beispiel wieder.
|
Sie doch schon recht gut jetzt aus, oder? Bis auf .... Nun, das Textfeld IDC_COLOR
zeigt beim Anzeigen des Dialogs immer noch nicht die aktuell eingestellte Farbe
an sondern erst nachdem Sie einen der Radiobuttons angeklickt haben. Was also hier
noch fehlt ist die Initialisierung des Textfeldes. Wir könnten jetzt zwar auch wieder
für das Textfeld eine Membervariable für den DDX-Mechanismus definieren und diese
dann im Ansichtsobjekt entsprechend setzen. Da jedoch der Inhalt des Textfeldes
eigentlich nur innerhalb für den Dialog von Interesse ist (die Anwendung erhält
die aktuelle Farbe ja über die Einstellung der Radiobuttons mitgeteilt), werden
wir einen anderen Weg gehen. Nachdem ein Dialog erstellt und dessen Controls initialisiert
wurden und bevor er das erstemal dargestellt wird, wird die Methode OnInitDialog(...)
aufgerufen. Diese Methode eignet sich damit hervorragend für Initialisierungsaufgaben.
 |
Versuchen Sie jetzt zuerst einmal selbst die Methode OnInitDialog(...)
zu implementieren. Wie gesagt, die Methode soll die aktuell eingestellte Farbe
im Textfeld IDC_COLOR wie folgt ausgeben: Farbe: xxxx.
 |
Na, haben Sie's geschafft? Wenn nicht, hier meine Lösung.. |
Lösung zur Initialisierung des Textfeldes
BOOL CButtonDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: Zusätzliche Initialisierung hier einfügen
// Textfarbe im
Ausgabefeld setzen
CStatic *pCText = static_cast<CStatic*>(GetDlgItem(IDC_COLOR));
switch (m_nSynColor)
{
case 0:
pCText->SetWindowText("Farbe: rot");
break;
case 1:
pCText->SetWindowText("Farbe: grün");
break;
case 2:
pCText->SetWindowText("Farbe: blau");
break;
}
return TRUE; //
return TRUE unless you set the focus to a control
// EXCEPTION: OCX-Eigenschaftenseiten sollten FALSE zurückgeben
} |
Das Schwierigste an dieser Übung dürfte das Auffinden der Methode OnInitDialog(...)
gewesen sein. Sie finden Sie im Klassen-Assistent unter den WINDOWS Nachrichten.
Der Code der Methode entspricht sonst weitgehend dem der Methode OnColorChange(...).
Ende der Lösung
|
Übersetzen und starten Sie das Beispiel nun und Sie werden einen funktionsfähigen
Dialog haben.
Damit Sie sehen, dass alle Einstellungen im Ansichtsobjekt auch richtig ankommen,
erweitern Sie dessen OnDraw(...) wie folgt:
void CButtonsView::OnDraw(CDC*
pDC)
{
CButtonsDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// ZU ERLEDIGEN: Hier Code zum Zeichnen der ursprünglichen Daten
hinzufügen
static COLORREF AvailColors[3]
= {RGB(255,0,0), RGB(0,255,0), RGB(0,0,255)};
pDC->SetBkMode(TRANSPARENT);
if (m_bSynColor)
{
pDC->SetTextColor(AvailColors[m_nSynColor]);
pDC->TextOut(10,10,"Syntax Coloring ein");
}
else
{
pDC->SetTextColor(RGB(0,0,0));
pDC->TextOut(10,10,"Syntax Coloring aus");
}
if (m_bHBar)
pDC->TextOut(10,30,"Horizontale Leiste
ein");
else
pDC->TextOut(10,30,"Horizontale Leiste
aus");
if (m_bVBar)
pDC->TextOut(10,50,"Vertikale Leiste
ein");
else
pDC->TextOut(10,50,"Vertikale Leiste
aus");
if (m_bTabStops)
pDC->TextOut(10,70,"TabStops ein");
else
pDC->TextOut(10,70,"TabStops aus");
} |
Damit OnDraw(...) auch ausgeführt wird wenn der Dialog geschlossen
wird, müssen Sie in der Methode OnShowdlg(...) noch die Methode Invalidate(...)
aufrufen.
void CButtonsView::OnShowdlg()
{
....
if (nRetValue == IDOK)
{
....
Invalidate();
}
else
TRACE("Dialog abgebrochen");
} |
|
Sie sehen, es ist doch schon recht viel Arbeit notwendig um selbst einen solchen
kleinen Dialog zu implementieren. Aber trösten Sie sich, ohne Unterstützung der
MFC wäre der Aufwand noch um einiges größer!
Aber setzen wir noch eins darauf. Da die Farbauswahl auch nur dann Sinn macht,
wenn das Syntax Coloring freigegeben ist, wollen wir nun die Radiobuttons
in Abhängigkeit vom Zustand der Checkbox IDC_SYNCOLOR freigeben oder sperren. Wie
schon mehrfach erwähnt, senden Checkboxen beim Anklicken BN_CLICKED Nachrichten
an den Dialog. Und für die Checkbox IDC_SYNCOLOR haben wir vorher auch schon den
entsprechenden Nachrichtenbearbeiter OnSyncolor(...) definiert. Was wir jetzt
nur noch tun müssen, ist die Radiobuttons in dieser Methode entsprechend zu sperren
bzw. wieder freizugeben. Um Controls freizugeben und zu sperren wird die CWnd
Methode EnableWindow(...) aufgerufen. Wir können hier die CWnd Methode
verwenden da Controls eigentlich sind anderes sind wie Fenster mit einem vordefinierten
Verhalten.
 |
Versuchen Sie jetzt wieder einmal selbst, die Methode OnSyncolor(...)
entsprechend anzupassen. Beachten Sie dabei bitte, dass auch das Textfeld IDC_COLOR
analog dazu freizugeben bzw. zu sperren ist. Und noch ein Hinweis: Sie können
sich bei der Lösung dieser Aufgabe die Tatsache zu nutze machen, dass die IDs
der Radiobuttons fortlaufend durchnummeriert sind.
 |
Na, haben Sie's geschafft? Wenn nicht, hier meine Lösung.. |
Lösung zum Sperren der Radiobuttons
void CButtonDlg::OnSyncolor()
{
// TODO: Code für die Behandlungsroutine der Steuerelement-....
// CButton Zeiger auf Dialogobjekt holen
CButton *pCBtn = static_cast<CButton*>(GetDlgItem(IDC_SYNCOLOR));
// CStatic Zeiger
auf Textfeld fuer Farbausgabe holen
CStatic *pColorField
= static_cast<CStatic*>(GetDlgItem(IDC_COLOR));
ASSERT(pCBtn);
// Falls Checkbox nicht markiert
if (pCBtn->GetCheck() == 0)
{
// Checkbox markieren
pCBtn->SetCheck(1);
for (int iIndex=IDC_RED; iIndex<=IDC_BLUE; iIndex++)
{
CButton *pBtn
= static_cast<CButton*>(GetDlgItem(iIndex));
pBtn->EnableWindow(TRUE);
}
pColorField->EnableWindow(TRUE);
}
else
{
// Checkbox nicht markieren
pCBtn->SetCheck(0);
for (int iIndex=IDC_RED; iIndex<=IDC_BLUE; iIndex++)
{
CButton *pBtn
= static_cast<CButton*>(GetDlgItem(iIndex));
pBtn->EnableWindow(FALSE);
}
pColorField->EnableWindow(FALSE);
}
} |
Ende der Lösung
|
Übersetzen und starten Sie das Beispiel nun.
|
Was fällt Ihnen dabei auf? Wenn Sie sich den Dialog genau ansehen werden Sie
am Anfang feststellen, dass das Syntax Coloring nicht markiert ist, die Radiobuttons
trotzdem aber freigegeben sind. Was hier wiederum noch fehlt ist die die Initialisierung
der Radiobuttons. Die Methode zum Initialisieren von Controls haben wir bereits
vorhin eingebaut, nämlich die Methode OnInitDialog(...). Diese gilt es jetzt
noch zu erweitern.
 |
Fügen Sie folgenden Code zu OnInitDialog(...) Methode
hinzu, damit die Radiobuttons (und das Textfeld) in Abhängigkeit vom Syntax
Coloring immer richtig dargestellt werden:
BOOL CButtonDlg::OnInitDialog()
{
....
switch (m_nSynColor)
{
....
}
// Falls Syntax Coloring
nicht gesetzt ist
// Farbauswahl sperren!
if (!m_bSynColor)
{
CButton *pSynColor = static_cast<CButton*>(GetDlgItem(IDC_SYNCOLOR));
CStatic *pColorText = static_cast<CStatic*>(GetDlgItem(IDC_COLOR));
for (int iIndex=IDC_RED; iIndex<=IDC_BLUE;
iIndex++)
{
CButton *pBtn
= static_cast<CButton*>(GetDlgItem(iIndex));
pBtn->EnableWindow(FALSE);
}
pColorText->EnableWindow(FALSE);
}
return TRUE; // return
TRUE unless you set the focus to a control
// EXCEPTION: OCX-Eigenschaftenseiten sollten FALSE zurückgeben
} |
Wenn Sie das Beispiel jetzt laufen lassen, sollte alles richtig funktionieren.
|
Und damit könnten wir eigentlich diese Lektion abschließen. Was ich Ihnen aber
noch zeigen möchte ist, wie man eine Art 'Übernehmen-Button' implementieren kann.
D.h. die aktuellen Einstellungen sollen beim Drücken eines Buttons an die Anwendung
übertragen werden ohne dass der Dialog dazu geschlossen werden muss.
 |
Fügen wir zuerst dem Dialog den hierfür einzusetzenden Button
hinzu. Um einem Dialog einen 'normalen' Button hinzuzufügen, klicken Sie im
Toolbar das folgende Symbol an:

Platzieren Sie den Button unterhalb der Checkbox Syntax Coloring (siehe
auch Abbildung am Anfang der Lektion) und weisen dem Button die ID IDC_NOW und
die Beschriftung Übernehmen zu.

Anschließend fügen Sie über den Klassen-Assistent wieder einen
Nachrichtenbearbeiter für den Button zum Dialog hinzu. Übernehmen Sie den vom
Assistenten vorgeschlagenen Namen OnNow(...) für die Methode.
|
Immer wenn der Anwender nun den Button anklickt, wird die neu hinzugefügte Methode
OnNow(...) aufgerufen. Doch wie übertragen wir jetzt die aktuellen Einstellungen
des Dialogs an das Ansichtsobjekt? Nun, da WINDOWS ein nachrichtenbasierendes System
ist werden wir auch hierfür eine Nachricht einsetzen. Wir definieren uns für diesen
Zwecke eine anwenderdefinierte Nachricht. Wenn Sie den Kurs bisher aufmerksam durchgearbeitet
haben werden Sie wissen, dass der Bereich für anwenderdefinierte Nachrichten ab
der Konstanten WM_APP beginnt.
 |
Sie dürfen hier auf keinen Fall den Bereich ab WM_USER verwenden,
da diese Nachrichten nur innerhalb einer Fensterklasse verwendet werden dürfen.
Wir aber versenden Nachrichten zwischen zwei Fensterklassen, der Fensterklasse
des Dialogs und der Fensterklasse des Ansichtsobjekts! |
Damit der Dialog aber seinen Erzeuger und damit den Empfänger dieser Nachricht
auch kennt (das ist das Ansichtsobjekt), geben wir beim Erstellen des Dialogs einen
Zeiger darauf mit.
 |
Fügen Sie zur Dialogklasse zunächst die Membervariable m_pView
vom Typ CWnd-Zeiger hinzu. In dieser Membervariable wird dann innerhalb
des Konstruktor der Zeiger auf das Ansichtsobjekt abgelegt.
CButtonDlg::CButtonDlg(CWnd*
pParent /*=NULL*/)
: CDialog(CButtonDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CButtonDlg)
m_bHBar = FALSE;
m_bVBar = FALSE;
m_bSynColor = FALSE;
m_bTabStops = FALSE;
m_nSynColor = -1;
//}}AFX_DATA_INIT
m_pView = pParent;
} |
Nun muss noch die Erstellung des Dialogs etwas korrigiert werden, da der
Konstruktor des Dialogs jetzt einen Zeiger auf den Empfänger der Nachricht erwartet.
void CButtonsView::OnShowdlg()
{
// TODO: Code für Befehlsbehandlungsroutine hier einfügen
CButtonDlg CMyDlg(this);
int
nRetValue;
....
} |
|
 |
Die im Konstruktor des Dialogs im Block AFX_DATA_INIT enthaltenen
Anweisungen wurden vom Klassen-Assistenten hinzugefügt und dienen zur Initialisierung
des Datentausches mit der Anwendung. |
Da der Dialog nun den Empfänger der Nachricht kennt, kann's ans Versenden der
Nachricht gehen.
 |
Definieren Sie dazu zuerst die Konstante für die anwenderdefinierte
Nachricht in der Header-Datei des Dialogs:
....
// ButtonDlg.h : Header-Datei
//
// Anwenderdefinierte Nachricht zur sofortigen
Datenuebernahme
#define APPLYNOW WM_APP
/////////////////////////////////////////////////////////////
// Dialogfeld CButtonDlg
|
Anschließend kann diese Nachricht im Nachrichtenbearbeiter des Buttons
OnNow(...) versandt werden. Da das Ansichtsobjekt nachher Zugriff auf die
Dialogdaten benötigt um die aktuellen Einstellungen auszulesen, geben wir im
LONG-Parameter der SendMessage(...) Methode einen Zeiger auf das Dialogobjekt
mit.
void CButtonDlg::OnNow()
{
// TODO: Code für die Behandlungsroutine der Steuerelement-....
m_pView->SendMessage(APPLYNOW,0,(LONG)this);
} |
Als nächstes muss diese vom Dialog kommende Nachricht im Ansichtsobjekt verarbeitet
werden. Da der Klassen-Assistent keine anwenderdefinierten Nachrichten unterstützt,
müssen Sie selbst diese Nachricht in die Nachrichtentabelle eintragen. Hierzu
wird das bereits bekannte Makro ON_MESSAGE(...) verwendet.
BEGIN_MESSAGE_MAP(CButtonsView,
CView)
//{{AFX_MSG_MAP(CButtonsView)
ON_COMMAND(ID_SHOWDLG, OnShowdlg)
//}}AFX_MSG_MAP
ON_MESSAGE(APPLYNOW,OnApplyNow)
END_MESSAGE_MAP() |
Hiermit wird die anwenderdefinierte Nachricht APPLYNOW dem, noch zu implementierenden,
Nachrichtenbearbeiter OnApplyNow(...) zugeordnet. Fügen Sie jetzt über
den Klassen-Assistent den Nachrichtenbearbeiter OnApplyNow(...) hinzu
und erweitern dessen Code wie folgt:
LONG CButtonsView::OnApplyNow
(UINT, LONG lParam)
{
CButtonDlg *pDlg = (CButtonDlg*)lParam;
m_bHBar = pDlg->m_bHBar;
m_bVBar = pDlg->m_bVBar;
m_bTabStops = pDlg->m_bTabStops;
m_bSynColor = pDlg->m_bSynColor;
m_nSynColor = pDlg->m_nSynColor;
Invalidate();
return 0L;
} |
Der LONG-Parameter enthält den Zeiger auf das Dialogobjekt, das die Nachricht
versandt hat. Um an die Dialogdaten zu gelangen, muss dieser Zeiger zuerst wieder
in den entsprechenden Dialogzeiger konvertiert werden.
Übersetzen und starten Sie das Programm nun. Was beobachten Sie?
|
Nun, vermutlich wird sich beim Anklicken des Buttons Übernehmen im Dialog im
Ansichtsobjekt überhaupt nichts tun. Die erste Vermutung wird sein, dass die Methode
OnApplyNow(...) des Ansichtsobjektes überhaupt nicht ausgeführt wird. Sie
können jedoch einmal die Funktion MessageBeep(...) in die Methode einbauen
und Sie werden feststellen, dass bei jedem Anklicken des Buttons ein Ton ausgegeben
wird. Also wird OnApplyNow(...) auch aufgerufen. Also ganz so einfach war
die Lösung doch wohl nicht. Bevor Sie nun Stunden mit der Fehlersuche verbringen,
verrate ich Ihnen lieber die Lösung. Die Lösung liegt im Aufruf einer weiteren
CDialog Methode UpdataData(...). Diese Methode ist dafür verantwortlich,
dass zum einen beim Aufbau des Dialogs die DDX-Daten in die Controls übernommen
werden und zum anderen auch dafür, dass die Control-Einstellungen wieder in die
DDX-Daten zurück übertragen werden. Damit UpdateData(...) weiß, in welche
Richtung der Datentransfer statt zu finden hat, erhält die Methode einen entsprechenden
BOOL-Parameter. Standardmäßig wird UpdateData(...) von der bereits bekannten
Methode OnInitDialog(...) aufgerufen wenn die Daten in die Controls transferiert
werden sollen und von der Methode OnOk(...) um den aktuellen Zustand der
Controls in den DDX-Daten abzuspeichern.
Bauen wir den Aufruf dieser Methode zum Abschluss dieser, doch etwas umfangreichen,
Lektion nun ein:
 |
Damit die Einstellung der Controls vor dem Absenden der anwenderdefinierten
Nachricht in den DDX-Daten abgelegt werden, erweitern Sie die OnNow(...)
des Dialogs noch um den erwähnten Aufruf der UpdateData(...) Methode:
void CButtonDlg::OnNow()
{
// TODO: Code für die Behandlungsroutine der Steuerelement-....
UpdateData(TRUE);
m_pView->SendMessage(APPLYNOW,0,(LONG)this);
} |
Damit ist das Beispiel endlich komplett. Sie finden es auch unter
08Dialoge\Buttons.
|
So viel zu den Buttons. Bei den Tipps&Tricks zu diesem Kapitel erfahren Sie dann
noch, wie Sie farbige Buttons oder Buttons mit Grafiken erstellen können.
Und ansonsten geht's weiter mit der nächsten Gruppe der Controls, den Listboxen,
Comboboxen und Editfeldern.
|