Thread: Question about painting

  1. #1
    Registered User
    Join Date
    Aug 2003
    Posts
    34

    Question about painting

    I'm reading Charles Petzold's "Programming Windows" and I think I didn't fully understand the term of erasing / (in)validating / painting a window.

    At the bottom of the post, you'll find the example code from the book.

    Now, if I understood correctly, the window is being created by CreateWindow. This function reserves memory space for the window and sends the first WM_CREATE message to windows procedure. Next, ShowWindow sends the first WM_ERASEBKGND and WM_SIZE messages. Finally, WM_PAINT from UpdateWindow paints the window.

    Now, the window procedure is (through the message loop) ready to receive additional messages (input from user).

    Now, my questions:

    1) As you can see, in this example, window procedure can only process WM_CREATE, WM_PAINT and WM_DESTROY messages, while other messages are processed by DefWindowProc. What does this function exactly do? Invalidates/repaints the whole client area?

    2) During WM_PAINT, BeginPaint paints the client area. Does this function paint the whole client area (always) or can it paint only part of it (after uncovering a hidden window part)? What erases the client area before painting?

    Code:
    #include <windows.h>
    
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
    
    static TCHAR szAppName[]=TEXT("HelloWin");
    HWND hwnd;
    MSG msg;
    WNDCLASS wndclass;
    
    wndclass.style=CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc=WndProc;
    wndclass.cbClsExtra=0;
    wndclass.cbWndExtra=0;
    wndclass.hInstance=hInstance;
    wndclass.hIcon=LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor=LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName=NULL;
    wndclass.lpszClassName=szAppName;
    
    if (!RegisterClass(&wndclass)) {
    MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
    return 0;
    }
    
    hwnd=CreateWindow(szAppName, TEXT("The Hello Program"), WS_CAPTION, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);
    
    while(GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }
    
    return msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    HDC hdc;
    PAINTSTRUCT ps;
    RECT rect;
    
    switch(message) {
    case WM_CREATE: return 0;
    
    case WM_PAINT:
    hdc=BeginPaint(hwnd, &ps);
    GetClientRect(hwnd, &rect);
    DrawText(hdc, TEXT("Hello, Windows 98!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_CENTER);
    EndPaint(hwnd, &ps);
    return 0;
    case WM_DESTROY:
    PostQuitMessage(0);
    return 0;
    }
    
    return DefWindowProc(hwnd, message, wParam, lParam);
    }

  2. #2
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    DefWindowProc provides default system handling for messages:
    The DefWindowProc function calls the default window procedure to provide default processing for any window messages that an application does not process. This function ensures that every message is processed.
    BeginPaint sets up the window for painting filling a PAINTSTRUCT with information such as the update region, whether or not the background needs erasing etc, ie:
    The BeginPaint function automatically sets the clipping region of the device context to exclude any area outside the update region. The update region is set by the InvalidateRect or InvalidateRgn function and by the system after sizing, moving, creating, scrolling, or any other operation that affects the client area. If the update region is marked for erasing, BeginPaint sends a WM_ERASEBKGND message to the window.
    It paints whatever is needed or it's told to do ie all or part of the client area.
    Last edited by Ken Fitlike; 08-21-2003 at 04:59 PM.
    CProgramming FAQ
    Caution: this person may be a carrier of the misinformation virus.

  3. #3
    Registered User
    Join Date
    Aug 2003
    Posts
    34
    Originally posted by Ken Fitlike
    DefWindowProc provides default system handling for messages
    Thanks but that didn't quite answer my question. Does it erase and repaint the entire client area?


    BeginPaint sets up the window for painting filling a PAINTSTRUCT with information such as the update region, whether or not the background needs erasing etc, ie:
    What if it does need erasing? Who erases it?

    And BTW, when is a part of the client area or the entire client area validated?

    In what order is the window being repainted? Invalidate, erase, validate, paint?

  4. #4
    jasondoucette.com JasonD's Avatar
    Join Date
    Mar 2003
    Posts
    278
    Thanks but that didn't quite answer my question. Does it erase and repaint the entire client area?

    It just calls BeginPaint and EndPaint. Look up both of those functions to see what they do.

    What if it does need erasing? Who erases it?

    No window ever 'needs' erasing. It's something that happens when you move a window over top of another window, and then move it away. The window that just got 'erased' by the other window needs to paint those portions of it that are now visible again. It also happens when you minimize and maximize a window. The OS doesn't save what the window looked like. It's up to you to redraw the entire window, in this case.

    And BTW, when is a part of the client area or the entire client area validated?

    Think of the screen as a piece of paper that you draw all of your windows on. When you open up a window that covers another window, it paints over what was drawn. Then, if you minimize it or move it, you need to redraw what is left in its old spot. For example, if you have a drawing with a tree in front of a house, and you 'move' the tree by redrawing it elsewhere, you need to 'repaint' the house where the old location of the tree was. This is the basics of how windows works. You needn't worry about when it happens ... you just need to know how to process the WM_PAINT message properly.

    In what order is the window being repainted? Invalidate, erase, validate, paint?

    An invalid portion of a window is a section that has been covered up and uncovered. It becomes valid once you call BeginPaint and EndPaint, assuming that you have re-drawn what is needed to make it valid. This re-drawing is up to you.

  5. #5
    Registered User
    Join Date
    Aug 2003
    Posts
    34
    Hey thanks for the comprehensive reply, but I'm afraid it confused me even more. I thought that every portion of the window that is invalid must be erased (background brush, e.g. white) and then drawn on (text, pictures).

    And on a newsgroup I read that WM_ERASEBKGND is usually sent to the window procedure before WM_PAINT.

    So I hope you understand my confusion.

  6. #6
    jasondoucette.com JasonD's Avatar
    Join Date
    Mar 2003
    Posts
    278
    No, not every program requires that the invalid portion of a window be erased. Charles Petzold chooses to do so, even when it is not needed. A large portion of his prorgams do not require this, however. You will notice that Charles Petzold's example all have a background brush set in the window class with a statement like the following:
    Code:
    wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
    However, there is another option. You can set no background brush, like so:
    Code:
    wndclass.hbrBackground = NULL ;
    Basically, if a window has a background brush, then the WM_ERASEBKGND message, when handled by DefWindowProc() as a result of you not processing it yourself, erases the invalid portion with the background brush. This is explained on the MSDN page for WM_ERASEBKGND:
    "The DefWindowProc function erases the background by using the class background brush specified by the hbrBackground member of the WNDCLASS structure."

    If there is no background brush, the default processing for this message does nothing. On the above MSDN page, it says "If hbrBackground is NULL, the application should process the WM_ERASEBKGND message and erase the background.", but this is not really true. You need not process it, if your WM_PAINT handler is planning on drawing over the entire area anyway. For a bunch of Charles Petzold's programs, this is the case. Therefore he should really set the background brush as NULL. Because he sets it to a white brush, when the window is resized (for most programs - not all), the entire window is erased with white, and then the entire window is drawn over with something else. This creates needless flicker.

    To add some further notes: The MSDN page states that when handling the WM_ERASEBKGND message, you can return 0 to mean that you did not handle it, so this is the same thing as not handling it at all. When you get to the processing of WM_PAINT, you will call the BeginPaint() function, which fills a PAINTSTRUCT structure that has a field in it that states whether or not the background was erased. You can choose to erase the background here, if you wish, or ignore it if you need not erase the background (which is the case if your drawing will fill the invalid area anyway).

    You may like to check out my Errata Addendum for Petzold's book in my signature.

  7. #7
    Registered User
    Join Date
    Aug 2003
    Posts
    34
    Originally posted by JasonD
    You need not process it, if your WM_PAINT handler is planning on drawing over the entire area anyway.
    Is this the case when your windows has no background, e.g. there's a picture across the entire client area?

    What if there's just text on the client area, then it's necessary to erase the background?

  8. #8
    jasondoucette.com JasonD's Avatar
    Join Date
    Mar 2003
    Posts
    278
    Yes, you are correct for the two examples you mentioned.

    The reason you need to erase the background for the text example is because the WM_PAINT handler does not re-draw everything that is invalid. You can see this yourself by setting the background brush to NULL, and start resizing the window. In the case of a program that displays an image (if the image is ALWAYS equal to or greater than the size of the client area), the drawing of the image will redraw any invalid portion, so why bother erasing it with white beforehand? In this case, try out both methods, and you'll see the one with the brush creates needless flicker, whereas the other one is smooth.

    Please note that if the WM_PAINT handler potentially has to draw an image smaller than the client area size, then there will be portions of the window that are not covered by the image that needs to be refreshed. If there's no background brush, the WM_PAINT will have to fill in this area itself.

  9. #9
    Registered User
    Join Date
    Aug 2003
    Posts
    34
    OK, thanks.

    I have a couple of more questions though:

    1) ValidateRect. In MSDN, it says:

    The ValidateRect function validates the client area within a rectangle by removing the rectangle from the update region of the specified window.
    When is usually this function used? Can you give me an example?

    2) In InvalidateRect, when the third argument (bErase) is set to FALSE, in the PAINTSTRUCT structure after calling BeginPaint fErase will be TRUE. What does that mean to a programmer? That he needs to manually erase background?

  10. #10
    jasondoucette.com JasonD's Avatar
    Join Date
    Mar 2003
    Posts
    278
    1. BeginPaint() and EndPaint() normally validate the rectangle for you, so you do not have to ever call this under normal circumstances. I cannot think of an instance where it should be called off of the top of my head. Basically, if you painted the an area of the client area, then you can validate it, so the WM_PAINT handler does not have to re-draw it. But normally, the WM_PAINT handler is the only thing that is drawing to the screen to being with.

    2. If you do not process the WM_ERASEBKGND message, or of you return 0 for it, then the PAINTSTRUCT structure will set fErase to indicate that the background has not been erased. It is up to you if you wish to erase it at this time by checking this field. Normally, if you have not erased it already by processing that message (or by setting a background brush), then you do not want it erased anyway.

  11. #11
    Registered User
    Join Date
    Aug 2003
    Posts
    34
    OK, thanks a bunch!!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Alice....
    By Lurker in forum A Brief History of Cprogramming.com
    Replies: 16
    Last Post: 06-20-2005, 02:51 PM
  2. Debugging question
    By o_0 in forum C Programming
    Replies: 9
    Last Post: 10-10-2004, 05:51 PM
  3. Question about pointers #2
    By maxhavoc in forum C++ Programming
    Replies: 28
    Last Post: 06-21-2004, 12:52 PM
  4. Question...
    By TechWins in forum A Brief History of Cprogramming.com
    Replies: 16
    Last Post: 07-28-2003, 09:47 PM
  5. Question, question!
    By oskilian in forum A Brief History of Cprogramming.com
    Replies: 5
    Last Post: 12-24-2001, 01:47 AM