The MASM Forum Archive 2004 to 2012

General Forums => The Workshop => Topic started by: hotrod on January 23, 2010, 01:28:35 AM

Title: WM_PAINT & InvalidateRect
Post by: hotrod on January 23, 2010, 01:28:35 AM
This code will draw a rectangle each time the button is pressed, but only the last one is left if the window loses focus. I know that WM_PAINT accumulates requests before painting and does it all in one pass. What is the work around for this problem? Thanks...


.elseif uMsg==WM_COMMAND
.if wParam==1001 ; Button
mov hBG_Brush, rv(CreateSolidBrush, colRed)
add RT.left, 31
add RT.right, 30
invoke InvalidateRect, hWin, addr RT, FALSE
.endif
ret
.elseif uMsg==WM_PAINT
mov hDC, rv(BeginPaint, hWin, addr PS)
invoke FillRect, hDC, addr RT, hBG_Brush
invoke EndPaint, hWin, addr PS
ret
Title: Re: WM_PAINT & InvalidateRect
Post by: MichaelW on January 23, 2010, 08:12:34 AM
One workaround would be to draw the rectangles on a bitmap, and after each one is drawn copy the entire bitmap to the client area of the window. The attachment contains an example.
Title: Re: WM_PAINT & InvalidateRect
Post by: hotrod on January 23, 2010, 11:28:24 AM
Thanks MichaelW, there is a lot of info in the example and will keep me busy for a couple of days; I really appreciate all the comments. What is causing the deletion of the previous object if (invoke InvalidateRect, hWin, addr RT, FALSE) bErase is set to false? I would normally say that the entire window is being repainted, but that can't be true if the previous object is still visible when the new one is added.

I have also notice this same issue with control (statics, edits) backgrounds which makes it a little more complicated in that using this approach, the static would loose its attributes such as SetDlgItemInt since it is being translated to a bitmap. Is there another solution?

Am I correct in saying that redrawing the entire window, which takes processing time, is the answer to both issues so graphics and controls can coexist or am I off in left field?
Title: Re: WM_PAINT & InvalidateRect
Post by: MichaelW on January 23, 2010, 01:20:49 PM
The previous rectangles remain visible only until the client area background is erased. The current rectangle is visible after the background is erased because your WM_PAINT handler is redrawing it. The attachment contains a test app that I hope illustrates what is happening.

Child controls in a window, including the static controls, are redrawn automatically.
Title: Re: WM_PAINT & InvalidateRect
Post by: Tight_Coder_Ex on January 25, 2010, 05:16:18 PM
Actually InvalidateRect, all it does is accumulates areas that will need updating.  If you want the button press to respond each time add invoke   UpdateWindow, hWin and then WM_PAINT is forced to respond to each button press
Do this immediately after InvalidateRect
Title: Re: WM_PAINT & InvalidateRect
Post by: hotrod on January 25, 2010, 05:58:22 PM
Hello Tight_Coder_Ex and thanks, but the issue is still there. I think I tried your suggestion before, but I have done so many that I can't remember them all. If you have a working example, I would appreciate it.



