WM_TIMER und InvalidateRect



  • Wie muss ich denn dan UpdateLayeredWindow aufrufen? Wenn ich meinem Window den Style WS_EX_LAYERED gebe dann wird nicht's mehr gezeichnet von Anfang an. Und Wenn ich es UpdateLayeredWindow(hwnd, NULL, NULL, NULL...); aufrufe grieg ich INVAILD_PARAMETER Fehler.



  • Hallo 🙂

    1. Fenster mit WS_EX_LAYERED erzeugen

    Dann Deine Zeichenfunktion implementieren in C könnte das so aussehen:

    void PaintLayer (HWND hwnd)
    {
    	RECT rect;
     	POINT pos_null={0,0};
    	GetWindowRect(hwnd,&rect); 
    	POINT w_pos={rect.left,rect.top};
    	SIZE w_size = {rect.right-rect.left, rect.bottom-rect.top};
    
    	HDC hDCwin = GetDC(hwnd); 	// MSDN sagt "Screen dc" muss aber window dc sein
    	HDC hMemDC = CreateCompatibleDC(hDCwin);
    	HBITMAP hMemBitmap = CreateCompatibleBitmap(hDCwin,w_size.cx,w_size.cy);
    	HGDIOBJ hobj = SelectObject(hMemDC,hMemBitmap);
    
    	// Zeichne was
    	HPEN hPen = CreatePen(PS_SOLID,10,RGB(0,255,0));
    	SelectObject(hMemDC, hPen);
    	MoveToEx( hMemDC, 0, 0, 0 );	
    	LineTo( hMemDC, 100, 100);
    	DeleteObject(hPen);
    
    	// UpdateLayeredWindow
    	BLENDFUNCTION bfunc;
    	bfunc.AlphaFormat = 0;				
    	bfunc.SourceConstantAlpha = 100; 	// Transparenzwert			
    	bfunc.BlendFlags = 0;				
    	bfunc.BlendOp = AC_SRC_OVER;	
    
    	UpdateLayeredWindow
    	(
    		hwnd, 
    		hDCwin, 
    		&w_pos, &w_size, 
    		hMemDC, 
    		&pos_null, 
    		0, 
    		&bfunc, 
    		ULW_ALPHA	
    	);
    
    	SelectObject(hMemDC,hobj);
    	DeleteObject(hMemBitmap);
    
    	DeleteDC(hMemDC);
    	ReleaseDC(hwnd, hDCwin);
    }
    

    Jetzt nur noch an den erforderlichen Stellen aufrufen
    zb bei WM_CREATE, in Deinem Timer oder ggf wenn das Fenster verschieben/vergrössert wird.

    Viel Spass 🙂


  • Mod

    Was soll den der Quatsch mit UpdateLayeredWindow...?

    Hier geht es um ein ganz "normales" Aktualisierungs Problem der Standardfenster.

    Ich würde auch eher vermuten, dass WM_PAINT nicht ausgelöst wird. WM_PAINT wird nur ausgelöst wenn keine anderen Nachrihcten mehr zu verarbeiten sind, oder das Update schon lange aussteht.

    Pack Doch mal Trace ausgaben in Deinen Code (WM_TIMER, WM_PAINT, mit GetTickCount)...

    BTW: Wenn Du in WM_PAINT sowieso alles neu zeichnest und keine Rücksicht auf die zu zeichnenenden Bereichnimmst, kannst Du sowieso alles auch immer gleich neu zeichnen 😉



  • Wenn ich nur meine OnPaint(HWND hwnd) mit GetDC(hwnd), also sofort zeichnung, und ich rufe die dan auf, ohne WS_EX_LAYERED(Der bei mir übrigens nicht mit WS_CHILD funktioniert), dan wird auch nur im zweiten timer was gezeichnet, und im ersten nicht. Ist das zu verstehen??? 😕 wie verhext...


  • Mod

    Was soll bitte GetDC in OnPaint?
    Klar wird dann immer gezeichnet... totaler Unfug.

    Wenn nichts gezeichnet wird, gehe ich davon mal aus, dass dnan der Invalidate falsch ist...



  • Martin Richter schrieb:

    Was soll den der Quatsch mit UpdateLayeredWindow...?

    Hier geht es um ein ganz "normales" Aktualisierungs Problem der Standardfenster.

    @.@ Der Quatsch mit UpdateLayereWindow sorgt dafür das man sich um WM_PAINT nicht kümmern muss..
    Alleine schon bei der Einleitung bezweifel ich einfach mal das Du die Funktion überhaupt verstehst.

    Martin Richter schrieb:

    Ich würde auch eher vermuten, dass WM_PAINT nicht ausgelöst wird.

    Was händisch damals wie heute auch ein Drama werden kann,
    vor allem wenn man seine SDI Werke dann in s MDI transportieren will..
    ..Gottlob gibts dafür ja heute UpdateLayeredWindow() aber was rede ich,
    der Ton in diesem Board sagt schon alles..



  • Also es sieht jetzt gerade wieder alles so aus (immernoch mit Timer Problem), und WM_PAINT kommnt schon bei beiden an nur beim ersten Timer wird nicht's gezeichnet.

    BOOL CLoading::CreateLoadingControl(HWND hWndParent, int x, int y, int width, int height, bool bVisible)
    { 
    	// Register Window
    	RegisterWindow();
    	// Create the window
    	// send the this pointer as the window creation parameter
    	m_hwnd = CreateWindow(szClassName, Text.c_str(), (bVisible ? WS_VISIBLE : 0) | WS_CHILD, x, y, width, height, hWndParent, NULL, hInstance, (LPVOID)this);
    
    	return (m_hwnd != NULL);
    }
    
    void CLoading::OnPaint(HWND hwnd, LPPAINTSTRUCT ps)
    {
    	RECT rc;
    	HDC hdcMem;
    	HBITMAP hbmMem, hbmOld;
    	HPEN hPen;
    	HBRUSH hBrush;
    
    	GetClientRect(hwnd, &rc);
    
    	hdcMem = CreateCompatibleDC(ps->hdc);
    
    	hbmMem = CreateCompatibleBitmap(ps->hdc,
                            rc.right-rc.left,
                            rc.bottom-rc.top);
    
    	hbmOld = (HBITMAP)SelectObject(hdcMem, hbmMem);
    
    	hBrush = (HBRUSH)SendMessage(GetParent(hwnd), WM_CTLCOLORBTN, (WPARAM)hdcMem, (LPARAM)hwnd);
    	if(hBrush == NULL)
    		hBrush = (HBRUSH)GetClassLong(GetParent(hwnd),GCL_HBRBACKGROUND);
    	FillRect(hdcMem, &rc, hBrush);
    	DeleteObject(hBrush);
    
    	// ... Code ...
    
    	BitBlt(ps->hdc,
    	rc.left, rc.top,
    	rc.right-rc.left, rc.bottom-rc.top,
    	hdcMem,
    	0, 0,
    	SRCCOPY);
    
    	SelectObject(hdcMem, hbmOld);
    	DeleteObject(hbmMem);
    	DeleteDC(hdcMem);
    }
    
    LRESULT CALLBACK CLoading::WinMsgHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    	switch( msg )
    	{
    		case WM_CREATE:
    			{
    				SetTimer(hwnd, 1, 125, NULL);
    				SetTimer(hwnd, 2, 750, NULL);
    				return 0;
    			}
    
    		case WM_ERASEBKGND:
    			{
    				return TRUE;
    			}
    
    		case WM_PAINT:
    			{
    				PAINTSTRUCT ps;
    				BeginPaint(hwnd, &ps);
    				OnPaint(hwnd, &ps);
    				EndPaint(hwnd, &ps);
    				return 0;
    			}
    
    		case WM_TIMER:
    			{
    				if(wParam == 1)
    				{
    					if(Position < 7)
    						++Position;
    					else if(Position == 7)
    						Position = 0;
    					InvalidateRect(hwnd, NULL, FALSE);
    				}
    				else if(wParam == 2 && bEnablePointsAnimation)
    				{
    					if(PositionPoints < 3)
    						++PositionPoints;
    					else if(PositionPoints == 3)
    						PositionPoints = 0;
    					InvalidateRect(hwnd, NULL, FALSE);
    				}
    				return 0;
    			}
    
    		case WM_CLOSE:
    			{
    				HWND hWndParent = GetParent(hwnd);
    				DestroyLoadingControl();
    				SetActiveWindow(hWndParent);
    				return 0;
    			}
    
    		default:
    			return DefWindowProc( hwnd, msg, wParam, lParam );
    	}
    }
    

    Vielen Dank
    Johannes



  • Mit UpdateLayeredWindow() (OHNE WM_PAINT oder ähnliche Veränkungen)
    Funktioniert das Ganze folgendermassen:

    Du informierst Windows wie das Fenster gezeichnet werden soll
    mittels UpdateLayeredWindow() und das wars.
    Wenn Dein Bild sich verändern soll wieder UpdateLayeredWindow() und das wars.

    So "unpopuläre" Programme wie ProcessExplorer machen das genauso.

    Machst Du das Ganze von Hand gibts unendlich viele Stolpersteine.
    Das geht bei Fenstern los die sich atypisch verhalten
    (gibts immer mal wieder das verdeckte Fesnter nach dem aufdecken weisse Flächen statt windows zeigen)
    über Childs von Childs die Childs anderer Childs verdecken,
    über Custom Controls, Subclassing bis hin zum MDI.

    Hast Du nun also alles aus WM_PAINT rausgeschmissen,
    die InvalidateRects unterlassen und UpdateLayeredWindow() wie im Beispiel implementiert,
    dann kannst Du davon ausgehen das der Programmfehler woanders liegt.

    Leider hast Du eine Menge Unbekannter in deinem Ausschnitt.
    Position ist eine solche Unbekannte,
    die BOOL Variable oder was das ist eine andere.

    Die Anzahl möglicher Probleme/Lösungen steigt exponentiell zur Anzahl der Unbekannten also n^2.

    Was mir so auf den zweiten Blick auffällt ist,
    das man das Ganze vermutlich auch mit 1 Timer lösen kann,
    und das recht einfach denn die laufen ja nichtmal asynchron.

    Zur Not mal durch den Debugger laufen lassen und gucken
    welche Werte PositionPoints in den Verzweigungen so annimmt.

    Viel Erfolg 🙂


  • Mod

    Und warum sollteman für einfache Fenster diese Methode verwenden, die weitaus komplizierter ist als das normale Verfahren mit WM_PAINT?



  • Weil es eben nicht komplizierter ist.

    Aber das versteht man wohl erst wenn man sie einmal benutzt hat..

    Clipping, Invalidates, Manuelles WM_PAINT ..
    den ganzen Kram erspart man sich
    und lässt das Windows machen (denn es kann es einfach besser).

    Man setzt 1x eine Benachrichtung (Message) darüber ab wo Windows die Zeichenfunktion findet und funkt nicht wahllose WM_PAINT s
    die Windows aus dem Tritt bringen.

    Man muss auch keine Controls verbiegen die man nicht kennt,
    das alles macht Windows für einen wenn man die Funktion benutzt..

    Und was ist daran komplizierter als BEGINPAINT, ENDPAINT, Clipping, Invalidate, ++ ??

    Im Gegenteil bringt es noch vieles (optinales) mit
    was früher auch nur mit Veränkungen zu erreichen war,
    wie eben Alphakanäle..
    ..und das mit wesentlich weniger Code..
    ..probiers einfach mal aus



  • WS_CHILD ist so ein Fall für sich,
    dafür muss man mal eine Etage tiefer in die WinApi
    und sich überlegen welchen Sinn es macht
    selbstgezeichnete Fenster als Child oder Parent zu benutzen..
    ..Sinn im Sinne von "harmonische Koexistenz mit der WinApi"
    ..nicht im Sinne von "alles was der Programmierer sich denken kann macht auch Sinn"

    Ein button zb ist ja auch ein window,
    wenn man an dessen Zeichenroutine nichts verändern will,
    warum sollte man die dann in ein selbstgezeichnetes Fenster stopfen ?

    Da gibs nichts zu vererben was nicht schon da wäre..
    ..aber die Zeichnerei kollidiert unter Umständen mit der bereits
    funktionierenden Zeichenmethode des Controls

    Also einfach

    window
    |--selbstgezeichnetes window
    |--button (window)

    anstelle von

    window
    |--selbstgezeichnetes window
    |----button (window)

    für den Fall das das selbstgezeichnete window der "container" sein soll

    auch eher so

    dummy window (unsichtbar)
    |--selbstgezeichneter mainframe
    |--unterfenster von mainframe

    spart übrigens auch auf dem alten Weg über WM_PAINT erheblich Stress..



  • < hab da ein OWNER anstelle von PARENT vergessen
    aber ich vermute Du weisst was ich meine



  • double buffering spart man sich mit UpdateLayerWindow() übrigens auch,
    zusammen mit dem oben beschriebenem Synchronisationsproblem
    (windows aus dem Tritt bringen)
    vermutlich ein Grund warum GDI fälschlicherweise als "extrem langsam"
    vorverurteilt wird..


  • Mod

    Ich finde es absoluten Quatsch mich in solchen simplen UI Programmen und das Rendern meines Contents zu kümmern.
    Warum sollte ich das tun?

    Es ist aus meiner Sicht kompliziert. Nicht mehr Windows kümmert sich um meine Darstellung. Ich muss die Darstellung zu einer "Bitmap" erstmal zusammenwurschteln. Teilweise Updates werden komplizierter. Sanftes Rollen unmöglich.

    Just my 2 cents. UpdateLayeredWindow ist nett, wo man es gebrauchen kann. hier(nach meiner persönlichen Meinung) nicht.
    Für mich ist die Diskussion hier beendet.



  • Martin Richter schrieb:

    Ich finde es absoluten Quatsch mich in solchen simplen UI Programmen und das Rendern meines Contents zu kümmern.
    Warum sollte ich das tun?

    Welcher Glaskugel hast Du denn "simples UI" entnommen ?
    (ich sehe da einzig die Ansätze einer Animation - kann alles mögliche werden)

    Martin Richter schrieb:

    Es ist aus meiner Sicht kompliziert. Nicht mehr Windows kümmert sich um meine Darstellung. Ich muss die Darstellung zu einer "Bitmap" erstmal zusammenwurschteln. Teilweise Updates werden komplizierter. Sanftes Rollen unmöglich.

    @.@ wetterst Du jetzt gegen selbstgezeichnete Controls/Content s im allgemeinen ?
    ..falls es Dir nicht aufgefallen ist aber Johannes zeichnet so ein Bitmap schon selber

    Martin Richter schrieb:

    UpdateLayeredWindow ist nett, wo man es gebrauchen kann. hier(nach meiner persönlichen Meinung) nicht.

    Der andere Weg ist holpriger, umständlicher und per Ferndiagnose nunmal nicht durchschaubar..


Anmelden zum Antworten