Zeichnen im Dialog/
Abfangen von ESC+RETURN

Bisher haben wir nur Dialoge betrachtet die das standardmäßige Aussehen und Verhalten zeigten. Es ist jedoch auch möglich, innerhalb eines Dialogs zu zeichnen (fast genauso wie in einem Fenster) und auch Eingaben vorzuverarbeiten. Der fertige Dialog zu dieser Tipps&Tricks Seite wird folgendes Aussehen besitzen:

Beginnen wir mit dem Zeichnen innerhalb eines Dialogs, und zwar mit dem Zeichnen des Dialog-Hintergrunds. Bevor WINDOWS einen Dialog darstellt, sendet es an ihn die Nachricht WM_CTLCOLORDLG damit der Dialog seinen Zeichensatz und seine Hintergrundfarbe setzen kann . Diese Nachricht wird im MFC-Nachrichtenbearbeiter OnCtlColor(...) verarbeitet. OnCtlColor(...) wird nun innerhalb der MFC aber nicht nur zur Verarbeitung der WM_CTLCOLORDLG Nachricht verwendet, sondern auch für weitere WINDOWS Nachrichten vom Typ WM_CTLCOLORxxx um im Dialog enthaltene Steuerelemente zu beeinflussen. Damit innerhalb der Methode OnCtlColor(...) festgestellt werden kann, ob der Dialog oder eines der in ihm enthaltenen Steuerelemente neu gezeichnet wird, erhält die Methode im letzten Parameter den Typ des zu zeichnenden Elements. Enthält dieser Parameter den Wert CTLCOLOR_DLG, so wird der Dialog selbst gezeichnet. Als Returnwert muss die Methode den Brush für den Hintergrund des aktuellen Objekts (Dialog oder Steuerelement) liefern. Somit könnte eine einfache OnCtlColor(...) Methode etwa wie folgt aussehen:

HBRUSH CDlgDrawDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

    // TODO: Attribute des Gerätekontexts hier ändern

    // TODO: Anderen Pinsel zurückgeben, falls Standard nicht verwendet werden soll
    // Brush fuer Dialog-Hintergrund zurueckgeben
    if (nCtlColor == CTLCOLOR_DLG)
        return *m_pBackBrush;
    // Sonst Standard-Einstellung uebernehmen
    return hbr;
}

Soll der Dialog neu gezeichnet werden, so gibt die Methode den Brush m_pBackBrush zurück, mit dem dann der Dialog-Hintergrund gezeichnet wird. Dieser Brush muss selbstverständlich vorher erstellt worden sein. Im Beispiel wird hierfür ein blauer Brush ohne Muster für den Dialog-Hintergrund im Konstruktor des Dialogs erstellt.

Obwohl über diese Methode auch Veränderungen an den im Dialog enthaltenen Steuerelemente vorgenommen werden können, wird hierfür im nächsten Tipps&Tricks ein anderer Weg gegangen.

Sehen wir und jetzt das eigentliche Zeichnen in einem Dialog an. Wenn Sie sich die Klasse des Dialogs einmal ansehen werden Sie feststellen, dass diese auch eine Methode OnPaint(...) enthält. Und wenn Sie den Kurs von Anfang an bearbeitet haben, so sollte Ihnen diese Methode bekannt vorkommen. Die Methode OnPaint(...) wird innerhalb der MFC immer dann aufgerufen, wenn ein Fenster (und auch ein Dialog ist ein Fenster!) eine WM_PAINT Nachricht erhält um seinen Inhalt neu darzustellen. Die Standard-Implementierung dieser Methode kennzeichnet lediglich den 'Inhalt' des Dialogs als gültig. Sie erinnern sich doch noch: der Inhalt eines Fensters wird durch den Aufruf der beiden Methoden BeginPaint(...) und EndPaint(...) als gültig gekennzeichnet. Um nun innerhalb eines Dialogs selbst zu zeichnen, muss nur der Aufruf der Basisklassen-Methode OnPaint(...) durch eigene Zeichenanweisungen ersetzt werden. Im nachfolgenden Beispiel wird zuerst eine Bitmap in den Dialog einkopiert und dann der OK-Button des Dialogs noch rot umrahmt. Beachten Sie auch wie die Position und Größe des OK-Buttons berechnet wird.

