Eingabe-Cursor

Der Eingabe-Cursor ist zunächst einmal eine einmalige System-Ressource, d.h. er ist für alle Anwendungen nur einmal vorhanden. In der Regel wird der Eingabe-Cursor als blinkender Strich oder Block dargestellt. Wir werden uns aber in dieser Lektion noch einen eigenen Eingabe-Cursor bauen, dessen Aussehen über eine Bitmap definiert wird.

Da der Eingabe-Cursor (auch Caret genannt) eine einmalige System-Ressource ist, muss eine Anwendung sich diesen bei Bedarf beim System 'ausleihen' und, wenn sie ihn nicht mehr benötigt, auch wieder zurückgeben. Doch zu welchem Zeitpunkt leiht eine Anwendung sich den Eingabe-Cursor aus und gibt ihn wieder frei? Nun, die Antwort ist im Prinzip ganz einfach: immer wenn ein Fenster aktiviert wird leiht es sich den Eingabe-Cursor aus und beim Deaktivieren des Fensters wird er wieder zurückgegeben. Wenn ein Fenster aktiviert wird (man sagt dazu auch: es erhält den Focus), sendet WINDOWS eine WM_SETFOCUS Nachricht an das Fenster. Der entsprechende MFC Nachrichtenbearbeiter hierzu heißt OnSetFocus(...). Verliert ein Fenster den Focus, so teilt WINDOWS dies dem Fenster über eine WM_KILLFOCUS Nachricht mit. Der MFC-Nachrichtenbearbeiter hierzu heißt dementsprechend OnKillFocus(...). Beide Methode liefern als Parameter einen Zeiger auf ein CWnd-Objekt zurück. Im ersten Fall zeigt der Zeiger auf Fensters, das den Focus verloren hat und im zweiten Fall auf das Fenster, das den Focus als nächstes erhält.

Bauen wir diese beiden Methoden in das Beispiel zu dieser Lektion ein. Erstellen Sie zunächst wie gewohnt eine SDI-Anwendung. Geben Sie dem Projekt den Namen Caret. Da wir im Beispiel etwas mit dem Eingabe-Cursor spielen wollen, verwenden wir nun als Basisklasse für das Ansichtsobjekt die Klasse CEditView. Beachten Sie daher im Schritt 6, dass Sie CEditView als Basisklasse für CCaretView auswählen.

Ein CEditView Ansichtsobjekt

Stellen Sie das Projekt fertig und übersetzen Sie es. Sie haben danach einen fertigen kleinen Editor, der sogar Kopieren und Einfügen (Copy&Paste) beherrscht!

So, als nächstes sind Sie wieder dran. Immer wenn das Fenster aktiviert bzw. deaktiviert wird, soll mittels MessageBeep(...) ein kurzer Piepton ausgegeben werden.

Lösung zum Focus

void CCaretView::OnSetFocus(CWnd* pOldWnd)
{
    CEditView::OnSetFocus(pOldWnd);
   
    // TODO: Code für die Behandlungsroutine für Nachrichten hier einfügen
    MessageBeep(-1);
}

void CCaretView::OnKillFocus(CWnd* pNewWnd)
{
    CEditView::OnKillFocus(pNewWnd);
   
    // TODO: Code für die Behandlungsroutine für Nachrichten hier einfügen
    MessageBeep(-1);
}

Das war doch wirklich nicht schwer, oder?

Ende der Lösung

Wenn Sie das Programm erneut übersetzen und starten, erhalten Sie jedes Mal wenn das Fenster aktiviert bzw. deaktiviert wird einen Piepton.

Gehen wir jetzt ans 'Erstellen' des Eingabe-Cursor. Zunächst werden wir einen vom System vordefinierten Eingabe-Cursor verwenden. Wenn Sie vorhin das Programm ausgeführt haben, so haben Sie als Eingabe-Cursor einen senkrechten Strich erhalten. Durch den Aufruf der CWnd-Methode CreateSolidCaret(...) kann die Größe des Eingabe-Cursor beeinflusst werden. Die Methode erhält als Parameter die neue Breite und Höhe des Eingabe-Cursors (in physikalischen Einheiten).

