Skinning mit WinAPI



  • Hi,

    es geht darum, eine Anwendung mit der WinAPI möglichst stark zu skinnen, d.h. das Erscheinungsbild zu verändern. Nun sind Window Regions ja kein großes Problem (und gut kapselbar), ebenso einfache Hintergrundbilder für ein Fenster.

    Das größere Problem sind hingegen die Steuerelemente, die ja auch geskinnt werden müssen (wie beispielsweise in Steam, nicht, dass ich den ****** benutzen würde, habs aber gesehen). Einerseits kann ich natürlich einfach die komplette Funktion der Steuerelemente selbst programmieren, dann läuft das Unterfangen aber auf das Umfangen einer Ingame-GUI heraus und dauert länger wie das restliche Programm, von den Bugs mal ganz zu schweigen. Andererseits bietet die WinAPI ja die Möglichkeit des Owner Draws, d.h. ich benutze weiterhin die Windows-Steuerelemente, zeichne aber selbst.

    Hat jemand schon Erfahrung damit gemacht und wie kompliziert ist es denn, komplexe Steuerelemente (Listbox z.B.) selbst zu zeichnen. Ich muss ja erstmal den ganzen Zustand der LB abfragen (geöffnet, Markierung usw.) und dann das Erscheinungbild selbst aus Bitmaps zusammenblitten, oder?

    ChrisM



  • Überdenke 3 mal ernsthaft ob du wirklich Zeit mit einer aufgemotzten GUI verlieren willst, wo du doch ganz gute Software mit konventionellem Design gestalten kannst.
    Fast kein großer Softwarehersteller vermurkst sich seine Designs mit experimentellen GUIS und die werden wohl ihre Gründe haben, wenn Sie auf ausgefallene, außergewöhnliche Designs verzichten.
    Oder glaubst du die können das nicht?

    Den Fehler habe ich auch schon mal gemacht als ich nen Server programmieren wollte.
    Habe Monate lang an ner 'Hammergeilen_SuperGUI' für das Konfigurationsfenster gesessen, die hinterher ehh keinem gefiel ausser mir selbst und den Server habe ich nie zustande gebracht, weil ich die Lust daran verloren hatte.

    Die Fülle an Schriften und Bildern verführt zu stilistischer Perversion, zur Inflation der typographischen Elemente, zum überladenen Kitsch, zum Blenden mit Flitter und zur Verzierung simpelster Fakten mit billigem, fadenscheinigen Stanniol. So. Nun überleg dir was du tust,

    MfG Tolga.

    PS: ownerdrawn Steuerelemente können auch sehr shcön werden und der Aufwand ist in den meisten Fällen sogar vertretbar.



  • Den Fehler habe ich auch schon mal gemacht als ich nen TrojanerServer programmieren wollte.

    😉



  • Hi,

    falls du auf SubSeven anspielst, ja, dessen GUI ist wirklich nicht schlecht. Leider völlig überladen, ein kleines Rootkit reicht für meine "Fernzugriffe" vollkommen. 😃

    Die Sache ist die, dass das Programm geskinnt werden muss, das ist nicht meine Entscheidung. Es muss also schon nach was aussehen und nicht das Windows-Einheitsgrau haben.

    Obwohl ich euch natürlich darin zustimme, dass Funktionalität immer Vorrang vor Design haben muss.

    EDIT: Wie funktioniert denn das mit Owner Draw jetzt genau? Kennt jemand ein gutes Tut? In der MSDN habe ich nichts Ausführliches dazu gefunden.

    ChrisM



  • Wegen OwnerDraw:

    Ich wollte mal einen Button mit OwnerDraw erstellen und darüber eine Bitmap legen....Man sieht zwar die Bitmap, aber zusätzlich noch einen typischen windows Button Schatten. ALso bringts OwnerDraw nicht.

    PS: Korrigiert mich falls doch mit OwnerDraw ohne diesen Button Schatten/ Rahmen geht.



  • Die Sache mit dem Ownnerdrawn ist fur Anfänger schwer zu überblicken. Ich habe damals auch paar Std. rumgetippt bis es anfing mir zu gehorchen... Es gibt im Web sehr viele verschiedene Vorgehensweisen aber ich finde meine am besten und sehr flexibel auch noch ;-)...

    SO GEHTS:

    Zuerst erstellst du nen Button mit der Eigenschaft BS_OWNERDRAWN...

    hButton= CreateWindow(TEXT("BUTTON"),// Der Fensterklassen-Name 
                         TEXT("Caption..."),// Der Text
                         WS_VISIBLE|WS_CHILD|BS_NOTIFY|BS_OWNERDRAW,// Der Fenster-Stil 
                         3, 43, 70, 24,// Die Fenster-Abmessungen 
                         hMain, //Mutterfenster
                         reinterpret_cast<HMENU>(id_send),// ID 
                         hInstance,// Die Programminstanz 
                         NULL);// Keine Extra-Daten
    

    Wenn das Control als Ownerdrawn erstellt worden ist hat es die nutzliche Eigenschaft, bei vielen Events eine WM_DRAWITEM - message an das Mutterfenster zu senden. Diese mesage fängst du in der WNDPROC des Mutterfenster ab und übergibst den lparam-wert folgnderweise an DEINE Funktion, die den Button malt.

    DEINE FUNKTION bestimmt nun das aussehen des Buttons, d.h. sie malt den Button komplett selbst.

    case WM_DRAWITEM: 
                      DrawButton(reinterpret_cast<DRAWITEMSTRUCT*>(lParam));
                      return 0;
    

    SO ungefähr sollte diese DrawButton - Funktion aussehen...

    bool DrawButton(DRAWITEMSTRUCT* pdis)
    {
    bool db = (pdis->itemState & ODS_DEFAULT); //Wenn Normalzustand des Buttons..
    bool sb = (pdis->itemState & ODS_SELECTED); //Wenn Button gedrückt wird
    bool fb = (pdis->itemState & ODS_FOCUS);//Wenn Button den Fokus hat
    bool disabled = (pdis->itemState & ODS_DISABLED); //Wenn Button disabled ist
    //Das System meldet dir automatisch wann du welchen Zustand malen musst.. Geil //was??
    
    RECT r = pdis->rcItem; // in der Struktur r werden Koordinaten und Maßen des //controls gespeichert..
    HBRUSH oldbc;
    HDC hdc = pdis->hDC;
    HPEN pen, oldpen;
    HFONT oldfh, fh;
    
    if(!disabled)
    	oldbc = reinterpret_cast<HBRUSH>(SelectObject(hdc, hb.handle()));//
    else
    	oldbc = reinterpret_cast<HBRUSH>(SelectObject(hdc, zb.handle()));//
    
    pen = CreatePen(PS_SOLID,2+db,RGB(GetRValue(AppColors),GetGValue(AppColors),GetBValue(AppColors)));	
    oldpen = reinterpret_cast<HPEN>(SelectObject(hdc, pen));
    TCHAR* fontname = new TCHAR[35];
    lstrcpy(fontname, TEXT("Verdana"));
    if (sb)
    lstrcpy(fontname, TEXT("Arial"));
    Font ff(fontname,8); // <<-- FONT
    fh = ff.handle();
    delete[] fontname;
    oldfh = reinterpret_cast<HFONT>(SelectObject(hdc, fh));
    
    PAINTSTRUCT ps;
    ps.fErase = false;
    ps.rcPaint = r;
    ps.hdc = hdc;
    
    BeginPaint(hMain, &ps);
    
    //---------------------------------------------------------
    Rectangle(hdc,r.left+sb,r.top+sb,(r.right)+sb,(r.bottom)+sb);
    //---------------------------------------------------------
    
    SetTextColor(hdc,RGB(200+(15*sb)-10*disabled,200+(15*sb)-10*disabled,220+(15*sb)-10*disabled)); // <<-- TEXTCOLOR
    SetBkMode(hdc,TRANSPARENT);
    TCHAR text[255];
    GetWindowText( pdis->hwndItem, text, sizeof(text) );
    r.top+=sb;
    r.left+=sb;
    r.bottom+=sb;
    r.right+=sb;
    DrawText(hdc, text, lstrlen(text), &r, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
    
    EndPaint(hMain, &ps);
    
    //Clean'in up..
    SelectObject(hdc, oldfh);
    SelectObject(hdc, oldbc);
    SelectObject(hdc, oldpen);
    
    DeleteObject(oldbc);
    DeleteObject(oldfh);
    DeleteObject(fh);
    DeleteObject(oldpen);
    DeleteObject(pen);
    
    ReleaseDC(pdis->hwndItem, hdc);
    return true;
    }
    

    So einfach ist das. natürlich musst du an manchen Stellen variablennamen und ähnliches ändern ist ja klar. Aber das Prinzipt ist das.

    Viel Spaß und viel Erfolg.

    Mfg: Tolga



  • In der FAQ ist ein Beispiel.

    Außerdem in der MSDN/Google mal nach Custom Draw suchen. Das ist für die Common Controls.



  • Hi,

    dankeschön! Dein Codesnippet hat mir wirklich weitergeholfen!

    ChrisM


Anmelden zum Antworten