void CDlgDrawDlg::OnPaint()
{
    if (IsIconic())
    {
        ....
    }
    else
    {
        // DC zum Zeichnen innerhalb des Dialogs holen
        CPaintDC dc(this);
        // Rechtecke des Buttons und des Dialogs
        CRect CBtnRect;
        CRect CDlgRect;
        // Groesse des Dialogs holen (Client-Area)
        GetClientRect(CDlgRect);
        // und um 5 Pixel in jeder Ausdehnung verkleinern
        CDlgRect.InflateRect(-5,-5);
        // Kenndaten der Bitmap holen
        BITMAP BmpParam;
        m_CBackBmp.GetBitmap(&BmpParam);
        // Bitmap in Dialog einkopieren
        dc.StretchBlt(CDlgRect.left,CDlgRect.top,CDlgRect.Width(),CDlgRect.Height(),m_pMemDC,
                        0,0,BmpParam.bmWidth,BmpParam.bmHeight,SRCCOPY);
        // CWnd-Objekt fuer OK-Button holen
        CWnd *pBtnWnd = GetDlgItem(IDOK);
        // Buttonposition/-groesse in Bildschirm-Koordinaten holen
        pBtnWnd->GetWindowRect(CBtnRect);
        // und nach Client-Koordinaten des Dialogs wandeln
        ScreenToClient(CBtnRect);
        // Button-Rechteck in jeder Ausdehnung um 5 Pixel vergroessern
        CBtnRect.InflateRect(5,5);
        // und Rechteck dann mit rot ausfuellen
        dc.FillRect(CBtnRect,&CBrush(RGB(0xff,0x00,0x00)));
    }
}

Beenden wird damit das Zeichnen innerhalb eines Dialogs und wenden uns dem Vorverarbeiten von Tasten zu. Zur Vorverarbeitung von Nachrichten stellt die MFC Klasse CWnd eine Methode PreTranslateMessage(...) zur Verfügung. Als Parameter erhält die Methode die von WINDOWS definierte Nachrichtenstruktur. Wenn diese Methode einen Wert ungleich 0 zurückliefert, so wird die Nachricht anschließend durch die MFC verworfen, d.h. bestimmte Nachrichten können damit von der weiteren Bearbeitung ausgeschlossen werden. Durch modifizieren der Elemente der Nachrichtenstruktur kann nun aber auch eine Nachricht sozusagen umkodiert werden. Um z.B. die ESCAPE- und RETURN-Taste zu verarbeiten, muss zuerst einmal die beim Drücken der Tasten erzeugte Nachricht herausgefiltert werden. Beide Tasten erzeugen eine WM_KEYDOWN Nachricht. Im wParam-Element der Nachrichtenstruktur steht hierbei die gedrückte Taste. Wurde die Taste ESCAPE gedrückt (wParam gleich VK_ESCAPE), so wird einfach ein Wert ungleich zurückgeliefert und damit der Tastendruck verworfen. Beim Drücken der RETURN-Taste (wParam gleich VK_RETURN) wird die Taste durch VK_TAB überschrieben, was dem System dann das Drücken der TAB-Taste vorspiegelt. Wird dann innerhalb des Dialogs die RETURN-Taste gedrückt, so wird das gleiche erreicht wie beim Drücken der TAB-Taste, es wird zum nächsten Steuerelement gesprungen.

 

BOOL CDlgDrawDlg::PreTranslateMessage(MSG* pMsg)
{
    // TODO: Speziellen Code hier einfügen und/oder Basisklasse aufrufen
    // Falls Taste gedrueckt wurde
    if (pMsg->message == WM_KEYDOWN)
    {
        // Wenn ESCAPE, dann Nachricht verwerfen
        if (pMsg->wParam == VK_ESCAPE)
        // Nachricht verwerfen!
            return 1;
        // Wenn RETURN, dann nach TAB umwandeln
        if (pMsg->wParam == VK_RETURN)
            pMsg->wParam = VK_TAB;
    }
    // Standard-Behandlung durchfuehren
    return CDialog::PreTranslateMessage(pMsg);
}

Das fertige Beispiel finden Sie unter 08Dialoge\DlgDraw.



Copyright © 2004

Senden Sie Emails mit Fragen oder Kommentaren zu dieser Website an: mailto:info@cpp-tutor.de
 Wolfgang Schröder, Lerchenweg 23, D-72805 Lichtenstein. Tel: +49 7129 6470