Eigene
Buttons
Eigene Buttons können auf mehrere Arten und Weisen gezeichnet werden. So können
für Buttons Bitmaps oder Icons verwendet werden oder ein Button kann sogar mittels
GDI-Anweisungen gezeichnet werden. Außerdem ist es möglich Buttons zu erstellen,
die keine rechteckige Form besitzen. Die oberen Buttons im folgendem Bild enthalten
Bitmaps und Icons während der untere Button eine runde Form besitzt und mittels
GDI-Anweisungen gezeichnet wurde.

Beginnen wir mit der Erstellung von Buttons die auf Bitmaps basieren. Innerhalb
des Dialogs wird der Button zunächst wie gewohnt erstellt. Die dabei eingestellte
Größe des Buttons ist jedoch nicht relevant, da sie sich zur Laufzeit nach der Größe
der Bitmap für den Button im Normalzustand richtet. Anschließend muss die Eigenschaft
Besitzerzeichnung des Buttons gesetzt und dem Button eine eindeutige Beschriftung
zugewiesen werden. Dies Beschriftung spielt gleich bei der Verknüpfung des Buttons
mit seinen Bitmaps noch eine wichtige Rolle.

Als nächstes kann es an das Erstellen der Bitmaps für den Button gehen. Ein Bitmap-Button
kann für jeden seiner vier Zustände (normal, selektiert, gedrückt und gesperrt)
eine eigene Bitmap darstellen. Die für den jeweiligen Zustand des Buttons darzustellende
Bitmap erhält nun als Namen die Beschriftung des Buttons plus einen Suffix
laut nachfolgender Tabelle:
| Suffix |
Buttonzustand |
| U |
normal |
| F |
selektiert |
| D |
gedrückt |
| X |
gesperrt |
Um einer Bitmap-Ressource einen Namen (und keine ID) zuzuweisen, ist im Eigenschaftsdialog
der Bitmap der Name in Anführungszeichen einzuschließen.

Damit ergeben sich im Beispiel für den Button mit der Beschriftung Button1
folgende Namen für die Bitmaps:

Da der Button im Beispiel niemals gesperrt wird ist auch nicht notwendig, hierfür
eine Bitmap zur Verfügung zu stellen.
Damit hätten wir die Vorarbeiten für Bitmap-Buttons erledigt. Bleibt zum Schluss
nur noch die Verbindung zwischen dem Button und den darzustellenden Bitmaps herzustellen.
Unter der MFC werden Bitmap-Buttons durch die Klasse CBitmapButton repräsentiert.
Diese Klasse enthält eine Methode AutoLoad(...) die als Parameter unter anderem
die ID des Bitmap-Buttons erhält. AutoLoad(...) lädt nun die entsprechenden
Bitmap-Ressourcen und verbindet Sie dann mit dem Button. Alles weitere erfolgt dann
automatisch. Selbstverständlich darf AutoLoad(...) erst dann aufgerufen werden,
wenn der Button bereits erstellt ist. Im Beispiel erfolgt dies innerhalb der Dialog-Methode
OnInitDialog(...).
BOOL COwnBtnDlg::OnInitDialog()
{
....
// ZU ERLEDIGEN: Hier zusätzliche Initialisierung einfügen
m_CBmpBtn.AutoLoad(IDC_BMPBTN,this);
return TRUE; // Geben Sie TRUE zurück, außer ein Steuerelement
soll den Fokus erhalten
} |
Die Erstellung eines Buttons mit einem Icon erfordert etwas weniger Aufwand wie
ein Bitmap-Button. Dafür wird für alle Buttonzustände aber auch immer das gleiche
Icon angezeigt. Wird die Button gedrückt, so wird die Position des Icons nur etwas
nach rechts unten verschoben. Auch dieser Button wird innerhalb Dialogs normal erstellt.
Zusätzlich muss noch die Eigenschaft Symbol des Buttons gesetzt werden.

