Common controls are not properly painted when I resize window
Asked Answered
B

1

6

INTRODUCTION:

I am creating tab control with child dialog boxes as pages.

I have Visual Styles enabled via #pragma comment. I have also called InitCommonControlsEx and #pragma comment( lib, "comctl32.lib" ) as well.

Initially, when window loads, dialog and its common controls have proper background, please see image below:

enter image description here

During resizing things are not so consistent -> background starts to mismatches visibly. I will provide screenshot below:

enter image description here

You can clearly see that checkbox and static control have improper background, while it seems to me that dialog box ( created to act as a child control ) has proper background.

Edited on November 24th, 2014:

After enclosing controls into group boxes there seems to be no painting problems. My monitor is old CRT ( Samsung SyncMaster 753s ), and I have bad eyesight, but again, it seems that everything paints properly. The window still flickers horribly on resize, but I have tried everything in my power to fix it.

QUESTION:

How can I fix this?

MY EFFORTS TO SOLVE THIS:

I haven't found anything yet but I am still Goggling while typing this question...

RELEVANT INFORMATION:

Here are the instructions for creating demo that illustrates the problem:

1.) Create empty C++ project in Visual Studio;

2.) add header file, name it pomocne_funkcije.h and copy/paste following:

#include <windows.h>
#include <windowsx.h>
#include <comutil.h>
#include <commctrl.h>
#include <stdio.h>
#include <vector>
#include <ole2.h>
#include <string>
#include <stdlib.h>
#include <locale.h>
#include <Uxtheme.h>

#pragma comment( linker, "/manifestdependency:\"type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
language='*'\"")

#pragma comment( lib, "comctl32.lib")
#pragma comment( lib,"Msimg32.lib")
#pragma comment( lib, "comsuppw.lib")
#pragma comment( lib, "UxTheme.lib")

3.) Create dialog box in resource editor, add checkbox static control.

Set the following for dialog box:

  • Border : none
  • Control : true
  • Control parent : true
  • Style : child
  • System menu : false

4.) Here is the code for main.cpp :

#include "pomocne_funkcije.h"

static HINSTANCE hInst;

// dialog procedure for firts tab
INT_PTR CALLBACK Ugovori(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        {
            EnableThemeDialogTexture( hDlg, ETDT_ENABLETAB );
        }
        return (INT_PTR)TRUE;
    }
    return (INT_PTR)FALSE;
}

// main window procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static HWND hDlgFirstTab;  // handle to the first page dialog box
    switch(msg)
    {
    case WM_CREATE:
        {
            RECT rcClient = {0};
            ::GetClientRect( hwnd, &rcClient );

            HWND hwndTab = CreateWindowEx( 0, WC_TABCONTROL, 
                L"Ugovori", WS_CHILD | WS_VISIBLE, 
                10, 10, rcClient.right - rcClient.left - 20,
                rcClient.bottom - rcClient.top - 63, 
                hwnd, (HMENU)3000, 
                ((LPCREATESTRUCT)lParam)->hInstance, 0 );

            TCITEM tci = {0};

            tci.mask = TCIF_TEXT;
            tci.pszText = L"Основни подаци";
            TabCtrl_InsertItem( hwndTab, 0, &tci );

            // set font so cyrilic symbols can be properly displayed instead of ???
            SendMessage( hwnd, WM_SETFONT, 
                (WPARAM)(HFONT)GetStockObject(DEFAULT_GUI_FONT),
                (LPARAM)TRUE );

            SendMessage( hwndTab, WM_SETFONT, 
                (WPARAM)(HFONT)GetStockObject(DEFAULT_GUI_FONT),
                (LPARAM)TRUE );

            // create page ( dialog box )
            hDlgFirstTab = CreateDialog( ((LPCREATESTRUCT)lParam)->hInstance,
                MAKEINTRESOURCE(IDD_DIALOG1), 
                hwnd, 
                (DLGPROC)Ugovori );  // dialog procedure 

            ShowWindow( hDlgFirstTab, SW_SHOW );

        }
        return 0L;
    case WM_MOVE:
    case WM_MOVING:
    case WM_SIZING:
    case WM_SIZE:
        {
            RECT rcClient = {0};
            GetClientRect( hwnd, &rcClient );

            SetWindowPos( GetDlgItem( hwnd, 3000 ), NULL, 
                rcClient.left + 10, // move it away from window edge by 10 pixels
                rcClient.top + 10,  // move it away from window edge by 10 pixels
                rcClient.right - rcClient.left - 20,  
                // - 63 was the size of the button, 
                // but I have deleted that button here to preserve space
                rcClient.bottom - rcClient.top - 63, 
                SWP_NOZORDER | SWP_NOCOPYBITS );

            // get tab control's client rectangle
            GetClientRect( GetDlgItem( hwnd, 3000 ), &rcTab );

            //============= place dialog box into tab's client area
            MapWindowPoints( GetDlgItem( hwnd, 3000 ), hwnd, 
                (LPPOINT)(&rcTab), 2 );
            // get tab's display area
            TabCtrl_AdjustRect( GetDlgItem( hwnd, 3000 ), 
                FALSE, &rcTab );
            // move dialog box
            SetWindowPos(hDlgFirstTab, NULL, 
                rcTab.left, rcTab.top,
                rcTab.right - rcTab.left, 
                rcTab.bottom - rcTab.top, 
                SWP_NOZORDER);

            //========================= done
            // repaint window
            InvalidateRect( hwnd, NULL, FALSE );
        }
        return 0L;
    case WM_ERASEBKGND:
        return 1L;
    case WM_PAINT:
        {
            PAINTSTRUCT ps = {0};
            HDC hdc = BeginPaint( hwnd, &ps );
            SendMessage( hwnd, WM_PRINTCLIENT, (WPARAM)hdc, 0 );
            EndPaint( hwnd, &ps );
        }
        return 0L;
    case WM_PRINTCLIENT:
        {
            RECT rcClient = {0};
            GetClientRect( hwnd, &rcClient );

            FillRect( (HDC)wParam, &rcClient, 
                (HBRUSH)GetStockObject(WHITE_BRUSH) );
        }
        return 0L;
    case WM_CLOSE:
        ::DestroyWindow(hwnd);
        return 0L;
    case WM_DESTROY:
        ::PostQuitMessage(0);
        return 0L;
    default:
        return ::DefWindowProc( hwnd, msg, wParam, lParam );
    }
    return 0;
}

