Erwin Kloibhofer
Erwin Kloibhofer

Reputation: 63

AnimateWindow() incorrectly draws background on high dpi (Win10)

I am creating a simple Win32/MFC application with a main window, and a child-window that uses AnimateWindow() to show (slide up) and hide (slide down) the window. When running the application on 100% dpi scaling, everything behaves normally.

Child window shown

I have overridden WM_ERASEBKGND to render a random red color, to demonstrate the effect. As the window slides-down (hide) on every "step" of the animation the "update rectangle" of the background is repainted, exactly where the child-window disappeared, and the background is to become visible again.

Child window retracted with the background re-drawn with random red colors

However, when changing the dpi-scaling via Windows Settings (in this case to 125%), an incorrect area is redrawn. It appears as if the area passed into OnEraseBkgnd() is still using the 100% dpi-scaling size, but also the position is off: it appears as if the x/y position of the upper-left corner is passed in screen-space instead of client-space. So the redrawn area looks different, depending on where the on the screen the window is positioned.

Update-rectangle with wrong size and position

The white area is where the child window was actually positioned, and where the redraw of the background should actually have happened.

I have confirmed this effect on Win10 (1803 and 1809) and on Win8.1 Is this a bug in the OS, or is there something I can do to avoid the problem - other than not using AnimateWindow()? ShowWindow() (with SW_SHOW or SW_HIDE) works just fine, btw.

Update: added the full source-code to reproduce the issue. The problem occurs when using no dpi-aware manifest at all, but it also occurs when using <gdiScaling xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">true</gdiScaling>

class CDialogTestApp : public CWinApp
{
    virtual BOOL InitInstance();
};

CDialogTestApp theApp;

class CAboutDlg : public CDialogEx
{
public:
    CAboutDlg() : CDialogEx(IDD_ABOUTBOX) {}
};

class CDialogTestDlg : public CDialogEx
{
public:
    CDialogTestDlg(CWnd* pParent = nullptr) : CDialogEx(IDD_DIALOGTEST_DIALOG, pParent) {}

protected:
    virtual BOOL OnInitDialog();
    afx_msg BOOL OnEraseBkgnd(CDC* pDC);
    afx_msg void OnLButtonUp(UINT nFlags, CPoint point);

    DECLARE_MESSAGE_MAP()

private:
    CAboutDlg mDialog;
};

BEGIN_MESSAGE_MAP(CDialogTestDlg, CDialogEx)
    ON_WM_ERASEBKGND()
    ON_WM_LBUTTONUP()
END_MESSAGE_MAP()

BOOL CDialogTestApp::InitInstance()
{
    CWinApp::InitInstance();

    CDialogTestDlg dlg;
    m_pMainWnd = &dlg;
    INT_PTR nResponse = dlg.DoModal();
    return FALSE;
}

BOOL CDialogTestDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    mDialog.Create(IDD_ABOUTBOX, this);
    return TRUE;
}

BOOL CDialogTestDlg::OnEraseBkgnd(CDC* pDC)
{
    COLORREF color = RGB(rand() & 255, 20, 40);

    CRect rect;
    GetClipBox(pDC->m_hDC, &rect);     // retrieve the update-rectangle

    CBrush brush(color);
    FillRect(pDC->m_hDC, &rect, (HBRUSH)brush.m_hObject);

    return TRUE;
}

void CDialogTestDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
    if (mDialog.IsWindowVisible())
    {
        mDialog.AnimateWindow(200, AW_HIDE | AW_SLIDE | AW_VER_POSITIVE);
    }
    else
    {
        mDialog.SetWindowPos(&CWnd::wndTop, 0, 50, 0, 0, SWP_NOSIZE);
        mDialog.AnimateWindow(200, AW_ACTIVATE | AW_SLIDE | AW_VER_NEGATIVE);
    }

    CDialogEx::OnLButtonUp(nFlags, point);
}

Upvotes: 1

Views: 455

Answers (0)

Related Questions