Registerkarten in Dialogfeldern - ohne Absturz bei Fokusverlust!
-
Es geht also darum, dass eine Dialogfeld-Anwendung, die fest verankerte Registerkarten enthält, sich aufhängt bzw. die Prozessorauslastung auf 100% treibt, sobald die Registerkarten den Fokus verlieren, also z.B. auf etwas außerhalb des Dialogfeldes (etwa auf den Desktop) geklickt wird. Ich habe sehr lange gebraucht, um diesen Fehler lokalisieren zu können, da ich natürlich zuerst daran dachte, mein eigener Code wäre dafür verantwortlich
Ich schreibe mal hier meine Lösungsvariante, die relativ kurz ist. Es geht also darum, ein Dialogfeld zu erstellen, dass Registerkarten enthält. Das "managed" ein CPropertySheet-Objekt.
In meinem Fall habe ich den PropertySheet in der OnInitDialog() des Hauptdialogfeldes erstellt.
Er soll zwei Dialogfelder ID_PROP1 und ID_PROP2 enthalten, die jeweils eine eigene, von CPropertyPage abgeleitete Klasse besitzen.
Hier also mal eine Anleitung, wie man Registerkarten herstellt und in einem Dialogfeld fest verankert:1. Erstelle für jede Registerkarte, die du haben willst, eine neue Dialogfeldvorlage: Einfügen->Ressource->Dialog-> IDD_PROPPAGE_MEDIUM (je nach Größe auch …_SMALL oder …_LARGE)
2. Erstelle für jede eingefügte Dialogfeldressource eine eigene, neue Klasse. Am einfachsten geht das, wenn die Dialogressource markiert ist und dann der Klassenassistent geöffnet wird. I.d.R. kommt dann automatisch die Frage, ob eine neue Klasse erstellt oder eine vorhandene Klasse ausgewählt werden soll. Hier also eine neue Klasse erstellen.
3. Gib der Klasse einen passenden Namen. Wähle als Basisklasse CPropertyPage aus.
4. Inkludiere in der Headerdatei der Dialogklasse des Hauptdialogfeldes (PropjektnameDlg.h) die Header der eben erstellten Klassen
5. Erstelle für jede Registerkartenklasse eine Instanz in der Deklaration der Hauptdialogfeldklasse ( CProjektnameDlg). Die Headerdatei der Dialogfeldklasse sollte dann in etwa so aussehen:ProjektnameDlg.h
#include "PropPage1.h" //! #include "PropPage2.h" //! ///////////////////////////////////////////////////////////////////////////// // CApplicationnameDlg Dialogfeld class CApplicationnameDlg : public CDialog { // Konstruktion public: //.... // Implementierung protected: HICON m_hIcon; CPropPage1 m_PropPage1; //! Instanz der ersten Registerkarte CPropPage2 m_PropPage2; //! Instanz der zweiten Registerkarte CPropertySheet m_PropSheet; //! //……
6. In der Funktion OnInitDialog() der Hauptdialogfeldklasse wird folgendes eingefügt:
ProjektnameDlg.cpp
//…. // ZU ERLEDIGEN: Hier zusätzliche Initialisierung einfügen m_PropSheet.AddPage(&m_PropPage1); //! Karte1 hinzufügen m_PropSheet.AddPage(&m_PropPage2); //! Karte2 hinzufügen m_PropSheet.Create(this,WS_CHILD|WS_VISIBLE); //! Registerkarten erstellen m_PropSheet.ModifyStyleEx(0,WS_EX_CONTROLPARENT); //! Style ändern
WICHTIG: Die ModifyStyleEx-Methode darf erst nach Erstellung des PropertySheets aufgerufen werden, sonst schmiert die Anwendung gleich zu Beginn ab. Das Flag WS_EX_CONTROLPARENT verhindert, dass die Anwendung in eine Endlosschleife über eventuelle Steuerelemente auf den Registerkarten hängen bleibt, wenn die Registerkarte den Fokus verliert (beliebtes Problem!)
Das war's! Jetzt müsst ihr nur noch die benötigten Steuerelemente auf eure einzelnen Registerkarten packen und mit Funktionalität versehen.
Um zu ermitteln, ob eine Registerkarte gerade angezeigt wird, fügt in jede Registerkartenklasse eine Behandlungsmethode für OnSetActive ein.
Microsoft ist das Problem o.g. auch bekannt. Sie reagierten mit einem Artikel, der eine ähnliche Methode verfolgt, diesen Fehler zu umgehen, nachzulesen hier.
Gruß
E-the-Real
P.S.:
Da das Problem öfters auftritt, wäre dieser Beitrag eventuell was für die FAQ.