.elseif uMsg==WM_COMMAND
.if wParam==1001 ; Button
mov hBG_Brush, rv(CreateSolidBrush, colRed)
add RT.left, 31
add RT.right, 30
invoke InvalidateRect, hWin, addr RT, FALSE
invoke UpdateWindow, hWin    ; Your code
.endif
ret
Title: Re: WM_PAINT & InvalidateRect
Post by: Tight_Coder_Ex on January 25, 2010, 06:12:28 PM
Maybe send me a copy of the entire application and I may be able to spot something that is not even in the area we are looking at
Title: Re: WM_PAINT & InvalidateRect
Post by: hotrod on January 25, 2010, 06:19:28 PM
Here you go and thanks.
Title: Re: WM_PAINT & InvalidateRect
Post by: qWord on January 25, 2010, 07:58:16 PM
maybe a global mem-DC is the right for your task:
WndProc proc    hWin:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL PS    :PAINTSTRUCT
LOCAL hDC   :HDC
LOCAL nx    :DWORD
LOCAL ny    :DWORD

    SWITCH uMsg
        CASE WM_CREATE
            push hWin
            pop hMain

            invoke CreateWindowEx, WS_EX_CLIENTEDGE, SADD("BUTTON"), SADD("MAKE RECT"),WS_CHILD + BS_PUSHBUTTON + WS_VISIBLE, 50, 100, 50, 25, hWin, 1001, hInstance, NULL

            .data
                hMemDC  dd ?                            ; global DC , back-buffer
                RT      RECT <10-30, 10, 30-30, 30>     ; new start-value: -20,10,0,30
            .code

            mov hDC,rv(GetDC,0) ; get Desktop-DC
            mov hMemDC,rv(CreateCompatibleDC,eax)                                               ; create MemDC

            mov nx,rv(GetSystemMetrics,SM_CXSCREEN)                                             ; use screen size for MemDC
            mov ny,rv(GetSystemMetrics,SM_CYSCREEN)                                             ;
            invoke DeleteObject,rv(SelectObject,hMemDC,rv(CreateCompatibleBitmap,hDC,nx,ny))    ;
            invoke ReleaseDC,0,hDC

            invoke DeleteObject,rv(SelectObject,hMemDC,rv(CreateSolidBrush,0ffffffh))           ; Fill memory DC with white color
            invoke DeleteObject,rv(SelectObject,hMemDC,rv(CreatePen,PS_SOLID,1,0ffffffh))       ;
            invoke Rectangle,hMemDC,0,0,nx,ny                                                   ;

            invoke DeleteObject,rv(SelectObject,hMemDC,rv(CreateSolidBrush,colRed))             ; select brush = red
            invoke DeleteObject,rv(SelectObject,hMemDC,rv(CreatePen,PS_SOLID,1,colRed))         ; select pen   = red

        CASE WM_COMMAND
            .if wParam==1001    ; Button

                add RT.left, 30
                add RT.right,30

                .if RT.right > 300  ; some limit for 'line break'
                    mov RT.left,10
                    mov RT.right,30
                    add RT.top,30
                    add RT.bottom,30
                .endif
                invoke Rectangle,hMemDC,RT.left,RT.top,RT.right,RT.bottom   ; draw rectangle in hMemDC using current brush and pen (color=red)
                invoke InvalidateRect,hWin,ADDR RT,FALSE                    ; invalidate drawn rectangle
            .endif
            ret
        CASE WM_PAINT
            mov hDC, rv(BeginPaint, hWin, addr PS)

            ; copy only what needed to screen
            mov eax,PS.rcPaint.left
            sub PS.rcPaint.right,eax
            mov edx,PS.rcPaint.top
            sub PS.rcPaint.bottom,edx
            invoke BitBlt,hDC,PS.rcPaint.left,PS.rcPaint.top,PS.rcPaint.right,PS.rcPaint.bottom,hMemDC,PS.rcPaint.left,PS.rcPaint.top,SRCCOPY

            invoke EndPaint, hWin, addr PS
            ret
        CASE WM_DESTROY
            invoke PostQuitMessage, NULL

        DEFAULT
            invoke DefWindowProc, hWin, uMsg, wParam, lParam
            ret
    ENDSW

    xor eax, eax
    ret
WndProc endp
Title: Re: WM_PAINT & InvalidateRect
Post by: hotrod on January 25, 2010, 08:24:54 PM
Works for me qWord. This is very similar to what I got from MichaelW for a BitBlt problem I was having. It seems that back buffering is the ideal way controlling the normal processing of Window's events for graphics and has given me a large boost. I think they should have made WM_ERASEBKGND more user friendly instead of embedding it. I have seen more references to it by users trying to program graphics than any other single issue.
Title: Re: WM_PAINT & InvalidateRect
Post by: Tight_Coder_Ex on January 25, 2010, 10:49:31 PM
CS_SAVEBITS eliminated the problem of erasure when moving the border, so long as the border wasn't resized to be smaller or cover existing rectangles

Solution, by either a compatible DC such as would be used for scrolling or an array of values so the entire set of rectangles could be recreated in thier original positions.
Title: Re: WM_PAINT & InvalidateRect
Post by: hotrod on January 25, 2010, 11:01:39 PM
Thanks Tight_Coder_Ex, I think it is working now. I have BitBlt as provided by qWord (above) and MichaelW (earlier BitBlt post) along with the array that I came up with.
Title: Re: WM_PAINT & InvalidateRect
Post by: xandaz on November 19, 2010, 11:54:55 PM
   Can someone tell me why the rectangle doesnt clear?