// WinMain

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, 
                   int nCmdShow)
{
    // store hInstance in global variable for later use

    hInst = hInstance;

    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;

    // initialize common controls

    INITCOMMONCONTROLSEX iccex;
    iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
    iccex.dwICC = ICC_LISTVIEW_CLASSES | ICC_UPDOWN_CLASS | 
       ICC_STANDARD_CLASSES | ICC_TAB_CLASSES;
    InitCommonControlsEx(&iccex);

    // register main window class

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;
    wc.hIcon = LoadIcon( hInstance, IDI_APPLICATION );
    wc.hCursor = LoadCursor( NULL, IDC_ARROW );
    wc.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );
    wc.lpszMenuName = NULL;
    wc.lpszClassName = L"Main_Window";
    wc.hIconSm = LoadIcon( hInstance, IDI_APPLICATION );

    if(!RegisterClassEx(&wc))
    {
        MessageBox(NULL, 
            L"Window Registration Failed!", L"Error!", 
            MB_ICONEXCLAMATION | MB_OK);

        return 0;
    }

    // create main window

    hwnd = CreateWindowEx( 0, L"Main_Window", 
        L"Contract manager", 
        WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, 
        CW_USEDEFAULT, 
        CW_USEDEFAULT, 
        CW_USEDEFAULT, 
        CW_USEDEFAULT, 
        NULL, NULL, hInstance, 0 );

    if(hwnd == NULL)
    {
        MessageBox(NULL, L"Nemogu da napravim prozor!", L"Greska!",
            MB_ICONEXCLAMATION | MB_OK);

        return 0; 
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    while(GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }

    return Msg.wParam;
}

I am working in Visual Studio 2008 on Windows XP using C++ and WinAPI.