Es gibt noch eine weitere CWnd-Methode CreateGrayCaret(...) mit den gleichen Parametern, die anstelle eines schwarzen Eingabe-Cursors einen hellgrauen erstellt.

Damit ist der Eingabe-Cursor zwar erstellt, aber er wird noch nicht angezeigt. Um den erstellten Eingabe-Cursor anzuzeigen muss die die CWnd-Methode ShowCaret(...) aufgerufen werden. Der Eingabe-Cursor wird dann an der aktuellen Position dargestellt und beginnt mit der in der Systemsteuerung eingestellten Blinkfrequenz zu blinken. Sie können die aktuelle Position des Eingabe-Cursors mit der CWnd-Methode SetCaretPos(...) jederzeit verändern. Die Methode erhält die Koordinaten der neuen Position (in physikalischen Einheiten) relativ zur Client-Area.

Außer der Position des Eingabe-Cursors kann auch dessen Blinkfrequenz verändert werden. Dies wird durch den Aufruf der API-Funktion SetCaretBlinkTime(...) erreicht. Die eingestellte Blinkfrequenz bleibt solange innerhalb des gesamten Systems gültig, bis sie wieder umgesetzt wird. Sie sollten daher vor dem Umsetzen der Blinkfrequenz die vorherige Blinkfrequenz mittels der API-Funktion GetCaretBlinkTime(...) auslesen.

Damit hätten wir jetzt alle Methoden bzw. Funktionen beschrieben um den Eingabe-Cursor beim Aktivieren des Fensters umzusetzen. Was jetzt 'nur' noch fehlt ist die Freigabe des Cursors beim Deaktivieren des Fensters. Bevor der Eingabe-Cursor freigegeben wird, sollte er immer verborgen werden. Dies wird mittels der CWnd-Methode HideCaret(...) erreicht. Ein verborgener Eingabe-Cursor wird aber noch nicht automatisch zerstört. Sie können ihn jederzeit wieder mittels ShowCaret(...) neu anzeigen.

Die beiden Methoden ShowCaret(...) und HideCaret(...) arbeiten additiv. Wenn Sie z.B. dreimal die Methode ShowCaret(...) aufgerufen haben um den Eingabe-Cursor anzuzeigen, dann müssen Sie auch dreimal die Methode HideCaret(...) aufrufen um den Eingabe-Cursor wieder zu verbergen.

Um den Eingabe-Cursor endgültig zu entfernen muss die API-Funktion DestroyCaret(...) aufgerufen werden.

Und auch hier dürfen Sie gleich wieder selbst tätig werden. Ändern Sie das Beispiel nun so ab, dass anstelle des dünnen Eingabe-Cursor ein so genannter Block-Cursor dargestellt wird. Dazu ersetzen Sie bitte die Aufrufe von MessageBeep(...) in den Methoden OnSetFocus(...) und OnKillFocus(...) durch die entsprechenden Anweisungen.
Ich zeige Dir die Lösung! Auch das dürfte nicht all zu schwierig sein. Trotzdem kann ich Ihnen auch meine Lösung anzeigen.

Lösung zum Blockcursor

void CCaretView::OnSetFocus(CWnd* pOldWnd)
{
    CEditView::OnSetFocus(pOldWnd);
   
    // TODO: Code für die Behandlungsroutine für Nachrichten hier einfügen
    // Ausgefuellten Eingabe-Cursor erstellen
    CreateSolidCaret(10,16);
    // Eingabe-Cursor anzeigen
    ShowCaret();
}

void CCaretView::OnKillFocus(CWnd* pNewWnd)
{
    CEditView::OnKillFocus(pNewWnd);
   
    // TODO: Code für die Behandlungsroutine für Nachrichten hier einfügen
    // Eingabe-Cursor verbergen
    HideCaret();
    // und dann zerstoeren
    DestroyCaret();
}

