Probleme mit subclassing von button -> repaint funktioniert nicht



  • Moin,

    ich habe den nachfolgenden Code und nachfolgendes Problem:

    Ich subclasse einen Button. Kein Problem. Wenn ich jetzt aber versuche, ein Bitmap draufzumalen, erschein... nichts. Weiß (Hintergrundfarbe). Sobald ich auf den Button klicke (da wo er sein sollte) wird ein grau Umrandeter Radio-punkt angezeigt. Aber nicht der Rest.

    Jetzt natürlich meine Frage: Warum? Wo liegt das Problem? Was amche ich falsch?

    Schonmal viel Spaß( 😃 ), euer ItsNotYou

    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	static HANDLE hSeite = 0;
    	static HDC hDCSeite = 0;
    
    	static HWND hButton1;
    	static HWND hButton2;
    
    	switch(message)
    	{
    	case WM_CREATE:
    		{
    			hDCSeite = CreateCompatibleDC(0);
    
    			hSeite = LoadImage(GetModuleHandle(NULL), "seite.bmp", IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR | LR_LOADFROMFILE);
    
    			SelectObject(hDCSeite, hSeite);
    
    			hButton1 = CreateWindow("button", "test1", WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP | BS_AUTORADIOBUTTON, 10, 10, 100, 100, hWnd, NULL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
    			hButton2 = CreateWindow("button", "test2", WS_CHILD | WS_VISIBLE | /*WS_GROUP |*/ BS_AUTORADIOBUTTON, 10, 110, 100, 100, hWnd, NULL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
    
    			PrevWndProcButton = SetWindowLongPtr(hButton1, GWLP_WNDPROC, (LONG_PTR)ButtonProc);
    
    			return(0);
    		}
    	case WM_PAINT:
    		{
    			PAINTSTRUCT ps;
    			BeginPaint(hWnd, &ps);
    			{
    				RECT rect;
    				GetClientRect(hWnd, &rect);
    
    				for(int count = 0; count < rect.bottom; count++)
    				{
    					BitBlt(ps.hdc, 0, count, 10, 1, hDCSeite, 0, 0, SRCCOPY);
    				}
    			}
    			EndPaint(hWnd, &ps);
    
    			return(0);
    		}
    	case WM_DESTROY:
    		{
    			SetWindowLongPtr(hButton1, GWLP_WNDPROC, PrevWndProcButton);
    			PostQuitMessage(0);
    			return(0);
    		}
    	}
    
    	return DefWindowProc(hWnd, message, wParam, lParam);
    }
    
    //---------------------------------------------------------
    
    LRESULT CALLBACK ButtonProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	static HANDLE hSeite = 0;
    	static HDC hDCSeite = 0;
    
    	switch(message)
    	{
    	case WM_CREATE:
    		{
    			hDCSeite = CreateCompatibleDC(0);
    
    			hSeite = LoadImage(GetModuleHandle(NULL), "back.bmp", IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR | LR_LOADFROMFILE);
    
    			SelectObject(hDCSeite, hSeite);
    
    			break;
    		}
    	case WM_PAINT:
    		{
    			PAINTSTRUCT ps;
    
    			BeginPaint(hWnd, &ps);
    			{
    				BitBlt(ps.hdc, 0, 0, 100, 100, hDCSeite, 0, 0, SRCCOPY);
    			}
    			EndPaint(hWnd, &ps);
    
    			break;
    		}
    	}
    
    	return CallWindowProc((WNDPROC)PrevWndProcButton, hWnd, message, wParam, lParam);
    }
    


  • Wenn die alte Fensterprozedur WM_PAINT verarbeitet ruft sie nochmal Begin-/EndPaint auf 🙄



  • und das sorgt wofür? dafür das der button nicht gezeichnet wird?



  • Nein, daß der von dir frisch gezeichnete Inhalt wieder überpinselt wird 😉 Wenn du etwas selber komplett erledigt hast, brauchst du nicht mehr auf die Standardimplementierung zurückgreifen.



  • was sollte ich stattdessen machen? das bitmap soll aufn button, (ziemlich) egal wie



  • bau dir doch deine ButtonProc so auf wie die WndProc:

    case WM_PAINT:
      //... Button zeichnen
      return 0;
    

    In dem Block steht alles drin, was dein Programm zum Zeichnen wissen muß, damit ist die Nachrichtenbehandlung fertig.

    (eventuell müsstest du den Rückgabewert noch anpassen - aber da kann dir die WinAPI-Doku eher weiterhelfen)



  • im sourcecode kann man sehen, dass wndproc u. buttonproc gleich aufgebaut sind, allerdings tritt dann das oben beschriebene problem auf. lasse ich das beginpaint weg, erscheint zwar der button komplett, aber ohne bitmap



  • das Problem ist eben, daß sie NICHT komplett gleich aufgebaut sind:

    // etwas vereinfacht:
    WndProc(...)
    {
      switch(message)
      {
      case WM_xxx:
        // handle Message xxx
        return 0; // verlässt die Funktion
      case WM_yyy:
        // handle Message yyy
        return 0;
      //...
      }
      return DefWindowProc(...); // nur aufgerufen, wenn keiner der case... aktiv wurde
    }
    
    ButtonProc(...)
    {
      switch(message)
      {
      case WM_xxx:
        // handle Message xxx
        break; // verlässt nicht die Funktion, sondern nur den switch()
      //...
      }
      return CallWindowProc(...); // in jedem Fall nach deiner Nachrichtenbehandlung aufgerufen
    }
    


  • das heißt, ich sollte bei einem button die WM_PAINT-Nachricht VOLLKOMMEN alleine übernehmen... Achso...

    ok, ich probiers



  • ich habe jetzt den fehler gefunden! die WM_CREATE-nachricht wird nicht aufgerufen (warum auch immer), ich habe die sachen aus der wm_create einfach beim ersten aufruf der wm_paint machen lass -> geht

    danke für eure hilfe, auch wenn der fehler letztlich woanders lag



  • Naja, das ist so nicht richtig.
    Das WM_CREATE wird auch bei den Buttons aufgerufen, nur landen diese Aufrufe nicht in Deiner eigenen WndProc, was aber ganz einfach daran liegt, dass Du die WndProc ja erst nach dem Aufruf von CreateWindow (also nach dem Erstellen) änderst.



  • ok wie auch immer, ich weiß jetzt wo mein fehler lag.

    danke für die hilfe,

    euer ItsNotYou


Anmelden zum Antworten