Button mit Bild (BS_BITMAP / BS_OWNERDRAW Thema)



  • Hallo zusammen,

    ich werd noch verrückt. Seit drei Tagen such ich jetzt eine Möglichkeit einen Button (Editor: Visual C++, nur API, keine MFC) zu erstellen, der aus einem Bild besteht (und später auch auf Mouse Down/Mouse Over reagiert).

    Hintergrund: Ich habe eine Anwendung erstellt, die einen eigenen Grafikstil hat und in Windows in Vollbild läuft. Deswegen sollen auch das Layout der Standard-Buttons durch eigene Grafik ersetzt werden.

    Soweit ich im halben Internet und Petzold (Kapitel 14 - 16) erfahren habe, gibts wohl zwei Möglichkeiten, Bilder auf Buttons zu bringen. Einmal über BS_BITMAP und einmal über BS_OWNERDRAW.

    Zu BS_BITMAP habe ichs schon geschafft ein Bild auf einen Button zu bringen, allerdings wird noch ein letzter Rand des Buttons angezeigt (trotz BS_FLAT), der sich anscheinend nicht entfernen lässt.

    Hier mal der Code:
    [code]
    HBITMAP hBitmap = (HBITMAP)LoadImage (GetModuleHandle (NULL), L"images/ButtonSetStartCoordsUp.bmp", IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR | LR_LOADFROMFILE);

    ...

    hwndButton_setStartCoords = CreateWindow(
    TEXT("button"),
    button[0].szText,
    WS_CHILD | WS_VISIBLE | BS_FLAT | BS_BITMAP,
    1200,
    170,
    200,
    30,
    hwnd,
    (HMENU) 1,
    ((LPCREATESTRUCT) lParam)->hInstance,
    NULL);

    ...

    SendMessage(hwndButton_setStartCoords, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)(HANDLE)hBitmap);
    [code]

    Wie gesagt, funktioniert diese Methode, allerdings bleibt ein Restrand. Kent jemand eine Möglichkeit kennt, wie dieser auch noch entfernt werden kann? Oder ist das definitiv nicht möglich?

    Dann ist da noch die Methode mit BS_OWNERDRAW.
    Soweit ich das verstehe, darf der Button beim Anlegen mit CreateWindow nur die Eigenschaften WS_CHILD | WS_VISIBLE | BS_OWNERDRAW übergeben bekommen.
    Dann muss mit WM_DRAWITEM der Button gezeichnet werden.

    Und hier setzts aus. Alle Beispiele die ich gefunden haben, benutzen entweder die MFC, oder benutzen, wenn sie denn die API nutzen, nur Funktionen zum Zeichnen auf den Button (Linien, Text, etc.), nutzen aber keine externe BMP-Datei um sie auf den Button zu legen.

    Kennt jemand ein Tutorial oder kann mir Beispielcode zeigen, der es ermöglicht, mit einem OWNERDRAW-Button ein Bild auf den Button zu legen, und wie das funktioniert?

    Viele Grüße,
    Manfred



  • Ich habe sowas hier bei mir eingebaut.
    War ein wenig kniffelig, aber ich poste das gleich mal, wenn ich die Codefragmente extrahiert habe.

    Ich hatte auch das Problem, daß der Rand nich wegzubekommen war und habe es dann mit OWNERDRAW gemacht.

    Bis gleich.



  • Alos, zunächst mal die Button-Definition:

    hButton = CreateWindow("BUTTON","",  // erstellt einen Button
    WS_CHILD | WS_VISIBLE | BS_OWNERDRAW|BS_PUSHBUTTON,
    BREITE-210,50,121,26,  // pos x, pos y, breite, hoehe
    pHwnd,(HMENU)2000,
    hInstGlobal, NULL);
    

    Das dürfte ja alles klar sein..

    Nun die Windows-Message Bearbeitung:
    Der Trick ist der, daß bei BS_OWNERDRAW der Button ganz normal erstellt wird, allerdings wird der Bereich wo sonst der Button erscheint einfach ausgespart. Der ausgesparte Bereich kann nun mit Grafikfunktionen gefüllt werden.
    Dies erledigt Windows, wenn man es dort einträgt, wo Windows die Message auswertet, die gesendet wird, wenn der Button erstellt wird.
    Im Fall eines Bildes (Bitmap) einfach diese in den ausgesparten Bereich BitBlitten.

    case WM_DRAWITEM:
    pdis = (LPDRAWITEMSTRUCT) lParam;
    switch (pdis->CtlID)
    {
    case 2000: 
    rapo1 = GetDC(hwnd); //HDC rapo1;
    rapo2 = CreateCompatibleDC(rapo1); //HDC rapo2;
    SelectObject(rapo2,rechbutton); //rechbutton ist die Bitmap die natürlich in den ausgesparten Bereich passen muß
    BitBlt(rapo1,BREITE-210,50,121,26,rapo2,0,0,SRCCOPY); //Hier die selben Masse wie bei der Buttonerstellung eingeben.
    DeleteObject(rapo2);
    ReleaseDC(hwnd, rapo1);
    break;
    
    case XXXX: // Hier folgen dann weiter Buttons
    .
    .
    break;
    }
    if (pdis->itemState & ODS_SELECTED) InvertRect (pdis->hDC, &pdis->rcItem); //Hier die Zeichenoperationen eingeben, die ausgeführt werden sollen, wenn der Button gedrückt wird. In diesem Fall wird er einfach invertiert.
    return 0;
    

    Ich glaube, das war es schon.

    Edit: pdis ist

    LPDRAWITEMSTRUCT pdis;
    

    Edit: rechbutton ist

    HBITMAP rechbutton;
    


  • Wenn ich das richtig sehe, blittest Du das Bild in das Owner - Fenster des Buttons. Kannst Du das nicht stattdessen direkt auf den Button blitten? Dessen DeviceContext wird ja in der DRAWITEMSTRUCT mitgeliefert.



  • Nein, das geht leider nicht.
    Weil Windows dann wieder einen kleinen Rahmen drumzieht, wenn lediglich auf den Button geblittet wird.
    Blittet man jedoch in den durch BS_OWNERDRAW ausgesparten Bereich, erscheint lediglich die Bitmap und Windows zieht keinen Rahmen.

    Bei BS_OWNERDRAW gibt es auch keinen sichtbaren Button mehr. Der Button wird komplett weggelassen, es erscheint nur eine ausgesparte Fläche im Owner-Fenster des Buttons, den man dann selbst mit seiner ButtonGrafik füllen muß.



  • Hallo Don Carsto,

    nach etwas Rumgefrickel und Gehirngemartere bei der Anpassung hats funktioniert 🙂
    Vielen Dank!

    Merkwürdig, daß das für eigentlich ja eine Grundfunktion so kompliziert ist...
    Mir grausts jetzt schon vor den Input-Feldern und Auswahllisten, die ich noch anpassen muss, aber wird schon werden...
    Vielleicht muss es einfach noch "klick" machen.

    Nochmals vielen Dank! 🙂

    Viele Grüße,
    Manfred


Anmelden zum Antworten