Das war doch wirklich nicht schwer, oder?

Ende der Lösung

Übersetzen und starten Sie das Programm. Anstelle des bisherigen Strich-Cursors erhalten Sie einen Block-Cursor.

Wenn Sie wollen, können Sie ja einmal versuchen den Cursor etwas schneller blinken zu lassen.

Damit haben Sie im Prinzip alles Wissenswerte über den Eingabe-Cursor erfahren. Wir wollen unser Beispiel nun noch wie erwähnt so erweitern, dass das Aussehen des Eingabe-Cursor über eine Bitmap definiert wird. Diese Bitmap kann entweder aus einer Bitmap-Datei stammen oder aus einer Ressource. Da wir Ressourcen noch nicht behandelt haben, laden wir die Bitmap im Beispiel aus einer Datei.

Wie man eine Bitmap aus einer Datei lädt und wieder freigibt sollten Sie in der Zwischenzeit schon wissen. Wir bauen den dafür notwendigen Code in den Konstruktor bzw. Destruktor des Ansichtobjekts ein:
CCaretView::CCaretView()
{
    // ZU ERLEDIGEN: Hier Code zur Konstruktion einfügen,
    // Bitmap fuer Caret laden
   HBITMAP hBmp = (HBITMAP)::LoadImage(0,"Caret.bmp",IMAGE_BITMAP,
                                       0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE);
    ASSERT(hBmp);
    // Geladene Bitmap einem CBitmap-Objekt zuweisen
    m_pBmpCaret = new CBitmap;
    m_pBmpCaret->Attach(hBmp);
}

CCaretView::~CCaretView()
{
    delete m_pBmpCaret;
}

Definieren Sie sich noch die Membervariable m_pBmpCaret vom Typ CBitmap* für die Aufnahme des Bitmap-Objektes das wir nachher als Eingabe-Cursor verwenden werden. Außerdem müssen Sie sich noch die Bitmap CARET.BMP aus dem Verzeichnis 99Templates in Ihr Arbeitsverzeichnis kopieren.

Damit haben Sie das Schwierigste auch schon hinter sich. Zum Schluss müssen Sie nur noch den Aufruf der Methode CreateSolidCaret(...) durch den Aufruf der CWnd-Methode CreateCaret(...) ersetzen. Als Parameter erhält die Methode einen Zeiger auf das Bitmap-Objekt, das für die Darstellung des Eingabe-Cursors verwendet werden soll.

Ersetzen Sie nun in der Methode OnSetFocus(...) den Aufruf der Methode CreateSolidCaret(...) durch den Aufruf von CreateCaret(...).
void CCaretView::OnSetFocus(CWnd* pOldWnd)
{
    CEditView::OnSetFocus(pOldWnd);
   
    // TODO: Code für die Behandlungsroutine für Nachrichten hier einfügen
    // Ausgefuellten Eingabe-Cursor erstellen
    CreateCaret(m_pBmpCaret);
    // Eingabe-Cursor anzeigen
    ShowCaret();
}

Wenn Sie da Programm neu übersetzen erhalten Sie einen Eingabe-Cursor in Form eines kleinen UFOs.

Ihr eigener Eingabecursor!

Wenn Sie sich einmal die Bitmap-Datei ansehen werden Sie feststellen, dass die Bitmap andere Farben besitzt als der angezeigte Eingabe-Cursor. Der Eingabe-Cursor wird durch entsprechende BLT-Operationen eingeblendet die als ROP-Code die XOR-Funktionen verwenden. Sie müssen in der Regel also ein wenig mit dem Bitmap-Farben herum spielen um die gewünschte Farbe des Eingabe-Cursors einzustellen.

Damit beenden wir die Behandlung der Eingaben. In der nächsten Lektion können Sie sich noch einige Tipps&Tricks zu den Eingaben ansehen.



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