.code
...
invoke GetClientRect,hStatic,addr rect
invoke InvalidateRect,hStatic,addr rect,TRUE
invoke SendMessage,hStatic,WM_SETTEXT,NULL,addr MyString

Title: Re: WM_PAINT & InvalidateRect
Post by: Antariy on November 20, 2010, 12:05:29 AM
Try this:


.code
...
invoke GetClientRect,hStatic,addr rect
invoke RedrawWindow,hStatic,addr rect,0,RDW_ERASE or RDW_INVALIDATE or RDW_ERASENOW ; <--- THIS
invoke SendMessage,hStatic,WM_SETTEXT,NULL,addr MyString

Title: Re: WM_PAINT & InvalidateRect
Post by: xandaz on November 20, 2010, 12:19:05 AM
   Thanks Antary but it doesn't work wither. ty
Title: Re: WM_PAINT & InvalidateRect
Post by: Antariy on November 20, 2010, 12:21:09 AM
If use:

invoke RedrawWindow,hStatic,addr rect,0,RDW_ERASE or RDW_INVALIDATE or RDW_UPDATENOW ; UPDATENOW instead


This should force repainting.
Title: Re: WM_PAINT & InvalidateRect
Post by: hotrod on November 20, 2010, 12:31:46 AM
It appears that you are using a static and trying to clear the text. If so, use an empty string:
QuoteMyString equ "",0

invoke SendMessage,hStatic,WM_SETTEXT,NULL,addr MyString
Title: Re: WM_PAINT & InvalidateRect
Post by: xandaz on November 20, 2010, 12:35:11 AM
   It crossed my mind hotrod but clearing it seems more better. thanks antariy.
Title: Re: WM_PAINT & InvalidateRect
Post by: xandaz on November 20, 2010, 12:36:36 AM
   oh yeah btw. It clears the area if i use NULL for window handle when using invalidaterect, but, the whole screen gets updated or something. It's strange.
Title: Re: WM_PAINT & InvalidateRect
Post by: xandaz on November 20, 2010, 12:38:16 AM
   Not working antariy..Anyone else want to give it a try?
   Thanks for th helpingness.
Title: Re: WM_PAINT & InvalidateRect
Post by: Antariy on November 20, 2010, 12:40:48 AM
Quote from: xandaz on November 20, 2010, 12:36:36 AM
   oh yeah btw. It clears the area if i use NULL for window handle when using invalidaterect, but, the whole screen gets updated or something. It's strange.

NULL (0) is a handle of desktop - i.e. entire screen. You are repainting all screen then.
My suggestions is followed your code. But you can set new text to static without manual redrawing of it. If you do not use special styles like transparency - this will work fine.

So, instead of:

.code
...
invoke GetClientRect,hStatic,addr rect
invoke RedrawWindow,hStatic,addr rect,0,RDW_ERASE or RDW_INVALIDATE or RDW_ERASENOW ; <--- THIS
invoke SendMessage,hStatic,WM_SETTEXT,NULL,addr MyString


Just:

.code
...
invoke SendMessage,hStatic,WM_SETTEXT,NULL,addr MyString

Title: Re: WM_PAINT & InvalidateRect
Post by: xandaz on November 20, 2010, 12:49:34 AM
  There's no transparency i think, and dont get me wrong cause i appreciate the help. The thing is that i'm going to display the current directory. if the previous string is longer than the current it will show parts of the previous. Thanks
Title: Re: WM_PAINT & InvalidateRect
Post by: hotrod on November 20, 2010, 12:50:51 AM
xandaz, you are trying to clear a control by using graphics and I don't think you are not going to be able to isolate the static from the rest of the window. The other possibility is to DrawText on the window and not use a static. You can then clear it with a rectangle.
Title: Re: WM_PAINT & InvalidateRect
Post by: xandaz on November 20, 2010, 01:31:33 AM
   Your answer seems satisfying enough. i quit. ill use a string.