Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!
Direct3D11 Settings Dialog - Teil 2 - Tabbed Dialog
Direct3D11 Settings Dialog - Teil 2 - Tabbed Dialog
Teil 1: Link
Teil 2: Den liest du gerade
Teil 3: Link
Inhalt
1. Einleitung
2. Tabbed Dialog - Die Theorie
3. Der Dialog - Resource
4. Die Programmierung des Dialogs
5. Eine Basisklasse für Pages
6. Dialog-Resourcen für Pages
7. Integration der Pages in den SettingsDialog
8. Demoprojekt
9. Ausblick
1. Einleitung
Hi Leute!
Willkommen zum 2. Teil des Tutorials.
Und nochmal, so soll er mal aussehen:
In diesem Teil geht es um den Dialog an sich. Ich erkläre, wie man mit Tabs arbeitet und das ganze soweit abstrahiert, dass man sich nur noch einzelne Klassen für die Pages erstellen muss.
Dazu sei noch gesagt, dass ich nicht auf die Implementierung von Laden und Speichern von Einstellungen eingehe. An entsprechenden Stellen weise ich aber darauf hin, was getan werden sollte.
2. Tabbed Dialog - Die Theorie
Ein "Tabbed Dialog" ist eine "DialogBox" (kennt ihr sicher), die aus mehreren Seiten (sogenannte Pages) besteht. Zwischen diesen Pages kann man umschalten.
Die eigentlichen Tabs sind die Viereckigen "Registerkarten" oben. Diese werden als "Tab Control" zusammengefasst und haben dazu noch unterhalb einen Bereich, der für die Pages genutzt wird.
Hier eine kleines Bildchen dazu:
Um so etwas zu implementieren, gibt es 2 Möglichkeiten:
1.) Die "Property Sheets" benutzen
2.) Selbst implementieren
Ich habe mich für letzteres entschieden.
3. Der Dialog - Resource
Fangen wir an mit dem Design des Dialogs.
Was soll er alles machen?
- Er soll mit einer einzigen Funktion (wird später DoSettingsDialog heißen) aufgerufen werden.
- Er soll die drei Standard-Buttons (OK, Cancel, Apply) enthalten
-- Bei "Cancel" wird abgebrochen
-- Bei "Apply" wird gespeichert, es wird nicht beendet
-- Bei OK wird erst gespeichert und dann beendet
- Er soll Tabs enthalten, mit denen zwischen den Pages gewechselt werden kann.
- Das Speichern soll der Dialog übernehmen, sodass das Programm nur noch die (veränderten) Einstellungen laden muss.
Los gehts: "Resource.h" erstellen.
So sieht sie aus:
C-/C++-Quelltext
1
2
3
4
5
6
7
8
#pragma once
//////////////////////////////////////////////////////////////////SettingsDialog
#define IDD_SETTINGS_DIALOG 100//Der Dialog
#define IDC_TAB_CONTROL 101//Das Tab Control
#define IDC_APPLY 102//Apply-Button
#define IDC_DSMA 103//"Don't show me again" Check-Box
Hier werden die IDs der einzelnen Dialoge und Controls definiert, über die sie dann im Programm angesprochen werden können.
Und nun zur Dialog-Resource (Resource.rc). Am Anfang sieht die Datei so aus:
C-/C++-Quelltext
1
2
3
4
5
6
7
8
9
10
//////////////////////////////////////////////////////////////////Includes
#include<Windows.h>
#include"Resource.h"//////////////////////////////////////////////////////////////////IDC_STATIC definieren, da dies nicht mehr in Windows.h definiert wird
#ifndef IDC_STATIC
#define IDC_STATIC -1
#endif
Hier werden die beiden Dateien "Windows.h" (Vordefinierte Dinge) und "Resource.h" (eigene Definitionen) inkludiert.
Dazu wird für statische Elemente (wie Text) noch IDC_STATIC definiert, da das nicht mehr Windows.h übernimmt.
Jetzt kann der eigentliche Dialog auch schon gebaut werden.
Dazu kann man entweder den Dialog-Wizard (wenn der so heißt) benutzen, oder das selbst schreiben.
Am Ende sollte es aber in etwa so aussehen:
//////////////////////////////////////////////////////////////////SettingsDialog//Größe insgesamt: 220 x 270. Er wird beim Erstellen genau in die Mitte des Bildschirms geschoben.
IDD_SETTINGS_DIALOG DIALOG 0, 0, 220, 270//Mit Titel, Minimierknopf, Menü (per Icon) und allem Schnickschnack.
STYLE WS_POPUP|WS_CAPTION|DS_MODALFRAME|WS_SYSMENU|WS_MINIMIZEBOX
//Meine bevorzugte Schriftart
FONT 8, "Tahoma"//Der Titel (sowie der ganze Dialog) in English gehalten
CAPTION "Settings"
{
//Das Tab Control//Genug Platz für 200x200 große Pages//Dazu noch links, rechts und unten je 5 Einheiten Platz. Oben muss noch die Größe der Tabs mit einberechnet werden (nochmal 10)
CONTROL "", IDC_TAB_CONTROL, "SysTabControl32", TCS_TABS, 5, 5, 210, 220//Wenn man nicht jedesmal alles neu einstellen will...
AUTOCHECKBOX "Don't show me again.", IDC_DSMA, 120,235, 80, 10//Und die 3 Standard-Buttons
PUSHBUTTON "Exit", IDCANCEL, 10, 250, 60, 15//Beenden
PUSHBUTTON "Apply", IDC_APPLY, 80, 250, 60, 15//Speichern
DEFPUSHBUTTON "OK", IDOK, 150,250, 60, 15//Speichern und Beenden
}
Die Kommentare sollten alles soweit erklären.
Ich lasse also Platz für die Pages in der Größe: 200x200. Ich denke, das sollte reichen.
Zu den Pages komme ich später.
4. Die Programmierung des Dialogs
Auch wenn der Dialog noch nicht wirklich etwas kann (außer geschlossen werden) ist es trotzdem wichtig, dass man sich frühzeitig Gedanken macht, wie man den Dialog in der eigentliche Programm einbindet.
Dazu gibt es 2 separate Dateien: SettingsDialog.h und SettingsDialog.cpp. Darin wird alles, was mit dem Dialog zu tun hat, implementiert.
Kommen wir zur Header-Datei. Was muss denn alles rein? Eigentlich bloß die Deklaration von DoSettingsDialog und die DialogProc.
Tada:
C-/C++-Quelltext
1
2
3
4
5
6
7
8
9
#pragma once
//Settings-Dialog aufrufen//Wenn der User auf OK geklickt hat, wird FALSE zurückgegeben.//Wenn er abgebrochen hat, wird TRUE zurückgegeben.
BOOL DoSettingsDialog();
//Settings-Dialog-Proc
BOOL WINAPI SettingsDialogProc(HWND hDialog, UINT uMessage, WPARAM wParam, LPARAM lParam);
Auf den Rückgabewert sollte unbedingt geachtet werden!
Was muss den DoSettingsDialog alles machen?
Wie der Name schon sagt, soll die Funktion den SettingsDialog anzeigen und verwalten.
Die Pages, auf die ich später näher eingehe, werden als Child-Fenster erstellt. Darum reicht ein einfaches DialogBox nicht aus.
Der Dialog wird mit CreateDialog erstellt und die Nachrichten werden ordnungsgemäß bearbeitet.
Anhand der letzem Message (WM_QUIT) wird entweder TRUE oder FALSE zurückgegeben - je nachdem, was der Benutzer eben wollte.
//Settings-Dialog aufrufen
BOOL DoSettingsDialog()
{
//An dieser Stellt müsste geklärt werden, ob der User das letzte mal DSMA//(Don't show me again) gewählt hat und falls ja abbrechen!//Den Dialog Erstellen
HWND hDialog= CreateDialog(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_SETTINGS_DIALOG), NULL, SettingsDialogProc);
if(!hDialog)
throw(std::exception("Creating Settings Dialog failed!"));
//Und Nachrichten bearbeiten
MSG Message;
while(GetMessage(&Message, NULL, 0, 0))
{
if(!IsDialogMessage(NULL, &Message))
{
TranslateMessage(&Message);
DispatchMessage(&Message);
}
}
//Ergebnis auswerten//Bei Abbruch wird TRUE zurückgegeben//Mit OK bestätigt -> FALSE//Immernoch -1 -> Fehler!if(Message.wParam == IDCANCEL) return(TRUE);
elseif(Message.wParam == IDOK) return(FALSE);
elsethrow(std::exception("Settings Dialog did not return a valid status code!"));
}
Kommen wir nun zur SettingsDialogProc.
Ich habe alles kommentiert:
//Settings-Dialog-Proc
BOOL WINAPI SettingsDialogProc(HWND hDialog, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
static HWND hTabControl= NULL; //das Tab-Controlswitch(uMessage)
{
case WM_INITDIALOG:
{
//TabControl initialisieren
InitCommonControls();
hTabControl= GetDlgItem(hDialog, IDC_TAB_CONTROL);
//An dieser Stelle werden später die Tabs eingetragen und die Pages erstellt.//"Don't show me again." initialisieren//Dazu müsste die letzte Wahl geladen werden und das Häkchen entsprechend gesetzt werden. //Dialog in die Mitte rücken und anzeigen
CenterWindow(hDialog);
ShowWindow(hDialog, TRUE);
return(TRUE);
}
//Der Benutzer hat wieder irgendetwas angestellt...case WM_COMMAND:switch(LOWORD(wParam))
{
case IDC_APPLY:
{
//Speichern//Das gilt auch für "Don't show me again." -> Lass dir etwas einfallen ;)//Später werden hier noch alle Pages gespeichert.//Aber nicht beenden!return(TRUE);
}
case IDCANCEL:
{
//Beenden
DestroyWindow(hDialog);
PostQuitMessage(LOWORD(wParam));
//Alle Pages beenden//-> späterreturn(TRUE);
}
case IDOK:
{
//Speichern und beenden
SendMessage(hDialog, WM_COMMAND, IDC_APPLY, 0);
DestroyWindow(hDialog);
PostQuitMessage(LOWORD(wParam));
//Alle Pages beenden//-> später
}
}
default:break;
}
return(FALSE);
}
Beim Erstellen des Dialogs ist euch sicher CenterWindow aufgefallen. Diese Funktion verschiebt ein Fenster so, dass es genau in der Mitte des Bildschirms liegt.
Nichts desto trotz, war das schon ein großer Schritt.
Durch neue Funktionen wie InitCommonControls kamen einige zusätzliche Header und Libs hinzu.
Das wird alles von SettingsDialog.h erledigt. Der Anfang der Datei sieht jetzt so aus:
C-/C++-Quelltext
1
2
3
4
5
6
7
#pragma once
#include<exception>
#include<map>
#include<Windows.h>
#include<Commctrl.h>
#pragma comment(lib, "Comctl32.lib")
#include"Resource.h"
Mit einer std::map werden später die Pages verwaltet.
5. Eine Basisklasse für Pages
Ich habe so oft von Pages geredet und gesagt, was damit alles gemacht werden soll. Jetzt ist es an der Zeit das umzusetzen.
Um das ganze etwas objektorientierter zu gestalten, benutze ich eine Basisklasse für die Pages, von der zukünftig alle speziellen Pages (für Video, Audio, etc.) abgeleitet werden.
Eine Page soll:
- Ihre Daten (Titel für Tab, ihre Resource) angeben
- Auf Commands (WM_COMMAND) reagieren
- Die Erstellung und Zerstörung der Page übernehmen die Init- und Exit-Methode
//String definierentypedef std::string String;
//...////////////////////////////////////////////////////////////////////Basisklasse für Pages im Settings-Dialogclass SettingsPageBase
{
private:
HWND hPageWindow; //Das Dialog-Fensterpublic://////////////////////////////////////////////////////////////////////////////////Konstruktor und Destruktor
SettingsPageBase();
virtual~SettingsPageBase();
//////////////////////////////////////////////////////////////////////////////////Init, Exit und Save//Initialisieren//Diese Methode muss abgeleitet werden!//In der eigenen Init-Methode muss erst SettingsPageBase::Init() aufgerufen werden//und erst dann können eigene Initialisierungs-methoden aufgerufen werden.//Diese Methode initialisiert die Dialog-Box der Page.virtualvoid Init(HWND hDialog);
//Herunterfahren// Diese Methode muss nicht abgeleitet werden, sollte aber.// Nachdem alles eigene heruntergefahren wurde, muss SettingsPageBase::Exit() eufgerufen werden!// Diese Methode zerstört die Page-Dialogbox.// Diese Methode wird automatisch aufgerufen, sobald der User "OK" oder "Exit" gedrückt hat. Bei "OK" wird vorher noch Apply Aufgerufen.virtualvoid Exit();
//Einstellungen übernehmen//Diese Methode muss implementiert werden! Die Basismethode muss nicht aufgerufen werden, da es gar keine gibt.//Sie wird automstisch aufgerufen, sobald der User auf "OK" oder "Apply" gedrückt hat.//Bei "OK" kommt zusätzlich noch hinzu, dass Exit aufgerufen wird.virtualvoid Apply() =0;
//////////////////////////////////////////////////////////////////////////////////Getter//Gibt das Handle des Dialogs (= Page) zurück.
HWND GetPageWindow();
//Gibt den Name der Page zurück.//Muss implementiert werden!virtual String GetPageName() =0;
//Gibt das Modul (Instanz) an, in dem sich die Dialog-Resource befindet.//Muss implementiert werden!virtual HMODULE GetDialogResourceModule() =0;
//Gibt die Resource-ID der Dialog-Resource zurück.//Muss implementiert werden!virtual DWORD GetDialogResourceID() =0;
//////////////////////////////////////////////////////////////////////////////////Event-Handling//Command-Handling//Bei einer WM_COMMAND Nachricht wird diese Methode aufgerufen. Sie muss implementiert werden!//Auf OK, Apply oder Cancel-Messages darf nicht reagiert werden - das übernimmt der Dialog.//Nur auf die Messages der eigenen Controls muss eingegangen werden.//Wenn die Nachricht verarbeitet werden konnte, muss TRUE zurückgegeben werden, ansonsten FALSE.virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam) =0;
};
Zusammengefasst muss eine abgeleitete die folgenden Methoden implemenieren:
- Konstruktor und Destruktor
- Init (Geerbte Methode zu erst aufrufen!)
- Exit (Geerbte Methode zu letzt aufrufen!)
- GetPageName, GetDialogResourceModule, GetDialogResourceID - Damit kann dann die Page (Child-Dialog) und der Tab erstellt werden
- OnCommand um auf die Messages der eigenen Controls zu reagieren
Fangen wir an mit Konstruktor und Destruktor: Die machen das übliche.
Nicht vergessen: Im eigenen Konstruktor muss der Konstruktor von SettingsPageBase aufgerufen werden!
Interessanter wird dann schon die Init-Methode. Sie erstellt den Child-Dialog - die Page.
Um auch die richtige Dialog-Resource zu benutzen (zur Resource komme ich später) gibt es ja die Getter.
Exit schließt die Page.
Am Ende sieht das dann so aus:
Hier taucht die Funktion PageProc auf. Das ist eine einheitliche Dialog-Proc für die Pages.
Sie leitet WM_COMMAND Messages an den Settings-Dialog weiter, dieser kann sie dann verarbeiten und schickt sie zum Schluss zurück an die Page mittels SettingsPageBase::OnCommand.
Als Code formuliert sieht das folgendermaßen aus:
C-/C++-Quelltext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//uniforme Page-Proc
BOOL WINAPI PageProc(HWND hDialog, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
switch(uMessage)
{
case WM_INITDIALOG:return(TRUE);
case WM_COMMAND://Commands weiterleiten an Parent, der leitet es dann an die Page weiter
SendMessage(GetParent(hDialog), WM_COMMAND, wParam, lParam);
return(TRUE);
default:break;
}
return(FALSE);
}
Die Getter sollten klar sein, deshalt gibt es an dieser Stellt keine Posts dazu.
6. Dialog-Resourcen für Pages
Jetzt kommt wieder der Designer-Teil.
Dazu gibt es aber klare Vorgaben, damit es überhaupt funktioniert.
Die Resource für eine Page sollte so aussehen:
C-/C++-Quelltext
1
2
3
4
5
6
7
8
//SettingsPageBase Resource
IDD_PAGE_BASE DIALOG 10, 20, 200, 200//Genaue Position und Größe!
STYLE WS_CHILD //Wird ein Child des Settings-Dialogs
FONT 8, "Tahoma"
{
//Hier kommt der Inhalt der Page rein.//Aufpassen, dass die 200x200 eingehalten werden!
}
Dieses Copy'n'Paste-Snippet sollte man als Grundstein für alle Pages nutzen.
7. Integration der Pages in den SettingsDialog
Der Settings-Dialog lief bereits ganz gut. Allerdings noch ganz ohne Pages.
Das wird jetzt geändert.
Woher kommen denn die Pages?
- Einige sind fest vorgegeben, da sie überaus notwendig sind. Wie zum Beispiel eine "Video"-Page (kommt in Teil 3 ) für 3D-Spiele.
- Eigene spiel-spezifische Pages
Verpackt werden einfach alle in eine Liste:
C-/C++-Quelltext
1
2
//Liste mit SettingsPagestypedef std::list<SettingsPageBase*> PageList;
Solch eine Liste füllt man dann mit eigenen Pages und übergibt sie an den SettingsDialog per Parameter von DoSettingsDialog.
Dieser muss etwas abgewandelt werden.
- Zuerst muss er eine Liste erhalten
- Diese Liste muss an die DialogProc übergeben werden, da sie ja die Pages verwalten soll
Kurz und knapp:
C-/C++-Quelltext
1
2
3
4
5
6
7
8
9
10
11
BOOL DoSettingsDialog(PageList * pPages)
{
//An dieser Stellt müsste geklärt werden, ob der User das letzte mal DSMA//(Don't show me again) gewählt hat und falls ja abbrechen!//Die Pages als zusätzlichen Parameter mit auf den Weg geben
LPARAM InitParam= (LPARAM)(pPages);
//Den Dialog Erstellen
HWND hDialog= CreateDialogParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_SETTINGS_DIALOG), NULL, SettingsDialogProc, InitParam);
...
Ab hier liegt es an der DialogProc. Also ran ans Wert!
Was muss den alles geändert werden?
Neue statische Variablen:
- Ein Container für alle Pages
- Ein Zeiger auf die fokusierte Page
In der Init-Phase (WM_INITDIALOG):
- Die Liste aus dem LPARAM Parameter herausfischen
- Für alle Einträge in der Pages-Liste:
-- Tab erstellen
-- Page erstellen
- Erste Page wählen
In der Exit-Phase (OK- oder Exit-Button gedrückt):
- Bei OK erstmal so tun als ob Apply gedrückt wurde
- Alle Pages durchgehen und alle beenden
Für Apply: Alle Pages durchgehen und Apply aufrufen.
Na dann mal los.
Der vorhin angesprochene Container ist bei mir eine std::map. Darin werden die Pages (SettingsPageBase*) nach einer laufenden Nummer (DWORD) sortiert.
Das mit der Nummer ist ein absolutes Muss, da Windows für die einzelnen Tabs ja auch nur Nummern hat.
C-/C++-Quelltext
1
2
static std::map<DWORD, SettingsPageBase*> Pages; //Pages nach Nummer sortiertstatic SettingsPageBase * pCurrentPage; //Aktuelle Page
Nach dem das Tab-Control initialisiert wurde, werden die einzelnen Tabs und die Pages erstellt.
Jedes Tab erhält eine Nummer (0, 1, 2, 3, 4, ...) und damit kann in der Map die zugehörige Page gefunden werden.
Ein bisschen Code sagt mehr als tausend Worte:
//Pages eintragen
PageList *pPageList= (PageList*)(lParam);
if(pPageList->empty()) throw(std::exception("Page List is empty!"));
TCITEM TabItem; //Grundstruktur für einen Tab
TabItem.mask = TCIF_TEXT; //Nur Text im Tab (es sich auch Icons möglich!)
DWORD dwPage=0; //Laufende Nummer//Alle durchgehenfor(PageList::iterator Iter= pPageList->begin(); Iter != pPageList->end(); Iter++)
{
//Tab-Page-Eintrag erstellen//Zuerst den Name eintragenchar Buffer[256];
strncpy_s(Buffer, (*Iter)->GetPageName().c_str(), (*Iter)->GetPageName().length());
TabItem.pszText= Buffer;
//Und jetzt den Tab erstellen
TabCtrl_InsertItem(hTabControl, dwPage, &TabItem);
//Page initialisieren und eintragen
(*Iter)->Init(hDialog); //Init-Methode aufrufen
Pages[dwPage++]= (*Iter); //Und in der Map verewigen
}
//Erste Page wählen
pCurrentPage= Pages[0];
TabCtrl_SetCurSel(hTabControl, 0);
ShowWindow(pCurrentPage->GetPageWindow(), TRUE);
Die erste Page wird als aktuelle Page gewählt, im TabControl selektiert und sichtbar gemacht.
Wen die TabCtrl-Funktionen interessieren, der sollte mal in der MSDN nachsehen. Da gibt es viele Infos dazu.
Es gibt noch viele weitere nützliche Funktionen rund um Tabs!
An den entsprechenden Stellen (siehe Code) kommen die folgenden Zeilen hinzu, damit auf die Knopfdrücke ordentlich reagiert werden kann:
Für case IDC_APPLY:
Wenn jetzt aber der Spieler ein Control der Page bedient (z.B. CheckBox betätigen) erfährt das der SettingsDialog, aber nicht die Page.
Also werden kurzerhand alle unbekannten WM_COMMANDS an die aktuelle Page weitergeleitet:
Ein wichtiges Problem wäre da schon noch. Die Pages sind initialisiert, werden gespeichert und beendet. Was fehlt denn da noch?
Irgendwie muss doch auch zwischen den Pages umgeschalten werden können!
Man muss irgendwie reagieren können, wenn der Spieler einen Tab gewählt hat.
Dafür gibt es die WM_NOTIFY Nachricht. Wenn der Spieler jetzt einen anderen Tab gewählt hat, dann wird die aktuelle Page unsichtbar gemacht und die neue sichtbar.
Umgesetzt liefert das folgenden Code, der am besten nach WM_COMMAND eingefügt wird:
C-/C++-Quelltext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
case WM_NOTIFY:switch(((LPNMHDR)lParam)->code)
{
case TCN_SELCHANGE://Der User hat einen anderen Tab gewählt
{
//Zuerst alte Page unsichtbar machen
ShowWindow(pCurrentPage->GetPageWindow(), FALSE);
//Und jetzt die gewählte Page sichtbar machen
DWORD dwChoosenPage = (DWORD)TabCtrl_GetCurSel(hTabControl);
pCurrentPage= Pages[dwChoosenPage];
ShowWindow(pCurrentPage->GetPageWindow(), TRUE);
return(TRUE);
}
}
break;
Das war es! Fertig!
Allerdings kann man mit dem Dialog noch recht wenig anfangen, da er keine Pages hat, die er anzeigen kann.
Also demonstriere ich mal, wie man bei einer neuen Page vorgeht.
8. Demoprojekt
Als Demo für den Dialog hab ich mir wirklich etwas ganz kleines einfallen lassen, da es im 3. Teil des Tutorials eine ganze "Video-Page" geben wird.
Die Page beinhaltet nur eine CheckBox und etwas Text.
Die Resourcen:
#include<Windows.h>
#include"SettingsDialog.h"
#include"DemoPage.h"int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, char*, int)
{
try
{
//Demo-Page erstellen
DemoPage DemoPage1("#1", hInstance);
DemoPage DemoPage2("#2", hInstance);
PageList Pages;
Pages.push_back(&DemoPage1);
Pages.push_back(&DemoPage2);
//Dialog startenif(DoSettingsDialog(&Pages))
{
//Abgebrochen!//Normal beenden
MessageBox(NULL, "Abbrechen geklickt", "", MB_OK);
return(0);
}
MessageBox(NULL, "OK geklickt", "", MB_OK);
//Alles klar! Der User ist mit den Einstellungen einverstanden.//Das Programm kann jetzt die (veränderten) Einstellungen laden.
}
catch(std::exception Err)
{
MessageBox(NULL, Err.what(), "Error!", MB_OK|MB_ICONERROR);
}
return(0);
}
Das Demo-Projekt kann hier heruntergeladen werden: Link
9. Ausblick
So, Teil 2 geschafft!
Wer Teil 1 ausgelassen hat, sollte ihn jetzt lesen, bevor er sich an Teil 3 macht.
Ansonsten hoffe ich ein einigermaßen gutes Framework für Tabbed Dialogs geliefert zu haben.
Im dritten Teil des Tutorials geht es dann ganz speziell um eine "Video-Page". Sie nutzt die Hilfsfunktionen aus Teil 1 um anständig alles wählbar zu machen.
Der SettingsDialog kann noch verbessert werden!
- Ihm fehlt noch eine Möglichkeit, wie er "Don't show me again." speichern und laden kann. Im Code wird an entsprechenden Stellen darauf hingewiesen.
- Ein Icon wäre nicht schlecht. PS: Benutzt LoadImage anstatt LoadIcon
Wie immer dürft ihr fleißig fragen und kritisieren.
Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »BlazeX« (25.11.2010, 16:01)