Im Unterschied zum Bitmap-Button erhält der Button jetzt aber zur Laufzeit die
im Dialog eingestellte Größe. Ist das später anzuzeigende Icon kleiner, so wird
es standardmäßig zentriert im Button dargestellt. Größere Icons werden einfach abgeschnitten.
Die Verbindung zwischen dem Button und dem anzuzeigenden Icon erfolgt in zwei Schritten.
Zuerst muss das Icon mittels der CWinApp Methode LoadIcon(...) geladen
werden. Das dabei zurückgelieferte Icon-Handle wird dann der Methode SetIcon(...)
der CButton Klasse übergeben.
BOOL COwnBtnDlg::OnInitDialog()
{
....
m_CBmpBtn.AutoLoad(IDC_BMPBTN,this);
// Icon-Button initialisieren
HICON hOwnIcon = AfxGetApp()->LoadIcon(IDI_BTNICON);
CButton *pBtn = static_cast<CButton*>(GetDlgItem(IDC_ICONBTN));
pBtn->SetIcon(hOwnIcon);
return TRUE; // Geben Sie TRUE zurück, außer ein Steuerelement
soll den Fokus erhalten
} |
Kommen wir nun zum letzten Fall, dass ein Button mittels GDI-Anweisungen gezeichnet
werden soll. Für diesen Fall müssen Sie sich für den Button zuerst eine eigene Klasse,
abgeleitet von CButton, erstellen. Dieser neuen Klasse ist dann die virtuelle
Methode DrawItem(...) hinzuzufügen. Aber bitte nicht mit der Methode für
die WINDOWS-Nachricht WM_DRAWITEM verwechseln! Und innerhalb dieser neu hinzugefügten
Methode DrawItem(...) können Sie dann den Button zeichnen. Dazu erhält die
Methode einen Zeiger auf eine Struktur vom Typ DRAWITEMSTRUCT übergeben,
die unter anderem die auszuführende Aktion beinhaltet. So enthält z.B. das Element
itemAction Informationen darüber, weshalb der Button gezeichnet werden soll.
Außerdem enthält die Struktur auch das Handle für den zum Zeichnen zu verwendenden
Device Context. Damit Sie aber nachher mit den CDC Methoden der MFC zeichnen
können, müssen Sie diesen HDC zuerst mittels der CDC Methode FromHandle(...)
in ein CDC Objekt umwandeln. Der runde Button im obigen Dialog wird im Beispiel
mit folgenden Anweisungen erstellt:
void CBtnDraw::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// TODO: Code einfügen, um das angegebene Element zu zeichnen
CString CTitle;
// Button-Beschriftung
CBrush *pBtnBrush;
// Button-Farbe
// CDC-Objekt aus uebergebenen hDC
erstellen
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
// Buttonzustand auswerten
switch (lpDrawItemStruct->itemAction)
{
// Button komplett neu zeichnen
case ODA_DRAWENTIRE:
pBtnBrush = new CBrush(RGB(255,255,0));
CTitle = "Klick me!";
break;
// Button besitzt Focus
case ODA_FOCUS:
pBtnBrush = new CBrush(RGB(0,255,0));
CTitle = "You found me!";
break;
// Button ist ausgewaehlt
case ODA_SELECT:
// Falls
Button gedrueckt
if (lpDrawItemStruct->itemState
& ODS_SELECTED)
{
pBtnBrush = new
CBrush(RGB(255,0,0));
CTitle = "That
was a hit!";
}
// Button
ist nicht gedrueckt
else
{
pBtnBrush = new
CBrush(RGB(0,255,0));
CTitle = "That's
good!";
}
break;
}
// Groesse des Buttons holen
CRect CBtnColor = m_CBtnRect;
// Weissen Brush erstellen
CBrush CBtnBackBrush(RGB(0xff,0xff,0xff));
pDC->SelectObject(&CBtnBackBrush);
// und Ellipse damit zeichnen
pDC->Ellipse(&m_CBtnRect);
// Ellipse um jeweils 5 Einheit
verkleinern
CBtnColor.DeflateRect(5,5);
// Farbe je nach Buttonzustand
setzen
pDC->SelectObject(pBtnBrush);
// Ellipse zeichnen
pDC->Ellipse(&CBtnColor);
// Beschriftung ausgeben
pDC->SetBkMode(TRANSPARENT);
pDC->DrawText(CTitle,m_CBtnRect,DT_CENTER|DT_VCENTER|DT_SINGLELINE);
delete pBtnBrush;
} |
Je nach Zustand erhält der Button eine andere Farbe und Beschriftung.
Doch einen kleinen Fehler hätte der so erstellte Button noch. Da ein Button im
Dialogeditor immer rechteckig angelegt wird, würde auch ein Klick innerhalb dieses
rechteckigen Bereichs und außerhalb der gezeichneten Ellipse als Buttonklick erkannt
werden. Um den Button jetzt abzurunden (und nicht nur rund zu zeichnen), wird eine
elliptische Region erstellt und diese Region dann als Fenster-Region ausgewählt.
Denken Sie immer daran, dass auch ein Button nur ein Fenster ist! Damit beim Erstellen
des Dialogs der Button normal erstellt werden kann, wird noch der Buttonstil auf
BS_OWNERDRAW gesetzt (Besitzerzeichnung).
void CBtnDraw::PreSubclassWindow()
{
// TODO: Speziellen Code hier einfügen und/oder Basisklasse aufrufen
CButton::PreSubclassWindow();
// Elliptische Region erstellen
die den urspruenglichen
// Button ausfuellt
CRgn CBtnRgn;
GetClientRect(&m_CBtnRect);
CBtnRgn.CreateEllipticRgnIndirect(&m_CBtnRect);
// Die erstellte Region als
Fenster-Region setzen
SetWindowRgn(CBtnRgn,TRUE);
// Button-Stil jetzt noch auf
Besitzerzeichnung setzen
ModifyStyle(0,BS_OWNERDRAW);
} |
Der Button wird zum Schluss im Dialogeditor wie gewohnt erstellt und dann über
den Klassen-Assistenten mit der soeben neu erstellen Klasse verbunden (Membervariable
vom Typ Control).
Das fertige Beispiel mit allen 3 Buttons finden Sie unter
08Dialoge\OwnBtn.
Auf der nächsten Seite, übrigens der letzten in diesem Kurs, können Sie sich
noch eine Aufstellung von zusätzlichen Programmen ansehen.
|