Bentwood answered 23/11, 2014 at 3:55 Comment(10)
I would guess the call to EnableThemeDialogTexture is part of the problem. Have you tried just omitting that?Hospitalet
I mostly solved the transparent control issue here. No idea about flicker (nor am I fully sure how what I did would impact the amount of flicker because my use case has a garbage collector in the way); would need to read this question more...Revelation
@andlabs: Yes I know, I left you a comment there :) Implementing WM_PRINTCLIENT and using DrawThemeParentBackground in WM_CTLCOLORSTATIC solves issue for radio button, checkbox nad static control. But if you add trackbar for example you will be screwed :( What can I say, MS Visual Styles...such a needless pain...Bentwood
Yep, figuring those out will be fun... =P But yeah, flicker is something I still need to figure out, and the stop-the-world garbage collector I mentioned isn't helping =PRevelation
@Cheersandhth.-Alf: Removing EnableThemeDialogTexture from WM_INITDIALOG makes child dialog without tab control's background color. since your comment got an upvote, I wanted to investigate further. I have removed EnableThemeDialogTexture as you said, and called it in main window's WM_CREATE handler. The effect was the same -> dialog box had proper background but after resize one could still see a mismatch... I will try harder and post my results. Thank you for trying to help. Best regards.Bentwood
@AlwaysLearningNewStuff: I'm sorry that it seems I don't have time to look at this now (reproduce it and investigate). I was just throwing out a gut-feeling impression. It has often helped people, but not always right. :(Hospitalet
@Cheersandhth.-Alf: Well, someone upvoted your comment... that means that they know you're right but do not want to help which is shameful... I will continue to struggle, althoug some good Visual Styles tutorials could be very helpful... Again, thanks for trying to help. To me it means just as same as if you solved my problem. Best regards.Bentwood
As in your other question, your code is handling many messages that you do not normally need to, especially the stack of them related to WM_SIZE. You should also be aware that when manipulating objects in a dialog, the coordinates are in dialog units not pixels (based on the dialog font).Jordain
@JeffD.: As in your other question, your code is handling many messages that you do not normally need to I need WM_ERASEBKGND for preventing flicker, WM_PRINTCLIENT is essential for common controls when Visual Styles are enabled ( it was the only way I could get transparent radio button / checkbox etc... ). I took your advice from my previous question and left only WM_SIZE but there is no progress. Removing EnableThemeDialogTexture from dialog and adding it in WM_CREATE ( by passing window handle instead of a dialog handle ) localized the problem to top left part of the window.Bentwood
@JeffD.: Continuing from above: it is now barely visible, but it is still visible when one looks at it... Thank you for helping, I really appreciate it. Best regards.Bentwood
L
4

Your tab dialogs are below the tab control in the Z order. This causes crazy overdraw issues on first resizing. After adding the tab dialogs as childs to your main window, call SetWindowPos(tab, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE) to move them to the top of the Z order.

Had this exact issue; took me days to figure it out. I created a simple application in Visual Studio:

  1. manifest reference to ComCtrl v 6
  2. InitCommonControlsEx()
  3. a dialog with a tab control
  4. a sub-dialog for the tab content (sibling to the tab)
  5. in the sub-dialog, a radio button and a static
  6. no fancy handling of WM_PRINTCLIENT or anything alike
  7. just EnableThemeDialogTexture() in WM_INITDIALOG of the sub-dialog

I went to check it on XP and … it worked perfect and looked beautiful, apart from flickering.

I added WS_CLIPCHILDREN and suddenly was able to reproduce your screenshots perfectly. I was first thinking this was the cause.

I kept pushing and added WS_COMPOSITED to prevent flickering, and the tab window suddenly was gone – entirely covered by the tab’s background. Now I realized the Z order was at fault.

Child windows are always created at the bottom of the Z order, i.e. below your tab control. Your code never moves them upwards. The tabs only ever displayed by pure luck/overdraw, producing the artifacts you observe. Fix the Z order and there will be no more artifacts.

Largess answered 19/10, 2019 at 0:59 Comment(4)
Is has been 4 years since I had this problem, I have no access to the XP machine anymore. Although I can not test your solution anymore, I have upvoted your answer. If I somehow manage to test it, and it works, I will officially accept it. Thank you for your effort! :)Bentwood
Alright! I initially attributed the error to WS_CLIPCHILDREN, but more testing revealed that it is the Z order of the windows and using WS_CLIPCHILDREN is just one way for the problem to manifest itself (WS_EX_COMPOSITED being the other one). I updated the post accordingly.Largess
Now when you mentioned it, I have also experienced that problem. I remember it clearly even to this day, because it was so strange... I need to spare some time to test your suggestion, as I do not do WinAPI for a long time and it is busy at work. Thank you so much for the effort, I forgot to tell you that (for now) I have upvoted your answer. As soon as I get some time to test this myself (and if your solution works) I will officially accept your answer and award bounty (I think you earned it). Regards.Bentwood
No hurry. I'm aware this is an old question and I primarily answered for others who may have the same problem and land here after searching the web (possibly including my future self) :) Best regards!Largess

© 2022 - 2024 — McMap. All rights reserved.