Ich habe mich zwar gerade erst darüber ausgelassen, dass man dialog basierende Anwendungen gar nicht braucht (sieh hier), aber ganz und gar unsinnig sind sie ja nicht.
Bei mir ist der häufigste Anwendungsfall eine simpler Dialog, der nur zum Steuern eines Programmes dient, das ein Icon in der Taskleiste abablegt.
Ärgerlich ist dann nur, dass man nicht vermeiden kann, dass beim Start der Anwendung der Dialog kurz aufflackert, bevor man ihn mit einem Timer oder mit PostMessage und einer benutzerdefinierten Nachricht wieder versteckt.
Aber es geht viel einfacher und dazu noch in einer Form, die der MFC-Konstruktion besser entspricht, als das Gerüst, das der Applikations-Wizard erzeugt.
Ich möchte das hier einfach kurz erläutern:
1. Erzeuge ich eine normale dialog basierende Anwendung mit dem Wizard.
2. Dann lege ich ein Objekt der Dialog Klasse mit dem Namen m_dlg in der CWinApp Klasse an. Ich möchte damit erreichen, dass die Dialogklasse so lange existiert wie die Applikation und nicht mehr nur als lokale Variable in InitInstance auftaucht.
3. Der gesamte Code, den der Wizard erzeugt hat:
CTestDLGDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
}
wird ersetzt durch:
// Create the Dialog
if (m_dlg.Create(CMyDlg::IDD))
{
m_pMainWnd = &m_dlg;
return TRUE;
}
else
return FALSE;
4. Kleine Schönheitskorrektur nun. Man verlagert das Laden des Applikations-Icons aus dem Konstruktor des Dialogs nach OnInitDialog.
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
Fertig ❗
Kurz erklärt: Aus dem modalen Dialog der durch DoModal gestartet wird wird nun ein modaler Dialog, der durch die Messageloop der CWinApp::Run gesteuert wird. Von der sonstigen Funktionsweise ändert sich nichts.
Dieser Konstrukt hält sich nach meiner Meinung weit mehr an das MFC Gerüst, als die Anwendung, die der Wizard erzeugt.
Die Vorteile sind schnell aufgezählt:
- InitInstance wird durchlaufen und CWinApp:Run wird verwendet.
Dies hat z.B. zur Folge, dass auch CWinApp::OnIdle durchlaufen wird und temporäre Map Objekte entsorgt werden. (siehe Kommentar unten) - Man kann die Applikation ohne Flackern zu dem Moment sichtbar machen an dem man es möchte. Dazu muss man nur das WS_VISIBLE Flag im Dialogtemplate entfernen und ShowWindow aufrufen, wenn es einem passt.
Vielleicht sehen ja andere Leser noch mehr Vorteile… die Diskussion ist eröffnet 😉
PS: Noch ein kleiner Nachtrag zu den oben erwähnten temporären Handle Maps in einer dialog basierenden Applikation. Diese temporären Handlemaps werden automatisch aufgeräumt und gelöscht wenn CWinApp::OnIdle ausgeührt wird. Das ist normalerweise der Fall wenn in CWinApp::Run keine Nachricht in der Messagequeue liegt, die abgearbeitet werden muss (PeekMessage gibt FALSE zurück). Im Gegensatz dazu werden Handlemaps nicht gelöscht wenn die Nachrichtenschleife mit CWnd::RunModalLoop für einen modalen Dialog ausgeführt wird. So also auch niemals in einer dialog basierenden Anwendung, oder wenn ein modaler Dialog in einer MFC-Applikation ausgeführt wird!
Das fast noch mal einen Artikel wert…